# Checks API

> Preflight compliance checks (“check before you ship”) of disclosure text, Stripe params, lifecycle emails and checkout HTML — stateless, for AI coding agents and CI.

| Operation | Endpoint |
|---|---|
| [Check checkout disclosure text](#checkDisclosure) | `POST /v1/checks/disclosure` |
| [Check Stripe Checkout Session params](#checkStripeCheckoutSession) | `POST /v1/checks/stripe_checkout_session` |
| [Check Stripe Payment Link params](#checkStripePaymentLink) | `POST /v1/checks/stripe_payment_link` |
| [Check a subscription lifecycle email](#checkEmail) | `POST /v1/checks/email` |
| [Check a checkout page (HTML or URL)](#checkPage) | `POST /v1/checks/page` |
| [Get the dates each required notice must be sent](#checkNoticeSchedule) | `POST /v1/checks/notice_schedule` |

## The DisclosureCheckResult object

Verdict on a checkout disclosure plus a verified replacement.

| Field | Type | Description |
|---|---|---|
| `id` | string | Id of this check run (chk_…). Checks are stateless and not stored. |
| `object` | enum | String representing the object's type. Always "check_result". |
| `livemode` | boolean | true for live-mode objects, false for test mode (ck_test_ keys act on an isolated sandbox). |
| `created` | integer | When the check ran. Unix timestamp in seconds. |
| `kind` | enum | Which check ran. One of `disclosure`, `stripe_checkout_session`, `stripe_payment_link`, `email`, `page`. |
| `verdict` | enum | Overall: fail if any check fails, else needs_review if any needs review, else pass (not_applicable when nothing applies). One of `pass`, `fail`, `needs_review`, `not_applicable`. |
| `summary` | object | Counts by status. |
| `summary.pass` | integer | Checks passing. |
| `summary.fail` | integer | Checks failing. |
| `summary.needs_review` | integer | Checks needing review. |
| `summary.not_applicable` | integer | Checks not applicable. |
| `checks` | array of CheckVerdicts | Every evaluated check. |
| `requirements` | array of CheckRequirementVerdicts | Verdict per legal requirement (federal first, then by state). |
| `warnings` | array of CheckWarnings | Notes about the input. |
| `jurisdictions` | enum | Jurisdictions evaluated. |
| `as_of` | string | Date the law was evaluated at. |
| `rules_version` | string | Version of the check implementations. |
| `catalog_version` | string | Version of the legal catalog. |
| `catalog_hash` | string | sha256 of the canonical legal catalog. |
| `disclaimer` | string | Not legal advice. |
| `next_action` | NextAction | What the caller (often an AI agent) should do next. |
| `replacement` | CheckReplacement | A compliant replacement, verified by the engine. |

## Check checkout disclosure text {#checkDisclosure}

`POST /v1/checks/disclosure`

Checks the automatic-renewal disclosure you are about to show next to a subscribe button: what it says (renewal until canceled, price, frequency, trial, term, how to cancel), where it sits (above the button, same page) and how it looks (size, contrast, set off), and the consent checkbox (present, not pre-checked). Returns the verdict per check and per legal requirement, plus `replacement`: compliant text and drop-in HTML generated from your price/interval/trial/cancel_url and verified by the engine. Call it whenever you write or change checkout copy. Stateless and deterministic: nothing is stored, the same input on the same as_of date gives the same verdicts. Every verdict comes from the Checkbox rules engine over the legal catalog (federal ROSCA / EFTA plus the state automatic-renewal laws); the response carries rules_version, catalog_version and a disclaimer (not legal advice). Read `verdict`, then `next_action`: it tells you exactly what to change and to call the check again until verdict is pass.

**Scope** `checks:read` · **Operation** `checkDisclosure`

SDK: `checkbox.checks.disclosure()` (Node) · `checkbox.checks.disclosure()` (Python). MCP / agents: operation `checkDisclosure`.

#### Body

| Field | Type | Description |
|---|---|---|
| `text` **required** | string | The disclosure shown next to the purchase button, exactly as rendered. |
| `merchant_name` | string | Brand shown to subscribers; used in generated text. |
| `plan_name` | string | Plan / product name. |
| `price` | number | Recurring price in major units (19.99). Or give `amount` in minor units. |
| `amount` | integer | Recurring price in minor units (1999 = $19.99), Stripe style. |
| `currency` | string | Three-letter ISO currency (default usd). |
| `interval` **required** | string | Billing interval: day, week, month or year (required). |
| `interval_count` | integer | Number of intervals between charges (default 1). |
| `trial_days` | integer | Free-trial length in days (0 or absent = no trial). |
| `cancel_url` | string | https URL where subscribers cancel online (used in generated text; a {{cancel_url}} placeholder otherwise). |
| `cancel_timing` | enum | When a cancellation takes effect (default period_end). One of `period_end`, `immediately`. |
| `button_text` | string | Label of the purchase button (default "Subscribe", or "Start free trial" with a trial). |
| `order_summary` | string | Other text next to the button (order summary), if any. |
| `jurisdictions` | enum | Where you sell: "all" (default) or state codes ("US-CA", "CA", "US-NY"; "US-FED" is always included). |
| `placement` | object | Where and how the disclosure is shown. |
| `placement.above_button` | boolean | The disclosure is shown before / above the purchase button (default true). |
| `placement.same_page_as_payment` | boolean | The disclosure is on the same page as the payment form and button (default true). |
| `placement.font_size_px` | number | Rendered font size of the disclosure (default 14). |
| `placement.contrast_ratio` | number | WCAG contrast ratio of the text against its background (default ≈14.7). |
| `placement.bold` | boolean | The disclosure is bold / semibold. |
| `placement.boxed` | boolean | The disclosure is set off in a bordered or shaded box. |
| `placement.body_font_size_px` | number | Font size of the surrounding body text (default 14). |
| `consent` | object | The consent checkbox, if any. Omitted = no checkbox. |
| `consent.checkbox` **required** | boolean | A checkbox the customer must tick to agree to the renewal terms. |
| `consent.prechecked` | boolean | The checkbox is ticked by default. |
| `consent.label` | string | The checkbox label (default "I agree to the automatic renewal terms above."). |
| `consent.required` | boolean | The form cannot be submitted without ticking it (default true). |
| `as_of` | string | Evaluate the law as of this date (YYYY-MM-DD, default today). |

#### Returns

Returns a [DisclosureCheckResult](https://checkbox.site/docs/api/checks#the-disclosurecheckresult-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/checks/disclosure \
  -u "$CHECKBOX_SECRET_KEY:" \
  -H "Content-Type: application/json" \
  -d '{
  "text": "Your subscription renews automatically every month at $19.99 until you cancel. Cancel anytime online at https://joybox.example/account/cancel.",
  "interval": "month",
  "merchant_name": "Joybox",
  "price": 19.99,
  "cancel_url": "https://joybox.example/account/cancel"
}'
```
```node
import Checkbox from '@checkbox-site/cli';

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

const check = await checkbox.checks.disclosure({
  text: 'Your subscription renews automatically every month at $19.99 until you cancel. Cancel anytime online at https://joybox.example/account/cancel.',
  interval: 'month',
  merchant_name: 'Joybox',
  price: 19.99,
  cancel_url: 'https://joybox.example/account/cancel',
});
console.log(check);
```
```python
import os
from checkbox import Checkbox

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

check = checkbox.checks.disclosure(
    text="Your subscription renews automatically every month at $19.99 until you cancel. Cancel anytime online at https://joybox.example/account/cancel.",
    interval="month",
    merchant_name="Joybox",
    price=19.99,
    cancel_url="https://joybox.example/account/cancel",
)
print(check)
```

```json title="Response"
{
  "id": "chk_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
  "object": "check_result",
  "livemode": false,
  "created": 1793318400,
  "kind": "disclosure",
  "verdict": "fail",
  "summary": {
    "pass": 6,
    "fail": 2,
    "needs_review": 1,
    "not_applicable": 1
  },
  "checks": [
    {
      "check_id": "signup.disclosure.price",
      "status": "fail",
      "severity": "critical",
      "title": "Discloses the recurring charge amount",
      "explanation": "The recurring charge amount is not stated in the terms next to the purchase button.",
      "confidence": 0.85,
      "requirements": [
        {
          "id": "US-CA.17602.a.1",
          "jurisdiction": "US-CA",
          "citation": "Cal. Bus. & Prof. Code § 17602(a)(1)",
          "title": "Clear and conspicuous disclosure of automatic renewal offer terms",
          "source_url": "https://leginfo.legislature.ca.gov/"
        }
      ],
      "fix": "Show the recurring charge amount in the terms next to the purchase button.",
      "static_estimate": "pass"
    }
  ],
  "requirements": [
    {
      "id": "US-CA.17602.a.1",
      "jurisdiction": "US-CA",
      "citation": "Cal. Bus. & Prof. Code § 17602(a)(1)",
      "title": "Clear and conspicuous disclosure of automatic renewal offer terms",
      "source_url": "https://leginfo.legislature.ca.gov/"
    }
  ],
  "warnings": [
    {
      "code": "stripe_tos_url_required",
      "message": "consent_collection.terms_of_service needs a Terms of Service URL in Stripe.",
      "param": "params.consent_collection"
    }
  ],
  "jurisdictions": "all",
  "as_of": "2026-10-30",
  "rules_version": "1.2.0",
  "catalog_version": "2026.10.30.1",
  "catalog_hash": "b1c2…",
  "disclaimer": "Automated compliance check … not legal advice.",
  "next_action": {
    "type": "verify_domain",
    "message": "Verify your site domain to turn on live mode.",
    "operation_id": "createDomainVerification",
    "mcp_tool": "call_api",
    "url": "https://checkbox.site/docs/api/activation"
  },
  "replacement": {
    "disclosure_text": "Your Joybox subscription automatically renews every month at $19.99 …",
    "consent_label": "I agree that my Joybox subscription renews automatically …",
    "button_text": "Subscribe — $19.99/month, renews automatically",
    "html": "<div class=\"checkbox-renewal-terms\">…</div>",
    "placeholders": [
      "{{cancel_url}}"
    ],
    "verified": true,
    "remaining_checks": [],
    "sha256": "3e49…"
  }
}
```

## Check Stripe Checkout Session params {#checkStripeCheckoutSession}

`POST /v1/checks/stripe_checkout_session`

Pass exactly the object your code gives stripe.checkout.sessions.create (plus merchant.merchant_name and merchant.cancel_url, and merchant.price/interval when line_items use a Price id). Checkbox models the Stripe-hosted page those params render and returns findings plus `correction`: a JSON Patch (RFC 6902) and the full corrected params (custom_text.submit with the renewal terms, consent_collection.terms_of_service = "required", custom_text.terms_of_service_acceptance as the consent label), verified by the engine. Stripe's own small grey styling keeps "clear and conspicuous" at needs_review on any Stripe-hosted page; also show the terms on your page before redirecting. Stateless and deterministic: nothing is stored, the same input on the same as_of date gives the same verdicts. Every verdict comes from the Checkbox rules engine over the legal catalog (federal ROSCA / EFTA plus the state automatic-renewal laws); the response carries rules_version, catalog_version and a disclaimer (not legal advice). Read `verdict`, then `next_action`: it tells you exactly what to change and to call the check again until verdict is pass.

**Scope** `checks:read` · **Operation** `checkStripeCheckoutSession`

SDK: `checkbox.checks.stripeCheckoutSession()` (Node) · `checkbox.checks.stripe_checkout_session()` (Python). MCP / agents: operation `checkStripeCheckoutSession`.

#### Body

| Field | Type | Description |
|---|---|---|
| `params` **required** | object | Exactly the object your code passes to Stripe (checkout.sessions.create / paymentLinks.create), or a retrieved Payment Link. |
| `merchant` | CheckPlanTerms | What Stripe params do not say: merchant_name, cancel_url, and price / interval when line_items reference a Price id. |
| `merchant.merchant_name` | string | Brand shown to subscribers; used in generated text. |
| `merchant.plan_name` | string | Plan / product name. |
| `merchant.price` | number | Recurring price in major units (19.99). Or give `amount` in minor units. |
| `merchant.amount` | integer | Recurring price in minor units (1999 = $19.99), Stripe style. |
| `merchant.currency` | string | Three-letter ISO currency (default usd). |
| `merchant.interval` | string | Billing interval: day, week, month or year. |
| `merchant.interval_count` | integer | Number of intervals between charges (default 1). |
| `merchant.trial_days` | integer | Free-trial length in days (0 or absent = no trial). |
| `merchant.cancel_url` | string | https URL where subscribers cancel online (used in generated text; a {{cancel_url}} placeholder otherwise). |
| `merchant.cancel_timing` | enum | When a cancellation takes effect (default period_end). One of `period_end`, `immediately`. |
| `jurisdictions` | enum | Where you sell: "all" (default) or state codes ("US-CA", "CA", "US-NY"; "US-FED" is always included). |
| `as_of` | string | Evaluate the law as of this date (YYYY-MM-DD, default today). |

#### Returns

Returns a `StripeCheckoutSessionCheckResult` 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/checks/stripe_checkout_session \
  -u "$CHECKBOX_SECRET_KEY:" \
  -H "Content-Type: application/json" \
  -d '{
  "params": {
    "mode": "subscription",
    "line_items": [
      {
        "price_data": {
          "currency": "usd",
          "unit_amount": 1999,
          "recurring": {
            "interval": "month"
          },
          "product_data": {
            "name": "Joybox Pro"
          }
        },
        "quantity": 1
      }
    ],
    "success_url": "https://joybox.example/welcome"
  }
}'
```
```node
import Checkbox from '@checkbox-site/cli';

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

const check = await checkbox.checks.stripeCheckoutSession({
  params: {
    mode: 'subscription',
    line_items: [
      {
        price_data: {
          currency: 'usd',
          unit_amount: 1999,
          recurring: { interval: 'month' },
          product_data: { name: 'Joybox Pro' },
        },
        quantity: 1,
      },
    ],
    success_url: 'https://joybox.example/welcome',
  },
});
console.log(check);
```
```python
import os
from checkbox import Checkbox

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

check = checkbox.checks.stripe_checkout_session(
    params={
        "mode": "subscription",
        "line_items": [
            {
                "price_data": {
                    "currency": "usd",
                    "unit_amount": 1999,
                    "recurring": {"interval": "month"},
                    "product_data": {"name": "Joybox Pro"},
                },
                "quantity": 1,
            },
        ],
        "success_url": "https://joybox.example/welcome",
    },
)
print(check)
```

```json title="Response"
{
  "id": "chk_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
  "object": "check_result",
  "livemode": false,
  "created": 1793318400,
  "kind": "disclosure",
  "verdict": "fail",
  "summary": {
    "pass": 6,
    "fail": 2,
    "needs_review": 1,
    "not_applicable": 1
  },
  "checks": [
    {
      "check_id": "signup.disclosure.price",
      "status": "fail",
      "severity": "critical",
      "title": "Discloses the recurring charge amount",
      "explanation": "The recurring charge amount is not stated in the terms next to the purchase button.",
      "confidence": 0.85,
      "requirements": [
        {
          "id": "US-CA.17602.a.1",
          "jurisdiction": "US-CA",
          "citation": "Cal. Bus. & Prof. Code § 17602(a)(1)",
          "title": "Clear and conspicuous disclosure of automatic renewal offer terms",
          "source_url": "https://leginfo.legislature.ca.gov/"
        }
      ],
      "fix": "Show the recurring charge amount in the terms next to the purchase button.",
      "static_estimate": "pass"
    }
  ],
  "requirements": [
    {
      "id": "US-CA.17602.a.1",
      "jurisdiction": "US-CA",
      "citation": "Cal. Bus. & Prof. Code § 17602(a)(1)",
      "title": "Clear and conspicuous disclosure of automatic renewal offer terms",
      "source_url": "https://leginfo.legislature.ca.gov/"
    }
  ],
  "warnings": [
    {
      "code": "stripe_tos_url_required",
      "message": "consent_collection.terms_of_service needs a Terms of Service URL in Stripe.",
      "param": "params.consent_collection"
    }
  ],
  "jurisdictions": "all",
  "as_of": "2026-10-30",
  "rules_version": "1.2.0",
  "catalog_version": "2026.10.30.1",
  "catalog_hash": "b1c2…",
  "disclaimer": "Automated compliance check … not legal advice.",
  "next_action": {
    "type": "verify_domain",
    "message": "Verify your site domain to turn on live mode.",
    "operation_id": "createDomainVerification",
    "mcp_tool": "call_api",
    "url": "https://checkbox.site/docs/api/activation"
  },
  "correction": {
    "patch": [
      {
        "op": "add",
        "path": "/consent_collection",
        "value": {
          "terms_of_service": "required"
        }
      }
    ],
    "corrected_params": {
      "consent_collection": {
        "terms_of_service": "required"
      }
    },
    "verification": {
      "checks": [
        {
          "check_id": "signup.consent.affirmative",
          "before": "fail",
          "after": "pass"
        }
      ],
      "passes": true,
      "remaining_checks": [],
      "note": "Stripe renders custom_text in its own small grey style …"
    },
    "placeholders": []
  }
}
```

## Check Stripe Payment Link params {#checkStripePaymentLink}

`POST /v1/checks/stripe_payment_link`

Like checkStripeCheckoutSession, for stripe.paymentLinks.create params or a retrieved Payment Link object. For an existing link (params.id) the correction also gives update_params for stripe.paymentLinks.update (same URL); Stripe cannot add a consent checkbox to an existing link, so needs_new_link_for_consent says when a new link (corrected_params) is required. Stateless and deterministic: nothing is stored, the same input on the same as_of date gives the same verdicts. Every verdict comes from the Checkbox rules engine over the legal catalog (federal ROSCA / EFTA plus the state automatic-renewal laws); the response carries rules_version, catalog_version and a disclaimer (not legal advice). Read `verdict`, then `next_action`: it tells you exactly what to change and to call the check again until verdict is pass.

**Scope** `checks:read` · **Operation** `checkStripePaymentLink`

SDK: `checkbox.checks.stripePaymentLink()` (Node) · `checkbox.checks.stripe_payment_link()` (Python). MCP / agents: operation `checkStripePaymentLink`.

#### Body

| Field | Type | Description |
|---|---|---|
| `params` **required** | object | Exactly the object your code passes to Stripe (checkout.sessions.create / paymentLinks.create), or a retrieved Payment Link. |
| `merchant` | CheckPlanTerms | What Stripe params do not say: merchant_name, cancel_url, and price / interval when line_items reference a Price id. |
| `merchant.merchant_name` | string | Brand shown to subscribers; used in generated text. |
| `merchant.plan_name` | string | Plan / product name. |
| `merchant.price` | number | Recurring price in major units (19.99). Or give `amount` in minor units. |
| `merchant.amount` | integer | Recurring price in minor units (1999 = $19.99), Stripe style. |
| `merchant.currency` | string | Three-letter ISO currency (default usd). |
| `merchant.interval` | string | Billing interval: day, week, month or year. |
| `merchant.interval_count` | integer | Number of intervals between charges (default 1). |
| `merchant.trial_days` | integer | Free-trial length in days (0 or absent = no trial). |
| `merchant.cancel_url` | string | https URL where subscribers cancel online (used in generated text; a {{cancel_url}} placeholder otherwise). |
| `merchant.cancel_timing` | enum | When a cancellation takes effect (default period_end). One of `period_end`, `immediately`. |
| `jurisdictions` | enum | Where you sell: "all" (default) or state codes ("US-CA", "CA", "US-NY"; "US-FED" is always included). |
| `as_of` | string | Evaluate the law as of this date (YYYY-MM-DD, default today). |

#### Returns

Returns a `StripePaymentLinkCheckResult` 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/checks/stripe_payment_link \
  -u "$CHECKBOX_SECRET_KEY:" \
  -H "Content-Type: application/json" \
  -d '{
  "params": {
    "mode": "subscription",
    "line_items": [
      {
        "price_data": {
          "currency": "usd",
          "unit_amount": 1999,
          "recurring": {
            "interval": "month"
          },
          "product_data": {
            "name": "Joybox Pro"
          }
        },
        "quantity": 1
      }
    ],
    "success_url": "https://joybox.example/welcome"
  }
}'
```
```node
import Checkbox from '@checkbox-site/cli';

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

const check = await checkbox.checks.stripePaymentLink({
  params: {
    mode: 'subscription',
    line_items: [
      {
        price_data: {
          currency: 'usd',
          unit_amount: 1999,
          recurring: { interval: 'month' },
          product_data: { name: 'Joybox Pro' },
        },
        quantity: 1,
      },
    ],
    success_url: 'https://joybox.example/welcome',
  },
});
console.log(check);
```
```python
import os
from checkbox import Checkbox

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

check = checkbox.checks.stripe_payment_link(
    params={
        "mode": "subscription",
        "line_items": [
            {
                "price_data": {
                    "currency": "usd",
                    "unit_amount": 1999,
                    "recurring": {"interval": "month"},
                    "product_data": {"name": "Joybox Pro"},
                },
                "quantity": 1,
            },
        ],
        "success_url": "https://joybox.example/welcome",
    },
)
print(check)
```

```json title="Response"
{
  "id": "chk_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
  "object": "check_result",
  "livemode": false,
  "created": 1793318400,
  "kind": "disclosure",
  "verdict": "fail",
  "summary": {
    "pass": 6,
    "fail": 2,
    "needs_review": 1,
    "not_applicable": 1
  },
  "checks": [
    {
      "check_id": "signup.disclosure.price",
      "status": "fail",
      "severity": "critical",
      "title": "Discloses the recurring charge amount",
      "explanation": "The recurring charge amount is not stated in the terms next to the purchase button.",
      "confidence": 0.85,
      "requirements": [
        {
          "id": "US-CA.17602.a.1",
          "jurisdiction": "US-CA",
          "citation": "Cal. Bus. & Prof. Code § 17602(a)(1)",
          "title": "Clear and conspicuous disclosure of automatic renewal offer terms",
          "source_url": "https://leginfo.legislature.ca.gov/"
        }
      ],
      "fix": "Show the recurring charge amount in the terms next to the purchase button.",
      "static_estimate": "pass"
    }
  ],
  "requirements": [
    {
      "id": "US-CA.17602.a.1",
      "jurisdiction": "US-CA",
      "citation": "Cal. Bus. & Prof. Code § 17602(a)(1)",
      "title": "Clear and conspicuous disclosure of automatic renewal offer terms",
      "source_url": "https://leginfo.legislature.ca.gov/"
    }
  ],
  "warnings": [
    {
      "code": "stripe_tos_url_required",
      "message": "consent_collection.terms_of_service needs a Terms of Service URL in Stripe.",
      "param": "params.consent_collection"
    }
  ],
  "jurisdictions": "all",
  "as_of": "2026-10-30",
  "rules_version": "1.2.0",
  "catalog_version": "2026.10.30.1",
  "catalog_hash": "b1c2…",
  "disclaimer": "Automated compliance check … not legal advice.",
  "next_action": {
    "type": "verify_domain",
    "message": "Verify your site domain to turn on live mode.",
    "operation_id": "createDomainVerification",
    "mcp_tool": "call_api",
    "url": "https://checkbox.site/docs/api/activation"
  },
  "correction": {
    "patch": [
      {
        "op": "add",
        "path": "/consent_collection",
        "value": {
          "terms_of_service": "required"
        }
      }
    ],
    "corrected_params": {
      "consent_collection": {
        "terms_of_service": "required"
      }
    },
    "verification": {
      "checks": [
        {
          "check_id": "signup.consent.affirmative",
          "before": "fail",
          "after": "pass"
        }
      ],
      "passes": true,
      "remaining_checks": [],
      "note": "Stripe renders custom_text in its own small grey style …"
    },
    "placeholders": [],
    "update_params": {
      "custom_text": {
        "submit": {
          "message": "…"
        }
      }
    },
    "needs_new_link_for_consent": true
  }
}
```

## Check a subscription lifecycle email {#checkEmail}

`POST /v1/checks/email`

Checks the content of an acknowledgment (welcome) email, trial-ending reminder, renewal reminder, price-change notice, yearly terms reminder or cancellation confirmation against the requirements for that notice (renewal terms, amount, frequency, how to cancel, trial cancellation). The email is modelled as sent on time, so the verdict is about content; get send dates from checkNoticeSchedule. Returns `required_by` (requirements that make the email mandatory for this plan) and a compliant `template` with {{merge_tags}}, verified by the engine. Stateless and deterministic: nothing is stored, the same input on the same as_of date gives the same verdicts. Every verdict comes from the Checkbox rules engine over the legal catalog (federal ROSCA / EFTA plus the state automatic-renewal laws); the response carries rules_version, catalog_version and a disclaimer (not legal advice). Read `verdict`, then `next_action`: it tells you exactly what to change and to call the check again until verdict is pass.

**Scope** `checks:read` · **Operation** `checkEmail`

SDK: `checkbox.checks.email()` (Node) · `checkbox.checks.email()` (Python). MCP / agents: operation `checkEmail`.

#### Body

| Field | Type | Description |
|---|---|---|
| `kind` **required** | enum | Which lifecycle email this is (the same names verifyTask, the plan and the X-Checkbox-Kind header use; trial_ending is an alias of trial_reminder, as in Notices). One of `acknowledgment`, `trial_reminder`, `trial_ending`, `renewal_reminder`, `price_change`, `annual_terms`, `charge_notice`, `cancellation_confirmation`. |
| `subject` **required** | string | Subject line. |
| `text` | string | Plain-text body (give text, html or both). |
| `html` | string | HTML body. |
| `terms` **required** | CheckPlanTerms | The plan the email is about (price, interval and cancel_url at least). |
| `terms.merchant_name` | string | Brand shown to subscribers; used in generated text. |
| `terms.plan_name` | string | Plan / product name. |
| `terms.price` | number | Recurring price in major units (19.99). Or give `amount` in minor units. |
| `terms.amount` | integer | Recurring price in minor units (1999 = $19.99), Stripe style. |
| `terms.currency` | string | Three-letter ISO currency (default usd). |
| `terms.interval` | string | Billing interval: day, week, month or year. |
| `terms.interval_count` | integer | Number of intervals between charges (default 1). |
| `terms.trial_days` | integer | Free-trial length in days (0 or absent = no trial). |
| `terms.cancel_url` | string | https URL where subscribers cancel online (used in generated text; a {{cancel_url}} placeholder otherwise). |
| `terms.cancel_timing` | enum | When a cancellation takes effect (default period_end). One of `period_end`, `immediately`. |
| `terms.previous_price` | number | price_change only: the OLD price before the change, major units. price / amount is the NEW price the email announces. |
| `terms.previous_amount` | integer | Price before a price change, minor units. |
| `terms.support_contact` | string | Support email / URL shown in the generated template. |
| `jurisdictions` | enum | Where you sell: "all" (default) or state codes ("US-CA", "CA", "US-NY"; "US-FED" is always included). |
| `as_of` | string | Evaluate the law as of this date (YYYY-MM-DD, default today). |

#### Returns

Returns an `EmailCheckResult` 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/checks/email \
  -u "$CHECKBOX_SECRET_KEY:" \
  -H "Content-Type: application/json" \
  -d '{
  "kind": "trial_reminder",
  "subject": "Your free trial ends on December 4",
  "terms": {
    "merchant_name": "Joybox",
    "plan_name": "Pro",
    "price": 19.99,
    "amount": 1999,
    "currency": "usd",
    "interval": "month",
    "interval_count": 1,
    "trial_days": 14,
    "cancel_url": "https://joybox.example/account/cancel",
    "cancel_timing": "period_end"
  },
  "text": "Your free trial ends on December 4, 2026. …"
}'
```
```node
import Checkbox from '@checkbox-site/cli';

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

const check = await checkbox.checks.email({
  kind: 'trial_reminder',
  subject: 'Your free trial ends on December 4',
  terms: {
    merchant_name: 'Joybox',
    plan_name: 'Pro',
    price: 19.99,
    amount: 1999,
    currency: 'usd',
    interval: 'month',
    interval_count: 1,
    trial_days: 14,
    cancel_url: 'https://joybox.example/account/cancel',
    cancel_timing: 'period_end',
  },
  text: 'Your free trial ends on December 4, 2026. …',
});
console.log(check);
```
```python
import os
from checkbox import Checkbox

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

check = checkbox.checks.email(
    kind="trial_reminder",
    subject="Your free trial ends on December 4",
    terms={
        "merchant_name": "Joybox",
        "plan_name": "Pro",
        "price": 19.99,
        "amount": 1999,
        "currency": "usd",
        "interval": "month",
        "interval_count": 1,
        "trial_days": 14,
        "cancel_url": "https://joybox.example/account/cancel",
        "cancel_timing": "period_end",
    },
    text="Your free trial ends on December 4, 2026. …",
)
print(check)
```

```json title="Response"
{
  "id": "chk_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
  "object": "check_result",
  "livemode": false,
  "created": 1793318400,
  "kind": "disclosure",
  "verdict": "fail",
  "summary": {
    "pass": 6,
    "fail": 2,
    "needs_review": 1,
    "not_applicable": 1
  },
  "checks": [
    {
      "check_id": "signup.disclosure.price",
      "status": "fail",
      "severity": "critical",
      "title": "Discloses the recurring charge amount",
      "explanation": "The recurring charge amount is not stated in the terms next to the purchase button.",
      "confidence": 0.85,
      "requirements": [
        {
          "id": "US-CA.17602.a.1",
          "jurisdiction": "US-CA",
          "citation": "Cal. Bus. & Prof. Code § 17602(a)(1)",
          "title": "Clear and conspicuous disclosure of automatic renewal offer terms",
          "source_url": "https://leginfo.legislature.ca.gov/"
        }
      ],
      "fix": "Show the recurring charge amount in the terms next to the purchase button.",
      "static_estimate": "pass"
    }
  ],
  "requirements": [
    {
      "id": "US-CA.17602.a.1",
      "jurisdiction": "US-CA",
      "citation": "Cal. Bus. & Prof. Code § 17602(a)(1)",
      "title": "Clear and conspicuous disclosure of automatic renewal offer terms",
      "source_url": "https://leginfo.legislature.ca.gov/"
    }
  ],
  "warnings": [
    {
      "code": "stripe_tos_url_required",
      "message": "consent_collection.terms_of_service needs a Terms of Service URL in Stripe.",
      "param": "params.consent_collection"
    }
  ],
  "jurisdictions": "all",
  "as_of": "2026-10-30",
  "rules_version": "1.2.0",
  "catalog_version": "2026.10.30.1",
  "catalog_hash": "b1c2…",
  "disclaimer": "Automated compliance check … not legal advice.",
  "next_action": {
    "type": "verify_domain",
    "message": "Verify your site domain to turn on live mode.",
    "operation_id": "createDomainVerification",
    "mcp_tool": "call_api",
    "url": "https://checkbox.site/docs/api/activation"
  },
  "email_kind": "trial_reminder",
  "required_by": [
    "US-CA.17602.a.3"
  ],
  "template": {
    "subject": "Your Joybox free trial ends on {{trial_end_date}}",
    "text": "Your free trial ends on {{trial_end_date}}. …",
    "html": "<p>Your free trial ends on {{trial_end_date}}. …</p>",
    "merge_tags": [
      "{{trial_end_date}}"
    ],
    "placeholders": [],
    "verified": true,
    "remaining_checks": []
  }
}
```

## Check a checkout page (HTML or URL) {#checkPage}

`POST /v1/checks/page`

Reads a checkout page's HTML (send the rendered HTML of client-side apps) with Checkbox's static extractor — purchase buttons, terms blocks, consent checkboxes, terms links — and runs the sign-up checks on it. Without a browser, placement and conspicuousness are estimates: a passing estimate is reported as needs_review (with static_estimate), never as pass; install the snippet or run a scan after deploying for rendered measurements. `url` (instead of html) fetches a public page and needs a live-mode key on an activated account. Pass terms to compare the disclosed price with your real one and get a verified replacement. Stateless and deterministic: nothing is stored, the same input on the same as_of date gives the same verdicts. Every verdict comes from the Checkbox rules engine over the legal catalog (federal ROSCA / EFTA plus the state automatic-renewal laws); the response carries rules_version, catalog_version and a disclaimer (not legal advice). Read `verdict`, then `next_action`: it tells you exactly what to change and to call the check again until verdict is pass.

**Scope** `checks:read` · **Operation** `checkPage`

SDK: `checkbox.checks.page()` (Node) · `checkbox.checks.page()` (Python). MCP / agents: operation `checkPage`.

#### Body

| Field | Type | Description |
|---|---|---|
| `html` | string | Checkout page HTML (rendered, after JavaScript, for client-side apps). Give html or url. |
| `url` | string | Fetch this live page instead (live mode, activated accounts only; public addresses only). |
| `page_kind` | enum | What the page is (default checkout). One of `checkout`, `pricing`. |
| `terms` | CheckPlanTerms | What Stripe params do not say: merchant_name, cancel_url, and price / interval when line_items reference a Price id. |
| `terms.merchant_name` | string | Brand shown to subscribers; used in generated text. |
| `terms.plan_name` | string | Plan / product name. |
| `terms.price` | number | Recurring price in major units (19.99). Or give `amount` in minor units. |
| `terms.amount` | integer | Recurring price in minor units (1999 = $19.99), Stripe style. |
| `terms.currency` | string | Three-letter ISO currency (default usd). |
| `terms.interval` | string | Billing interval: day, week, month or year. |
| `terms.interval_count` | integer | Number of intervals between charges (default 1). |
| `terms.trial_days` | integer | Free-trial length in days (0 or absent = no trial). |
| `terms.cancel_url` | string | https URL where subscribers cancel online (used in generated text; a {{cancel_url}} placeholder otherwise). |
| `terms.cancel_timing` | enum | When a cancellation takes effect (default period_end). One of `period_end`, `immediately`. |
| `jurisdictions` | enum | Where you sell: "all" (default) or state codes ("US-CA", "CA", "US-NY"; "US-FED" is always included). |
| `as_of` | string | Evaluate the law as of this date (YYYY-MM-DD, default today). |

#### Returns

Returns a `PageCheckResult` 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/checks/page \
  -u "$CHECKBOX_SECRET_KEY:" \
  -H "Content-Type: application/json" \
  -d '{
  "html": "<form>…<button>Subscribe</button></form>",
  "terms": {
    "merchant_name": "Joybox",
    "plan_name": "Pro",
    "price": 19.99,
    "amount": 1999,
    "currency": "usd",
    "interval": "month",
    "interval_count": 1,
    "trial_days": 14,
    "cancel_url": "https://joybox.example/account/cancel",
    "cancel_timing": "period_end"
  }
}'
```
```node
import Checkbox from '@checkbox-site/cli';

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

const check = await checkbox.checks.page({
  html: '<form>…<button>Subscribe</button></form>',
  terms: {
    merchant_name: 'Joybox',
    plan_name: 'Pro',
    price: 19.99,
    amount: 1999,
    currency: 'usd',
    interval: 'month',
    interval_count: 1,
    trial_days: 14,
    cancel_url: 'https://joybox.example/account/cancel',
    cancel_timing: 'period_end',
  },
});
console.log(check);
```
```python
import os
from checkbox import Checkbox

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

check = checkbox.checks.page(
    html="<form>…<button>Subscribe</button></form>",
    terms={
        "merchant_name": "Joybox",
        "plan_name": "Pro",
        "price": 19.99,
        "amount": 1999,
        "currency": "usd",
        "interval": "month",
        "interval_count": 1,
        "trial_days": 14,
        "cancel_url": "https://joybox.example/account/cancel",
        "cancel_timing": "period_end",
    },
)
print(check)
```

```json title="Response"
{
  "id": "chk_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
  "object": "check_result",
  "livemode": false,
  "created": 1793318400,
  "kind": "disclosure",
  "verdict": "fail",
  "summary": {
    "pass": 6,
    "fail": 2,
    "needs_review": 1,
    "not_applicable": 1
  },
  "checks": [
    {
      "check_id": "signup.disclosure.price",
      "status": "fail",
      "severity": "critical",
      "title": "Discloses the recurring charge amount",
      "explanation": "The recurring charge amount is not stated in the terms next to the purchase button.",
      "confidence": 0.85,
      "requirements": [
        {
          "id": "US-CA.17602.a.1",
          "jurisdiction": "US-CA",
          "citation": "Cal. Bus. & Prof. Code § 17602(a)(1)",
          "title": "Clear and conspicuous disclosure of automatic renewal offer terms",
          "source_url": "https://leginfo.legislature.ca.gov/"
        }
      ],
      "fix": "Show the recurring charge amount in the terms next to the purchase button.",
      "static_estimate": "pass"
    }
  ],
  "requirements": [
    {
      "id": "US-CA.17602.a.1",
      "jurisdiction": "US-CA",
      "citation": "Cal. Bus. & Prof. Code § 17602(a)(1)",
      "title": "Clear and conspicuous disclosure of automatic renewal offer terms",
      "source_url": "https://leginfo.legislature.ca.gov/"
    }
  ],
  "warnings": [
    {
      "code": "stripe_tos_url_required",
      "message": "consent_collection.terms_of_service needs a Terms of Service URL in Stripe.",
      "param": "params.consent_collection"
    }
  ],
  "jurisdictions": "all",
  "as_of": "2026-10-30",
  "rules_version": "1.2.0",
  "catalog_version": "2026.10.30.1",
  "catalog_hash": "b1c2…",
  "disclaimer": "Automated compliance check … not legal advice.",
  "next_action": {
    "type": "verify_domain",
    "message": "Verify your site domain to turn on live mode.",
    "operation_id": "createDomainVerification",
    "mcp_tool": "call_api",
    "url": "https://checkbox.site/docs/api/activation"
  },
  "source": "html",
  "extraction": {
    "mode": "static_html",
    "url": "https://joybox.example/checkout",
    "purchase_buttons": [
      "Subscribe"
    ],
    "disclosures": [
      "Renews monthly at $19.99 until you cancel."
    ],
    "checkboxes": [
      {
        "label": "I agree to the automatic renewal terms.",
        "prechecked": false,
        "required": true
      }
    ],
    "links": [
      {
        "text": "Terms of Service",
        "href": "https://joybox.example/terms"
      }
    ],
    "limitations": [
      "Static HTML: stylesheets … were not applied."
    ]
  },
  "replacement": {
    "disclosure_text": "Your Joybox subscription automatically renews every month at $19.99 …",
    "consent_label": "I agree that my Joybox subscription renews automatically …",
    "button_text": "Subscribe — $19.99/month, renews automatically",
    "html": "<div class=\"checkbox-renewal-terms\">…</div>",
    "placeholders": [
      "{{cancel_url}}"
    ],
    "verified": true,
    "remaining_checks": [],
    "sha256": "3e49…"
  }
}
```

## Get the dates each required notice must be sent {#checkNoticeSchedule}

`POST /v1/checks/notice_schedule`

For a subscription plan (price, interval, trial) and where you sell, returns every legally required notice — acknowledgment, trial-ending reminder, renewal reminders, yearly terms reminder, price-change notice — with the recommended send time and the compliant window (opens / closes), the jurisdictions each send satisfies and the citations. Uses the same timing as Checkbox Notices: per-state windows intersected, or the fewest sends that cover conflicting windows. Stateless. Check each email's content with checkEmail, or let Checkbox Notices send them.

**Scope** `checks:read` · **Operation** `checkNoticeSchedule`

SDK: `checkbox.checks.noticeSchedule()` (Node) · `checkbox.checks.notice_schedule()` (Python). MCP / agents: operation `checkNoticeSchedule`.

#### Body

| Field | Type | Description |
|---|---|---|
| `terms` **required** | CheckPlanTerms | What Stripe params do not say: merchant_name, cancel_url, and price / interval when line_items reference a Price id. |
| `terms.merchant_name` | string | Brand shown to subscribers; used in generated text. |
| `terms.plan_name` | string | Plan / product name. |
| `terms.price` | number | Recurring price in major units (19.99). Or give `amount` in minor units. |
| `terms.amount` | integer | Recurring price in minor units (1999 = $19.99), Stripe style. |
| `terms.currency` | string | Three-letter ISO currency (default usd). |
| `terms.interval` | string | Billing interval: day, week, month or year. |
| `terms.interval_count` | integer | Number of intervals between charges (default 1). |
| `terms.trial_days` | integer | Free-trial length in days (0 or absent = no trial). |
| `terms.cancel_url` | string | https URL where subscribers cancel online (used in generated text; a {{cancel_url}} placeholder otherwise). |
| `terms.cancel_timing` | enum | When a cancellation takes effect (default period_end). One of `period_end`, `immediately`. |
| `start_date` | string | Sign-up date of the subscription to plan for (default today). |
| `price_change` | object | A planned price change to schedule the notice for. |
| `price_change.effective_date` **required** | string | Date the new price is first charged. |
| `price_change.new_price` | number | New recurring price, major units. |
| `cycles` | integer | How many renewals to plan (default: one year of renewals). |
| `payment_methods` | array of enum | Payment methods offered (some notices apply only to debit / ACH). |
| `jurisdictions` | enum | Where you sell: "all" (default) or state codes ("US-CA", "CA", "US-NY"; "US-FED" is always included). |
| `as_of` | string | Evaluate the law as of this date (YYYY-MM-DD, default today). |

#### Returns

Returns a `NoticeScheduleResult` 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/checks/notice_schedule \
  -u "$CHECKBOX_SECRET_KEY:" \
  -H "Content-Type: application/json" \
  -d '{
  "terms": {
    "merchant_name": "Joybox",
    "plan_name": "Pro",
    "price": 19.99,
    "amount": 1999,
    "currency": "usd",
    "interval": "month",
    "interval_count": 1,
    "trial_days": 14,
    "cancel_url": "https://joybox.example/account/cancel",
    "cancel_timing": "period_end"
  }
}'
```
```node
import Checkbox from '@checkbox-site/cli';

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

const check = await checkbox.checks.noticeSchedule({
  terms: {
    merchant_name: 'Joybox',
    plan_name: 'Pro',
    price: 19.99,
    amount: 1999,
    currency: 'usd',
    interval: 'month',
    interval_count: 1,
    trial_days: 14,
    cancel_url: 'https://joybox.example/account/cancel',
    cancel_timing: 'period_end',
  },
});
console.log(check);
```
```python
import os
from checkbox import Checkbox

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

check = checkbox.checks.notice_schedule(
    terms={
        "merchant_name": "Joybox",
        "plan_name": "Pro",
        "price": 19.99,
        "amount": 1999,
        "currency": "usd",
        "interval": "month",
        "interval_count": 1,
        "trial_days": 14,
        "cancel_url": "https://joybox.example/account/cancel",
        "cancel_timing": "period_end",
    },
)
print(check)
```

```json title="Response"
{
  "id": "chk_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
  "object": "notice_schedule",
  "livemode": false,
  "created": 1793318400,
  "start_date": "2026-11-01",
  "notices": [
    {
      "kind": "trial_ending",
      "occasion": "Trial ends",
      "anchor_date": "2026-11-15",
      "send_date": "2026-11-03",
      "send_at": 1793750400,
      "window_opens_at": 1793318400,
      "window_closes_at": 1794182400,
      "days_before": 12,
      "jurisdictions": [
        "US-CA"
      ],
      "requirement_ids": [
        "US-CA.17602.a.3"
      ],
      "citations": [
        "Cal. Bus. & Prof. Code § 17602(a)(3)"
      ],
      "default_window": false
    }
  ],
  "not_required": [
    {
      "kind": "renewal_reminder",
      "reason": "Monthly term: renewal reminders apply to terms of 12+ months."
    }
  ],
  "jurisdictions": "all",
  "as_of": "2026-10-30",
  "rules_version": "1.2.0",
  "catalog_version": "2026.10.30.1",
  "catalog_hash": "b1c2…",
  "disclaimer": "Automated compliance check … not legal advice.",
  "next_action": {
    "type": "verify_domain",
    "message": "Verify your site domain to turn on live mode.",
    "operation_id": "createDomainVerification",
    "mcp_tool": "call_api",
    "url": "https://checkbox.site/docs/api/activation"
  }
}
```
