Skip to navigation

Credentials

The two kinds of key, and the headers that bind them to a workspace

Bearer authentication

Authenticate every request with a Bearer token:

Authorization: Bearer rray_live_...

A missing, malformed or revoked credential returns 401 auth.unauthorized. Every response carries an RRay-Request-Id header — quote it to support.

The public error catalog (GET /errors and GET /errors/{code}) is the one unauthenticated surface: it is the dereference target of the type URL on every error, so it has to be readable without a key.

Two kinds of credential

CredentialLooks likeWhere it comes fromScope
Workspace keyrray_live_... / rray_test_...Dashboard → KeysBound to exactly one workspace
Platform keyaccount-level tokenDashboard → Settings → APIActs for the whole account

A workspace key is the recommended send credential. It carries its workspace, so it never names one per request. The secret is shown once at creation; we store only a peppered hash. Revoking a key takes effect immediately.

A platform key acts for the whole account: it can send mail and manage account-level resources (workspaces, billing limits, keys). Because it is not tied to a workspace, it must name the target workspace on every operation that acts in one.

The workspace header

RRay-Workspace-Id selects the workspace for a single-workspace operation: sending, listing messages, reading one message or its body. Proprietary headers are RRay--prefixed; there are no X- headers in this API.

CredentialRRay-Workspace-IdOutcome
Workspace keyomittedRouted to the key’s own workspace
Workspace keysent400 validation.workspace_header_invalid
Platform keysent, owned by youRouted to that workspace
Platform keyomitted400 validation.workspace_header_required
Platform keynot one of yours404 workspace.not_found

A workspace key rejects the header rather than ignoring it — even when the value matches its own workspace. Silently accepting it would let you believe the header was routing somewhere it never was.

{
"type": "https://api.rray.app/errors/validation.workspace_header_invalid",
"title": "Invalid workspace header",
"status": 400,
"code": "validation.workspace_header_invalid",
"category": "validation",
"detail": "The workspace header does not match the grammar this endpoint expects.",
"request_id": "36661076a55a4275bb6acc7714494f1f",
"instance": "/v1/messages",
"invalid_params": [
{
"field": "RRay-Workspace-Id",
"message": "Workspace headers are not accepted with a workspace-scoped API key — the key already pins its workspace.",
"kind": "workspace_header_invalid"
}
]
}

A foreign or unknown workspace id always returns workspace.not_found — we never reveal whether an id exists under another account. The header rules, including the plural RRay-Workspace-Ids on GET /v1/stats, are laid out per operation in Workspaces.

Which operations need the header

OperationRRay-Workspace-Id
POST /v1/messages, POST /v1/messages/batchRequired with a platform key, rejected with a workspace key
GET /v1/messages, GET /v1/messages/{id}, GET /v1/messages/{id}/bodyRequired with a platform key, rejected with a workspace key
GET /v1/statsAs above, or RRay-Workspace-Ids for several workspaces
POST /v1/workspaces, GET /v1/workspacesNot used — these act on the account
GET /v1/usage, GET /v1/billingNot used — these act on the account

Errors you can get from a credential

CodeStatusWhen
auth.unauthorized401No credential, or one that is malformed, expired or revoked
auth.forbidden403A valid credential that this operation does not accept
auth.platform_credential_required403A workspace key on an account-level operation
validation.workspace_header_required400Platform key without a workspace header
validation.workspace_header_invalid400Header sent with a workspace key, or the wrong header for the operation
validation.workspace_header_conflict400Both RRay-Workspace-Id and RRay-Workspace-Ids sent
workspace.not_found404A workspace id that is not one of yours

Keeping keys safe

  • Keys are bearer secrets. Send them from your server, never from a browser or a mobile app.
  • Use rray_test_... for development and rray_live_... for production, so a mistake in one cannot send real mail from the other.
  • Rotate by minting the new key, deploying it, then revoking the old one. Revocation is immediate, so the overlap matters.
  • Minting more keys for a workspace does not raise its rate limit — they share one budget.