# Scans API

> Synthetic-shopper runs: Checkbox visits the checkout from each state and device, completes a sign-up when configured, receives the emails and tries to cancel online.

| Operation | Endpoint |
|---|---|
| [Run a scan](#createScan) | `POST /v1/sites/{site}/scans` |
| [List scans](#listScans) | `GET /v1/scans` |
| [Retrieve a scan](#retrieveScan) | `GET /v1/scans/{scan}` |

## The Scan object

Most recent scan (only with expand[]=latest_scan).

| Field | Type | Description |
|---|---|---|
| `id` | string | Unique identifier of the scan (scan_…). |
| `object` | enum | String representing the object's type. Always "scan". |
| `livemode` | boolean | true for live-mode objects, false for test mode (ck_test_ keys act on an isolated sandbox). |
| `site` | string | The scanned site. |
| `status` | enum | queued → running → completed \| failed. One of `queued`, `running`, `completed`, `failed`. |
| `trigger` | enum | What started the scan. One of `manual`, `scheduled`, `page_changed`, `api`. |
| `regions` | array of enum | States the synthetic shopper visited from (empty for evaluation-only runs). |
| `error` | string \| null | Why the scan failed. |
| `warnings` | array of strings | Partial failures of a scan that still produced results. |
| `evaluation` | string \| null | The evaluation this scan produced (null until evaluated). |
| `score` | number \| null | Score of that evaluation (0-100, one decimal place). |
| `insurable` | boolean \| null | Insurable flag of that evaluation. |
| `started` | integer \| null | When the scan started. Unix timestamp in seconds. |
| `finished` | integer \| null | When the scan finished. Unix timestamp in seconds. |
| `created` | integer | When the scan was queued. Unix timestamp in seconds. |

## Run a scan {#createScan}

`POST /v1/sites/{site}/scans`

Queues a synthetic-shopper scan of the site. Poll retrieveScan (or wait for the scan.completed webhook); a scan takes 1-3 minutes. When it completes, the evaluation and findings are updated. In live mode the account must be activated.

**Scope** `scans:write` · **Idempotent** (send `Idempotency-Key`) · **Live mode needs [activation](https://checkbox.site/docs/concepts/activation)** · **Operation** `createScan`

SDK: `checkbox.scans.create()` (Node) · `checkbox.scans.create()` (Python). MCP / agents: operation `createScan`.

#### Parameters

| Parameter | In | Type | Description |
|---|---|---|---|
| `site` **required** | path | string | Unique identifier of the site (site_…). |

#### Body

| Field | Type | Description |
|---|---|---|
| `regions` | array of enum | States to scan from (default: the site configuration). |

#### Returns

Returns a [Scan](https://checkbox.site/docs/api/scans#the-scan-object) object.

In live mode before activation: `403 account_not_activated` with `next_action.operation_id: "createDomainVerification"`.

Errors use the [standard envelope](https://checkbox.site/docs/api/errors) with a stable `code` and, where useful, `next_action`.

```curl
curl https://app.checkbox.site/v1/sites/site_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b/scans \
  -u "$CHECKBOX_SECRET_KEY:" \
  -H "Content-Type: application/json" \
  -d '{}'
```
```node
import Checkbox from '@checkbox-site/cli';

const checkbox = new Checkbox(process.env.CHECKBOX_SECRET_KEY);

const scan = await checkbox.scans.create('site_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b');
console.log(scan);
```
```python
import os
from checkbox import Checkbox

checkbox = Checkbox(os.environ["CHECKBOX_SECRET_KEY"])

scan = checkbox.scans.create("site_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b")
print(scan)
```

```json title="Response"
{
  "id": "scan_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
  "object": "scan",
  "livemode": false,
  "site": "site_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
  "status": "completed",
  "trigger": "api",
  "regions": [
    "US-CA",
    "US-NY"
  ],
  "error": null,
  "warnings": [],
  "evaluation": "eval_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
  "score": 82,
  "insurable": false,
  "started": 1793318400,
  "finished": 1793318400,
  "created": 1793318400
}
```

## List scans {#listScans}

`GET /v1/scans`

Scans newest first, optionally for one site.

**Scope** `scans:read` · **Operation** `listScans`

SDK: `checkbox.scans.list()` (Node) · `checkbox.scans.list()` (Python). MCP / agents: operation `listScans`.

#### Parameters

| Parameter | In | Type | Description |
|---|---|---|---|
| `limit` | query | integer | Number of objects to return, 1-100 (default 10). |
| `starting_after` | query | string | Cursor: an object id; returns the objects after it (older). |
| `ending_before` | query | string | Cursor: an object id; returns the objects before it (newer). |
| `site` | query | string | Only objects of this site (site_…). |

#### Returns

Returns a list (`{ object: "list", data, has_more, url }`) of [Scan](https://checkbox.site/docs/api/scans#the-scan-object) objects.

Errors use the [standard envelope](https://checkbox.site/docs/api/errors) with a stable `code` and, where useful, `next_action`.

```curl
curl -G https://app.checkbox.site/v1/scans \
  -u "$CHECKBOX_SECRET_KEY:" \
  -d limit=3
```
```node
import Checkbox from '@checkbox-site/cli';

const checkbox = new Checkbox(process.env.CHECKBOX_SECRET_KEY);

const scans = await checkbox.scans.list({ limit: 3 });
console.log(scans.data);
```
```python
import os
from checkbox import Checkbox

checkbox = Checkbox(os.environ["CHECKBOX_SECRET_KEY"])

scans = checkbox.scans.list(limit=3)
print(scans.data)
```

```json title="Response"
{
  "object": "list",
  "data": [
    {
      "id": "scan_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
      "object": "scan",
      "livemode": false,
      "site": "site_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
      "status": "completed",
      "trigger": "api",
      "regions": [
        "US-CA"
      ],
      "error": null,
      "warnings": [],
      "evaluation": "eval_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
      "score": 82,
      "insurable": false,
      "started": 1793318400,
      "finished": 1793318400,
      "created": 1793318400
    }
  ],
  "has_more": false,
  "url": "/v1/sites"
}
```

## Retrieve a scan {#retrieveScan}

`GET /v1/scans/{scan}`

Returns a scan. When status is completed, `evaluation` names the verdict (retrieveEvaluation).

**Scope** `scans:read` · **Operation** `retrieveScan`

SDK: `checkbox.scans.retrieve()` (Node) · `checkbox.scans.retrieve()` (Python). MCP / agents: operation `retrieveScan`.

#### Parameters

| Parameter | In | Type | Description |
|---|---|---|---|
| `scan` **required** | path | string | Unique identifier of the scan (scan_…). |

#### Returns

Returns a [Scan](https://checkbox.site/docs/api/scans#the-scan-object) object.

Errors use the [standard envelope](https://checkbox.site/docs/api/errors) with a stable `code` and, where useful, `next_action`.

```curl
curl https://app.checkbox.site/v1/scans/scan_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b \
  -u "$CHECKBOX_SECRET_KEY:"
```
```node
import Checkbox from '@checkbox-site/cli';

const checkbox = new Checkbox(process.env.CHECKBOX_SECRET_KEY);

const scan = await checkbox.scans.retrieve('scan_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b');
console.log(scan);
```
```python
import os
from checkbox import Checkbox

checkbox = Checkbox(os.environ["CHECKBOX_SECRET_KEY"])

scan = checkbox.scans.retrieve("scan_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b")
print(scan)
```

```json title="Response"
{
  "id": "scan_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
  "object": "scan",
  "livemode": false,
  "site": "site_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
  "status": "completed",
  "trigger": "api",
  "regions": [
    "US-CA",
    "US-NY"
  ],
  "error": null,
  "warnings": [],
  "evaluation": "eval_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
  "score": 82,
  "insurable": false,
  "started": 1793318400,
  "finished": 1793318400,
  "created": 1793318400
}
```
