
# API key management

[Getting started → API keys](../getting-started/api-keys) walks through
creating a key. This page is the operator reference: every option a
virtual key can carry, when to use it, and how to retire keys safely.

## Anatomy of a virtual key

A virtual key is a token with metadata attached. The token authenticates
the request; the metadata controls what that request is allowed to do.

The metadata you can attach:

| Field | Purpose |
|-------|---------|
| `name` | Human label, shown in usage history. |
| `models` | Allowlist of model IDs. Empty = all models. |
| `region` | Pin requests through this key to a region (e.g. `eu-west`). |
| `daily_credit_limit` | Max credits this key can spend per UTC day. |
| `monthly_credit_limit` | Max credits per UTC calendar month. |
| `expires_at` | Optional auto-expiry timestamp. |
| `prefer_low_carbon` | When set, biases auto-routing on this key toward lower-grid-intensity providers. |
| `enabled` | Toggle without deleting. |

All of these can be edited from **Dashboard → Keys → key name → Edit**.
Edits take effect on the next request, no caching delay.

## Scoping patterns

A few patterns we see often:

**One key per environment per service.** The most common shape:
`prod-api`, `staging-api`, `local-dev`. Each one is allowlisted to the
models that environment actually uses.

**One key per third-party integration.** If you give a token to a
desktop client (ChatBox, Cline, Claude Code, …), put it in its own
key with a daily limit. The blast radius of a leaked key is then
"yesterday's daily limit" instead of "everything."

**One key per researcher / experiment.** When the same project runs
multiple lines of experiments, separate keys make the dashboard's
top-models chart instantly readable per experiment.

**One key per region requirement.** When a particular workload must
stay in `eu-west`, set the region on the key rather than on every
request. The constraint travels with the key and a misconfigured client
can't accidentally send the request elsewhere.

## Limits and what happens when they're hit

When a request would push a key over its `daily_credit_limit` or
`monthly_credit_limit`, the gateway returns `429 Too Many Requests`
with an explanatory body:

```json
{
  "error": {
    "type": "rate_limited",
    "code": "key_daily_limit_exceeded",
    "message": "API key 'prod-api' has reached its daily credit limit (5.00).",
    "param": null
  }
}
```

The response includes `Retry-After` indicating when the limit resets
(midnight UTC). Bumping a limit takes effect immediately for subsequent
requests.

## Rotation

The rotation pattern that does not require zero-downtime support from
the gateway:

1. **Create** a second key with the same scope.
2. **Deploy** it everywhere the old key was used (config, secret
   manager, CI variables).
3. **Verify** the new key is in use by watching the usage history; the
   old key's request rate should drop to zero.
4. **Delete** the old key.

Aim to rotate at least every 90 days, and immediately after any of
the events listed in [Getting started → API keys](../getting-started/api-keys#rotate-or-revoke).

## Revocation

A revoked key returns `401 Unauthorized` on the next request. There is
no warning, no grace period, no caching delay. Revocation cannot be
undone — if you revoke the wrong key, create a new one.

The dashboard preserves the key's usage history after revocation. The
token itself is discarded.

## Audit trail

Every key creation, edit, rotation, and revocation produces an entry
in **Settings → Audit log**. The log records: who acted, when, on
which key, and what changed. Export as CSV for retention in your own
audit pipeline.
