# Authentication

> Bearer keys bound to one allocation, key formats, scopes and what each failure returns.

Source: https://cairva.xyz/docs/authentication

The gateway authenticates every request with a bearer key. A key belongs to one allocation, carries one or two scopes, and is the only credential the API accepts.

Base URL: `https://cairva.xyz/v1`

## Send the key

Put the key in the `Authorization` header:

```http
Authorization: Bearer cv_live_XXXXXXXX_...
```

OpenAI SDKs do this for you when you pass the key as `apiKey` (JavaScript) or `api_key` (Python). See [SDKs](https://cairva.xyz/docs/sdks).

## Key format

| Prefix | Issued for |
| --- | --- |
| `cv_live_` | Allocations on live routes |
| `cv_test_` | Allocations on sandbox routes |

A full key looks like `cv_test_AbC12xYz_` followed by a long random secret. The eight characters after the mode are the key's public prefix. That prefix is what the workspace and audit logs show, so you can tell keys apart without exposing the secret.

Cairva stores only a keyed hash of the full key. A lost key can't be recovered, only rotated.

## Scopes

| Scope | Endpoints |
| --- | --- |
| `inference` | `POST /v1/chat/completions`, `GET /v1/models` |
| `usage:read` | `GET /v1/allocations/current`, `GET /v1/usage` |

Give each key the least it needs. A reporting job that reads usage only needs `usage:read` and can't spend your allowance.

## One key, one allocation

Each key is bound to a single allocation. It can only call that allocation's model, spend that allocation's allowance and read that allocation's usage. To use two allocations, use two keys.

## Failures

All authentication failures return `401` with `"type": "authentication_error"` and `"code": "unauthenticated"`. The message tells you which case you hit:

| Message | Cause |
| --- | --- |
| `Missing API key. Send it as: Authorization: Bearer <key>` | No header, or not a `Bearer` header |
| `Malformed API key` | The value doesn't look like a Cairva key |
| `Invalid API key` | No key matches |
| `This API key was revoked` | The key was revoked or rotated |

A valid key without the scope an endpoint needs gets `403 scope_missing`.

```json
{
  "error": {
    "message": "scope_missing: this key lacks the \"usage:read\" scope",
    "type": "invalid_request_error",
    "code": "scope_missing"
  }
}
```

> [!WARNING]
> Anyone holding a key with the `inference` scope can spend your allowance. Keep keys on servers. Don't ship them in browser or mobile code. If a key leaks, rotate it in the workspace. The old key stops working on its next request.
