# API keys

API keys are created and managed in the RelationsAI platform, under **Settings → API Keys**; the API itself cannot create, rotate or revoke keys. This page covers key types, scopes, expiry, rotation, revocation and how to store keys safely.

**Availability: Console only** Key management happens in the console only.

## Who can manage keys

Workspace owners and admins can create, rotate and revoke keys. Other members cannot see the key list; they ask an admin for a key. A key belongs to the workspace it was created in and to the member who created it (see [Keys follow their creator's role](#keys-follow-their-creators-role)).

## Key types

| Property | Test key | Live key |
| --- | --- | --- |
| Prefix | `rai_test_` | `rai_live_` |
| Mode | Test mode, `"livemode": false` | Live mode, `"livemode": true` |
| Calls | Simulated: never dials anyone | Real calls to real people |
| Charges | Never | Charged to the workspace balance |
| Data | Test-mode objects only | Live objects only |
| Expiry | 30, 90, 180 or 365 days, or never | 30, 90, 180 or 365 days; always expires |
| Browser use | Allowed from browser-based tools | Refused |

A live key sent from browser code is refused with [`live_key_not_allowed_in_browser`](/errors/live_key_not_allowed_in_browser). A key is the prefix followed by 40 letters and digits. Build and test with a test key; create a live key only for the system that will place real calls.

## Get an API key

You create keys in the RelationsAI platform. You need to be an owner or admin of the workspace.

### Step 1: Sign in to the platform
Go to [voice.relationsai.com](https://voice.relationsai.com) and sign in to the workspace the key is for.

### Step 2: Open Settings
Select **Settings** in the side navigation.

### Step 3: Open the API Keys tab
Select the **API Keys** tab. It lists the workspace's keys with their name, environment, permissions, expiry and when each was last used.

### Step 4: Create the key
Select **Create API key**, then:

1. **Name** the key after the system that will use it, for example `CRM sync`, so you know what breaks if you revoke it. Names are 1 to 64 characters.
2. Choose the **Environment**: **Test** to build and try things for free, **Live** for real calls.
3. Choose the **Permissions** (scopes). Read scopes are ticked by default; write scopes, such as **Place calls** (`calls:write`), must be added. Scopes your own role cannot grant are shown as unavailable.
4. Choose when it **Expires**.
5. Select **Create API key**.

### Step 5: Copy and store the key
The full key is shown **once**. Copy it, store it on your server (see [Storing keys safely](#storing-keys-safely)) and confirm that you have stored it. Then send it in the `Authorization` header: `Authorization: Bearer <token>`.

If the tab says the API is not yet enabled for your workspace, keys cannot be created yet: email [support@relationsai.com](mailto:support@relationsai.com) to join the preview. The same keys also appear on the **API & Developers** page, next to webhooks.

After that, the console shows only the key's prefix and last four characters, with its name, environment, permissions, status, who created it and when, its expiry, when and from which address it was last used, and how many requests and errors it had in the last 24 hours.

Check a new key with [`GET /workspace`](/api/workspace#retrieve-the-workspace): `api_key` in the response shows its id, name, environment, effective scopes and expiry.

## Scopes

Grant only what an integration needs. Each operation needs one scope, shown in the [API reference](/api) for that operation. A request without it fails with [`insufficient_scope`](/errors/insufficient_scope).

| Scope | Allows | Default |
| --- | --- | --- |
| `workspace:read` | Read the workspace, the current key and its limits. | Every key |
| `agents:read` | List and retrieve agents and check their readiness. | Yes |
| `numbers:read` | List and retrieve phone numbers. | Yes |
| `calls:read` | List and retrieve calls, with status, outcome, timing and cost. | Yes |
| `calls:write` | Place outbound calls. Live calls are charged to the workspace. | No |
| `transcripts:read` | Read call transcripts, verbatim. | Yes |
| `recordings:read` | Get short-lived links to call recordings. | Yes |
| `analysis:read` | Read call summaries, extracted fields and goal outcome. | Yes |
| `campaigns:read` | List and retrieve campaigns and their recipients. | Yes |
| `campaigns:write` | Create and edit campaigns, add recipients, and start, pause, resume and stop them. Live campaigns are charged. | No |
| `contacts:read` | List and retrieve contacts, including consent and custom fields. | Yes |
| `contacts:write` | Create, update and delete contacts and record consent. | No |
| `dnc:read` | List and look up do-not-call entries. | Yes |
| `dnc:write` | Add numbers to and remove numbers from the do-not-call list. Removals are audited. | No |
| `webhooks:read` | List and retrieve webhook endpoints and their deliveries. | Yes |
| `webhooks:write` | Create, update, delete and test webhook endpoints, rotate their secrets and retry deliveries. | No |

Some typical sets:

| Integration | Scopes |
| --- | --- |
| Reporting or BI export | `calls:read`, `transcripts:read`, `analysis:read`, `campaigns:read` |
| CRM that triggers calls and stores results | `agents:read`, `calls:read`, `calls:write`, `transcripts:read`, `recordings:read`, `analysis:read` |
| Contact and consent sync | `contacts:read`, `contacts:write`, `dnc:read`, `dnc:write` |
| Campaign scheduler | `agents:read`, `campaigns:read`, `campaigns:write`, `contacts:read` |

No key can change workspace settings, compliance settings, members, billing, integrations or other keys: those stay in the console.

## Keys follow their creator's role

A key's **effective scopes** are the scopes it was created with, limited to what the member who created it is allowed to do right now. This is checked on every request, so a change takes effect immediately:

- **The creator's role is reduced.** The key loses the scopes the new role cannot grant. An operation that needs one of them fails with [`insufficient_permissions`](/errors/insufficient_permissions) (an operation that needs a scope the key never had still fails with [`insufficient_scope`](/errors/insufficient_scope)). `GET /v1/workspace` shows the effective scopes.
- **The creator leaves the workspace or is blocked.** Every request with the key fails with [`insufficient_permissions`](/errors/insufficient_permissions).

Plan for this: when someone who created production keys changes role or leaves, have an admin create replacement keys from an account that will stay, deploy them, then revoke the old ones.

## Expiry and rotation

Every live key expires after 30, 90, 180 or 365 days; a test key can also be set to never expire. After the expiry, requests fail with [`api_key_expired`](/errors/api_key_expired). Rotate before the date: `expires_at` from `GET /v1/workspace` tells your monitoring when.

**Rotating** a key in the console creates a replacement with the same name, scopes and environment, shown once like a new key. You choose how long the old key keeps working:

| Grace period | Use when |
| --- | --- |
| Immediately | The old key may have leaked. |
| 1 hour | One service, deployed quickly. |
| 24 hours | Several services, or a slower release process. |
| 7 days | Many systems, or teams that deploy weekly. |

During the grace period both keys work and the old one shows as **Expiring**; afterwards the old key answers [`api_key_expired`](/errors/api_key_expired). A good rotation:

1. Rotate the key in the console with a grace period long enough for your deploy.
2. Put the new secret in your secret manager and deploy it everywhere the old key is used.
3. Check that requests succeed with the new key (`GET /v1/workspace` shows the new `api_key.id`), and that the old key's **Last used** time in the console stops moving.
4. Let the grace period run out, or revoke the old key.

The replacement key belongs to the admin who rotates it, so it follows that person's role from then on.

## Revoking a key

Revoke a key in the console as soon as it may have leaked or is no longer needed. Revocation is immediate and cannot be undone: the next request with the key fails with [`api_key_revoked`](/errors/api_key_revoked), and recording links the key obtained stop working too. If the key was in use, rotate first (with **Immediately** if it leaked) so the replacement is ready.

## Older keys

Keys that start with `sk_live_` were issued before API v1. They predate scopes and test mode, and they keep working where they worked before, but API v1 refuses them with [`legacy_api_key_not_supported`](/errors/legacy_api_key_not_supported). The console lists them as legacy keys and offers **Create a scoped key instead**. They cannot be rotated; create a scoped key, move to it, then revoke the old one.

## Storing keys safely

- Keep keys in a secret manager or in environment variables set by your deployment, read at runtime. The examples in these docs show `<token>` where your key goes.
- Never put a key in source control, client-side code, mobile apps, logs, error reports, analytics, URLs, or support emails. Log the key's id (`key_...`) or the request id instead.
- Use a separate key per system and per environment, so you can rotate or revoke one without touching the others.
- If a key is exposed, rotate it with **Immediately**, then check its recent use in the console.

More in [Security practices](/fundamentals/security-practices).
