# Authentication

Two headers on every request:

```http
X-Api-Key: gai_…
X-Tenant-Id: 3f2a9c1e-7b44-4c0d-9d2f-8a1b6e5c0d31
```

Both are required. The key identifies you, and the workspace header names the workspace
the request acts in. A key is pinned to one workspace, so the two must agree. A mismatch
is refused rather than silently reinterpreted.

## Issue a key

Create a key under **API keys** in your account settings. No endpoint issues, lists or
revokes one: a credential that authenticates the API is not itself issued over the API,
so a leaked key cannot be used to mint another or to widen its own scope.

Only a sha256 digest is stored. The key is shown once, when you create it, and is never
returned again. A lost key is replaced rather than recovered.

## Key permissions

A key acts as the user who created it and **can never do more than that user can**. If
their access is reduced, the key's access is reduced immediately.

`key_permissions` narrows it further, and is how a read-only integration is built:

```json
{ "key_permissions": ["media:view", "media:download", "agent:view"] }
```

An empty array applies no narrowing at all: the key carries everything its owner has.
That is rarely appropriate for an integration.

Each operation in the [reference](/reference) states the permission it requires, so you
can assemble the smallest set that does the job.

## IP allowlists and expiry

| Field | |
|---|---|
| `key_ip_whitelist` | Addresses or CIDR ranges the key may be used from. Ranges matter, because most callers egress from a block rather than a single address. Empty means anywhere. |
| `key_expires` | When the key stops working. Must be in the future. Empty means no expiry. |

Revoke a key from the screen you created it on. Revocation is immediate: the key stops
working on its next request, with no grace period. It cannot be undone, so issue a new
key instead.

## Throttling on failed attempts

Repeated failures from one address are throttled. Expect this when testing key handling.
It is not a new kind of failure in the key itself.

## User authentication

First-party clients authenticate as a signed-in user with a bearer token instead of a
key. The permission model is identical; only the credential differs. If you are building
an integration, use a key.
