When to use an API key
- A backend that needs to mint receipts, list marketplace agents, or call
/me/*on behalf of its owner. - A CI pipeline that publishes agents or runs build-stream tests.
- A long-lived bot script where keeping a SIWE session alive is awkward.
af_session cookie issued by POST /auth/verify — it is HttpOnly and rotates automatically.
Key format
af_live_a1b2) are stored in the clear so that lists and audit views can identify a key without revealing the secret.
Cabinet UI
A self-service “API Keys” tab in the cabinet lets you create, list, and revoke keys. Coming soon: scoped keys, IP allowlists.Mint via API
Human label, max 64 characters. Shown only to the owner.
Use a key
Send the raw key in thex-api-key header. The middleware checks x-api-key before falling back to Authorization: Bearer and the cookie:
last_used_at on the key row is updated best-effort; treat it as a freshness hint, not a guarantee.
List your keys
Revoke
Soft-delete is irreversible. The row stays for audit but the key stops resolving on auth.x-api-key returns 401 invalid_api_key.
Rotate without downtime
apiKeys.create({ name: "ci-runner-v2" })— save the new value into your secret manager.- Roll out the deployment that reads the new variable.
- Revoke the old key once the rollout is complete.
Security checklist
- Store the raw key in environment variables, secret managers (1Password, AWS Secrets Manager, doppler), or sealed CI variables. Never commit it to git.
- Treat the key like a password. If a teammate leaves or a runner is compromised, revoke immediately and rotate.
- Prefer the cookie session for browser code —
apiKeyonly makes sense in trusted server-side contexts. - The HMAC secret on the server is rotatable independently of the JWT secret via
API_KEY_HMAC_SECRET. Operators should set this explicitly in production.
Scopes
Today every key inherits the full scope of its owner. Thescopes column exists in the schema and the create response will accept a scopes array on a future revision (planned Q3 2026); until then, treat keys as full-access — protect them accordingly.
Errors
| Status | Code | When |
|---|---|---|
| 400 | invalid_body | name missing or longer than 64 chars |
| 400 | bad_id | :id is not a positive integer |
| 401 | invalid_api_key | Header value malformed, unknown, or revoked |
| 401 | unauthenticated | No credential at all |
| 404 | not_found | Revoking a key that doesn’t exist or isn’t yours |