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

# Control Plane (v1)

> Manage AI Gateways, agents, phantom tokens, and usage over the public /v1 Management API — authenticated with an API key or OAuth token.

# AI Gateway control plane

The AI Gateway control plane lets you create and manage gateways, agents, and phantom tokens — and read cost/usage — over the public **Management API** at `api.knoxcall.com/v1`. These are the same resources you configure in the dashboard, exposed as API-key-authenticated REST endpoints so you can provision them from code, CI, or the KnoxCall SDKs.

<Note>
  This is the **control plane** — it configures the gateway. To actually send AI traffic through an agent, call the **data plane** described in [Execute AI Request](/api-reference/ai-gateway/execute). The data plane lives on your tenant proxy subdomain and authenticates with a phantom token, not an API key.
</Note>

The dashboard/admin surface documented on the [List Gateways](/api-reference/ai-gateway/gateways-list), [Create Agent](/api-reference/ai-gateway/agents-create), and [Mint Phantom Token](/api-reference/ai-gateway/tokens-mint) pages performs the same operations from a session-authenticated `/admin/ai-gateway` host. The `/v1/ai-gateway` endpoints below are the public, API-key equivalent and are what the KnoxCall SDKs call.

## Base URL

All control-plane endpoints are served under `/v1/ai-gateway`.

<CardGroup cols={2}>
  <Card title="Production" icon="server">
    ```text theme={"dark"}
    https://api.knoxcall.com/v1/ai-gateway
    ```

    Use a production key (`tk_live_…` or `AKE…`).
  </Card>

  <Card title="Sandbox" icon="flask">
    ```text theme={"dark"}
    https://sandbox.knoxcall.com/v1/ai-gateway
    ```

    Use a test key (`tk_test_…`).
  </Card>
</CardGroup>

## Authentication

Authenticate exactly as you do for the rest of the Management API — either a long-lived API key or an OAuth 2.1 access token. See the [Authentication guide](/api-reference/authentication) for the full picture.

| Method    | Header                                                      | Notes                                                                                  |
| --------- | ----------------------------------------------------------- | -------------------------------------------------------------------------------------- |
| API key   | `Authorization: Bearer tk_live_…` or `x-api-key: tk_live_…` | Tenant is resolved from the key.                                                       |
| OAuth 2.1 | `Authorization: Bearer kc_live_…` + `X-Tenant-ID: <tenant>` | Short-lived token minted via `POST /oauth/token`; the SDKs send `X-Tenant-ID` for you. |

### Required scope

Every endpoint is gated on the `ai_gateway` scope. The action determines the capability your key must hold:

| Operation                                     | HTTP                        | Capability on `ai_gateway` |
| --------------------------------------------- | --------------------------- | -------------------------- |
| List / read a gateway, agent, token, or usage | `GET`                       | `read`                     |
| Create / update / delete a gateway or agent   | `POST` / `PATCH` / `DELETE` | `write`                    |
| Mint a phantom token                          | `POST …/tokens`             | `mint`                     |

A key that lacks the required capability receives `403` with error type `forbidden`. No other scope grants access to these endpoints.

## Response envelope

Every response uses the standard Management API envelope.

**Single resource** — `{ data, meta }`:

```json theme={"dark"}
{
  "data": {
    "id": "3f2a1c4e-8b9d-4e7a-9f1c-2d6e5a8b3c10",
    "name": "Production"
  },
  "meta": {
    "request_id": "0d5b2a9e-1f3c-4a7d-8e2b-6c9a1f4d7e35"
  }
}
```

**List** — `{ data: [...], meta }` with pagination fields:

```json theme={"dark"}
{
  "data": [ /* … */ ],
  "meta": {
    "total": 3,
    "page": 1,
    "per_page": 20,
    "total_pages": 1,
    "request_id": "0d5b2a9e-1f3c-4a7d-8e2b-6c9a1f4d7e35"
  }
}
```

**Error** — `{ error }`:

```json theme={"dark"}
{
  "error": {
    "type": "not_found",
    "message": "Gateway not found.",
    "request_id": "0d5b2a9e-1f3c-4a7d-8e2b-6c9a1f4d7e35"
  }
}
```

## Pagination

List endpoints accept `page` and `per_page` query parameters and slice the full tenant result set.

| Parameter  | Default | Max   |
| ---------- | ------- | ----- |
| `page`     | `1`     | —     |
| `per_page` | `20`    | `100` |

The `meta` object returns `total`, `page`, `per_page`, and `total_pages`.

## Sandbox and phantom-token environments

Gateways and agents are **not** environment-partitioned — the same gateway and agent objects are visible whether you authenticate with a live or a test key. The live/test distinction lives in the **phantom token**: a token minted with a test key is a test-environment token (`kc_test_…`), and a token minted with a live key is a live token (`kc_live_…`). There is no environment parameter on any request — the server derives it from the key you used to mint.

## Idempotency

Mutating requests (`POST`, `PATCH`, `DELETE`) honour the router-level `X-Idempotency-Key` header. Send the same key to retry a request safely without creating duplicates.

## Errors

In addition to the [shared authentication errors](/api-reference/authentication#authentication-errors), the control plane returns:

| Type                | HTTP | When                                                                                                                    |
| ------------------- | ---- | ----------------------------------------------------------------------------------------------------------------------- |
| `validation`        | 400  | A required field (e.g. `name`, `slug`, or `dpop_jkt` when `dpop_required`) is missing.                                  |
| `invalid_reference` | 400  | `primary_route_id`, `firewall_policy_id`, or `pii_redact_policy_id` does not reference a resource owned by your tenant. |
| `invalid_id`        | 400  | A path or query identifier is not a valid UUID.                                                                         |
| `forbidden`         | 403  | The key lacks the required `ai_gateway` capability.                                                                     |
| `not_found`         | 404  | The gateway, agent, or token does not exist for this tenant.                                                            |
| `conflict`          | 409  | A gateway or agent with that slug already exists for this tenant.                                                       |
| `invalid_slug`      | 422  | The slug is not 2–64 lowercase alphanumerics/hyphens starting and ending with an alphanumeric.                          |
| `internal_error`    | 500  | Unexpected server error — contact support with the `request_id`.                                                        |

## Endpoint index

| Method & path                                             | Purpose                                                                   |
| --------------------------------------------------------- | ------------------------------------------------------------------------- |
| `GET /v1/ai-gateway/gateways`                             | [List gateways](/api-reference/ai-gateway/gateways#list-gateways)         |
| `POST /v1/ai-gateway/gateways`                            | [Create a gateway](/api-reference/ai-gateway/gateways#create-a-gateway)   |
| `GET /v1/ai-gateway/gateways/{id}`                        | [Get a gateway](/api-reference/ai-gateway/gateways#get-a-gateway)         |
| `PATCH /v1/ai-gateway/gateways/{id}`                      | [Update a gateway](/api-reference/ai-gateway/gateways#update-a-gateway)   |
| `DELETE /v1/ai-gateway/gateways/{id}`                     | [Archive a gateway](/api-reference/ai-gateway/gateways#archive-a-gateway) |
| `GET /v1/ai-gateway/gateways/{gatewayId}/agents`          | [List agents](/api-reference/ai-gateway/agents#list-agents)               |
| `POST /v1/ai-gateway/gateways/{gatewayId}/agents`         | [Create an agent](/api-reference/ai-gateway/agents#create-an-agent)       |
| `GET /v1/ai-gateway/agents/{agentId}`                     | [Get an agent](/api-reference/ai-gateway/agents#get-an-agent)             |
| `PATCH /v1/ai-gateway/agents/{agentId}`                   | [Update an agent](/api-reference/ai-gateway/agents#update-an-agent)       |
| `DELETE /v1/ai-gateway/agents/{agentId}`                  | [Archive an agent](/api-reference/ai-gateway/agents#archive-an-agent)     |
| `GET /v1/ai-gateway/agents/{agentId}/tokens`              | [List tokens](/api-reference/ai-gateway/tokens#list-tokens)               |
| `POST /v1/ai-gateway/agents/{agentId}/tokens`             | [Mint a token](/api-reference/ai-gateway/tokens#mint-a-token)             |
| `DELETE /v1/ai-gateway/agents/{agentId}/tokens/{tokenId}` | [Revoke a token](/api-reference/ai-gateway/tokens#revoke-a-token)         |
| `GET /v1/ai-gateway/usage`                                | [Usage & cost](/api-reference/ai-gateway/usage)                           |
