# Notices API

> The legally required acknowledgment, trial-ending, renewal, yearly-terms, price-change and cancellation emails, timed from the legal catalog: you send them and Checkbox tracks them (mode merchant, the default), or Checkbox sends them for you (mode checkbox).

| Operation | Endpoint |
|---|---|
| [Retrieve notice settings](#retrieveNoticeSettings) | `GET /v1/sites/{site}/notice_settings` |
| [Update notice settings](#updateNoticeSettings) | `POST /v1/sites/{site}/notice_settings` |
| [List due notices](#listDueNotices) | `GET /v1/notices/due` |
| [List notice email templates](#listNoticeTemplates) | `GET /v1/notices/templates` |
| [Check a notice email template](#checkNoticeTemplate) | `POST /v1/notices/templates/check` |
| [Retrieve a due notice](#retrieveDueNotice) | `GET /v1/notices/{notice}` |
| [Mark a due notice as sent](#markNoticeSent) | `POST /v1/notices/{notice}/sent` |
| [Record a due notice as missed](#markNoticeMissed) | `POST /v1/notices/{notice}/missed` |

## The NoticeSettings object

Checkbox Notices for a site: the legally required emails Checkbox sends on the merchant's behalf.

| Field | Type | Description |
|---|---|---|
| `id` | string | Unique identifier of the notice settings (ntcs_…). |
| `object` | enum | String representing the object's type. Always "notice_settings". |
| `livemode` | boolean | true for live-mode objects, false for test mode (ck_test_ keys act on an isolated sandbox). |
| `site` | string | The site. |
| `mode` | enum | merchant ("You send", the default): you send the required emails and Checkbox tells you what and when (listDueNotices, notice.due), checks your templates and confirms each send. checkbox: Checkbox sends them for you (enabled_kinds). One of `merchant`, `checkbox`. |
| `bcc` | string | The site's inbound address. Copies to it confirm a notice only when your sending domain authenticates them (DKIM/SPF, a trusted sending domain) and they go to the subscriber; to confirm any copy, BCC each due notice's own bcc address instead (listDueNotices / the notice.due webhook). |
| `enabled_kinds` | array of enum | mode checkbox: the notices Checkbox sends for the merchant. |
| `brand_name` | string \| null | Brand shown in notices (defaults to the site name). |
| `product_name` | string \| null | Product name used in notices. |
| `logo_url` | string \| null | Logo URL (https). |
| `support_email` | string \| null | Reply-to / support address. |
| `cancel_url` | string \| null | Where subscribers cancel online (site domain, hosted cancellation page or billing portal). |
| `brand_color` | string \| null | Hex brand colour. |
| `from_local_part` | string | Mailbox name of the sender. |
| `subscribers` | object \| null | Who Checkbox can send notices to (null when unknown). |
| `subscribers.active` | integer | Active subscriptions Checkbox knows from the billing platform. |
| `subscribers.with_email` | integer | Of those, how many have a known email address (Checkbox can only send to these). |
| `policy` | object \| null | Confirmed cancellation policy (required before notices go out). |
| `policy.cancel_timing` | enum | When a cancellation takes effect. One of `period_end`, `immediately`. |
| `policy.confirmed` | integer | When the policy was confirmed. Unix timestamp in seconds. |
| `sender` | object \| null | Sending address, when one is available. |
| `sender.from` | string | From header. |
| `sender.own_domain` | boolean | Sent from the merchant's verified domain. |
| `sending_domain` | object | Custom sending domain status (set it up in the dashboard). |
| `sending_domain.domain` | string \| null | Custom sending domain. |
| `sending_domain.status` | string | none, pending, verified or failed. |
| `delivery_configured` | boolean | This Checkbox deployment can send email. |
| `kinds` | array of objects | Every notice kind. |
| `updated` | integer \| null | Last change. Unix timestamp in seconds. |
| `next_action` | NextAction \| null | What the caller (often an AI agent) should do next. |

## Retrieve notice settings {#retrieveNoticeSettings}

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

Who sends the site's required emails (mode), the BCC address, which notices Checkbox sends in checkbox mode, and what to do next (next_action).

**Scope** `notices:read` · **Operation** `retrieveNoticeSettings`

SDK: `checkbox.noticeSettings.retrieve()` (Node) · `checkbox.notice_settings.retrieve()` (Python). MCP / agents: operation `retrieveNoticeSettings`.

#### Parameters

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

#### Returns

Returns a [NoticeSettings](https://checkbox.site/docs/api/notices#the-noticesettings-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/notice_settings \
  -u "$CHECKBOX_SECRET_KEY:"
```
```node
import Checkbox from '@checkbox-site/node';

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

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

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

notice_setting = checkbox.notice_settings.retrieve("site_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b")
print(notice_setting)
```

```json title="Response"
{
  "id": "ntcs_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
  "object": "notice_settings",
  "livemode": false,
  "site": "site_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
  "mode": "merchant",
  "bcc": "k_3f9a1c2e@inbound.checkbox.site",
  "enabled_kinds": [
    "acknowledgment",
    "renewal_reminder"
  ],
  "brand_name": "Acme Coffee Club",
  "product_name": "Coffee subscription",
  "logo_url": null,
  "support_email": "help@acme.example",
  "cancel_url": "https://acme.example/account/cancel",
  "brand_color": "#1f2937",
  "from_local_part": "notices",
  "subscribers": {
    "active": 12,
    "with_email": 12
  },
  "policy": {
    "cancel_timing": "period_end",
    "confirmed": 1793318400
  },
  "sender": {
    "from": "Acme Coffee Club <notices@acme.example>",
    "own_domain": true
  },
  "sending_domain": {
    "domain": "acme.example",
    "status": "verified"
  },
  "delivery_configured": true,
  "kinds": [
    {
      "kind": "renewal_reminder",
      "label": "Renewal reminder",
      "description": "Sent before an annual renewal.",
      "enabled": true,
      "check_ids": [
        "reminder.annual_renewal_notice"
      ]
    }
  ],
  "updated": 1793318400,
  "next_action": {
    "type": "verify_domain",
    "message": "Verify your site domain to turn on live mode.",
    "operation_id": "createDomainVerification",
    "mcp_tool": "call_api",
    "endpoint": "POST /v1/sites/{site}/domain_verification",
    "url": "https://checkbox.site/docs/concepts/activation#verify"
  }
}
```

## Update notice settings {#updateNoticeSettings}

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

Chooses who sends the required emails (mode: merchant = you send them and Checkbox tracks them, the default; checkbox = Checkbox sends them), and sets branding, cancel_url and the confirmed cancellation policy. In checkbox mode, turning a notice on (enabled_kinds) requires cancel_url, a confirmed policy, a connected Stripe or Recharge account and a sending address; otherwise resource_conflict explains what is missing.

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

SDK: `checkbox.noticeSettings.update()` (Node) · `checkbox.notice_settings.update()` (Python). MCP / agents: operation `updateNoticeSettings`.

#### Parameters

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

#### Body

| Field | Type | Description |
|---|---|---|
| `mode` | enum | merchant: you send the emails (Checkbox tracks them); checkbox: Checkbox sends them. Turning an enabled_kinds notice on implies checkbox. One of `merchant`, `checkbox`. |
| `enabled_kinds` | array of enum | Notices to send (turning one on needs cancel_url, a confirmed policy and a connected Stripe/Recharge account). trial_reminder is accepted as an alias of trial_ending. |
| `brand_name` | string \| null | Brand name. |
| `product_name` | string \| null | Product name. |
| `logo_url` | string \| null | Logo URL (https). |
| `support_email` | string \| null | Support address. |
| `cancel_url` | string \| null | Online cancellation URL. |
| `brand_color` | string \| null | Hex colour. |
| `from_local_part` | string | Sender mailbox name. |
| `policy` | object \| null | Cancellation policy the notices state. |
| `policy.cancel_timing` **required** | enum | When a cancellation takes effect (must match your Terms). One of `period_end`, `immediately`. |
| `policy.confirm` **required** | enum | Confirms this matches your Terms and your cancellation flow. |

#### Returns

Returns a [NoticeSettings](https://checkbox.site/docs/api/notices#the-noticesettings-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/notice_settings \
  -u "$CHECKBOX_SECRET_KEY:" \
  -H "Content-Type: application/json" \
  -d '{
  "brand_name": "Acme Coffee Club"
}'
```
```node
import Checkbox from '@checkbox-site/node';

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

const noticeSetting = await checkbox.noticeSettings.update('site_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b', { brand_name: 'Acme Coffee Club' });
console.log(noticeSetting);
```
```python
import os
from checkbox import Checkbox

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

notice_setting = checkbox.notice_settings.update("site_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b", brand_name="Acme Coffee Club")
print(notice_setting)
```

```json title="Response"
{
  "id": "ntcs_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
  "object": "notice_settings",
  "livemode": false,
  "site": "site_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
  "mode": "merchant",
  "bcc": "k_3f9a1c2e@inbound.checkbox.site",
  "enabled_kinds": [
    "acknowledgment",
    "renewal_reminder"
  ],
  "brand_name": "Acme Coffee Club",
  "product_name": "Coffee subscription",
  "logo_url": null,
  "support_email": "help@acme.example",
  "cancel_url": "https://acme.example/account/cancel",
  "brand_color": "#1f2937",
  "from_local_part": "notices",
  "subscribers": {
    "active": 12,
    "with_email": 12
  },
  "policy": {
    "cancel_timing": "period_end",
    "confirmed": 1793318400
  },
  "sender": {
    "from": "Acme Coffee Club <notices@acme.example>",
    "own_domain": true
  },
  "sending_domain": {
    "domain": "acme.example",
    "status": "verified"
  },
  "delivery_configured": true,
  "kinds": [
    {
      "kind": "renewal_reminder",
      "label": "Renewal reminder",
      "description": "Sent before an annual renewal.",
      "enabled": true,
      "check_ids": [
        "reminder.annual_renewal_notice"
      ]
    }
  ],
  "updated": 1793318400,
  "next_action": {
    "type": "verify_domain",
    "message": "Verify your site domain to turn on live mode.",
    "operation_id": "createDomainVerification",
    "mcp_tool": "call_api",
    "endpoint": "POST /v1/sites/{site}/domain_verification",
    "url": "https://checkbox.site/docs/concepts/activation#verify"
  }
}
```

## List due notices {#listDueNotices}

`GET /v1/notices/due`

The legally required emails you owe subscribers (sites in "You send" mode, the default): kind, the subscriber (billing-platform ids), the legal window for the subscriber's state, the recommended send date, the copy-ready email and the BCC address. Default: everything not yet sent (upcoming, due, overdue), soonest first. Live keys list live subscribers' notices, test keys test-mode ones. Send each one on its send_date (inside its window), then call markNoticeSent (or BCC the notice's bcc address). Subscribe to the notice.due webhook to get each one on its send date.

**Scope** `notices:read` · **Operation** `listDueNotices`

SDK: `checkbox.notices.listDue()` (Node) · `checkbox.notices.list_due()` (Python). MCP / agents: operation `listDueNotices`.

#### 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 this site (site_…). Default: every site of the account. |
| `status` | query | string | Comma-separated statuses (upcoming, due, sent, overdue, canceled, missed). Default: upcoming,due,overdue — what still needs sending. |
| `kind` | query | string | Comma-separated kinds (acknowledgment, trial_ending, renewal_reminder, annual_terms, price_change, cancellation_confirmation). |
| `subscription` | query | string | Only notices for this billing-platform subscription id. |

#### Returns

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

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

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

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

notices = checkbox.notices.list_due(limit=3)
print(notices.data)
```

```json title="Response"
{
  "object": "list",
  "data": [
    {
      "id": "ntd_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
      "object": "due_notice",
      "livemode": false,
      "site": "site_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
      "kind": "trial_ending",
      "label": "Trial-ending reminder",
      "required": true,
      "status": "due",
      "subscriber": {
        "platform": "stripe",
        "subscription": "sub_1QabcXYZ",
        "customer": "cus_Qabc123",
        "name": "Alex Rivera",
        "email_masked": "a•••@example.com",
        "region": "US-CA"
      },
      "window_start": 1793318400,
      "window_end": 1793318400,
      "send_at": 1793318400,
      "send_date": "2026-11-12",
      "anchor": 1793318400,
      "jurisdictions": [
        "US-CA"
      ],
      "requirement_ids": [
        "US-CA.17602.a.3"
      ],
      "citations": [
        "Cal. Bus. & Prof. Code § 17602(a)(3)"
      ],
      "email": {
        "template_id": "trial_ending.v1",
        "subject": "Your Acme Coffee free trial ends on November 19, 2026",
        "text": "Hi Alex,\n\nYour free trial ends on November 19, 2026. …",
        "html": "<div …><p …>Hi Alex,</p>…</div>",
        "missing_fields": []
      },
      "bcc": "k_3f9a1c2e+trial_ending.9f8e7d6c5b4a39281706f5e4d3c2b1a0@inbound.checkbox.site",
      "sent": {
        "at": 1793318400,
        "source": "bcc",
        "late": false,
        "provider_message_id": "re_9f8e7d6c",
        "evidence": null
      },
      "missed": {
        "at": 1793318400,
        "reason": "Subscriber already left; not sent."
      },
      "created": 1793318400,
      "updated": 1793318400,
      "next_action": {
        "type": "verify_domain",
        "message": "Verify your site domain to turn on live mode.",
        "operation_id": "createDomainVerification",
        "mcp_tool": "call_api",
        "endpoint": "POST /v1/sites/{site}/domain_verification",
        "url": "https://checkbox.site/docs/concepts/activation#verify"
      }
    }
  ],
  "has_more": false,
  "url": "/v1/sites"
}
```

## List notice email templates {#listNoticeTemplates}

`GET /v1/notices/templates`

Copy-ready HTML + text templates for every required notice (acknowledgment with and without a trial, trial ending, renewal, yearly terms, price change, cancellation confirmation), with {{merge_fields}}. Each passes the preflight email check for its kind when filled. Pass ?site= to get each kind's BCC address too. Adapt one, then call checkNoticeTemplate.

**Scope** `notices:read` · **Operation** `listNoticeTemplates`

SDK: `checkbox.notices.listTemplates()` (Node) · `checkbox.notices.list_templates()` (Python). MCP / agents: operation `listNoticeTemplates`.

#### Parameters

| Parameter | In | Type | Description |
|---|---|---|---|
| `site` | query | string | Site (site_…) whose BCC addresses to include. |

#### Returns

Returns a list (`{ object: "list", data, has_more, url }`) of `NoticeTemplate` objects.

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/notices/templates \
  -u "$CHECKBOX_SECRET_KEY:"
```
```node
import Checkbox from '@checkbox-site/node';

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

const notices = await checkbox.notices.listTemplates();
console.log(notices.data);
```
```python
import os
from checkbox import Checkbox

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

notices = checkbox.notices.list_templates()
print(notices["data"])
```

```json title="Response"
{
  "object": "list",
  "data": [
    {
      "id": "trial_ending.v1",
      "kind": "trial_ending",
      "variant": "trial_ending",
      "label": "Trial-ending reminder",
      "when": "Before a free trial converts, inside the subscriber's state window.",
      "use_when": "A free trial is about to convert to a paid subscription.",
      "subject": "Your {{brand_name}} free trial ends on {{trial_end_date}}",
      "text": "Hi {{customer_name}},\n\nYour free trial ends on {{trial_end_date}}. …",
      "html": "<div …><p …>Hi {{customer_name}},</p>…</div>",
      "merge_fields": [
        "customer_name"
      ],
      "check_kind": "trial_reminder",
      "bcc": "k_3f9a1c2e+trial_ending@inbound.checkbox.site"
    }
  ],
  "merge_fields": [
    {
      "name": "trial_end_date",
      "description": "When the free trial ends and the first charge happens."
    }
  ],
  "has_more": false,
  "url": "/v1/notices/templates"
}
```

## Check a notice email template {#checkNoticeTemplate}

`POST /v1/notices/templates/check`

Checks your own version of a notice email: Checkbox fills the merge fields with the site's plan, brand and cancel URL (sample dates), then runs the preflight email check for the kind across the site's jurisdictions. Stateless. Fix what fails (or start from listNoticeTemplates) and check again until verdict is pass.

**Scope** `notices:read` · **Operation** `checkNoticeTemplate`

SDK: `checkbox.notices.checkTemplate()` (Node) · `checkbox.notices.check_template()` (Python). MCP / agents: operation `checkNoticeTemplate`.

#### Body

| Field | Type | Description |
|---|---|---|
| `site` **required** | string | Site whose plan, brand, cancel URL and jurisdictions fill the merge fields and scope the check. |
| `kind` **required** | enum | Which notice the template is for. One of `acknowledgment`, `trial_ending`, `renewal_reminder`, `annual_terms`, `price_change`, `cancellation_confirmation`. |
| `subject` **required** | string | Your subject (merge fields allowed). |
| `text` | string | Your plain-text body (merge fields allowed). |
| `html` | string | Your HTML body (merge fields allowed). |

#### Returns

Returns a `NoticeTemplateCheck` 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/notices/templates/check \
  -u "$CHECKBOX_SECRET_KEY:" \
  -H "Content-Type: application/json" \
  -d '{
  "site": "site_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
  "kind": "trial_ending",
  "subject": "Your {{brand_name}} free trial ends on {{trial_end_date}}",
  "text": "Hi {{customer_name}}, your free trial ends on {{trial_end_date}}. Unless you cancel before then, you will be charged {{price}} every {{billing_period}} until you cancel. Cancel online anytime at {{cancel_url}}."
}'
```
```node
import Checkbox from '@checkbox-site/node';

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

const notice = await checkbox.notices.checkTemplate({
  site: 'site_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b',
  kind: 'trial_ending',
  subject: 'Your {{brand_name}} free trial ends on {{trial_end_date}}',
  text: 'Hi {{customer_name}}, your free trial ends on {{trial_end_date}}. Unless you cancel before then, you will be charged {{price}} every {{billing_period}} until you cancel. Cancel online anytime at {{cancel_url}}.',
});
console.log(notice);
```
```python
import os
from checkbox import Checkbox

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

notice = checkbox.notices.check_template(
    site="site_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
    kind="trial_ending",
    subject="Your {{brand_name}} free trial ends on {{trial_end_date}}",
    text="Hi {{customer_name}}, your free trial ends on {{trial_end_date}}. Unless you cancel before then, you will be charged {{price}} every {{billing_period}} until you cancel. Cancel online anytime at {{cancel_url}}.",
)
print(notice)
```

```json title="Response"
{
  "object": "notice_template_check",
  "kind": "trial_ending",
  "verdict": "pass",
  "checks": [
    {
      "check_id": "reminder.trial_ending",
      "status": "pass",
      "explanation": "The reminder states the charge, the date and how to cancel."
    }
  ],
  "unknown_fields": [],
  "warnings": [
    {
      "code": "unknown_merge_fields",
      "message": "Unknown merge fields: {{plan}}."
    }
  ],
  "filled": {
    "subject": "Your Acme trial ends November 19, 2026",
    "text": "Hi Alex, …",
    "html": "<p>Hi Alex, …</p>"
  },
  "next_action": {
    "type": "verify_domain",
    "message": "Verify your site domain to turn on live mode.",
    "operation_id": "createDomainVerification",
    "mcp_tool": "call_api",
    "endpoint": "POST /v1/sites/{site}/domain_verification",
    "url": "https://checkbox.site/docs/concepts/activation#verify"
  }
}
```

## Retrieve a due notice {#retrieveDueNotice}

`GET /v1/notices/{notice}`

One notice you owe a subscriber, with its window, recommended send date, copy-ready email, BCC address and send status.

**Scope** `notices:read` · **Operation** `retrieveDueNotice`

SDK: `checkbox.notices.retrieve()` (Node) · `checkbox.notices.retrieve()` (Python). MCP / agents: operation `retrieveDueNotice`.

#### Parameters

| Parameter | In | Type | Description |
|---|---|---|---|
| `notice` **required** | path | string | Unique identifier of the due notice (ntd_…). |

#### Returns

Returns a `DueNotice` 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/notices/ntd_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b \
  -u "$CHECKBOX_SECRET_KEY:"
```
```node
import Checkbox from '@checkbox-site/node';

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

const notice = await checkbox.notices.retrieve('ntd_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b');
console.log(notice);
```
```python
import os
from checkbox import Checkbox

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

notice = checkbox.notices.retrieve("ntd_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b")
print(notice)
```

```json title="Response"
{
  "id": "ntd_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
  "object": "due_notice",
  "livemode": false,
  "site": "site_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
  "kind": "trial_ending",
  "label": "Trial-ending reminder",
  "required": true,
  "status": "due",
  "subscriber": {
    "platform": "stripe",
    "subscription": "sub_1QabcXYZ",
    "customer": "cus_Qabc123",
    "name": "Alex Rivera",
    "email_masked": "a•••@example.com",
    "region": "US-CA"
  },
  "window_start": 1793318400,
  "window_end": 1793318400,
  "send_at": 1793318400,
  "send_date": "2026-11-12",
  "anchor": 1793318400,
  "jurisdictions": [
    "US-CA",
    "US-FED"
  ],
  "requirement_ids": [
    "US-CA.17602.a.3"
  ],
  "citations": [
    "Cal. Bus. & Prof. Code § 17602(a)(3)"
  ],
  "email": {
    "template_id": "trial_ending.v1",
    "subject": "Your Acme Coffee free trial ends on November 19, 2026",
    "text": "Hi Alex,\n\nYour free trial ends on November 19, 2026. …",
    "html": "<div …><p …>Hi Alex,</p>…</div>",
    "missing_fields": []
  },
  "bcc": "k_3f9a1c2e+trial_ending.9f8e7d6c5b4a39281706f5e4d3c2b1a0@inbound.checkbox.site",
  "sent": {
    "at": 1793318400,
    "source": "bcc",
    "late": false,
    "provider_message_id": "re_9f8e7d6c",
    "evidence": null
  },
  "missed": {
    "at": 1793318400,
    "reason": "Subscriber already left; not sent."
  },
  "created": 1793318400,
  "updated": 1793318400,
  "next_action": {
    "type": "verify_domain",
    "message": "Verify your site domain to turn on live mode.",
    "operation_id": "createDomainVerification",
    "mcp_tool": "call_api",
    "endpoint": "POST /v1/sites/{site}/domain_verification",
    "url": "https://checkbox.site/docs/concepts/activation#verify"
  }
}
```

## Mark a due notice as sent {#markNoticeSent}

`POST /v1/notices/{notice}/sent`

Records that you sent this notice (optionally with your email provider's message id). Idempotent: a notice already marked sent is returned unchanged. A send before window_start is refused (it does not satisfy the law: send again inside the window). An overdue notice marked sent stops failing its check and is recorded as late; a sent_at earlier than when it became overdue is refused (only the BCC copy can prove an in-window send). Canceled and missed notices cannot be marked. BCC'ing the notice's bcc address does the same with the email itself as evidence.

**Scope** `notices:write` · **Idempotent** (send `Idempotency-Key`) · **Operation** `markNoticeSent`

SDK: `checkbox.notices.markSent()` (Node) · `checkbox.notices.mark_sent()` (Python). MCP / agents: operation `markNoticeSent`.

#### Parameters

| Parameter | In | Type | Description |
|---|---|---|---|
| `notice` **required** | path | string | Unique identifier of the due notice (ntd_…). |

#### Body

| Field | Type | Description |
|---|---|---|
| `provider_message_id` | string | Your email provider's message id (Resend / Postmark / SES), stored as proof. |
| `sent_at` | integer | When it was sent (unix seconds). Default: now. A time after the window closed records the notice as sent late; a time before window_start is refused (too early to count), and so is a time before an overdue notice became overdue. |

#### Returns

Returns a `DueNotice` 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/notices/ntd_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b/sent \
  -u "$CHECKBOX_SECRET_KEY:" \
  -H "Content-Type: application/json" \
  -d '{}'
```
```node
import Checkbox from '@checkbox-site/node';

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

const notice = await checkbox.notices.markSent('ntd_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b');
console.log(notice);
```
```python
import os
from checkbox import Checkbox

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

notice = checkbox.notices.mark_sent("ntd_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b")
print(notice)
```

```json title="Response"
{
  "id": "ntd_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
  "object": "due_notice",
  "livemode": false,
  "site": "site_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
  "kind": "trial_ending",
  "label": "Trial-ending reminder",
  "required": true,
  "status": "due",
  "subscriber": {
    "platform": "stripe",
    "subscription": "sub_1QabcXYZ",
    "customer": "cus_Qabc123",
    "name": "Alex Rivera",
    "email_masked": "a•••@example.com",
    "region": "US-CA"
  },
  "window_start": 1793318400,
  "window_end": 1793318400,
  "send_at": 1793318400,
  "send_date": "2026-11-12",
  "anchor": 1793318400,
  "jurisdictions": [
    "US-CA",
    "US-FED"
  ],
  "requirement_ids": [
    "US-CA.17602.a.3"
  ],
  "citations": [
    "Cal. Bus. & Prof. Code § 17602(a)(3)"
  ],
  "email": {
    "template_id": "trial_ending.v1",
    "subject": "Your Acme Coffee free trial ends on November 19, 2026",
    "text": "Hi Alex,\n\nYour free trial ends on November 19, 2026. …",
    "html": "<div …><p …>Hi Alex,</p>…</div>",
    "missing_fields": []
  },
  "bcc": "k_3f9a1c2e+trial_ending.9f8e7d6c5b4a39281706f5e4d3c2b1a0@inbound.checkbox.site",
  "sent": {
    "at": 1793318400,
    "source": "bcc",
    "late": false,
    "provider_message_id": "re_9f8e7d6c",
    "evidence": null
  },
  "missed": {
    "at": 1793318400,
    "reason": "Subscriber already left; not sent."
  },
  "created": 1793318400,
  "updated": 1793318400,
  "next_action": {
    "type": "verify_domain",
    "message": "Verify your site domain to turn on live mode.",
    "operation_id": "createDomainVerification",
    "mcp_tool": "call_api",
    "endpoint": "POST /v1/sites/{site}/domain_verification",
    "url": "https://checkbox.site/docs/concepts/activation#verify"
  }
}
```

## Record a due notice as missed {#markNoticeMissed}

`POST /v1/notices/{notice}/missed`

Records that an overdue notice was not sent and will not be (for example, the subscriber already left). Nothing is attested as sent: the notice moves to missed and stops failing its compliance check, and the record stays in the history. Only overdue notices can be recorded as missed. Idempotent.

**Scope** `notices:write` · **Idempotent** (send `Idempotency-Key`) · **Operation** `markNoticeMissed`

SDK: `checkbox.notices.markMissed()` (Node) · `checkbox.notices.mark_missed()` (Python). MCP / agents: operation `markNoticeMissed`.

#### Parameters

| Parameter | In | Type | Description |
|---|---|---|---|
| `notice` **required** | path | string | Unique identifier of the due notice (ntd_…). |

#### Body

| Field | Type | Description |
|---|---|---|
| `reason` | string | Why it was not sent (kept with the record). |

#### Returns

Returns a `DueNotice` 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/notices/ntd_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b/missed \
  -u "$CHECKBOX_SECRET_KEY:" \
  -H "Content-Type: application/json" \
  -d '{}'
```
```node
import Checkbox from '@checkbox-site/node';

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

const notice = await checkbox.notices.markMissed('ntd_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b');
console.log(notice);
```
```python
import os
from checkbox import Checkbox

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

notice = checkbox.notices.mark_missed("ntd_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b")
print(notice)
```

```json title="Response"
{
  "id": "ntd_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
  "object": "due_notice",
  "livemode": false,
  "site": "site_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
  "kind": "trial_ending",
  "label": "Trial-ending reminder",
  "required": true,
  "status": "due",
  "subscriber": {
    "platform": "stripe",
    "subscription": "sub_1QabcXYZ",
    "customer": "cus_Qabc123",
    "name": "Alex Rivera",
    "email_masked": "a•••@example.com",
    "region": "US-CA"
  },
  "window_start": 1793318400,
  "window_end": 1793318400,
  "send_at": 1793318400,
  "send_date": "2026-11-12",
  "anchor": 1793318400,
  "jurisdictions": [
    "US-CA",
    "US-FED"
  ],
  "requirement_ids": [
    "US-CA.17602.a.3"
  ],
  "citations": [
    "Cal. Bus. & Prof. Code § 17602(a)(3)"
  ],
  "email": {
    "template_id": "trial_ending.v1",
    "subject": "Your Acme Coffee free trial ends on November 19, 2026",
    "text": "Hi Alex,\n\nYour free trial ends on November 19, 2026. …",
    "html": "<div …><p …>Hi Alex,</p>…</div>",
    "missing_fields": []
  },
  "bcc": "k_3f9a1c2e+trial_ending.9f8e7d6c5b4a39281706f5e4d3c2b1a0@inbound.checkbox.site",
  "sent": {
    "at": 1793318400,
    "source": "bcc",
    "late": false,
    "provider_message_id": "re_9f8e7d6c",
    "evidence": null
  },
  "missed": {
    "at": 1793318400,
    "reason": "Subscriber already left; not sent."
  },
  "created": 1793318400,
  "updated": 1793318400,
  "next_action": {
    "type": "verify_domain",
    "message": "Verify your site domain to turn on live mode.",
    "operation_id": "createDomainVerification",
    "mcp_tool": "call_api",
    "endpoint": "POST /v1/sites/{site}/domain_verification",
    "url": "https://checkbox.site/docs/concepts/activation#verify"
  }
}
```
