# Compliance plans API

> The agent-to-agent fix loop: an ordered, machine-readable plan of changes (requirement + citation, where, the exact patch / copy / Stripe params, how to verify), verification of each change, and questions for the human where a machine cannot decide.

| Operation | Endpoint |
|---|---|
| [Get the compliance plan for a site](#retrieveCompliancePlan) | `GET /v1/sites/{site}/plan` |
| [Get one task of a compliance plan](#retrieveTask) | `GET /v1/tasks/{task}` |
| [Claim, decline or reopen a task](#updateTask) | `POST /v1/tasks/{task}` |
| [Record the human answer to a manual task](#answerTask) | `POST /v1/tasks/{task}/answer` |
| [Verify a task after applying its change](#verifyTask) | `POST /v1/tasks/{task}/verify` |
| [Re-check the whole site now](#verifySite) | `POST /v1/sites/{site}/verify` |
| [Get a verification (long-poll with ?wait=30)](#retrieveVerification) | `GET /v1/verifications/{verification}` |
| [List verifications of a site](#listVerifications) | `GET /v1/sites/{site}/verifications` |

## The CompliancePlan object

The ordered, machine-readable list of changes that makes a site compliant, recomputed from its latest evaluation.

| Field | Type | Description |
|---|---|---|
| `id` | string | Unique identifier of the compliance plan (plan_…). |
| `object` | enum | String representing the object's type. Always "compliance_plan". |
| `livemode` | boolean | true for live-mode objects, false for test mode (ck_test_ keys act on an isolated sandbox). |
| `site` | string | The site. |
| `status` | enum | action_required (an agent can apply open tasks now), verifying (changes applied; waiting for confirming observations), compliant (every applicable check Checkbox could observe passes — an automated assessment, not a legal determination; see coverage for what was not observed), blocked (only human answers or declined critical tasks remain). One of `action_required`, `verifying`, `compliant`, `blocked`. |
| `score` | integer \| null | Compliance score 0-100 of the latest evaluation. |
| `insurable` | boolean \| null | Preview flag the planned Checkbox Warranty will key on (no checks failing at high severity). No warranty or insurance is offered yet; this is not an underwriting decision. |
| `evaluation` | string \| null | The evaluation the plan was built from (null before the first check). |
| `evaluated` | integer \| null | When that evaluation ran. Unix timestamp in seconds. |
| `summary` | string | Two or three sentences for an LLM: where the site stands and what comes next. |
| `counts` | object | Tasks per status. |
| `counts.open` | integer | Open tasks. |
| `counts.in_progress` | integer | Claimed tasks. |
| `counts.verifying` | integer | Tasks waiting for a confirming observation. |
| `counts.manual` | integer | Tasks waiting for a human answer. |
| `counts.done` | integer | Done tasks. |
| `counts.wont_fix` | integer | Declined tasks. |
| `tasks` | array of Tasks | Tasks in the order to do them (done tasks last). |
| `coverage` | object | What the assessment rests on. Tell the human about not_observed and attested items; never call the site "compliant" without them. |
| `coverage.observed` | array of strings | Lifecycle stages Checkbox observed (signup, acknowledgment_email, reminders, cancellation, policies, billing_settings). |
| `coverage.not_observed` | array of strings | Stages Checkbox has no observation of: their checks cannot fail, so passing says nothing about them. |
| `coverage.attested` | array of strings | Task keys resting on a human confirmation instead of an observation. |
| `coverage.billing_connected` | boolean | A billing platform is connected (dashboard). |
| `next_action` | PlanNextAction | What the caller (usually an AI coding agent) should do next. |
| `rules_version` | string \| null | Rules / catalog version of the evaluation. |
| `disclaimer` | string | Automated assessment; not legal advice. |
| `generated` | integer | When the plan was computed. Unix timestamp in seconds. |

## Get the compliance plan for a site {#retrieveCompliancePlan}

`GET /v1/sites/{site}/plan`

Ordered tasks that make the site compliant, recomputed from its latest evaluation: each with the requirement and citation, where to change (file / URL / selector / Stripe object), the exact change (unified-diff template with machine-applicable edits, copy, or Stripe params) and how to verify it. Do plan.next_action: apply the first open task, call verifyTask, repeat until status is compliant. Tasks with status manual hold a question for the human (answerTask); they never block the others.

**Scope** `compliance_plans:read` · **Operation** `retrieveCompliancePlan`

SDK: `checkbox.plans.retrieve()` (Node) · `checkbox.plans.retrieve()` (Python). MCP / agents: operation `retrieveCompliancePlan`.

#### Parameters

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

#### Returns

Returns a [CompliancePlan](https://checkbox.site/docs/api/compliance-plans#the-complianceplan-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/sites/site_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b/plan \
  -u "$CHECKBOX_SECRET_KEY:"
```
```node
import Checkbox from '@checkbox-site/cli';

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

const plan = await checkbox.plans.retrieve('site_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b');
console.log(plan);
```
```python
import os
from checkbox import Checkbox

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

plan = checkbox.plans.retrieve("site_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b")
print(plan)
```

```json title="Response"
{
  "id": "plan_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
  "object": "compliance_plan",
  "livemode": false,
  "site": "site_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
  "status": "action_required",
  "score": 62,
  "insurable": false,
  "evaluation": "eval_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
  "evaluated": 1793318400,
  "summary": "StreamMax fails 4 critical checks…",
  "counts": {
    "open": 3,
    "in_progress": 0,
    "verifying": 1,
    "manual": 1,
    "done": 2,
    "wont_fix": 0
  },
  "tasks": [
    {
      "id": "task_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
      "object": "task",
      "livemode": false,
      "site": "site_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
      "key": "signup_disclosure",
      "title": "Show the renewal terms and an unchecked consent box above the Subscribe button",
      "status": "open",
      "priority": "critical",
      "order": 1,
      "check_ids": [
        "signup.disclosure.autorenew_language"
      ],
      "findings": [],
      "requirement": {
        "jurisdictions": [
          "US-FED"
        ],
        "citation": "15 U.S.C. § 8403(1); Cal. Bus. & Prof. Code § 17602(a)(1)",
        "citations": [
          {
            "id": "US-CA.17602.a.1",
            "jurisdiction": "US-CA",
            "citation": "Cal. Bus. & Prof. Code § 17602(a)(1)",
            "source_url": "https://leginfo.legislature.ca.gov/"
          }
        ],
        "plain_language": "Before the purchase, state clearly that the subscription renews automatically until canceled, the price and frequency, and how to cancel."
      },
      "why": "The checkout in US-CA shows no renewal terms near \"Start now\"; the consent checkbox is pre-checked.",
      "where": {
        "kind": "code",
        "file_hint": "app/checkout/page.tsx",
        "url": "https://shop.example/checkout",
        "selector": "#subscribe-button",
        "stripe_object": "checkout.session"
      },
      "change": {
        "type": "patch",
        "content": "--- a/app/checkout/page.tsx\n+++ b/app/checkout/page.tsx\n@@ …",
        "language": "html",
        "explanation": "Insert directly above the purchase button; never pre-check the box.",
        "edits": [
          {
            "op": "insert_before",
            "target": {},
            "content": "<div class=\"checkbox-renewal-terms\">…</div>",
            "attribute": "checked"
          }
        ],
        "engine_verified": true,
        "operation_id": "updateSite",
        "endpoint": "POST /v1/sites/site_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b"
      },
      "verify": {
        "method": "page_fetch",
        "how": "POST /v1/tasks/task_…/verify",
        "expected": "Every listed check passes (layout checks may stay needs_review on a static fetch until the confirming scan)."
      },
      "blocked_on": null,
      "closed_reason": null,
      "depends_on": [],
      "blocked_by": [],
      "question": {
        "text": "Did your subscribers choose email as the channel for account notices?",
        "options": [
          "yes"
        ],
        "why": "New York requires notices in the manner the subscriber selected; no page or email shows that choice.",
        "effect": "yes records the attestation (site fact notice_channel_email) and re-evaluates."
      },
      "answer": {
        "value": "yes",
        "note": null,
        "answered": 1793318400,
        "confirmed_by_human": false,
        "pending_confirmation_url": null
      },
      "decision": {
        "status": "wont_fix",
        "reason": "Annual plan retired; no subscribers left."
      },
      "last_verification": {
        "id": "ver_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
        "status": "failed",
        "created": 1793318400
      },
      "next_action": {
        "type": "apply_task",
        "description": "Apply task_… (add the renewal terms above the Subscribe button in app/checkout/page.tsx), then call verifyTask.",
        "task": "task_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
        "operation_id": "verifyTask",
        "mcp_tool": "verify_task",
        "endpoint": "POST /v1/tasks/task_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b/verify",
        "command": "checkbox fix task_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b"
      },
      "first_seen": 1793318400,
      "done_at": 1793318400
    }
  ],
  "coverage": {
    "observed": [
      "signup"
    ],
    "not_observed": [
      "cancellation"
    ],
    "attested": [],
    "billing_connected": false
  },
  "next_action": {
    "type": "apply_task",
    "description": "Apply task_… (add the renewal terms above the Subscribe button in app/checkout/page.tsx), then call verifyTask.",
    "task": "task_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
    "operation_id": "verifyTask",
    "mcp_tool": "verify_task",
    "endpoint": "POST /v1/tasks/task_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b/verify",
    "command": "checkbox fix task_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b"
  },
  "rules_version": "1.2.0",
  "disclaimer": "Checkbox checks against its legal catalog; it is not legal advice.",
  "generated": 1793318400
}
```

## Get one task of a compliance plan {#retrieveTask}

`GET /v1/tasks/{task}`

The task as in the current plan (recomputed): the change to apply, how to verify, its last verification and next_action.

**Scope** `compliance_plans:read` · **Operation** `retrieveTask`

SDK: `checkbox.tasks.retrieve()` (Node) · `checkbox.tasks.retrieve()` (Python). MCP / agents: operation `retrieveTask`.

#### Parameters

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

#### Returns

Returns a `Task` 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/tasks/task_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b \
  -u "$CHECKBOX_SECRET_KEY:"
```
```node
import Checkbox from '@checkbox-site/cli';

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

const task = await checkbox.tasks.retrieve('task_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b');
console.log(task);
```
```python
import os
from checkbox import Checkbox

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

task = checkbox.tasks.retrieve("task_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b")
print(task)
```

```json title="Response"
{
  "id": "task_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
  "object": "task",
  "livemode": false,
  "site": "site_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
  "key": "signup_disclosure",
  "title": "Show the renewal terms and an unchecked consent box above the Subscribe button",
  "status": "open",
  "priority": "critical",
  "order": 1,
  "check_ids": [
    "signup.disclosure.autorenew_language",
    "signup.consent.affirmative"
  ],
  "findings": [],
  "requirement": {
    "jurisdictions": [
      "US-FED"
    ],
    "citation": "15 U.S.C. § 8403(1); Cal. Bus. & Prof. Code § 17602(a)(1)",
    "citations": [
      {
        "id": "US-CA.17602.a.1",
        "jurisdiction": "US-CA",
        "citation": "Cal. Bus. & Prof. Code § 17602(a)(1)",
        "source_url": "https://leginfo.legislature.ca.gov/"
      }
    ],
    "plain_language": "Before the purchase, state clearly that the subscription renews automatically until canceled, the price and frequency, and how to cancel."
  },
  "why": "The checkout in US-CA shows no renewal terms near \"Start now\"; the consent checkbox is pre-checked.",
  "where": {
    "kind": "code",
    "file_hint": "app/checkout/page.tsx",
    "url": "https://shop.example/checkout",
    "selector": "#subscribe-button",
    "stripe_object": "checkout.session"
  },
  "change": {
    "type": "patch",
    "content": "--- a/app/checkout/page.tsx\n+++ b/app/checkout/page.tsx\n@@ …",
    "language": "html",
    "explanation": "Insert directly above the purchase button; never pre-check the box.",
    "edits": [
      {
        "op": "insert_before",
        "target": {
          "selector": "#subscribe-button",
          "text": "Start now",
          "tag": "button"
        },
        "content": "<div class=\"checkbox-renewal-terms\">…</div>",
        "attribute": "checked"
      }
    ],
    "engine_verified": true,
    "operation_id": "updateSite",
    "endpoint": "POST /v1/sites/site_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b"
  },
  "verify": {
    "method": "page_fetch",
    "how": "POST /v1/tasks/task_…/verify",
    "expected": "Every listed check passes (layout checks may stay needs_review on a static fetch until the confirming scan)."
  },
  "blocked_on": null,
  "closed_reason": null,
  "depends_on": [],
  "blocked_by": [],
  "question": {
    "text": "Did your subscribers choose email as the channel for account notices?",
    "options": [
      "yes"
    ],
    "why": "New York requires notices in the manner the subscriber selected; no page or email shows that choice.",
    "effect": "yes records the attestation (site fact notice_channel_email) and re-evaluates."
  },
  "answer": {
    "value": "yes",
    "note": null,
    "answered": 1793318400,
    "confirmed_by_human": false,
    "pending_confirmation_url": null
  },
  "decision": {
    "status": "wont_fix",
    "reason": "Annual plan retired; no subscribers left."
  },
  "last_verification": {
    "id": "ver_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
    "status": "failed",
    "created": 1793318400
  },
  "next_action": {
    "type": "apply_task",
    "description": "Apply task_… (add the renewal terms above the Subscribe button in app/checkout/page.tsx), then call verifyTask.",
    "task": "task_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
    "operation_id": "verifyTask",
    "mcp_tool": "verify_task",
    "endpoint": "POST /v1/tasks/task_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b/verify",
    "command": "checkbox fix task_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b"
  },
  "first_seen": 1793318400,
  "done_at": 1793318400
}
```

## Claim, decline or reopen a task {#updateTask}

`POST /v1/tasks/{task}`

status in_progress claims the task (shown to other agents and the dashboard); wont_fix declines it with a reason (the merchant's decision, recorded; a declined critical or high task keeps the plan blocked); open clears the decision. Applying a change does not need this call: call verifyTask.

**Scope** `compliance_plans:write` · **Idempotent** (send `Idempotency-Key`) · **Operation** `updateTask`

SDK: `checkbox.tasks.update()` (Node) · `checkbox.tasks.update()` (Python). MCP / agents: operation `updateTask`.

#### Parameters

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

#### Body

| Field | Type | Description |
|---|---|---|
| `status` **required** | enum | in_progress claims the task (other agents see it), wont_fix declines it (needs reason; a declined critical/high task keeps the plan from being compliant), open clears either. One of `open`, `in_progress`, `wont_fix`. |
| `reason` | string | Why (required for wont_fix). |

#### Returns

Returns a `Task` 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/tasks/task_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b \
  -u "$CHECKBOX_SECRET_KEY:" \
  -H "Content-Type: application/json" \
  -d '{
  "status": "in_progress"
}'
```
```node
import Checkbox from '@checkbox-site/cli';

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

const task = await checkbox.tasks.update('task_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b', { status: 'in_progress' });
console.log(task);
```
```python
import os
from checkbox import Checkbox

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

task = checkbox.tasks.update("task_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b", status="in_progress")
print(task)
```

```json title="Response"
{
  "id": "task_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
  "object": "task",
  "livemode": false,
  "site": "site_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
  "key": "signup_disclosure",
  "title": "Show the renewal terms and an unchecked consent box above the Subscribe button",
  "status": "open",
  "priority": "critical",
  "order": 1,
  "check_ids": [
    "signup.disclosure.autorenew_language",
    "signup.consent.affirmative"
  ],
  "findings": [],
  "requirement": {
    "jurisdictions": [
      "US-FED"
    ],
    "citation": "15 U.S.C. § 8403(1); Cal. Bus. & Prof. Code § 17602(a)(1)",
    "citations": [
      {
        "id": "US-CA.17602.a.1",
        "jurisdiction": "US-CA",
        "citation": "Cal. Bus. & Prof. Code § 17602(a)(1)",
        "source_url": "https://leginfo.legislature.ca.gov/"
      }
    ],
    "plain_language": "Before the purchase, state clearly that the subscription renews automatically until canceled, the price and frequency, and how to cancel."
  },
  "why": "The checkout in US-CA shows no renewal terms near \"Start now\"; the consent checkbox is pre-checked.",
  "where": {
    "kind": "code",
    "file_hint": "app/checkout/page.tsx",
    "url": "https://shop.example/checkout",
    "selector": "#subscribe-button",
    "stripe_object": "checkout.session"
  },
  "change": {
    "type": "patch",
    "content": "--- a/app/checkout/page.tsx\n+++ b/app/checkout/page.tsx\n@@ …",
    "language": "html",
    "explanation": "Insert directly above the purchase button; never pre-check the box.",
    "edits": [
      {
        "op": "insert_before",
        "target": {
          "selector": "#subscribe-button",
          "text": "Start now",
          "tag": "button"
        },
        "content": "<div class=\"checkbox-renewal-terms\">…</div>",
        "attribute": "checked"
      }
    ],
    "engine_verified": true,
    "operation_id": "updateSite",
    "endpoint": "POST /v1/sites/site_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b"
  },
  "verify": {
    "method": "page_fetch",
    "how": "POST /v1/tasks/task_…/verify",
    "expected": "Every listed check passes (layout checks may stay needs_review on a static fetch until the confirming scan)."
  },
  "blocked_on": null,
  "closed_reason": null,
  "depends_on": [],
  "blocked_by": [],
  "question": {
    "text": "Did your subscribers choose email as the channel for account notices?",
    "options": [
      "yes"
    ],
    "why": "New York requires notices in the manner the subscriber selected; no page or email shows that choice.",
    "effect": "yes records the attestation (site fact notice_channel_email) and re-evaluates."
  },
  "answer": {
    "value": "yes",
    "note": null,
    "answered": 1793318400,
    "confirmed_by_human": false,
    "pending_confirmation_url": null
  },
  "decision": {
    "status": "wont_fix",
    "reason": "Annual plan retired; no subscribers left."
  },
  "last_verification": {
    "id": "ver_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
    "status": "failed",
    "created": 1793318400
  },
  "next_action": {
    "type": "apply_task",
    "description": "Apply task_… (add the renewal terms above the Subscribe button in app/checkout/page.tsx), then call verifyTask.",
    "task": "task_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
    "operation_id": "verifyTask",
    "mcp_tool": "verify_task",
    "endpoint": "POST /v1/tasks/task_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b/verify",
    "command": "checkbox fix task_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b"
  },
  "first_seen": 1793318400,
  "done_at": 1793318400
}
```

## Record the human answer to a manual task {#answerTask}

`POST /v1/tasks/{task}/answer`

For tasks with status manual: record the human's answer to task.question (ask them; never answer on their behalf). Answers that assert a legal fact (yes, confirmed) are stored as a proposal and return next_action.type "ask_human_to_confirm" with a link the human opens while signed in to Checkbox; only their confirmation writes the attestation, sets the site fact and re-evaluates. no / needs_change turn the task into a change for the agent; done (after a dashboard-only step) re-checks at once. Every answer is stored in the evidence vault.

**Scope** `compliance_plans:write` · **Idempotent** (send `Idempotency-Key`) · **Operation** `answerTask`

SDK: `checkbox.tasks.answer()` (Node) · `checkbox.tasks.answer()` (Python). MCP / agents: operation `answerTask`.

#### Parameters

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

#### Body

| Field | Type | Description |
|---|---|---|
| `answer` **required** | string | One of the task's question.options (yes / no, or confirmed / needs_change). |
| `note` | string | Context from the human (stored with the attestation). |

#### Returns

Returns a `Task` 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/tasks/task_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b/answer \
  -u "$CHECKBOX_SECRET_KEY:" \
  -H "Content-Type: application/json" \
  -d '{
  "answer": "yes"
}'
```
```node
import Checkbox from '@checkbox-site/cli';

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

const task = await checkbox.tasks.answer('task_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b', { answer: 'yes' });
console.log(task);
```
```python
import os
from checkbox import Checkbox

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

task = checkbox.tasks.answer("task_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b", answer="yes")
print(task)
```

```json title="Response"
{
  "id": "task_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
  "object": "task",
  "livemode": false,
  "site": "site_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
  "key": "signup_disclosure",
  "title": "Show the renewal terms and an unchecked consent box above the Subscribe button",
  "status": "open",
  "priority": "critical",
  "order": 1,
  "check_ids": [
    "signup.disclosure.autorenew_language",
    "signup.consent.affirmative"
  ],
  "findings": [],
  "requirement": {
    "jurisdictions": [
      "US-FED"
    ],
    "citation": "15 U.S.C. § 8403(1); Cal. Bus. & Prof. Code § 17602(a)(1)",
    "citations": [
      {
        "id": "US-CA.17602.a.1",
        "jurisdiction": "US-CA",
        "citation": "Cal. Bus. & Prof. Code § 17602(a)(1)",
        "source_url": "https://leginfo.legislature.ca.gov/"
      }
    ],
    "plain_language": "Before the purchase, state clearly that the subscription renews automatically until canceled, the price and frequency, and how to cancel."
  },
  "why": "The checkout in US-CA shows no renewal terms near \"Start now\"; the consent checkbox is pre-checked.",
  "where": {
    "kind": "code",
    "file_hint": "app/checkout/page.tsx",
    "url": "https://shop.example/checkout",
    "selector": "#subscribe-button",
    "stripe_object": "checkout.session"
  },
  "change": {
    "type": "patch",
    "content": "--- a/app/checkout/page.tsx\n+++ b/app/checkout/page.tsx\n@@ …",
    "language": "html",
    "explanation": "Insert directly above the purchase button; never pre-check the box.",
    "edits": [
      {
        "op": "insert_before",
        "target": {
          "selector": "#subscribe-button",
          "text": "Start now",
          "tag": "button"
        },
        "content": "<div class=\"checkbox-renewal-terms\">…</div>",
        "attribute": "checked"
      }
    ],
    "engine_verified": true,
    "operation_id": "updateSite",
    "endpoint": "POST /v1/sites/site_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b"
  },
  "verify": {
    "method": "page_fetch",
    "how": "POST /v1/tasks/task_…/verify",
    "expected": "Every listed check passes (layout checks may stay needs_review on a static fetch until the confirming scan)."
  },
  "blocked_on": null,
  "closed_reason": null,
  "depends_on": [],
  "blocked_by": [],
  "question": {
    "text": "Did your subscribers choose email as the channel for account notices?",
    "options": [
      "yes"
    ],
    "why": "New York requires notices in the manner the subscriber selected; no page or email shows that choice.",
    "effect": "yes records the attestation (site fact notice_channel_email) and re-evaluates."
  },
  "answer": {
    "value": "yes",
    "note": null,
    "answered": 1793318400,
    "confirmed_by_human": false,
    "pending_confirmation_url": null
  },
  "decision": {
    "status": "wont_fix",
    "reason": "Annual plan retired; no subscribers left."
  },
  "last_verification": {
    "id": "ver_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
    "status": "failed",
    "created": 1793318400
  },
  "next_action": {
    "type": "apply_task",
    "description": "Apply task_… (add the renewal terms above the Subscribe button in app/checkout/page.tsx), then call verifyTask.",
    "task": "task_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
    "operation_id": "verifyTask",
    "mcp_tool": "verify_task",
    "endpoint": "POST /v1/tasks/task_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b/verify",
    "command": "checkbox fix task_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b"
  },
  "first_seen": 1793318400,
  "done_at": 1793318400
}
```

## Verify a task after applying its change {#verifyTask}

`POST /v1/tasks/{task}/verify`

Runs the fastest re-check that can confirm the task (static fetch of the live checkout or the html you send, the Stripe params you send, the email copy you send, a re-fetch of the Terms) and queues the confirming observation (a scan, a billing-platform sync, the next email). A failing pre-check fails the verification at once with a diff. Pass ?wait=30 to long-poll; otherwise poll retrieveVerification or wait for the task.verified webhook. Then follow next_action.

**Scope** `compliance_plans:write` · **Idempotent** (send `Idempotency-Key`) · **Operation** `verifyTask`

SDK: `checkbox.tasks.verify()` (Node) · `checkbox.tasks.verify()` (Python). MCP / agents: operation `verifyTask`.

#### Parameters

| Parameter | In | Type | Description |
|---|---|---|---|
| `task` **required** | path | string | Unique identifier of the task (task_…). |
| `wait` | query | integer \| null | Long-poll: wait up to this many seconds (0-30) for the verification to resolve before answering. |

#### Body

| Field | Type | Description |
|---|---|---|
| `html` | string | page_fetch tasks: the rendered page HTML before you deploy, at most ~900 KB (request bodies are limited to 1 MB). Pre-check only; the confirming scan runs on the live page. |
| `stripe_checkout_session` | object | stripe_params tasks: exactly what your code now passes to stripe.checkout.sessions.create. |
| `email` | object | email_copy tasks: the new email template (pre-check; the real email confirms). |
| `email.kind` | enum | Email kind (default: from the task). One of `acknowledgment`, `trial_reminder`, `renewal_reminder`, `price_change`, `annual_terms`, `charge_notice`, `cancellation_confirmation`. |
| `email.subject` **required** | string | Subject. |
| `email.text` | string | Plain-text body. |
| `email.html` | string | HTML body. |

#### Returns

Returns a `Verification` 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/tasks/task_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b/verify \
  -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 task = await checkbox.tasks.verify('task_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b');
console.log(task);
```
```python
import os
from checkbox import Checkbox

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

task = checkbox.tasks.verify("task_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b")
print(task)
```

```json title="Response"
{
  "id": "ver_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
  "object": "verification",
  "livemode": false,
  "site": "site_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
  "task": "task_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
  "scope": "task",
  "status": "pending",
  "method": "page_fetch",
  "precheck": {
    "source": "static_fetch",
    "verdict": "pass",
    "url": "https://shop.example/checkout",
    "note": "Static fetch: text checks are exact; layout is estimated until the confirming scan."
  },
  "scan": "scan_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
  "checks": [
    {
      "check_id": "signup.disclosure.price",
      "expected": "pass",
      "observed": "fail",
      "source": "static_fetch",
      "detail": "No recurring price near the purchase button."
    }
  ],
  "diff": [
    {
      "check_id": "signup.disclosure.price",
      "expected": "pass",
      "observed": "fail",
      "source": "static_fetch",
      "detail": "No recurring price near the purchase button."
    }
  ],
  "plan_status": "action_required",
  "next_action": {
    "type": "apply_task",
    "description": "Apply task_… (add the renewal terms above the Subscribe button in app/checkout/page.tsx), then call verifyTask.",
    "task": "task_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
    "operation_id": "verifyTask",
    "mcp_tool": "verify_task",
    "endpoint": "POST /v1/tasks/task_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b/verify",
    "command": "checkbox fix task_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b"
  },
  "created": 1793318400,
  "completed": 1793318400,
  "expires": 1793318400
}
```

## Re-check the whole site now {#verifySite}

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

Re-fetches the registered policies, pre-checks the checkout, re-evaluates and queues a scan. Use it for the first check after setup, or after applying several tasks. Pass ?wait=30 to long-poll; then get the plan (retrieveCompliancePlan).

**Scope** `compliance_plans:write` · **Idempotent** (send `Idempotency-Key`) · **Operation** `verifySite`

SDK: `checkbox.sites.verify()` (Node) · `checkbox.sites.verify()` (Python). MCP / agents: operation `verifySite`.

#### Parameters

| Parameter | In | Type | Description |
|---|---|---|---|
| `site` **required** | path | string | Unique identifier of the site (site_…). |
| `wait` | query | integer \| null | Long-poll: wait up to this many seconds (0-30) for the verification to resolve before answering. |

#### Returns

Returns a `Verification` 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/sites/site_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b/verify \
  -u "$CHECKBOX_SECRET_KEY:" \
  -X POST
```
```node
import Checkbox from '@checkbox-site/cli';

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

const site = await checkbox.sites.verify('site_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b');
console.log(site);
```
```python
import os
from checkbox import Checkbox

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

site = checkbox.sites.verify("site_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b")
print(site)
```

```json title="Response"
{
  "id": "ver_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
  "object": "verification",
  "livemode": false,
  "site": "site_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
  "task": "task_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
  "scope": "task",
  "status": "pending",
  "method": "page_fetch",
  "precheck": {
    "source": "static_fetch",
    "verdict": "pass",
    "url": "https://shop.example/checkout",
    "note": "Static fetch: text checks are exact; layout is estimated until the confirming scan."
  },
  "scan": "scan_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
  "checks": [
    {
      "check_id": "signup.disclosure.price",
      "expected": "pass",
      "observed": "fail",
      "source": "static_fetch",
      "detail": "No recurring price near the purchase button."
    }
  ],
  "diff": [
    {
      "check_id": "signup.disclosure.price",
      "expected": "pass",
      "observed": "fail",
      "source": "static_fetch",
      "detail": "No recurring price near the purchase button."
    }
  ],
  "plan_status": "action_required",
  "next_action": {
    "type": "apply_task",
    "description": "Apply task_… (add the renewal terms above the Subscribe button in app/checkout/page.tsx), then call verifyTask.",
    "task": "task_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
    "operation_id": "verifyTask",
    "mcp_tool": "verify_task",
    "endpoint": "POST /v1/tasks/task_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b/verify",
    "command": "checkbox fix task_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b"
  },
  "created": 1793318400,
  "completed": 1793318400,
  "expires": 1793318400
}
```

## Get a verification (long-poll with ?wait=30) {#retrieveVerification}

`GET /v1/verifications/{verification}`

The verification's status (pending, passed, failed), expected vs observed per check, the diff and next_action. ?wait=N (≤ 30) holds the request until it resolves or N seconds pass.

**Scope** `compliance_plans:read` · **Operation** `retrieveVerification`

SDK: `checkbox.verifications.retrieve()` (Node) · `checkbox.verifications.retrieve()` (Python). MCP / agents: operation `retrieveVerification`.

#### Parameters

| Parameter | In | Type | Description |
|---|---|---|---|
| `verification` **required** | path | string | Unique identifier of the verification (ver_…). |
| `wait` | query | integer \| null | Long-poll: wait up to this many seconds (0-30) for the verification to resolve before answering. |

#### Returns

Returns a `Verification` 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/verifications/ver_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b \
  -u "$CHECKBOX_SECRET_KEY:"
```
```node
import Checkbox from '@checkbox-site/cli';

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

const verification = await checkbox.verifications.retrieve('ver_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b');
console.log(verification);
```
```python
import os
from checkbox import Checkbox

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

verification = checkbox.verifications.retrieve("ver_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b")
print(verification)
```

```json title="Response"
{
  "id": "ver_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
  "object": "verification",
  "livemode": false,
  "site": "site_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
  "task": "task_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
  "scope": "task",
  "status": "pending",
  "method": "page_fetch",
  "precheck": {
    "source": "static_fetch",
    "verdict": "pass",
    "url": "https://shop.example/checkout",
    "note": "Static fetch: text checks are exact; layout is estimated until the confirming scan."
  },
  "scan": "scan_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
  "checks": [
    {
      "check_id": "signup.disclosure.price",
      "expected": "pass",
      "observed": "fail",
      "source": "static_fetch",
      "detail": "No recurring price near the purchase button."
    }
  ],
  "diff": [
    {
      "check_id": "signup.disclosure.price",
      "expected": "pass",
      "observed": "fail",
      "source": "static_fetch",
      "detail": "No recurring price near the purchase button."
    }
  ],
  "plan_status": "action_required",
  "next_action": {
    "type": "apply_task",
    "description": "Apply task_… (add the renewal terms above the Subscribe button in app/checkout/page.tsx), then call verifyTask.",
    "task": "task_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
    "operation_id": "verifyTask",
    "mcp_tool": "verify_task",
    "endpoint": "POST /v1/tasks/task_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b/verify",
    "command": "checkbox fix task_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b"
  },
  "created": 1793318400,
  "completed": 1793318400,
  "expires": 1793318400
}
```

## List verifications of a site {#listVerifications}

`GET /v1/sites/{site}/verifications`

Verifications newest first (optionally for one task).

**Scope** `compliance_plans:read` · **Operation** `listVerifications`

SDK: `checkbox.verifications.list()` (Node) · `checkbox.verifications.list()` (Python). MCP / agents: operation `listVerifications`.

#### Parameters

| Parameter | In | Type | Description |
|---|---|---|---|
| `site` **required** | path | string | Unique identifier of the site (site_…). |
| `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). |
| `task` | query | string | Only this task (task_…). |

#### Returns

Returns a list (`{ object: "list", data, has_more, url }`) of `Verification` 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/sites/site_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b/verifications \
  -u "$CHECKBOX_SECRET_KEY:" \
  -d limit=3
```
```node
import Checkbox from '@checkbox-site/cli';

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

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

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

verifications = checkbox.verifications.list("site_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b", limit=3)
print(verifications.data)
```

```json title="Response"
{
  "object": "list",
  "data": [
    {
      "id": "ver_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
      "object": "verification",
      "livemode": false,
      "site": "site_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
      "task": "task_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
      "scope": "task",
      "status": "pending",
      "method": "page_fetch",
      "precheck": {
        "source": "static_fetch",
        "verdict": "pass",
        "url": "https://shop.example/checkout",
        "note": "Static fetch: text checks are exact; layout is estimated until the confirming scan."
      },
      "scan": "scan_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
      "checks": [
        {
          "check_id": "signup.disclosure.price",
          "expected": "pass",
          "observed": "fail",
          "source": "static_fetch",
          "detail": "No recurring price near the purchase button."
        }
      ],
      "diff": [
        {
          "check_id": "signup.disclosure.price",
          "expected": "pass",
          "observed": "fail",
          "source": "static_fetch",
          "detail": "No recurring price near the purchase button."
        }
      ],
      "plan_status": "action_required",
      "next_action": {
        "type": "apply_task",
        "description": "Apply task_… (add the renewal terms above the Subscribe button in app/checkout/page.tsx), then call verifyTask.",
        "task": "task_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
        "operation_id": "verifyTask",
        "mcp_tool": "verify_task",
        "endpoint": "POST /v1/tasks/task_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b/verify",
        "command": "checkbox fix task_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b"
      },
      "created": 1793318400,
      "completed": 1793318400,
      "expires": 1793318400
    }
  ],
  "has_more": false,
  "url": "/v1/sites"
}
```
