> ## 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)                  |
| `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`            |

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