API keys
Signing in with the CLI, creating keys in the dashboard, and using, verifying, and rotating them.
API keys authenticate external SDK requests to the Cosmo Realtime API. Each key carries one or more scopes that determine which endpoints it can access.
There are two ways to get one. On your own machine, sign in with the cosmo CLI and let the SDKs find the credential themselves — nothing to copy, nothing to paste into your environment. For a server, a CI job, or a key that needs to mint end-user tokens, create one in the dashboard instead.
Sign in with the CLI
curl -fsSL https://platform.askcosmo.ai/docs/install.sh | sh
cosmo initThe installer picks whichever tool is already on your machine; brew install socratic-ai/tap/cosmo, uv tool install cosmo-cli, and pipx install cosmo-cli each do the same thing by hand. The CLI is a tool, not a library, so it installs into its own environment — plain pip install works too, but only inside an activated virtualenv, since a current macOS or Linux Python refuses to install into itself.
cosmo init opens your browser, you pick a workspace, and the CLI writes the resulting key to ~/.cosmo/credentials (mode 0600). The key is named after your machine's hostname, so you can tell which one to revoke later, and it carries the realtime verb scopes (realtime:start/dial/read/logs/delete) plus support:write and user_tokens:mint — so the token route in your own app works off it during local development. Its mints are budgeted and the tokens it mints live at most an hour, which TokenSource refresh makes invisible; production routes use a dashboard provisioning key instead. It expires after 90 days; run cosmo login to replace it (a key from before minting joined these grants can't mint — sign in again). Setup also installs the Cosmo skill for your coding agent — see Set up with the CLI.
cosmo login is the same sign-in on its own, for when you want the credential and nothing else.
Nothing runs in the background afterwards — sign-in is a one-time write, and every later read is a file read.
Check it whenever you're unsure which workspace you're pointed at:
cosmo whoamiSigned in to Acme (acme) — realtime ready.cosmo whoami exits non-zero when the credential won't actually start a session, so cosmo whoami && python agent.py stops before your app does. Add --json for a machine-readable result.
When you're done on a machine, cosmo logout revokes the key server-side and then removes it — not just a local delete.
Keeping work and personal workspaces apart? Every command takes --profile <name>, and the SDKs read the same selection from COSMO_PROFILE — which is what matters, since they run inside your own process where a command-line flag can't reach them. COSMO_CREDENTIALS_FILE moves the file itself, for containers and CI.
Create a key in the dashboard
Reach for this when the CLI's sign-in doesn't fit: a server or CI job with no browser, a key that needs scopes beyond what sign-in grants (chat, connectors, or document access, say), or the dedicated provisioning key a production token-minting server should hold.
- Open platform.askcosmo.ai.
- Go to Developer platform → API keys (
/<workspace>/developer/api-keys). - Click Create API Key.
- Give the key a descriptive name (for example,
voice-app-production). - Select the scopes for the key's job:
- Voice — start sessions (
realtime:start) — the only scope required to start realtime sessions. The other voice verbs (dial,read,logs,delete) have their own checkboxes. - User tokens — mint (
user_tokens:mint) — required to mint end-user JWTs viamintToken. If your app's users connect from browsers or shipped binaries, the key on your token-minting server needs this scope — see End-user credentials.cosmo loginkeys carry it too, budgeted and clamped for the local dev loop; a production minting server holds a dashboard key with only this scope.
- Voice — start sessions (
- Click Create.
- Copy the key immediately — it's shown only once.
cosmo_...Keys begin with cosmo_. Store the value in a secret manager or environment variable — never in source code.
Use a key
After cosmo login, construct the client with no credential at all and let it resolve one — COSMO_API_KEY first, then the credentials file, whose stored origin comes with it:
const client = new RealtimeClient({});client = RealtimeClient()let client = try RealtimeClient()To pass a key explicitly — a dashboard key on a server, say — hand it over as a Bearer credential:
Authorization: Bearer cosmo_...const client = new RealtimeClient({
apiKey: process.env.COSMO_API_KEY,
});client = RealtimeClient(api_key=os.environ["COSMO_API_KEY"])let client = RealtimeClient(
apiKey: ProcessInfo.processInfo.environment["COSMO_API_KEY"]!
)An API key is workspace-scoped and server-side only, and a leaked one is not a quota problem: with realtime:use alone — the minimum a browser key would need — anyone holding it can list every session in the workspace, read any transcript, download any recording, and delete sessions. For browsers and distributed apps, mint a short-lived per-user token on your backend with mintToken and pass that instead — token in TypeScript, .token(jwt) in Swift. Handing a cosmo_… key to that parameter is refused at construction in every SDK: it would authenticate, which is exactly how a key ends up shipped.
Verify a key
GET /api/v1/external/realtime/verify checks a credential without starting a session — no room, no agent, no charge. Use it as a startup check, after a rotation, or as a CI smoke test.
const info = await client.verify();
info.workspace?.slug; // which workspace — confirms you hit the environment you meant
info.scopes; // ['realtime:start', 'realtime:use', ...] — reported after hierarchy expansion
info.canStartSessions; // false -> valid key, missing realtime:start
info.realtimeVoiceAvailable; // false -> no default voice stack configured hereinfo = await client.verify()
info.workspace, info.scopes, info.can_start_sessions, info.realtime_voice_availablelet info = try await client.verify()
info.workspace?.slug, info.scopes, info.canStartSessions, info.realtimeVoiceAvailableA rejected credential raises (VerifyError / VerifyError); a valid but under-scoped one doesn't — that's a field on the result, so the two are never confused.
Minted end-user tokens can call it too. They report the external_user_id they're bound to, and workspace comes back null: a token runs on someone else's device, so it's told what it may do without being told whose workspace it belongs to.
cosmo whoami runs this same check against the stored credential, without a client of your own.
Rotate a key
For a CLI credential, run cosmo login again: it mints the replacement and retires the key it replaces, so there is no window with two live keys and nothing to clean up afterwards.
For a dashboard key:
- Create a new key in the dashboard with the same scopes.
- Update your deployment to use the new key (environment variable or secret manager).
- Verify the new key is working.
- Revoke the old key in the dashboard.
The session-start endpoint returns a short-lived LiveKit room token. Revoking an API key invalidates future session-start calls but doesn't terminate active in-flight sessions.
Follow security practices
- Never commit API keys to source control. Use environment variables or a secrets manager.
- Restrict scope to what you need.
realtime:startis sufficient to run realtime sessions; add the other voice verbs only where the integration uses them, anduser_tokens:mintonly belongs on the key your token-minting server holds. - Rotate keys on team member departure or suspected exposure.
- Use separate keys per environment. Keep production and staging keys independent.