# Errors

> Every stable error code the Checkbox API returns, with its HTTP status, type and meaning. Error responses link here through doc_url.

Every error response has the shape `{ "error": { type, code, message, param?, doc_url, request_id, next_action? } }`.
Branch on `code`, not on `message`: codes are part of the contract and are never renamed or reused, while messages may
change. When `next_action` is present, follow it.

| HTTP | Meaning |
|---|---|
| 400 | The request is invalid (`param` names the field) |
| 401 | No key, or the key is unknown, revoked or expired |
| 402 | Only on deployments with billing enabled: the feature needs a paid plan |
| 403 | The key is not allowed to do this (scope, role, publishable key, activation, limits) |
| 404 | No such object in this mode, or no such endpoint |
| 409 | The object's state does not allow it, or the same Idempotency-Key is in flight |
| 410 | The object is archived |
| 413, 415 | Body too large, or not JSON |
| 429 | Rate limited: retry after `Retry-After` |
| 5xx | Checkbox's side. Retrying with the same Idempotency-Key is safe |

## Codes {#codes}

| Code | HTTP | Type |
|---|---|---|
| [`parameter_invalid`](#parameter_invalid) | 400 | `invalid_request_error` |
| [`parameter_missing`](#parameter_missing) | 400 | `invalid_request_error` |
| [`parameter_unknown`](#parameter_unknown) | 400 | `invalid_request_error` |
| [`body_invalid_json`](#body_invalid_json) | 400 | `invalid_request_error` |
| [`url_not_allowed`](#url_not_allowed) | 400 | `invalid_request_error` |
| [`api_version_invalid`](#api_version_invalid) | 400 | `invalid_request_error` |
| [`idempotency_key_invalid`](#idempotency_key_invalid) | 400 | `idempotency_error` |
| [`idempotency_key_reused`](#idempotency_key_reused) | 400 | `idempotency_error` |
| [`test_key_missing`](#test_key_missing) | 400 | `invalid_request_error` |
| [`test_key_not_test_mode`](#test_key_not_test_mode) | 400 | `invalid_request_error` |
| [`test_key_rejected`](#test_key_rejected) | 400 | `invalid_request_error` |
| [`authorization_pending`](#authorization_pending) | 400 | `invalid_request_error` |
| [`slow_down`](#slow_down) | 400 | `rate_limit_error` |
| [`expired_token`](#expired_token) | 400 | `invalid_request_error` |
| [`device_code_invalid`](#device_code_invalid) | 400 | `invalid_request_error` |
| [`access_denied`](#access_denied) | 403 | `permission_error` |
| [`api_key_missing`](#api_key_missing) | 401 | `authentication_error` |
| [`api_key_invalid`](#api_key_invalid) | 401 | `authentication_error` |
| [`publishable_key_not_allowed`](#publishable_key_not_allowed) | 403 | `permission_error` |
| [`origin_not_allowed`](#origin_not_allowed) | 403 | `permission_error` |
| [`insufficient_scope`](#insufficient_scope) | 403 | `permission_error` |
| [`insufficient_role`](#insufficient_role) | 403 | `permission_error` |
| [`account_not_activated`](#account_not_activated) | 403 | `permission_error` |
| [`test_mode_domain_unverified`](#test_mode_domain_unverified) | 403 | `permission_error` |
| [`test_mode_scan_budget_exhausted`](#test_mode_scan_budget_exhausted) | 429 | `rate_limit_error` |
| [`account_suspended`](#account_suspended) | 403 | `permission_error` |
| [`resource_missing`](#resource_missing) | 404 | `invalid_request_error` |
| [`route_not_found`](#route_not_found) | 404 | `invalid_request_error` |
| [`resource_conflict`](#resource_conflict) | 409 | `invalid_request_error` |
| [`scan_already_queued`](#scan_already_queued) | 409 | `invalid_request_error` |
| [`consent_reference_locked`](#consent_reference_locked) | 409 | `invalid_request_error` |
| [`test_run_in_progress`](#test_run_in_progress) | 409 | `invalid_request_error` |
| [`idempotency_key_in_use`](#idempotency_key_in_use) | 409 | `idempotency_error` |
| [`resource_archived`](#resource_archived) | 410 | `invalid_request_error` |
| [`body_too_large`](#body_too_large) | 413 | `invalid_request_error` |
| [`content_type_unsupported`](#content_type_unsupported) | 415 | `invalid_request_error` |
| [`site_limit_reached`](#site_limit_reached) | 403 | `invalid_request_error` |
| [`webhook_endpoint_limit_reached`](#webhook_endpoint_limit_reached) | 403 | `invalid_request_error` |
| [`plan_required`](#plan_required) | 402 | `invalid_request_error` |
| [`rate_limit_exceeded`](#rate_limit_exceeded) | 429 | `rate_limit_error` |
| [`feature_unavailable`](#feature_unavailable) | 503 | `api_error` |
| [`internal_error`](#internal_error) | 500 | `api_error` |

### parameter_invalid {#parameter_invalid}

HTTP 400 · `invalid_request_error`

A parameter has an invalid value; `param` names it.

### parameter_missing {#parameter_missing}

HTTP 400 · `invalid_request_error`

A required parameter is missing; `param` names it.

### parameter_unknown {#parameter_unknown}

HTTP 400 · `invalid_request_error`

The request has a parameter this endpoint does not accept.

### body_invalid_json {#body_invalid_json}

HTTP 400 · `invalid_request_error`

The request body is not valid JSON.

### url_not_allowed {#url_not_allowed}

HTTP 400 · `invalid_request_error`

A URL points to a private, local or otherwise unreachable address.

### api_version_invalid {#api_version_invalid}

HTTP 400 · `invalid_request_error`

The Checkbox-Version header names an unknown API version.

### idempotency_key_invalid {#idempotency_key_invalid}

HTTP 400 · `idempotency_error`

The Idempotency-Key header is empty or longer than 255 characters.

### idempotency_key_reused {#idempotency_key_reused}

HTTP 400 · `idempotency_error`

The Idempotency-Key was already used with different parameters within 24 hours.

### test_key_missing {#test_key_missing}

HTTP 400 · `invalid_request_error`

A Stripe test run needs a Stripe TEST-mode key stored for the site (createTestCredential).

### test_key_not_test_mode {#test_key_not_test_mode}

HTTP 400 · `invalid_request_error`

The billing key is a live-mode (or malformed) key; test runs only accept test-mode keys (rk_test_… / sk_test_…).

### test_key_rejected {#test_key_rejected}

HTTP 400 · `invalid_request_error`

Stripe rejected the test-mode key or it lacks a permission test runs need; `message` names it.

### authorization_pending {#authorization_pending}

HTTP 400 · `invalid_request_error`

Device login: the human has not approved yet. Keep polling createDeviceToken every `interval` seconds.

### slow_down {#slow_down}

HTTP 400 · `rate_limit_error`

Device login: polling too fast. Add 5 seconds to the interval and keep polling.

### expired_token {#expired_token}

HTTP 400 · `invalid_request_error`

Device login: the device code expired (15 minutes) or was already used. Start again with createDeviceAuthorization.

### device_code_invalid {#device_code_invalid}

HTTP 400 · `invalid_request_error`

Device login: the device code is unknown.

### access_denied {#access_denied}

HTTP 403 · `permission_error`

Device login: the human clicked Deny. Do not retry without asking them.

### api_key_missing {#api_key_missing}

HTTP 401 · `authentication_error`

No API key was provided (Authorization: Bearer ck_…).

### api_key_invalid {#api_key_invalid}

HTTP 401 · `authentication_error`

The API key is unknown, revoked or expired.

### publishable_key_not_allowed {#publishable_key_not_allowed}

HTTP 403 · `permission_error`

A publishable key (pk_…) cannot call this endpoint; use a secret key on your server.

### origin_not_allowed {#origin_not_allowed}

HTTP 403 · `permission_error`

A publishable-key request came from a page (Origin) that is not on one of the site's domains (base URL or checkout URL).

### insufficient_scope {#insufficient_scope}

HTTP 403 · `permission_error`

The restricted key lacks the scope this endpoint needs.

### insufficient_role {#insufficient_role}

HTTP 403 · `permission_error`

The key (or the person who created it) does not have a role that allows this action.

### account_not_activated {#account_not_activated}

HTTP 403 · `permission_error`

Live mode needs an activated account: verify the domain of one of your sites (or use a test key).

### test_mode_domain_unverified {#test_mode_domain_unverified}

HTTP 403 · `permission_error`

Test mode only scans, monitors, fetches and test-runs localhost or domains the account has verified; verify the domain (createDomainVerification) or send the HTML instead.

### test_mode_scan_budget_exhausted {#test_mode_scan_budget_exhausted}

HTTP 429 · `rate_limit_error`

The daily test-mode scan budget for sites on unverified domains is used up; verify the domain (createDomainVerification) to lift it.

### account_suspended {#account_suspended}

HTTP 403 · `permission_error`

The account was suspended by Checkbox staff; contact support.

### resource_missing {#resource_missing}

HTTP 404 · `invalid_request_error`

No object with this id exists in this mode of the account.

### route_not_found {#route_not_found}

HTTP 404 · `invalid_request_error`

No such endpoint. See /v1/openapi.json for the list.

### resource_conflict {#resource_conflict}

HTTP 409 · `invalid_request_error`

The object is in a state that does not allow this action.

### scan_already_queued {#scan_already_queued}

HTTP 409 · `invalid_request_error`

A scan of this site is already queued or running.

### consent_reference_locked {#consent_reference_locked}

HTTP 409 · `invalid_request_error`

The consent already carries a different customer_reference; references are set once and never changed.

### test_run_in_progress {#test_run_in_progress}

HTTP 409 · `invalid_request_error`

The site already has a queued or running test run; wait for it (retrieveTestRun) or cancel it (cancelTestRun).

### idempotency_key_in_use {#idempotency_key_in_use}

HTTP 409 · `idempotency_error`

A request with this Idempotency-Key is still being processed; retry shortly.

### resource_archived {#resource_archived}

HTTP 410 · `invalid_request_error`

The object is archived; restore it before changing it.

### body_too_large {#body_too_large}

HTTP 413 · `invalid_request_error`

The request body is too large.

### content_type_unsupported {#content_type_unsupported}

HTTP 415 · `invalid_request_error`

Send JSON bodies with Content-Type: application/json.

### site_limit_reached {#site_limit_reached}

HTTP 403 · `invalid_request_error`

The account reached its fair-use site limit (default 25); contact support to raise it.

### webhook_endpoint_limit_reached {#webhook_endpoint_limit_reached}

HTTP 403 · `invalid_request_error`

The account reached the maximum number of webhook endpoints (16 per mode).

### plan_required {#plan_required}

HTTP 402 · `invalid_request_error`

Only when billing is enabled on this deployment: the feature needs a paid plan.

### rate_limit_exceeded {#rate_limit_exceeded}

HTTP 429 · `rate_limit_error`

Too many requests; slow down and retry after the Retry-After header.

### feature_unavailable {#feature_unavailable}

HTTP 503 · `api_error`

The feature is not configured on this deployment (for example the database migration is missing).

### internal_error {#internal_error}

HTTP 500 · `api_error`

Something went wrong on Checkbox's side. Retrying with the same Idempotency-Key is safe.
