# Authentication

Create and use API keys, understand the organization and scopes a key covers, and handle authentication errors.

Every request to the Victoria AI API is authenticated with an API key, sent as a Bearer token. A key belongs to one organization, and every request made with it reads and writes that organization's data.

## Create a key

In the Victoria AI app, open **Settings → API Keys** and generate a key. Organization owners and admins can manage keys.

- Keys start with `vk_`.
- The full key is shown once, when you generate it. Victoria AI stores only a hash of the key, so it can't be shown again. Copy it into a secret manager or your deployment's environment variables straight away.
- Give each integration its own key, so you can revoke one without breaking the others.

## Send the key

Put the key in the `Authorization` header of every request, after the word `Bearer` and a single space:

```bash
curl https://api.versionseven.ai/v1/auth/verify \
  -H "Authorization: Bearer $VICTORIA_API_KEY"
```

[`GET /v1/auth/verify`](https://docs.versionseven.ai/api-reference/auth/verify-api-key) needs no scope and returns the organization the key belongs to, which makes it a quick way to check a key.

Keep keys on your server. Don't put them in browser code or mobile apps, where anyone can read them.

## Organizations

A key only ever sees its own organization's data. An ID that belongs to another organization is treated as if it doesn't exist:

- In the path, it answers a not-found error, such as `404 LEAD_NOT_FOUND`.
- In a request body, such as the `stage_id` of a deal, it answers `400`, such as `400 INVALID_STAGE`.

Neither response confirms that the ID exists somewhere else.

## Scopes

Each endpoint requires a scope, made of a resource family and an access level.

| Family | Covers |
| - | - |
| `leads` | Leads and their campaign enrolments |
| `campaigns` | Campaigns, sequences, analytics, queues, webhooks and reference data |
| `crm` | Pipelines and deals |
| `accounts` | Connected sender accounts |

The level is `read` or `write`, and `write` includes `read`: a key with `leads:write` can also call endpoints that need `leads:read`. A key without the scope an endpoint needs answers `403 INSUFFICIENT_SCOPE`.

> **Note:** Keys generated in the Victoria AI app today have every scope, so they can call every endpoint.

| Endpoint | Required scope |
| - | - |
| [`GET /v1/auth/verify`](https://docs.versionseven.ai/api-reference/auth/verify-api-key) | None |
| [`GET /v1/accounts`](https://docs.versionseven.ai/api-reference/accounts/list-accounts) | `accounts:read` |
| [`POST /v1/accounts/connect-link`](https://docs.versionseven.ai/api-reference/accounts/create-connect-link) | `accounts:write` |
| [`POST /v1/leads`](https://docs.versionseven.ai/api-reference/leads/create-lead) | `leads:write` |
| [`GET /v1/leads`](https://docs.versionseven.ai/api-reference/leads/list-leads) | `leads:read` |
| [`GET /v1/leads/{lead_id}`](https://docs.versionseven.ai/api-reference/leads/retrieve-lead) | `leads:read` |
| [`PATCH /v1/leads/{lead_id}`](https://docs.versionseven.ai/api-reference/leads/update-lead) | `leads:write` |
| [`DELETE /v1/leads/{lead_id}`](https://docs.versionseven.ai/api-reference/leads/remove-lead-from-campaign) | `leads:write` |
| [`GET /v1/campaigns`](https://docs.versionseven.ai/api-reference/campaigns/list-campaigns) | `campaigns:read` |
| [`POST /v1/campaigns`](https://docs.versionseven.ai/api-reference/campaigns/create-campaign) | `campaigns:write` |
| [`GET /v1/campaigns/{campaign_id}`](https://docs.versionseven.ai/api-reference/campaigns/retrieve-campaign) | `campaigns:read` |
| [`PATCH /v1/campaigns/{campaign_id}`](https://docs.versionseven.ai/api-reference/campaigns/update-campaign) | `campaigns:write` |
| [`PUT /v1/campaigns/{campaign_id}/sequence`](https://docs.versionseven.ai/api-reference/campaigns/replace-sequence) | `campaigns:write` |
| [`PATCH /v1/campaigns/{campaign_id}/sequence/steps/{step_id}`](https://docs.versionseven.ai/api-reference/campaigns/update-sequence-step) | `campaigns:write` |
| [`GET /v1/campaigns/{campaign_id}/analysis`](https://docs.versionseven.ai/api-reference/campaigns/get-campaign-analysis) | `campaigns:read` |
| [`GET /v1/campaigns/{campaign_id}/queue`](https://docs.versionseven.ai/api-reference/campaigns/get-campaign-queue) | `campaigns:read` |
| [`GET /v1/campaigns/{campaign_id}/preflight`](https://docs.versionseven.ai/api-reference/campaigns/get-campaign-preflight) | `campaigns:read` |
| [`PUT /v1/campaigns/{campaign_id}/senders`](https://docs.versionseven.ai/api-reference/campaigns/assign-senders) | `campaigns:write` |
| [`GET /v1/campaigns/{campaign_id}/ai-fields`](https://docs.versionseven.ai/api-reference/campaigns/list-ai-fields) | `campaigns:read` |
| [`POST /v1/campaigns/{campaign_id}/ai-fields`](https://docs.versionseven.ai/api-reference/campaigns/create-ai-field) | `campaigns:write` |
| [`PATCH /v1/campaigns/{campaign_id}/ai-fields/{field_id}`](https://docs.versionseven.ai/api-reference/campaigns/update-ai-field) | `campaigns:write` |
| [`GET /v1/campaigns/{campaign_id}/responder`](https://docs.versionseven.ai/api-reference/campaigns/get-responder) | `campaigns:read` |
| [`PATCH /v1/campaigns/{campaign_id}/responder`](https://docs.versionseven.ai/api-reference/campaigns/update-responder) | `campaigns:write` |
| [`POST /v1/campaigns/{campaign_id}/preview`](https://docs.versionseven.ai/api-reference/campaigns/preview-campaign) | `campaigns:read` |
| [`GET /v1/campaigns/{campaign_id}/bookings`](https://docs.versionseven.ai/api-reference/campaigns/list-bookings) | `campaigns:read` |
| [`POST /v1/campaigns/{campaign_id}/ab-testing`](https://docs.versionseven.ai/api-reference/campaigns/set-ab-testing) | `campaigns:write` |
| [`POST /v1/campaigns/{campaign_id}/ab-testing/promote`](https://docs.versionseven.ai/api-reference/campaigns/promote-ab-winner) | `campaigns:write` |
| [`GET /v1/campaigns/{campaign_id}/daily-stats`](https://docs.versionseven.ai/api-reference/campaigns/get-daily-stats) | `campaigns:read` |
| [`GET /v1/campaigns/{campaign_id}/step-funnel`](https://docs.versionseven.ai/api-reference/campaigns/get-step-funnel) | `campaigns:read` |
| [`GET /v1/campaigns/{campaign_id}/senders/breakdown`](https://docs.versionseven.ai/api-reference/campaigns/get-sender-breakdown) | `campaigns:read` |
| [`GET /v1/campaigns/{campaign_id}/personalization-quality`](https://docs.versionseven.ai/api-reference/campaigns/get-personalization-quality) | `campaigns:read` |
| [`GET /v1/campaigns/{campaign_id}/ab-cohorts`](https://docs.versionseven.ai/api-reference/campaigns/get-ab-cohorts) | `campaigns:read` |
| [`GET /v1/campaigns/{campaign_id}/webhooks`](https://docs.versionseven.ai/api-reference/campaigns/list-webhooks) | `campaigns:read` |
| [`POST /v1/campaigns/{campaign_id}/webhooks`](https://docs.versionseven.ai/api-reference/campaigns/create-webhook) | `campaigns:write` |
| [`DELETE /v1/campaigns/{campaign_id}/webhooks/{webhook_id}`](https://docs.versionseven.ai/api-reference/campaigns/delete-webhook) | `campaigns:write` |
| [`POST /v1/campaigns/{campaign_id}/webhooks/{webhook_id}/secret`](https://docs.versionseven.ai/api-reference/campaigns/rotate-webhook-secret) | `campaigns:write` |
| [`GET /v1/crm/pipelines`](https://docs.versionseven.ai/api-reference/crm-pipelines/list-pipelines) | `crm:read` |
| [`GET /v1/crm/pipelines/{pipeline_id}`](https://docs.versionseven.ai/api-reference/crm-pipelines/retrieve-pipeline) | `crm:read` |
| [`PATCH /v1/crm/pipelines/{pipeline_id}`](https://docs.versionseven.ai/api-reference/crm-pipelines/update-pipeline) | `crm:write` |
| [`GET /v1/crm/deals`](https://docs.versionseven.ai/api-reference/crm-deals/list-deals) | `crm:read` |
| [`POST /v1/crm/deals`](https://docs.versionseven.ai/api-reference/crm-deals/create-deal) | `crm:write` |
| [`GET /v1/crm/deals/{deal_id}`](https://docs.versionseven.ai/api-reference/crm-deals/retrieve-deal) | `crm:read` |
| [`PATCH /v1/crm/deals/{deal_id}`](https://docs.versionseven.ai/api-reference/crm-deals/update-deal) | `crm:write` |
| [`GET /v1/sequence-templates`](https://docs.versionseven.ai/api-reference/reference/list-sequence-templates) | `campaigns:read` |
| [`GET /v1/webhooks/examples`](https://docs.versionseven.ai/api-reference/reference/list-webhook-examples) | `campaigns:read` |

## Rotate a key

1. Generate a new key in **Settings → API Keys**.
2. Deploy your integration with the new key.
3. Revoke the old key. Requests made with a revoked key answer `401 UNAUTHORIZED`.

If a key is ever exposed, revoke it straight away.

## Authentication errors

| Status | Code | Meaning |
| - | - | - |
| `401` | `UNAUTHORIZED` | The `Authorization` header is missing or malformed, or the key is unknown or revoked. |
| `401` | `API_KEY_EXPIRED` | The key is past its expiry date. |
| `403` | `INSUFFICIENT_SCOPE` | The key doesn't have the scope the endpoint requires. |
| `403` | `ORGANIZATION_DEACTIVATED` | The organization that owns the key has been deactivated. |
| `503` | `AUTH_UNAVAILABLE` | The key couldn't be checked. Retry after a short wait. |

A header counts as malformed unless it's exactly `Bearer`, one space, and the key.
