> ## Documentation Index
> Fetch the complete documentation index at: https://docs.communityhub.cloud/llms.txt
> Use this file to discover all available pages before exploring further.

# API Keys

> Log in with your Data Hub account to view or generate your personal API token.

export const ApiKeyWidget = () => {
  const AUTH0_DOMAIN = 'communityhub-auth.us.auth0.com';
  const AUTH0_CLIENT_ID = 't72IvpRvZDU9L4LedsSJjkCNo1AP5Y7q';
  const AUTH0_AUDIENCE = 'https://communityhub-auth.us.auth0.com/api/v2/';
  const TOKEN_API_BASE = 'https://api.communityhub.cloud/v1';
  const REDIRECT_URI = typeof window !== "undefined" ? window.location.origin + window.location.pathname : "";
  const [sdkReady, setSdkReady] = useState(false);
  const [authClient, setAuthClient] = useState(null);
  const [status, setStatus] = useState("loading");
  const [profile, setProfile] = useState(null);
  const [tokenState, setTokenState] = useState({
    phase: "idle"
  });
  const [initError, setInitError] = useState(null);
  const [copyLabel, setCopyLabel] = useState("Copy");
  const startedRef = useRef(false);
  useEffect(() => {
    if (startedRef.current) return;
    startedRef.current = true;
    let cancelled = false;
    let attempts = 0;
    const waitForSdk = () => {
      if (cancelled) return;
      if (typeof window !== "undefined" && window.createAuth0Client) {
        setSdkReady(true);
        return;
      }
      attempts += 1;
      if (attempts > 100) {
        setInitError("The Auth0 SDK script never loaded. Confirm the script tag is present on this page and not blocked by CSP (check the browser console).");
        return;
      }
      setTimeout(waitForSdk, 100);
    };
    waitForSdk();
    return () => {
      cancelled = true;
    };
  }, []);
  useEffect(() => {
    if (!sdkReady) return;
    let cancelled = false;
    const init = async () => {
      try {
        const authorizationParams = {
          redirect_uri: REDIRECT_URI
        };
        if (AUTH0_AUDIENCE) authorizationParams.audience = AUTH0_AUDIENCE;
        const client = await window.createAuth0Client({
          domain: AUTH0_DOMAIN,
          clientId: AUTH0_CLIENT_ID,
          authorizationParams,
          cacheLocation: "localstorage",
          useRefreshTokens: true
        });
        if (cancelled) return;
        setAuthClient(client);
        const query = window.location.search;
        if (query.includes("code=") && query.includes("state=")) {
          try {
            await client.handleRedirectCallback();
          } catch (callbackErr) {
            console.error("Auth0 redirect callback failed", callbackErr);
          }
          window.history.replaceState({}, document.title, window.location.pathname);
        }
        const authed = await client.isAuthenticated();
        if (cancelled) return;
        setStatus(authed ? "logged-in" : "logged-out");
        if (authed) {
          const user = await client.getUser();
          if (!cancelled) setProfile(user || null);
        }
      } catch (err) {
        console.error("Auth0 client init failed", err);
        if (!cancelled) {
          setInitError("Couldn't reach Auth0. Double-check the domain and client ID configured in this widget.");
          setStatus("logged-out");
        }
      }
    };
    init();
    return () => {
      cancelled = true;
    };
  }, [sdkReady]);
  useEffect(() => {
    if (status !== "logged-in" || !authClient) return;
    let cancelled = false;
    const check = async () => {
      setTokenState({
        phase: "checking"
      });
      try {
        const accessToken = await authClient.getTokenSilently();
        const res = await fetch(TOKEN_API_BASE + "/user/token", {
          method: "GET",
          headers: {
            Authorization: "Bearer " + accessToken
          }
        });
        if (res.status === 404) {
          if (!cancelled) setTokenState({
            phase: "none"
          });
          return;
        }
        if (!res.ok) throw new Error("Request failed with status " + res.status);
        const data = await res.json();
        if (!cancelled) {
          setTokenState({
            phase: "masked",
            masked: data.masked,
            createdAt: data.createdAt
          });
        }
      } catch (err) {
        console.error("Token lookup failed", err);
        if (!cancelled) {
          setTokenState({
            phase: "error",
            message: err.message || String(err)
          });
        }
      }
    };
    check();
    return () => {
      cancelled = true;
    };
  }, [status, authClient]);
  const login = useCallback(() => {
    if (!authClient) return;
    authClient.loginWithRedirect({
      authorizationParams: {
        redirect_uri: REDIRECT_URI
      }
    });
  }, [authClient]);
  const logout = useCallback(() => {
    if (!authClient) return;
    setProfile(null);
    setTokenState({
      phase: "idle"
    });
    authClient.logout({
      logoutParams: {
        returnTo: REDIRECT_URI
      }
    });
  }, [authClient]);
  const callTokenEndpoint = useCallback(async mode => {
    if (!authClient) return;
    setTokenState(prev => ({
      ...prev,
      phase: "working"
    }));
    try {
      const accessToken = await authClient.getTokenSilently();
      const res = await fetch(TOKEN_API_BASE + "/user/token" + (mode === "rotate" ? "/rotate" : ""), {
        method: "POST",
        headers: {
          Authorization: "Bearer " + accessToken,
          "Content-Type": "application/json"
        }
      });
      if (!res.ok) throw new Error("Request failed with status " + res.status);
      const data = await res.json();
      setTokenState({
        phase: "revealed",
        plaintext: data.token,
        masked: data.masked,
        createdAt: data.createdAt
      });
    } catch (err) {
      console.error("Token " + mode + " failed", err);
      setTokenState({
        phase: "error",
        message: err.message || String(err)
      });
    }
  }, [authClient]);
  const generateToken = useCallback(() => callTokenEndpoint("create"), [callTokenEndpoint]);
  const regenerateToken = useCallback(() => {
    const ok = typeof window !== "undefined" && window.confirm("Regenerating replaces your current token. Anything already using the old one will stop working immediately. Continue?");
    if (!ok) return;
    callTokenEndpoint("rotate");
  }, [callTokenEndpoint]);
  const copyToken = useCallback(() => {
    if (tokenState.phase !== "revealed") return;
    navigator.clipboard.writeText(tokenState.plaintext).then(() => {
      setCopyLabel("Copied!");
      setTimeout(() => setCopyLabel("Copy"), 1500);
    }).catch(err => {
      console.error("Clipboard write failed", err);
    });
  }, [tokenState]);
  const buttonClass = "px-4 py-2 rounded-lg text-sm font-medium bg-zinc-950 text-white dark:bg-white dark:text-zinc-950 hover:opacity-90 disabled:opacity-50 disabled:cursor-not-allowed";
  const secondaryButtonClass = "px-4 py-2 rounded-lg text-sm font-medium border border-zinc-950/20 dark:border-white/20 hover:bg-zinc-950/5 dark:hover:bg-white/5";
  return <div className="p-5 border dark:border-white/10 rounded-2xl not-prose space-y-4">
      {initError && <div className="text-sm text-red-600 dark:text-red-400">{initError}</div>}

      {status === "loading" && <div className="text-sm text-zinc-950/60 dark:text-white/60">Loading…</div>}

      {status === "logged-out" && <div className="space-y-3">
          <p className="text-sm text-zinc-950/70 dark:text-white/70">
            Log in with your Data Hub account to view or generate your API token.
          </p>
          <button className={buttonClass} onClick={login} disabled={!authClient}>
            Log in
          </button>
        </div>}

      {status === "logged-in" && <div className="space-y-4">
          <div className="flex items-center justify-between">
            <p className="text-sm text-zinc-950/70 dark:text-white/70">
              Signed in{profile && profile.name ? " as " + profile.name : ""}
              {profile && profile.email ? " (" + profile.email + ")" : ""}
            </p>
            <button className={secondaryButtonClass} onClick={logout}>
              Log out
            </button>
          </div>

          {tokenState.phase === "checking" && <div className="text-sm text-zinc-950/60 dark:text-white/60">
              Checking for an existing token…
            </div>}

          {tokenState.phase === "none" && <div className="space-y-3">
              <p className="text-sm text-zinc-950/70 dark:text-white/70">
                You don't have an API token yet.
              </p>
              <button className={buttonClass} onClick={generateToken}>
                Generate API token
              </button>
            </div>}

          {tokenState.phase === "working" && <div className="text-sm text-zinc-950/60 dark:text-white/60">Working…</div>}

          {tokenState.phase === "masked" && <div className="space-y-3">
              <div className="font-mono text-sm bg-zinc-950/5 dark:bg-white/10 rounded-lg px-3 py-2">
                {tokenState.masked}
              </div>
              <p className="text-xs text-zinc-950/60 dark:text-white/60">
                {tokenState.createdAt ? "Created " + tokenState.createdAt + ". " : ""}
                For security, the full token only shows once. Regenerate to get a new one you
                can copy.
              </p>
              <button className={secondaryButtonClass} onClick={regenerateToken}>
                Regenerate
              </button>
            </div>}

          {tokenState.phase === "revealed" && <div className="space-y-3">
              <div className="flex items-center gap-2">
                <div className="font-mono text-sm bg-zinc-950/5 dark:bg-white/10 rounded-lg px-3 py-2 flex-1 overflow-x-auto">
                  {tokenState.plaintext}
                </div>
                <button className={secondaryButtonClass} onClick={copyToken}>
                  {copyLabel}
                </button>
              </div>
              <p className="text-xs text-amber-600 dark:text-amber-400">
                Copy this now — it won't be shown in full again. Paste it into the Authorization
                field wherever the docs ask for your token.
              </p>
              <button className={secondaryButtonClass} onClick={regenerateToken}>
                Regenerate
              </button>
            </div>}

          {tokenState.phase === "error" && <div className="space-y-2">
              <p className="text-sm text-red-600 dark:text-red-400">
                {tokenState.message || "Something went wrong talking to the token service."}
              </p>
              <button className={secondaryButtonClass} onClick={generateToken}>
                Try again
              </button>
            </div>}
        </div>}
    </div>;
};

Everything else in these docs, including the full [API Reference](/api-reference), is open to anyone —
no login required. Log in below only if you want to call the API yourself and need a personal token.

<ApiKeyWidget />

## Using your token

Once you have a token, paste it into the Authorization field on any endpoint's "Try it" panel, or
into the token field on the [Filter Variables playground](/api-reference/query/variables-playground).

Treat this token like a password. Anyone with it can act as you against the Data API. If you think
it's been exposed, come back here and regenerate it — the old one stops working immediately.

<Note>
  This page works without logging in — you'll just see a "Log in" button. Nothing on the rest of
  the site requires authentication.
</Note>
