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
- The scheme is
Bearer, followed by one space and the key. No quotes, no trailing characters. - A key is
rai_test_orrai_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 anAuthorizationheader 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
| Binding | Effect |
|---|---|
| One workspace | Every request acts in that workspace. Another workspace's ids answer resource_not_found. |
| One mode | rai_test_ keys see test-mode data only, rai_live_ keys live data only. The other mode's ids answer resource_not_found. |
| Its scopes | Chosen when the key is created; see API keys. |
| Its creator | The key never does more than the member who created it may do now. |
| An expiry | Live 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.
- The key was never given the scope:
insufficient_scope. - The key has the scope but its creator's role no longer allows it, or the creator left the workspace:
insufficient_permissions.
Both are 403 and carry details.required_scopes:
JSON
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
Originheader (which browsers send) answerslive_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
authentication_required(401): NoAuthorizationheader, a scheme other thanBearer, or an empty token.invalid_api_key(401): Not a valid RelationsAI API key.api_key_expired(401): The key expired, or its rotation grace period ended.api_key_revoked(401): The key was revoked.legacy_api_key_not_supported(401): Ansk_live_key.insufficient_scope(403): The key lacks the operation's scope.insufficient_permissions(403): The key's creator no longer holds the permission, or left the workspace.api_access_not_enabled(403): The workspace is not enabled for the API.ip_not_allowed(403): The request's address is not on the workspace IP allowlist.live_key_not_allowed_in_browser(403): A live key sent from a browser.service_unavailable(503): A check could not run; retry afterRetry-After.
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.