# Keys

> The Keys operations of the Kabu API: scoped, preset-based API keys.

Scoped, preset-based API keys.

Generated from the contract, version `2.0.0-draft.2` ([OpenAPI](/openapi.yaml)). Base URL `https://usekabu.com/api`. Errors are problem details, each [code](/docs/errors) with its own page.

## `POST /v2/api_keys` · Create a key (subset of the caller's scopes)

- **Auth:** an API key, or a signed-in human's token (the Kabu apps).
- **Scopes:** `keys:write`.
- **Signed-in humans:** `admin`.

| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| `Idempotency-Key` | header | string, at most 255 characters | no | Retry-safe key. For messages, sessions and responses it binds to the created object for its life; elsewhere for 24 h. |
| `Kabu-Org` | header | string | no | Select a live membership (default personal org); non-member is 404. Human tokens are live-only. Caller user id is distinct from billing owner. |

**Request body**: [ApiKeyCreate](#schema-ApiKeyCreate).

| Status | Means | Body |
|---|---|---|
| `201` | OK | [ApiKeyWithSecret](#schema-ApiKeyWithSecret) |
| `4XX` | Error | [Problem](/docs/api/discovery#schema-Problem) |
| `5XX` | Error | [Problem](/docs/api/discovery#schema-Problem) |

```sh
curl -X POST "https://usekabu.com/api/v2/api_keys" \
  -H "Authorization: Bearer $KABU_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"name":"…"}'
```

## `GET /v2/api_keys` · List keys

- **Auth:** an API key, or a signed-in human's token (the Kabu apps).
- **Scopes:** `keys:write`.
- **Signed-in humans:** `admin`.

| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| `limit` | query | integer, from 1 to 100 | no |  |
| `after` | query | string | no | Opaque cursor from next_cursor. |
| `Kabu-Org` | header | string | no | Select a live membership (default personal org); non-member is 404. Human tokens are live-only. Caller user id is distinct from billing owner. |

| Status | Means | Body |
|---|---|---|
| `200` | OK | [ApiKeyList](#schema-ApiKeyList) |
| `4XX` | Error | [Problem](/docs/api/discovery#schema-Problem) |
| `5XX` | Error | [Problem](/docs/api/discovery#schema-Problem) |

```sh
curl "https://usekabu.com/api/v2/api_keys" \
  -H "Authorization: Bearer $KABU_API_KEY"
```

## `DELETE /v2/api_keys/{key_id}` · Revoke a key now

- **Auth:** an API key, or a signed-in human's token (the Kabu apps).
- **Scopes:** `keys:write`.
- **Signed-in humans:** `admin`.

| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| `key_id` | path | string | yes | A `key_…` id. |
| `Kabu-Org` | header | string | no | Select a live membership (default personal org); non-member is 404. Human tokens are live-only. Caller user id is distinct from billing owner. |

| Status | Means | Body |
|---|---|---|
| `204` | Done |  |
| `4XX` | Error | [Problem](/docs/api/discovery#schema-Problem) |
| `5XX` | Error | [Problem](/docs/api/discovery#schema-Problem) |

```sh
curl -X DELETE "https://usekabu.com/api/v2/api_keys/$KEY_ID" \
  -H "Authorization: Bearer $KABU_API_KEY"
```

## `POST /v2/api_keys/{key_id}/rotate` · Rotate; the old secret works for grace_seconds

- **Auth:** an API key, or a signed-in human's token (the Kabu apps).
- **Scopes:** `keys:write`.
- **Signed-in humans:** `admin`.

| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| `key_id` | path | string | yes | A `key_…` id. |
| `Idempotency-Key` | header | string, at most 255 characters | no | Retry-safe key. For messages, sessions and responses it binds to the created object for its life; elsewhere for 24 h. |
| `Kabu-Org` | header | string | no | Select a live membership (default personal org); non-member is 404. Human tokens are live-only. Caller user id is distinct from billing owner. |

**Request body** (optional): [ApiKeyRotate](#schema-ApiKeyRotate).

| Status | Means | Body |
|---|---|---|
| `200` | OK | [ApiKeyWithSecret](#schema-ApiKeyWithSecret) |
| `4XX` | Error | [Problem](/docs/api/discovery#schema-Problem) |
| `5XX` | Error | [Problem](/docs/api/discovery#schema-Problem) |

```sh
curl -X POST "https://usekabu.com/api/v2/api_keys/$KEY_ID/rotate" \
  -H "Authorization: Bearer $KABU_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{}'
```

## Schemas

### ApiKeyCreate

| Property | Type | Required | Description |
|---|---|---|---|
| `name` | string, at most 80 characters | yes |  |
| `preset` | [KeyPreset](#schema-KeyPreset) | no | Speaking as an agent and speaking as a human never share a key. `agent` (default): messages:write, messages:read, sessions:write, sessions:read, claims:write, media:write, events:read. `service`: answers:delegate, steer:write, recipients:write, webhooks:write, messages:read, sessions:read, events:read; never messages:write. `admin`: projects, agents, webhooks, keys, org, usage, observability and the agent scopes; never answers:delegate or steer:write. A key holding messages:write together with answers:delegate or steer:write is refused with 400 incompatible_scopes. |
| `scopes` | array of [Scope](#schema-Scope), unique items | no | Overrides the preset; must be a subset of the caller's. messages:write never combines with answers:delegate or steer:write. |
| `project` | [ProjectRef](/docs/api/messages#schema-ProjectRef) | no | A project slug (auto-created on first use) or `prj_…` id. Omit for `default`. A slug that normalises (lowercase, letters and digits only) to an existing project's key names that project, so `My App`, `my-app` and `my_app` are one project. Automatic creation stops at 1,000 live projects per org and mode with `429 quota_exceeded`; existing projects stay usable. Nothing remembers a deleted project (projects.delete): the next message that names its slug creates a fresh one. |
| `agent` | string | no |  |
| `default_to` | [RecipientRef](/docs/api/messages#schema-RecipientRef) | no | `rcp_…`, `ext:<your external id>`, or `me` (the key's `default_to`). |
| `mode` | string, one of `"live"`, `"test"` | no |  |
| `expires_at` | [Timestamp](/docs/api/messages#schema-Timestamp) or null | no |  |

Unknown properties are refused.

### KeyPreset

Speaking as an agent and speaking as a human never share a key. `agent` (default): messages:write, messages:read, sessions:write, sessions:read, claims:write, media:write, events:read. `service`: answers:delegate, steer:write, recipients:write, webhooks:write, messages:read, sessions:read, events:read; never messages:write. `admin`: projects, agents, webhooks, keys, org, usage, observability and the agent scopes; never answers:delegate or steer:write. A key holding messages:write together with answers:delegate or steer:write is refused with 400 incompatible_scopes.

Type: string, one of `"agent"`, `"service"`, `"admin"`.

### Scope

Type: string, one of `"messages:write"`, `"messages:read"`, `"messages:urgent"`, `"sessions:write"`, `"sessions:read"`, `"steer:write"`, `"claims:write"`, `"answers:delegate"`, `"media:write"`, `"events:read"`, `"projects:write"`, `"agents:write"`, `"recipients:write"`, `"webhooks:write"`, `"keys:write"`, `"org:write"`, `"usage:read"`, `"observability:read"`.

### ApiKeyWithSecret

Type: [ApiKey](#schema-ApiKey) and object.

### ApiKey

| Property | Type | Required | Description |
|---|---|---|---|
| `object` | `"api_key"` | yes |  |
| `id` | string | yes |  |
| `name` | string | yes |  |
| `preset` | string | yes |  |
| `scopes` | array of string | yes |  |
| `mode` | string | yes |  |
| `project` | string or null | yes |  |
| `agent` | string or null | yes |  |
| `default_to` | string or null | yes |  |
| `prefix` | string | yes | First 12 characters, for identification. |
| `last_used_at` | [Timestamp](/docs/api/messages#schema-Timestamp) or null | yes |  |
| `expires_at` | [Timestamp](/docs/api/messages#schema-Timestamp) or null | yes |  |
| `created_at` | [Timestamp](/docs/api/messages#schema-Timestamp) | yes |  |

### ApiKeyList

| Property | Type | Required | Description |
|---|---|---|---|
| `object` | `"list"` | yes |  |
| `data` | array of [ApiKey](#schema-ApiKey) | yes |  |
| `has_more` | boolean | yes |  |
| `next_cursor` | string or null | yes |  |

### ApiKeyRotate

| Property | Type | Required | Description |
|---|---|---|---|
| `grace_seconds` | integer, from 0 to 604800 | no |  |

Unknown properties are refused.
