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

# Errors

> Typed error envelopes and how to handle them.

Every API error returns a typed JSON envelope with a stable error type, a machine-readable code, a human-readable message, and the request ID. Business outcomes (such as `needs_review` or `inspection_required`) are successful evaluation decisions, not errors.

## Error envelope

```json theme={null}
{
  "error": {
    "type": "invalid_request",
    "code": "missing_services",
    "message": "services must be a non-empty array of { service, selections }.",
    "param": "services",
    "request_id": "req_abc123"
  }
}
```

| Field        | Description                                                                   |
| ------------ | ----------------------------------------------------------------------------- |
| `type`       | The error category. One of the seven types listed below.                      |
| `code`       | A stable, machine-readable code specific to the error.                        |
| `message`    | A human-readable explanation of what went wrong.                              |
| `param`      | The request parameter that caused the error, when applicable.                 |
| `request_id` | The unique request identifier. Present on every response, success or failure. |

## Error types

| Type                   | HTTP status | When it happens                                                                                 |
| ---------------------- | ----------- | ----------------------------------------------------------------------------------------------- |
| `invalid_request`      | 400         | The request body, headers, or parameters are missing or malformed.                              |
| `authentication_error` | 401         | The API key is missing, invalid, revoked, or has the wrong environment prefix.                  |
| `permission_error`     | 403         | The key is valid but lacks the required scope, or the account is not entitled to the operation. |
| `not_found`            | 404         | The requested resource does not exist or is not accessible to this account.                     |
| `rate_limit_error`     | 429         | The rate limit for this account and environment has been exceeded.                              |
| `idempotency_error`    | 400         | The idempotency key is missing or malformed.                                                    |
| `api_error`            | 500         | An unexpected error on Kordless's side. The `request_id` identifies the failure.                |

## Common error codes

### Authentication errors (401)

| Code              | Message                                                                    |
| ----------------- | -------------------------------------------------------------------------- |
| `missing_api_key` | No bearer token in the `Authorization` header.                             |
| `invalid_api_key` | The key is not a Kordless secret key, or it does not match any stored key. |
| `revoked_api_key` | The key has been revoked or its grace period has expired.                  |

### Permission errors (403)

| Code                      | Message                                              |
| ------------------------- | ---------------------------------------------------- |
| `insufficient_scope`      | The key lacks the required scope for this operation. |
| `admin_required`          | The operation requires an organization admin.        |
| `api_access_not_entitled` | The account does not have API access.                |

### Invalid request errors (400)

| Code                      | Message                                                                                 |
| ------------------------- | --------------------------------------------------------------------------------------- |
| `invalid_json`            | The request body is not valid JSON.                                                     |
| `body_too_large`          | The request body exceeds 64 KB.                                                         |
| `missing_idempotency_key` | The `Idempotency-Key` header is missing on a write operation.                           |
| `missing_services`        | The `services` array is missing or empty.                                               |
| `missing_service_key`     | A service entry is missing its `service` key.                                           |
| `too_many_services`       | A simulation prices at most 10 services.                                                |
| `no_published_book`       | The account has no published Book to quote against.                                     |
| `commitment_rejected`     | The commitment could not be created (e.g., Test key, quote not in a committable state). |
| `missing_external_ref`    | The `external_ref` field is required to confirm a handoff.                              |

### Not found errors (404)

| Code                         | Message                                                                 |
| ---------------------------- | ----------------------------------------------------------------------- |
| `api_key_not_found`          | The specified API key does not exist on this account.                   |
| `webhook_endpoint_not_found` | The specified webhook endpoint does not exist on this account.          |
| `request_log_not_found`      | The specified request ID was not found in this account and environment. |

## Rate limit errors (429)

When the rate limit is exceeded, the response includes a `retry-after` header with the number of seconds until the window resets:

```json theme={null}
{
  "error": {
    "type": "rate_limit_error",
    "code": "rate_limited",
    "message": "Too many requests. Retry after the current window resets.",
    "request_id": "req_abc123"
  }
}
```

## Business outcomes are not errors

A quote that returns `needs_input`, `unavailable`, or `failed` is a successful evaluation decision, not an API error. These outcomes are returned with HTTP status `201` (for produced quotes) or `200` (for simulations), because the request was processed correctly — the evaluator simply determined that the request could not be priced.

Handle these outcomes in your integration logic, not in your error handling:

| Outcome       | What it means                            | How to handle                                |
| ------------- | ---------------------------------------- | -------------------------------------------- |
| `priced`      | The evaluator produced a price.          | Display the total and line items.            |
| `needs_input` | Required information is missing.         | Collect the missing inputs and retry.        |
| `unavailable` | The work is outside the Book's coverage. | Inform the user or route to manual handling. |
| `failed`      | The evaluator encountered an error.      | Retry, or route to manual handling.          |

## The request ID

Every response — success or error — includes an `x-request-id` header. Use this ID to find the request in the [Request logs](/developers/request-logs) or when contacting support. The ID is also present in the error envelope's `request_id` field.

For the full error schema, see the [API reference](/api-reference/openapi).
