# Authentication and scopes

Authenticate every request with an organization API key sent as a Bearer token, and give each key only the scopes it needs.

Every request to the Vocenya API, and to the platform MCP server, is authenticated with an **organization API key**. There is no user session: the key is the credential. (AI apps can also connect to the platform MCP server with [OAuth](https://vocenya.com/docs/mcp#connect-with-oauth-no-key), where an owner approves the same scopes on a consent screen.)

## Sending the key

Send the key in the `Authorization` header as a Bearer token:

```bash
curl https://vocenya.com/api/public/v1/me -H "Authorization: Bearer $VOCENYA_API_KEY"
```

The key is only read from the `Authorization` header, never from the query string or the request body.

## Live and test keys

| Prefix | Mode | Works on |
| --- | --- | --- |
| `vk_live_` | Live | Your real calls, leads, bookings and chats. Outbound calls ring real people. |
| `vk_test_` | Test | A separate set of test data. Test calls never ring anyone. |

Both kinds are followed by 40 random characters and use the same endpoints, scopes and limits. A key only sees records of its own mode: an id from the other mode returns `404`, and every object carries `livemode`. Test keys are available on every account, even before setup is finished; live keys once the account is set up. See [Test mode](https://vocenya.com/docs/test-mode).

## Who owns a key

Keys belong to your Vocenya **organization**, not to the person who created them, so an integration keeps working when a teammate leaves. Account owners create and revoke keys in the portal under [Developers](https://vocenya.com/app/developers).

- The full key is shown once, at creation. Vocenya keeps only a SHA-256 hash and a short prefix (like `vk_live_a1b2c3d4` or `vk_test_a1b2c3d4`) so you can tell keys apart.
- A key can be revoked at any time; it stops working immediately.
- A key can also carry an expiry date, after which it is refused.
- Each key records when it was last used, shown on the Developers page.

## Scopes

Each key carries a list of scopes. A request to an endpoint whose scope the key lacks is refused with `403` and the code `missing_scope`; the [API reference](https://vocenya.com/docs/reference) shows the scope every endpoint needs.

| Scope | Allows |
| --- | --- |
| `calls:read` | Read calls |
| `leads:read` | Read leads |
| `leads:write` | Create leads |
| `bookings:read` | Read bookings |
| `outbound:write` | Queue outbound AI calls and check their status |
| `dnc:read` | Read the Do Not Call list |
| `dnc:write` | Add to and remove from the Do Not Call list |
| `chats:read` | Read website chats |
| `chats:write` | Reply to and close website chats |
| `webhooks:manage` | Manage webhooks |

A few actions need two scopes:

- Creating a lead with a `callback` (an immediate AI call back) needs `leads:write` **and** `outbound:write`.
- Subscribing a webhook endpoint to an event needs `webhooks:manage` plus the read scope of that event's record, for example `leads:read` for `lead.created`. See [Webhook events](https://vocenya.com/docs/webhook-events).

The same scopes apply to the tools of the [platform MCP server](https://vocenya.com/docs/mcp).

## Errors

| Status | When |
| --- | --- |
| `401` `unauthenticated` | The key is missing, malformed, unknown, expired or revoked. The response carries a `WWW-Authenticate: Bearer` header. |
| `403` `missing_scope` | The key is valid but lacks the scope this endpoint needs. |
| `429` `too_many_failed_attempts` | More than 30 requests a minute with a missing or invalid key from one IP address. Wait for the `Retry-After` seconds. |

## HIPAA mode

For organizations in HIPAA mode, the API and the MCP server never return call transcripts, AI summaries, the reason and details of a lead, or chat messages, and webhooks carry ids only. Call recordings are never available through the API for any account. `GET /me` tells you whether the account is in HIPAA mode (`data.organization.hipaa_mode`).

## Keeping keys safe

- Store keys in environment variables or a secrets manager, never in source control or front-end code.
- Use one key per integration, with only the scopes it needs, so you can revoke one without breaking the others.
- Rotate a key by creating the new one, deploying it, then revoking the old one.
