API Keys
Proxy keys (sk-rc-…) authenticate /v1/* traffic. Each key is scoped to exactly one owner — you, or one organization — fixed at mint time.
A proxy key (sk-rc-…) authenticates all traffic on /v1/*. Each key is
scoped to exactly one owner — your personal account, or one organization —
chosen when the key is minted and fixed thereafter.
Three credential types — don't mix them
The API accepts three kinds of credentials, and each route accepts exactly one.
| Kind | Header | What it grants |
|---|---|---|
Proxy key (sk-rc-…) | Authorization: Bearer sk-rc-… or X-API-Key: sk-rc-… | Forwarded LLM traffic on /v1/*. |
| Stytch session JWT | Authorization: Bearer <jwt> | Account management under /me/* and /auth/apikeys. Held by the dashboard in a cookie. |
| Admin token | X-Admin-Token: <token> | /admin/* routes. Out-of-band; not used by the dashboard. |
Never authorize /v1/* with a Stytch JWT or admin token. Never put a proxy key
on /me/*.
Minting a key
In the dashboard, Keys → New key, then:
- Give it a name (for you; any string).
- Pick an Owner —
Personal(default), or one of the organizations you belong to. - Click Create.
The full key (sk-rc-…) is shown once. Copy it immediately — the dashboard
won't display it again, and there's no way to recover it.
API equivalent
curl -X POST https://api.ada.ai/auth/apikeys \
-H "Authorization: Bearer $JWT" \
-H "Content-Type: application/json" \
-d '{"name": "Production"}'
# → { "id": 42, "name": "Production", "prefix": "sk-rc-...", "api_key": "sk-rc-<full>", "created_at": "..." }To scope the key to an organization, include organization_id:
curl -X POST https://api.ada.ai/auth/apikeys \
-H "Authorization: Bearer $JWT" \
-H "Content-Type: application/json" \
-d '{"name": "Acme prod", "organization_id": 7}'The caller must already be a member of that org. A non-member and a nonexistent
org both return 403 not a member of that organization — deliberately
identical, so the response can't be used to probe which org IDs exist.
Key scoping
A key's owner is fixed at mint time. There is no way to change it afterward — mint a new key if you need to switch owners.
- A personal key resolves only against your personal upstreams.
- An org key resolves only against that org's upstreams — never your personal ones, and never another org's.
- A single key uses all of its owner's enabled upstreams. There's no per-upstream scoping in v1: to separate traffic within one owner, mint separate keys — they share the same upstream pool.
See Organizations for team-owned upstreams.
Listing your keys
curl https://api.ada.ai/auth/apikeys \
-H "Authorization: Bearer $JWT"Returns metadata only — id, name, prefix, scope, timestamps — never the
plaintext or a hash.
Rotating a key
In the dashboard: Keys → the key → Rotate. This generates a new value (shown once) and immediately invalidates the old one — across every pod, with no grace period.
No grace period
Rotate during a deploy window, not mid-traffic. The old key stops working the
instant you rotate, so any client still holding it will get 401.
There is no API equivalent that returns the new plaintext to a non-dashboard caller; rotate from the dashboard or mint a fresh key.
Revoking a key
curl -X POST https://api.ada.ai/auth/apikeys/42/revoke \
-H "Authorization: Bearer $JWT"Permanent, no grace period — the key's cached auth entry is invalidated immediately on every pod. Organization admins can also revoke a member's org-scoped key from the org's Keys tab.
Building a CLI?
The dashboard's "New key" flow assumes you can copy/paste from a browser. For CLI tools that need to bootstrap a key non-interactively, Ada AI exposes a browser-assisted device flow — see CLI authentication.