import { useState } from "react"; import { Exit } from "effect"; import * as AsyncResult from "effect/unstable/reactivity/AsyncResult"; import { useAtomSet, useAtomValue } from "@effect/atom-react"; import { toast } from "sonner"; import { apiKeyWriteKeys } from "../api/reactivity-keys"; import { trackEvent } from "../api/analytics"; import { apiKeysAtom, createApiKey, revokeApiKey } from "../api/account-atoms"; import { Button } from "../components/button"; import { PageContainer, PageHeader } from "../components/page"; import { CopyButton } from "../components/copy-button"; import { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "../components/dialog"; import { Input } from "../components/input"; import { Label } from "../components/label"; // --------------------------------------------------------------------------- // Shared API-keys page. Reads/writes the provider-neutral `/account/api-keys` // surface, so it works identically on cloud (WorkOS) and self-host (Better // Auth). API keys are how a user authenticates the Executor API + MCP endpoint // from scripts/agents (Authorization: Bearer ). // --------------------------------------------------------------------------- type ApiKeySummary = { readonly id: string; readonly name: string; readonly obfuscatedValue: string; readonly createdAt: string; readonly lastUsedAt: string | null; }; type CreatedKey = ApiKeySummary & { readonly value: string }; const formatDate = (value: string | null): string => { if (!value) return "Never"; const date = new Date(value); return Number.isNaN(date.getTime()) ? value : new Intl.DateTimeFormat(undefined, { month: "short", day: "numeric", year: "numeric", }).format(date); }; const defaultApiKeyName = (): string => `API key ${new Intl.DateTimeFormat(undefined, { month: "short", day: "numeric", year: "numeric", }).format(new Date())}`; export function ApiKeysPage() { const result = useAtomValue(apiKeysAtom); const doCreate = useAtomSet(createApiKey, { mode: "promiseExit" }); const doRevoke = useAtomSet(revokeApiKey, { mode: "promiseExit" }); const [createOpen, setCreateOpen] = useState(false); const [name, setName] = useState(""); const [createdKey, setCreatedKey] = useState(null); const [creating, setCreating] = useState(false); const [revokingId, setRevokingId] = useState(null); const handleCreate = async () => { const trimmed = name.trim(); if (!trimmed) return; setCreating(true); const exit = await doCreate({ payload: { name: trimmed }, reactivityKeys: apiKeyWriteKeys }); setCreating(false); trackEvent("api_key_created", { success: Exit.isSuccess(exit) }); if (Exit.isSuccess(exit)) { setCreatedKey(exit.value); setName(""); toast.success("API key created"); return; } toast.error("Failed to create API key"); }; const handleRevoke = async (key: ApiKeySummary) => { setRevokingId(key.id); const exit = await doRevoke({ params: { apiKeyId: key.id }, reactivityKeys: apiKeyWriteKeys }); setRevokingId(null); trackEvent("api_key_revoked", { success: Exit.isSuccess(exit) }); if (Exit.isSuccess(exit)) { toast.success(`Revoked ${key.name}`); return; } toast.error("Failed to revoke API key"); }; const closeCreate = (open: boolean) => { setCreateOpen(open); if (!open) { setName(""); setCreatedKey(null); setCreating(false); } }; return ( { setName(defaultApiKeyName()); setCreateOpen(true); }} > New key } >
Authorization: Bearer <api-key>

API keys work as PATs and have full access to your account.

{AsyncResult.match(result, { onInitial: () => (
Loading API keys...
), onFailure: () => (
Failed to load API keys
), onSuccess: ({ value }) => value.apiKeys.length === 0 ? (

No API keys

Create a key and send it in the Authorization Bearer header.

) : (
Name Created Last used Actions
{value.apiKeys.map((key: ApiKeySummary) => (

{key.name}

{key.obfuscatedValue}

{formatDate(key.createdAt)}

{formatDate(key.lastUsedAt)}

))}
), })} Create API key The key will act as your user in the current organization. {createdKey ? (
trackEvent("api_key_copied", { kind: "value" })} />
trackEvent("api_key_copied", { kind: "bearer_header" })} />

Send this value as a Bearer token. It is only shown once.

) : (
setName(event.target.value)} placeholder="Local CLI" maxLength={80} autoFocus />
)} {!createdKey && ( )}
); }