# Management API Keys

> Programmatic credentials for managing your AnyRouter account — keys, presets, BYOK, and credits.


# Management API Keys

Management keys (`ak_…`) let you administer your AnyRouter account from scripts, CI pipelines, or other tools — without your dashboard password. They authenticate the management plane: LLM key CRUD, presets, BYOK providers, credits, and your own management keys.

::endpoint{method="POST" path="/api/v1/management-keys"}

:::note
A management key is authenticated by sending it as a **Bearer token** (`Authorization: Bearer ak_live_…`). Management keys are **not** for LLM inference — to call `/api/v1/chat/completions` or `/api/v1/messages`, use an **LLM key** (`sk-ar-…`) from [/dashboard/keys](https://anyrouter.dev/dashboard/keys).
:::

## Two kinds of keys

| Key type | Prefix | Purpose | Where to manage |
|---|---|---|---|
| **LLM API keys** | `sk-ar-` | Send chat, messages, embeddings, image, and responses requests | [/dashboard/keys](https://anyrouter.dev/dashboard/keys) |
| **Management API keys** | `ak_` | Programmatically manage your account: list/create/revoke LLM keys, configure BYOK, read credits | [/dashboard/settings#keys](https://anyrouter.dev/dashboard/settings#keys) |

## Request

Create a key from the dashboard (**Create key** → name it → select scopes → copy the secret shown **once**), or programmatically from a session-authed request:

| Field | Type | Required | Description |
|---|---|---|---|
| `name` | string | Yes | Human-readable key name. |
| `description` | string | No | Free-text note (e.g. what uses the key). |
| `scopes` | string[] | Yes | The scopes this key grants (see below). |
| `expires_at` | string | No | ISO 8601 expiration timestamp. |

### Scopes

Scopes follow the `action:resource` pattern. `write:X` implicitly grants `read:X`. Grant the smallest set a key needs — a leaked `read:credits`-only key cannot create LLM keys or change BYOK.

| Scope | Grants access to |
|---|---|
| `read:llm-keys` | `GET /api/v1/keys`, `GET /api/v1/key` |
| `write:llm-keys` | `POST /api/v1/keys`, `PATCH /api/v1/keys/{hash}`, `DELETE /api/v1/keys/{hash}` |
| `read:presets` | `GET /api/v1/presets`, `GET /api/v1/presets/{slug}` |
| `write:presets` | `POST /api/v1/presets`, `PATCH /api/v1/presets/{slug}`, `DELETE /api/v1/presets/{slug}` |
| `read:byok` | `GET /api/v1/auth/byok/...` |
| `write:byok` | `POST/PATCH/DELETE /api/v1/auth/byok/...` |
| `read:credits` | `GET /api/v1/credits` |
| `read:analytics` | `GET /api/v1/analytics/*` (read-only; there is no write scope) |
| `read:management-keys` | `GET /api/v1/management-keys` |
| `write:management-keys` | `POST /api/v1/management-keys`, `DELETE /api/v1/management-keys/{id}` |
| `read:security` | `GET /api/v1/security/keys`, `GET /api/v1/security/ip-allowlist` |
| `write:security` | `POST /api/v1/security/ip-allowlist`, `DELETE /api/v1/security/ip-allowlist/{id}` |

### Other routes

| Method & path | Purpose |
|---|---|
| `GET /api/v1/management-keys` | List your management keys. Pass `?include_invalid=true` to include revoked/expired keys. |
| `DELETE /api/v1/management-keys/{id}` | Revoke a key immediately (optional `revocation_reason` body). |

## Response

`POST /api/v1/management-keys` returns the plaintext `secret` **once** — store it immediately. Revocation is immediate: the next request with a revoked key returns `401`.

## Examples

::::tabs

:::tab{label="Create (session)"}
```bash
curl -X POST https://anyrouter.dev/api/v1/management-keys \
  -H "Content-Type: application/json" \
  --cookie "__session=<your-session-cookie>" \
  -d '{
    "name": "CI deploy bot",
    "description": "Used by the GitHub Actions deploy workflow",
    "scopes": ["read:credits", "write:llm-keys"],
    "expires_at": "2027-01-01T00:00:00Z"
  }'
```
:::

:::tab{label="Authenticate"}
```bash
curl https://anyrouter.dev/api/v1/credits \
  -H "Authorization: Bearer ak_live_..."
```
:::

:::tab{label="List"}
```bash
curl https://anyrouter.dev/api/v1/management-keys \
  -H "Authorization: Bearer ak_live_..."
```
:::

:::tab{label="Revoke"}
```bash
curl -X DELETE https://anyrouter.dev/api/v1/management-keys/<key_id> \
  -H "Authorization: Bearer ak_live_..." \
  -H "Content-Type: application/json" \
  -d '{"revocation_reason": "Rotated for quarterly audit"}'
```
:::

::::

## BYOK keys

A management key with `read:byok` lists your provider keys; `write:byok` adds, rotates, donates, and removes them — the same operations as the BYOK page in the dashboard.

| Method & path | Scope | Purpose |
|---|---|---|
| `GET /api/v1/auth/byok` | `read:byok` | List your keys, grouped by provider. |
| `POST /api/v1/auth/byok` | `write:byok` | Add a key. |
| `PATCH /api/v1/auth/byok/keys/{keyid}` | `write:byok` | Edit or rotate one key in place, keeping its id and its pool donation. |
| `POST /api/v1/byok/donated` | `write:byok` | Donate a key to the shared pool (requires explicit consent in the body). |
| `DELETE /api/v1/byok/donated/{id}` | `write:byok` | Withdraw a donation — the key goes back to serving only your own requests. |

Every key object carries two fields beyond the id, provider, label, and quota:

| Field | Values | Meaning |
|---|---|---|
| `kind` | `byok`, `donated` | `donated` while the key is shared with the community [shared pool](/features/shared-pool), including while it is temporarily paused; `byok` when it serves only your own requests. Read-only — it changes when you donate or withdraw, not by editing the key. |
| `source` | `cli`, `dashboard`, `api` | Which app added the key. `api` is the default when you don't pass one, and for keys added before the field existed. Set once, at creation. |

Pass `source` when adding a key so you can tell one script's keys from another's later:

```bash
curl -X POST https://anyrouter.dev/api/v1/auth/byok \
  -H "Authorization: Bearer ak_live_..." \
  -H "Content-Type: application/json" \
  -d '{"provider_id": "openai", "api_key": "sk-proj-…", "label": "laptop", "source": "cli"}'
```

`source` accepts `cli`, `dashboard`, or `api`; any other value is rejected with `400 source must be 'cli', 'dashboard', or 'api'`. The dashboard sends `dashboard` for keys you add there. The installed `anyr` binary doesn't send `cli` yet, so keys it adds report `api` until a new release ships — set it yourself to tell your scripts' keys apart.

```json
{
  "id": "byok_01JQZ…",
  "provider_id": "openai",
  "label": "laptop",
  "key_preview": "sk-p****7c2",
  "enabled": true,
  "route_coverage": "all",
  "kind": "byok",
  "source": "cli"
}
```

`key_preview` is masked — the first four characters, `****`, and the last three. The key itself is never returned, and keys are listed per workspace.

## Organization-scoped keys

If your account belongs to an organization, keys you create while the org is active are **owned by the org** — any member with access can use them and revocation is org-wide. Personal keys are owned by your user and only authenticate you.

:::warning
Store secrets in a secret manager (1Password, Vault, GitHub Actions Secrets), never in source control. Use the narrowest scope set, set an `expires_at` where possible, rotate on a schedule, and revoke any key you suspect is compromised.
:::

## Errors

| Status | Meaning | Fix |
|---|---|---|
| 401 | Key invalid, revoked, or expired | Create a fresh key from the dashboard. |
| 403 | Key valid but lacks the route's scope | Add the required scope, or use a higher-scoped key. |

## Related

- [API Keys](/api-reference/keys) — the `sk-ar-…` LLM keys these credentials manage
- [Credits API](/api-reference/credits) — read balance with `read:credits`
- [Presets API](/api-reference/presets) — manage presets with `write:presets`
- [BYOK](/features/byok) — configure provider keys with `write:byok`
