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_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6

The 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_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6

The 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)
  • ExpiresNever or 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:

  1. Create a new key in the dashboard.
  2. Update every client that was using the old key (env var, MCP config, CLI profile).
  3. Confirm the new key is live for at least one full deploy cycle.
  4. 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 .env ignored 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.

On this page