# API reference

> The Checkbox REST API — base URL, authentication, versioning, errors, pagination, idempotency and expansion. Every operation is listed by resource.

The API is REST over HTTPS with JSON in and out, at `https://app.checkbox.site/v1`. It follows the conventions you know
from Stripe: secret keys in the `Authorization` header, test and live mode chosen by the key, prefixed ids, cursor
pagination, `Idempotency-Key` on POST, dated versions and errors with stable codes. The machine-readable contract is
OpenAPI 3.1 at [`/v1/openapi.json`](https://app.checkbox.site/v1/openapi.json) (also `/v1/openapi.yaml`).

## Authentication {#authentication}

Send a secret key as a Bearer token, or as the HTTP Basic user name (curl's `-u "$KEY:"`):

```bash
curl https://app.checkbox.site/v1/account \
  -H "Authorization: Bearer $CHECKBOX_SECRET_KEY"
```

| Key | Use |
|---|---|
| `ck_test_…`, `ck_live_…` | Secret keys, server side. They carry a role (admin, member or viewer) |
| `rk_test_…`, `rk_live_…` | Restricted keys with per-resource scopes (`{ "checks": "read" }`) |
| `pk_test_…`, `pk_live_…` | Publishable keys for the browser ([Elements](https://checkbox.site/docs/elements)); refused elsewhere |

A missing or wrong key is `401` (`api_key_missing`, `api_key_invalid`); a key without the scope is `403`
(`insufficient_scope`). See [test and live mode](https://checkbox.site/docs/concepts/test-and-live).

## Conventions {#conventions}

- **Objects** have `id`, `object` (the type) and `livemode`; timestamps are Unix seconds (`created`); fields are
  snake_case.
- **Ids** are `<prefix>_<32 hex>` (`site_…`, `scan_…`, `fnd_…`, `task_…`, `evt_…`). Inputs also accept the raw UUID.
- **Lists** return `{ "object": "list", "data": [...], "has_more": true, "url": "/v1/..." }`. Page with `limit`
  (1–100, default 10), `starting_after` and `ending_before` (object ids).
- **Expansion:** `expand[]=latest_scan` or `expand[]=domain_verification` on `retrieveSite` includes the related object.
- **Request ids:** every response has a `Request-Id` header (`req_…`), also inside error bodies. Include it when you
  contact support.

```bash
curl -G https://app.checkbox.site/v1/sites \
  -u "$CHECKBOX_SECRET_KEY:" \
  -d limit=2
```

## Errors {#errors}

Errors have an HTTP status and a body with a stable `code`:

```json
{
  "error": {
    "type": "invalid_request_error",
    "code": "parameter_invalid",
    "message": "price (major units, e.g. 19.99) or amount (minor units) is required",
    "param": "price",
    "doc_url": "https://checkbox.site/docs/api/errors#parameter_invalid",
    "request_id": "req_1eead3177abc4ebc906a8af0fddcc0c4",
    "next_action": { "type": "fix_request", "message": "Correct price and call the check again." }
  }
}
```

`type` is one of `invalid_request_error`, `authentication_error`, `permission_error`, `rate_limit_error`,
`idempotency_error`, `api_error`. Codes are never renamed or reused; [every code](https://checkbox.site/docs/api/errors) is documented.
`next_action`, when present, tells an agent what to do about it.

## Idempotency {#idempotency}

Every POST accepts an `Idempotency-Key` header (up to 255 characters). The same key with the same body within 24 hours
returns the stored response with `Idempotent-Replayed: true`; with a different body it is `400 idempotency_key_reused`;
while the first request is still running, `409 idempotency_key_in_use`. Server errors are not stored, so retrying with
the same key is always safe. The SDKs send a key on every POST automatically.

```bash
curl https://app.checkbox.site/v1/sites \
  -u "$CHECKBOX_SECRET_KEY:" \
  -H "Idempotency-Key: create-joybox-site" \
  -H "Content-Type: application/json" \
  -d '{ "url": "https://joybox.example" }'
```

## Versioning {#versioning}

Versions are dates. Send `Checkbox-Version: 2026-10-30` to pin a request; without it, the account's pinned version is
used (set on first use; change it with `updateAccount`). Responses echo `Checkbox-Version` and `Checkbox-Mode`.
Known versions: `2026-10-30`.

## Rate limits {#rate-limits}

300 requests per minute per account and mode, with `RateLimit-*` headers. Over the limit: `429 rate_limit_exceeded`
with `Retry-After`. Some routes have their own limits (preflight checks 120 per minute; scans and evaluations 60 per
hour per site).

## Resources {#resources}

| Resource | What it is |
|---|---|
| [Agent setup](https://checkbox.site/docs/api/agent-setup) | Device login and setup sessions behind `setup_checkbox` and `checkbox init` |
| [Account](https://checkbox.site/docs/api/account) | Mode, activation, limits, next step |
| [Sites](https://checkbox.site/docs/api/sites) | The businesses Checkbox monitors; snippet; domain verification |
| [Scans](https://checkbox.site/docs/api/scans) | Synthetic-shopper runs |
| [Evaluations](https://checkbox.site/docs/api/evaluations) | Score, insurable flag, blockers |
| [Findings](https://checkbox.site/docs/api/findings) | Latest result of each legal check, with citations |
| [Compliance plans](https://checkbox.site/docs/api/compliance-plans) | Ordered tasks, verification, human answers |
| [Fixes](https://checkbox.site/docs/api/fixes) | Engine-verified platform-specific fixes |
| [Evidence](https://checkbox.site/docs/api/evidence) | The tamper-evident vault |
| [Certificates](https://checkbox.site/docs/api/certificates) | Signed monitoring certificates |
| [Notices](https://checkbox.site/docs/api/notices) | Required lifecycle emails sent for you |
| [Law updates](https://checkbox.site/docs/api/law-updates) | Changes in the law and their effect |
| [Checks](https://checkbox.site/docs/api/checks) | Stateless preflight checks |
| [Elements](https://checkbox.site/docs/api/elements) | The disclosure element, disclosures and consents |
| [Test runs](https://checkbox.site/docs/api/test-runs) | Synthetic test subscriptions |
| [Webhook endpoints](https://checkbox.site/docs/api/webhook-endpoints) and [Events](https://checkbox.site/docs/api/events) | Signed event delivery |
