# Authentication

Every request to the RelationsAI API carries an API key in the `Authorization` header. This page explains the header format, how the key decides which workspace and mode you act in, and what the API returns when authentication fails. For the exact rules, see [Authentication](/fundamentals/authentication) in API fundamentals.

## Before you begin

You need an API key, created in the RelationsAI platform under **Settings → API Keys** (see [Get an API key](/getting-started/api-keys#get-an-api-key)) and a workspace that is enabled for the API preview.

## Sending your key

Send the key as a Bearer token on every request, over HTTPS:

```http
Authorization: Bearer rai_test_...
```

```bash
curl https://voice-api.relationsai.com/v1/workspace \
     -H "Authorization: Bearer <token>"
```
```js
const response = await fetch("https://voice-api.relationsai.com/v1/workspace", {
  headers: {
    Authorization: "Bearer <token>",
  },
});
console.log(response.status, await response.json());
```
```python

response = requests.get(
    "https://voice-api.relationsai.com/v1/workspace",
    headers={
        "Authorization": "Bearer <token>",
    },
    timeout=30,
)
print(response.status_code, response.json())
```

[`GET /workspace`](/api/workspace#retrieve-the-workspace) is the best first request: it answers `200 OK` with the workspace, the key's name, environment, effective scopes and expiry, the workspace's capabilities and the limits that apply. It needs no scope beyond `workspace:read`, which every key has, and it never changes anything.

```json
{
  "id": "ws_7Hk2Lm9QxR4tB8vN3cZp6W",
  "object": "workspace",
  "livemode": false,
  "name": "Acme Lending",
  "api_key": {
    "id": "key_4Nf8Gt2Hx6Jq9Lm3Pv7Rs1",
    "name": "CRM sync (test)",
    "environment": "test",
    "scopes": ["workspace:read", "agents:read", "numbers:read", "calls:read", "calls:write", "transcripts:read", "recordings:read", "analysis:read"],
    "created_at": "2026-10-20T08:15:00.000Z",
    "expires_at": null
  }
}
```

Only RelationsAI API keys are accepted. Console sign-in sessions, other tokens and older `sk_live_` keys are refused.

## Workspace and mode

A key belongs to exactly one workspace and one mode, and there is no header or parameter to pick either:

- `rai_test_...` keys act in **test mode** (`"livemode": false`): simulated calls, no charges.
- `rai_live_...` keys act in **live mode** (`"livemode": true`): real calls, charged to the workspace.

Objects created with a test key exist only in test mode, and a live key can never read them (and the other way round). An id from the other mode, or from another workspace, answers [`resource_not_found`](/errors/resource_not_found), exactly as if it did not exist. See [Environments](/getting-started/environments).

## Scopes

Each key carries scopes such as `calls:read` or `campaigns:write`, chosen when it is created. Each operation needs one scope, shown in the API reference. A request outside the key's scopes fails with [`insufficient_scope`](/errors/insufficient_scope), and `details.required_scopes` says which scope was missing. See [API keys](/getting-started/api-keys#scopes) for the full list.

A key can never do more than the member who created it is allowed to do **right now**. If that member's role changes, the key's effective scopes shrink with it; if the member leaves the workspace, the key stops working. Both cases answer [`insufficient_permissions`](/errors/insufficient_permissions).

## Keep keys on your server

API keys are secrets. Use them only from server-side code: never in a web page, a mobile app, a browser extension or anything else you ship to users.

The API enforces this for live keys: a request that carries an `Origin` header (which browsers add) with a live key is refused with [`live_key_not_allowed_in_browser`](/errors/live_key_not_allowed_in_browser), before the key is even checked. Test keys can be used from browser-based tools. If your workspace has an IP allowlist in its security settings, API requests from other addresses are refused with [`ip_not_allowed`](/errors/ip_not_allowed).

## Authentication errors

- [`authentication_required`](/errors/authentication_required) (401): No `Authorization` header, a scheme other than `Bearer`, or an empty token. Send `Authorization: Bearer <key>`.
- [`invalid_api_key`](/errors/invalid_api_key) (401): A typo, a truncated key, extra quotes or whitespace, or a credential that is not an API key. Copy the key again from where you stored it; if it is lost, create a new one.
- [`api_key_expired`](/errors/api_key_expired) (401): The key reached its expiry, or its rotation grace period ended. Switch to the replacement key, or create a new key.
- [`api_key_revoked`](/errors/api_key_revoked) (401): An admin revoked the key. Ask an admin for a new key.
- [`legacy_api_key_not_supported`](/errors/legacy_api_key_not_supported) (401): An older `sk_live_` key. Create a scoped key under **Settings → API Keys**.
- [`insufficient_scope`](/errors/insufficient_scope) (403): The key lacks the scope the operation needs. Create a key with the scopes in `details.required_scopes`.
- [`insufficient_permissions`](/errors/insufficient_permissions) (403): The key's creator no longer has that permission, or left the workspace. Ask an admin to create a new key from an account that holds the permission.
- [`api_access_not_enabled`](/errors/api_access_not_enabled) (403): The workspace is not enabled for the API preview. Write to [support@relationsai.com](mailto:support@relationsai.com).
- [`ip_not_allowed`](/errors/ip_not_allowed) (403): The workspace IP allowlist does not include your server. Ask an admin to add your server's address in the console security settings.
- [`live_key_not_allowed_in_browser`](/errors/live_key_not_allowed_in_browser) (403): A live key sent from browser code. Call the API from your server; use a test key in browser tools.

None of these errors did anything: retrying the same request will fail the same way until you fix the cause. If the API cannot check a key because of a temporary problem on our side, it refuses the request with [`service_unavailable`](/errors/service_unavailable) rather than letting it through; retry after `Retry-After` seconds.

## Check that it works

Run the request above. A `200 OK` with the expected `name`, `livemode` and `api_key.scopes` means the key, the workspace and the mode are right. Then continue with the [Quickstart](/getting-started/quickstart).
