> ## Documentation Index
> Fetch the complete documentation index at: https://docs.kaireonai.com/llms.txt
> Use this file to discover all available pages before exploring further.

# API Keys

> Generate, list, and revoke API keys for programmatic access. Keys carry a scopes array that determines whether they can reach the data plane only (the default) or also the control plane.

API keys provide machine-to-machine authentication for the KaireonAI platform. Keys use the `krn_` prefix and are hashed before storage — the raw key is only shown once at creation time.

## The two API planes

The platform API is split into two planes:

| Plane             | Endpoints                                                                                                                                                                                            | Who can call it                                                                                             |
| ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| **Data plane**    | Exactly `POST /api/v1/recommend`, `POST /api/v1/respond`, `POST /api/v1/respond/bulk`, and `POST /api/v1/capture` (a legacy alias of `/respond`)                                                     | Any `krn_` API key — this is the runtime decisioning loop your own systems call machine-to-machine          |
| **Control plane** | Everything else — all management CRUD (schemas, offers, categories, channels, creatives, decision flows, decisioning gates, connectors, pipelines, segments, models, reports, settings, API keys, …) | First-party only: a browser session, an MCP connection, or an API key minted with the `control-plane` scope |

## Key scopes

Every API key carries a `scopes` field — a JSON array of permission strings set at creation time and never changed afterwards.

| `scopes` value                  | Behaviour                                                                                                                                    |
| ------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| `[]` (empty array, the default) | **Data-plane-only** key — permitted on `recommend` / `respond` / `respond/bulk` / `capture` only. Calls to management endpoints return `403` |
| `["control-plane"]`             | Management key — full control-plane access in addition to the data plane                                                                     |
| `["scim"]`                      | SCIM provisioning key — `/scim/v2/*` endpoints only                                                                                          |

<Warning>
  **Migration note:** as of the control-plane / data-plane split, existing API
  keys with no explicit scopes are now **data-plane-only** (recommend/respond).
  To manage resources programmatically, mint a new key with the
  `control-plane` scope.
</Warning>

A key without the required access gets an HTTP `403` with this body:

```json theme={null}
{
  "title": "Forbidden",
  "detail": "This API key is scoped to the data plane (recommend / respond). Management endpoints require a first-party session, an MCP connection, or an API key minted with the \"control-plane\" scope."
}
```

Scopes are validated at creation via Zod: each entry is a string of 1–64 characters; maximum 16 entries per key. There is no update route — scope assignment is create-time only. The in-process auth cache refreshes every 15 seconds, so a revoked or newly created key takes up to 15 seconds to propagate.

### Defined scopes

| Scope             | Grants                                                                                                                 |
| ----------------- | ---------------------------------------------------------------------------------------------------------------------- |
| `"control-plane"` | All management endpoints (plus the data plane). Admin-only to mint                                                     |
| `"scim"`          | `/scim/v2/*` SCIM provisioning endpoints, used with `Authorization: Bearer <key>` (see [SCIM v2](/api-reference/scim)) |

<Note>
  Keep `control-plane` keys internal — treat them like server credentials.
  Never embed one in a client app, mobile app, or browser code. Ship
  data-plane-only keys (the default) to anything customer-facing.
</Note>

The master/internal server key (`API_KEY` env var) and cron/webhook secrets are internal infrastructure, not customer keys — they are unaffected by scopes.

***

## POST /api/v1/api-keys

Generate a new API key. **Admin only.**

### Request Body

| Field       | Type      | Required | Description                                                                                                       |
| ----------- | --------- | -------- | ----------------------------------------------------------------------------------------------------------------- |
| `name`      | string    | No       | Human-readable key name (defaults to `key-{timestamp}`)                                                           |
| `expiresAt` | string    | No       | ISO 8601 expiration date. Omit for non-expiring keys                                                              |
| `scopes`    | string\[] | No       | Permission scopes. Each entry 1–64 chars, max 16. Omit or pass `[]` for a data-plane-only key (recommend/respond) |

### Example — data-plane key (default)

```bash theme={null}
curl -X POST https://playground.kaireonai.com/api/v1/api-keys \
  -H "Content-Type: application/json" \
  -H "X-Tenant-Id: my-tenant" \
  -d '{
    "name": "CI Pipeline Key",
    "expiresAt": "2027-01-01T00:00:00Z"
  }'
```

### Example — control-plane key (management access)

```bash theme={null}
curl -X POST https://playground.kaireonai.com/api/v1/api-keys \
  -H "Content-Type: application/json" \
  -H "X-Tenant-Id: my-tenant" \
  -d '{
    "name": "Infra Automation Key",
    "scopes": ["control-plane"]
  }'
```

### Example — SCIM-scoped key

```bash theme={null}
curl -X POST https://playground.kaireonai.com/api/v1/api-keys \
  -H "Content-Type: application/json" \
  -H "X-Tenant-Id: my-tenant" \
  -d '{
    "name": "Okta SCIM Provisioner",
    "scopes": ["scim"]
  }'
```

### Response (201)

```json theme={null}
{
  "id": "clx...",
  "name": "CI Pipeline Key",
  "key": "krn_a1b2c3d4e5f6...",
  "prefix": "krn_a1b2c3d4",
  "scopes": [],
  "expiresAt": "2027-01-01T00:00:00.000Z",
  "createdAt": "2026-03-18T12:00:00.000Z",
  "warning": "Store this key securely. It will not be shown again."
}
```

<Warning>
  The raw API key (`key` field) is only returned on creation. Store it securely — it cannot be retrieved later. Scopes are set at creation and cannot be updated; revoke and re-create the key to change them.
</Warning>

***

## GET /api/v1/api-keys

List all active (non-revoked) API keys for the tenant. Returns prefixes and scopes — not the full key. **Admin only.**

### Response

```json theme={null}
{
  "data": [
    {
      "id": "clx...",
      "name": "CI Pipeline Key",
      "prefix": "krn_a1b2c3d4",
      "scopes": [],
      "expiresAt": "2027-01-01T00:00:00.000Z",
      "lastUsedAt": "2026-03-17T09:30:00.000Z",
      "createdAt": "2026-03-18T12:00:00.000Z"
    }
  ],
  "total": 1
}
```

***

## DELETE /api/v1/api-keys?id={id}

Revoke an API key (soft delete). The key immediately stops working. **Admin only.**

### Query Parameters

| Parameter | Type   | Required | Description           |
| --------- | ------ | -------- | --------------------- |
| `id`      | string | Yes      | The API key record ID |

### Request Body (optional)

| Field    | Type   | Required | Description                              |
| -------- | ------ | -------- | ---------------------------------------- |
| `reason` | string | No       | Reason for revocation (stored for audit) |

### Response

`204 No Content` on success.
