> ## 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.

# Errors

> The canonical KnoxCall error envelope, the full type-to-status table, and how to branch on error.type and correlate a request_id.

# Errors

Every failure on the KnoxCall Management API (`/v1`) returns **one canonical shape**. There
is a single `error` object with a machine-readable `type`, a human-readable `message`, and a
`request_id`. You branch on `type`; you show `message` to a human; you quote `request_id` to
support.

## The error envelope

```json theme={"dark"}
{
  "error": {
    "type": "not_found",
    "message": "Route not found",
    "request_id": "550e8400-e29b-41d4-a716-446655440000"
  }
}
```

| Field | Type | Description |
| - | - | - |
| `error.type` | string | Stable, machine-readable code. Branch on this — never on the HTTP status alone or on `message`. |
| `error.message` | string | Human-readable explanation. Safe to surface to your users; may change wording over time. |
| `error.request_id` | string (UUID) | The unique ID for this request. A **bare UUID** — never `req_`-prefixed. |

<Note>
  The `error` object is the only top-level key on a failure response — there is no `data` or
  `meta` on an error. Success responses use `{ "data": ..., "meta": ... }`; failures use
  `{ "error": ... }`. Check for the presence of `error` first.
</Note>

## Type → status table

The HTTP status is derived from `error.type`. The mapping is stable — code against it.

| Error Type | HTTP Status | Meaning |
| - | - | - |
| `authentication_required` | 401 | No API key was provided in the request |
| `invalid_api_key` | 401 | The API key is invalid, revoked, or expired |
| `wrong_key_type` | 403 | A test key was used against production, or vice versa |
| `subscription_inactive` | 403 | The tenant's subscription is paused, cancelled, or past due |
| `forbidden` | 403 | The key is valid but lacks the role/scope required for this action (RBAC denial) |
| `validation_error` | 400 | The request body or query parameters failed validation |
| `not_found` | 404 | The requested resource does not exist (or is not visible to this key) |
| `method_not_allowed` | 405 | The request method is outside the set its group of endpoints supports, for example a `PATCH` to `/v1/api-keys/{id}`. The `Allow` response header lists the methods the group does support. Sent by `/v1/api-keys`, `/v1/audit-logs`, `/v1/logs`, `/v1/clients`, `/v1/environments`, `/v1/routes` and `/v1/webhooks`. No role or policy permission is checked for it, so it is the same for every credential |
| `conflict` | 409 | A resource with the same unique identifier already exists |
| `in_use` | 409 | The resource cannot be deleted because other resources depend on it |
| `plan_limit` | 402 / 403 | The tenant has reached a limit imposed by their current plan |
| `rate_limit_exceeded` | 429 | Too many requests — back off and retry (see [Rate limits](/api-reference/rate-limits)) |
| `internal_error` | 500 | An unexpected server error occurred — contact support with the `request_id` |
| `dependency_unavailable` | 503 | KnoxCall could not reach one of its own dependencies (`error.dependency` is `control_plane` or `rate_limit_store`) in time, so the request was refused rather than served from stale data or unmetered. Carries `Retry-After` (also echoed as `error.retry_after`, whole seconds). Safe to retry after that wait; **not** a credential problem — never re-authenticate on it. Answered on the data plane and the AI plane too, with the [response block](/api-reference/response-metadata) |
| `sandbox_test_card_declined` | 402 | **Test data space only.** The request released a documented KnoxCall [test card](/essentials/vaults/overview#test-cards) that simulates a decline. Nothing was forwarded |
| `sandbox_test_card_requires_action` | 402 | **Test data space only.** The request released the test card that simulates a 3-D Secure challenge. Nothing was forwarded |

### Idempotency error types

Sent only on mutating requests that carry an idempotency key. See
[Idempotency](/api-reference/idempotency) for the full flow.

| Error Type | HTTP Status | Meaning |
| - | - | - |
| `invalid_idempotency_key` | 400 | The `X-Idempotency-Key` value is malformed or too long |
| `request_in_progress` | 409 | A request with the same key is still being processed — retry shortly |
| `idempotency_key_reuse` | 422 | The same key was reused with a **different** request body |

<Note>
  `422` is used **only** for `idempotency_key_reuse`. Ordinary input-validation failures are
  always `400 validation_error` — never `422`.
</Note>

## Branching on `error.type`

Match on `error.type`, not on HTTP status codes and not on the human `message`. Statuses are
shared across several types (three different types map to `403`), and `message` wording is
not part of the contract.

<CodeGroup>
  ```typescript Node.js theme={"dark"}
  // OAuth access token minted via POST /oauth/token — see /api-reference/authentication
  const resp = await fetch("https://api.knoxcall.com/v1/routes/does-not-exist", {
    headers: { Authorization: `Bearer ${process.env.KNOXCALL_ACCESS_TOKEN}` }
  });

  if (!resp.ok) {
    const { error } = await resp.json();
    switch (error.type) {
      case "not_found":
        // handle a missing resource
        break;
      case "rate_limit_exceeded":
        // read Retry-After and back off
        break;
      case "dependency_unavailable":
        // KnoxCall could not serve it: wait Retry-After, then resend — the request never ran
        break;
      case "invalid_api_key":
      case "authentication_required":
        // re-authenticate
        break;
      default:
        console.error(`KnoxCall error ${error.type}: ${error.message} (request ${error.request_id})`);
    }
  }
  ```

  ```python Python theme={"dark"}
  # token: an OAuth access token minted via POST /oauth/token — see /api-reference/authentication
  resp = requests.get(
      "https://api.knoxcall.com/v1/routes/does-not-exist",
      headers={"Authorization": f"Bearer {token}"},
  )

  if resp.status_code >= 400:
      error = resp.json()["error"]
      if error["type"] == "not_found":
          ...  # handle a missing resource
      elif error["type"] == "rate_limit_exceeded":
          ...  # read Retry-After and back off
      elif error["type"] == "dependency_unavailable":
          ...  # KnoxCall could not serve it: wait Retry-After, then resend
      else:
          raise RuntimeError(
              f"KnoxCall {error['type']}: {error['message']} "
              f"(request {error['request_id']})"
          )
  ```
</CodeGroup>

<Tip>
  The first-party [SDKs](/sdks/overview) already map these types to typed exceptions
  (`NotFoundError`, `RateLimitError`, `AuthenticationError`, and so on) so you can `catch`
  them directly instead of inspecting raw responses.
</Tip>

## Correlating a request — `request_id` and `X-Request-Id`

Every response carries the same request ID in two places:

* **`error.request_id`** in the JSON body (on failures) and **`meta.request_id`** (on
  successes).
* The **`X-Request-Id`** response header — present on **every** response, success or error.

```http theme={"dark"}
HTTP/1.1 404 Not Found
X-Request-Id: 550e8400-e29b-41d4-a716-446655440000
Content-Type: application/json

{
  "error": {
    "type": "not_found",
    "message": "Route not found",
    "request_id": "550e8400-e29b-41d4-a716-446655440000"
  }
}
```

The header lets you record the ID from your HTTP client even when you never parsed the body
(for example on a `500` you logged and moved past). The two values are always identical.

<Warning>
  When you contact support about a failed request, **quote the `request_id`**. It lets us
  trace the exact request through our systems — the upstream call, the tenant, the timing —
  without you having to reconstruct what happened.
</Warning>

## What's Next?

<CardGroup cols={2}>
  <Card title="Rate limits" icon="gauge" href="/api-reference/rate-limits">
    Headers, `Retry-After`, and backoff guidance for `429` responses.
  </Card>

  <Card title="Idempotency" icon="repeat" href="/api-reference/idempotency">
    Safe retries for mutating requests, replay semantics, and `409` / `422` behavior.
  </Card>

  <Card title="API Overview" icon="book" href="/api-reference/overview">
    Base URLs, response envelope, pagination, and quick examples.
  </Card>

  <Card title="Authentication" icon="lock" href="/api-reference/authentication">
    OAuth 2.1 + DPoP, CLI login, and API key types.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.