Skip to content
RelationsAI
API fundamentals

Authentication

This reference page specifies how requests are authenticated: the header, key formats, how scopes are checked and every authentication and authorisation error. For a task-oriented introduction, see Authentication in Getting started.

Header format

Text
Authorization: Bearer rai_test_... Authorization: Bearer rai_live_...
  • The scheme is Bearer, followed by one space and the key. No quotes, no trailing characters.
  • A key is rai_test_ or rai_live_ followed by 40 letters and digits. The prefix decides the mode.
  • Send the header on every request. There are no sessions, cookies or query-string keys.
  • The only operation that takes no key is GET/recordings/{recording_token}: its URL carries a short-lived token instead, and an Authorization header sent to it is ignored.

Only RelationsAI API keys are accepted. Console sign-in tokens and any other credentials answer invalid_api_key; older sk_live_ keys answer legacy_api_key_not_supported.

What a key is bound to

BindingEffect
One workspaceEvery request acts in that workspace. Another workspace's ids answer resource_not_found.
One moderai_test_ keys see test-mode data only, rai_live_ keys live data only. The other mode's ids answer resource_not_found.
Its scopesChosen when the key is created; see API keys.
Its creatorThe key never does more than the member who created it may do now.
An expiryLive keys always expire; test keys may not.

Scope checks

Each operation requires one scope, listed in the API reference (as x-required-scopes in the OpenAPI document). Every key also has workspace:read.

A request is allowed when the operation's scope is among the key's effective scopes: the scopes the key was created with, limited to what its creator's current role allows. Effective scopes are worked out on every request, and GET/workspace returns them as api_key.scopes.

Both are 403 and carry details.required_scopes:

JSONJSON
{ "error": { "code": "insufficient_scope", "message": "This API key does not have the scope this operation requires.", "details": { "required_scopes": ["calls:write"] }, "request_id": "req_4k7m2qat5xvz3c6vqnwpbdfh", "retry": "no", "doc_url": "https://docs.relationsai.com/errors/insufficient_scope" } }

Some problems are reported before the key is checked: an unknown path or method (404, 405) and a body that is not JSON, is too large or has the wrong content type (400, 413, 415). The fields of the body are validated only after authentication and permission checks, so a key without the scope gets 403 even for a body with invalid fields.

Workspace and network checks

After the key itself, each request is checked against the workspace:

  • API access. During the private preview the workspace must be enabled for the API, else api_access_not_enabled.
  • IP allowlist. If the workspace has an IP allowlist in its security settings, requests from other addresses answer ip_not_allowed.
  • Browsers. A request with a live key and an Origin header (which browsers send) answers live_key_not_allowed_in_browser. This is decided before the key is looked up, so browser code cannot even learn whether a live key is valid. Test keys may be used from browser-based tools; browsers can only read responses from origins the API allows.

Fails closed

If the API cannot complete one of these checks (for example, it cannot look up the key or read the workspace's IP allowlist), it refuses the request with service_unavailable and a Retry-After header. It never lets a request through because a check could not run. Nothing was done; retry later with the same Idempotency-Key.

Errors

Every one of these means the request did nothing: nothing was created, dialled or charged, and nothing was stored under an Idempotency-Key you sent, so you can send the same key again once the cause is fixed. All 401 and 403 codes have retry: "no": retrying unchanged fails the same way.