Authentication
API keys for Ray9 — format, lifecycle, and how every machine client (REST, MCP, CLI) sends them.
Every machine client authenticates with a Ray9 API key. Keys are issued from the dashboard, scoped to your organization, and sent as a Bearer token on every request. There is no signed-request scheme to implement — if you can send an HTTP header, you can call Ray9.
Key format
A Ray9 key looks like this:
rk_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6The rk_ prefix is stable — we keep it constant so credential scanners (GitHub Secret Scanning, GitGuardian, TruffleHog, etc.) match leaked keys reliably. The body is 32 characters from [a-z0-9], giving roughly 165 bits of entropy.
We only ever show you the full key once, at creation. After that, only the last four characters are stored in cleartext (so the dashboard can disambiguate between keys you've named the same thing); the rest is hashed.
Sending the key
Every request sends the key in an Authorization header using the Bearer scheme:
Authorization: Bearer rk_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6The Bearer scheme name is case-insensitive (RFC 7235), so bearer, BEARER, and Bearer all work — but for portability with linters and frameworks, use Bearer. There must be exactly one space between Bearer and the key.
The same header works for:
- Direct REST calls (
curl,fetch, your HTTP client) - MCP-over-HTTP (the streamable-HTTP MCP server)
- The CLI, which sends it on your behalf after
ray9 auth login
You don't need to send anything else — no client ID, no signed timestamp, no nonce.
Issuing a key
Sign in to Ray9, go to Settings → API keys, and click Create key. You'll be asked for two things:
- Name (optional, ≤ 80 chars) — a label so you can tell keys apart in the dashboard. We recommend the environment or service that'll use it:
prod-server,local-dev,ci,claude-desktop-on-laptop. - Expiry — never (default), or one of
1d,7d,30d,60d,90d,1y. Expired keys 401 immediately, and there's no grace period.
The full key shows up exactly once, on the create-confirmation panel. Copy it somewhere safe before closing the dialog. If you lose it, revoke and create a new one — there's no recovery flow by design.
Listing your keys
The keys table shows every active key with:
- Name (or
(unnamed)) - Suffix — the last four characters of the key, matched against your stored copy
- Created — when the key was issued
- Last used — when Ray9 last successfully authenticated this key (updated on every call)
- Expires —
Neveror the absolute expiry timestamp
Soft-deleted (revoked) keys aren't listed. They live on in our audit log but never show up in user-facing surfaces.
Rotating
Rotate keys whenever the operating environment changes — a teammate leaves, a deploy target gets re-imaged, a key may have been logged. The pattern is create new → swap → revoke old, in that order:
- Create a new key in the dashboard.
- Update every client that was using the old key (env var, MCP config, CLI profile).
- Confirm the new key is live for at least one full deploy cycle.
- Revoke the old key.
Don't revoke first. Revoke is instant — anything still using the old key starts 401-ing the moment you click.
Revoking
Click Revoke in the keys table. The key is soft-deleted server-side and starts returning 401 unauthenticated on its next request. There is no undo; if you revoke by accident, create a new key.
We deliberately don't cascade revocation across surfaces — revoking one key doesn't sign you out of the dashboard or invalidate other keys on the same account.
Storing keys safely
- Never commit a key to git. If you do, revoke it before doing anything else; the prefix
rk_is in every public credential scanner's pattern set, and you'll get spam within minutes. - Use environment variables for servers and CI. The standard variable is
RAY9_API_KEY; the MCP server, the CLI, and our own client libraries all read it. - Use the OS keychain for local development. The CLI handles this for you via
ray9 auth login; for ad-hoc curl,direnv+ an.envignored by git is enough. - Use a separate key per environment — at minimum prod / staging / dev, ideally per service. A leaked dev key is a much smaller blast radius than a leaked prod-everything key.
Authentication errors
A bad or missing key returns:
{
"requestId": "req_…",
"error": {
"code": "unauthenticated",
"message": "Missing or invalid credentials"
}
}…with HTTP status 401. The shape never tells you why the key is bad (unknown, expired, revoked all collapse to the same response) — that's intentional, to avoid being a credential-validation oracle.
If you get a 401 on a key you believe is valid, check, in order: that the header reads Authorization: Bearer rk_… exactly, that you're hitting https://api.ray9.ai (not a stale base URL), and that the key isn't expired or revoked in the dashboard.
For the full error envelope, see Errors.