Receipts API
Consent receipts (opt-in per account): for each subscriber checkout where they are on, a tamper-evident record of the disclosure visibly rendered next to the pay button and the consent given — a structured snapshot of the disclosure (visible text, sanitized HTML, legally relevant styles, checkbox st
ate, button label, layout), hashed, chained into the evidence vault, signed and re-rendered as an image on demand. For Stripe-hosted checkouts, reconstructed from Stripe's record. Evidence you can produce; not legal protection.
| Operation | Endpoint |
|---|---|
| Mint a capture token for a checkout | POST /v1/receipt_tokens |
| List consent receipts | GET /v1/receipts |
| Record a consent receipt | POST /v1/receipts |
| Erase a subscriber's receipts | POST /v1/receipts/erase |
| Retrieve a consent receipt | GET /v1/receipts/{receipt} |
| Render a receipt as an image | GET /v1/receipts/{receipt}/render.png |
| Verify a receipt's integrity | GET /v1/receipts/{receipt}/verify |
| Download a self-verifying receipt bundle | GET /v1/receipts/{receipt}/export |
| Retrieve receipt settings | GET /v1/receipt_settings |
| Update receipt settings | POST /v1/receipt_settings |
The Receipt object
A consent receipt: a tamper-evident record of what one subscriber saw next to the pay button and agreed to at checkout. Evidence you can produce; not a legal opinion.
| Field | Type | Description |
|---|---|---|
id | string | Unique identifier of the receipt (rcpt_…). |
object | enum | String representing the object's type. Always "receipt". |
livemode | boolean | true for live-mode objects, false for test mode (ck_test_ keys act on an isolated sandbox). |
created | integer | When Checkbox recorded the receipt (server time). Unix timestamp in seconds. |
site | string | Site the receipt belongs to. |
source | enum | elements: <checkbox-disclosure>; snippet: /r.js; api: your server; stripe_*: reconstructed from Stripe's record of a Stripe-hosted page. One of elements, snippet, api, stripe_checkout, stripe_payment_link. |
binding | enum | bound: the browser presented a valid capture token your server minted; unbound: no (valid) token, so the receipt is a client claim; server: recorded by your server or reconstructed from Stripe. One of bound, unbound, server. |
reconstructed | boolean | true when built from Stripe's record instead of the page the subscriber saw (lower evidentiary weight). |
evidentiary_weight | enum | How the record was obtained, strongest first. Not a legal assessment. One of direct_capture, direct_capture_unbound, server_record, reconstructed_from_stripe. |
detection | object | Disclosure detection. |
detection.method | enum | How the disclosure region was found. One of elements, marked, auto, server, stripe. |
detection.confidence | enum | Confidence that the region is the disclosure. One of high, medium, low. |
customer_reference | string | null | Verified subscriber reference (from the capture token, your server, or Stripe). |
client_reference | string | null | Reference the page claimed (unverified). |
email_sha256 | string | null | HMAC-SHA-256 (keyed per account) of the subscriber's lowercased email, when your server gave one. Personal data: erased with the receipt. |
checkout_session | string | null | Stripe Checkout Session id. |
subscription | string | null | Subscription id, when known. |
consent | string | null | cons_… recorded with this receipt by <checkbox-disclosure>. |
pre_checkout_receipt | string | null | For Stripe-hosted checkouts: the rcpt_… captured on your page before the redirect. |
page_url | string | null | Page (origin + path, query string removed). |
language | string | null | Page / browser language. |
user_agent_class | string | null | Device + browser family (never the full user agent). |
viewport | object | null | Viewport at submit. |
viewport.width | number | CSS px. |
viewport.height | number | CSS px. |
client_timestamp | integer | null | Device clock at capture (unix seconds; informational). |
disclosure | object | What the disclosure said and how it was shown. |
disclosure.text | string | Disclosure text the subscriber saw (personal data redacted). |
disclosure.sha256 | string | SHA-256 of the text. |
disclosure.presentation | object | How the disclosure was presented (null fields: not measured). |
checkbox | object | null | The consent checkbox (null: none found). |
checkbox.present | boolean | A consent checkbox was found. |
checkbox.checked | boolean | It was ticked at submit. |
checkbox.label | string | null | Its label as captured. Null for receipts reconstructed from Stripe: Stripe's record has no checkbox wording, so none is claimed. |
checkbox.required | boolean | It was required. |
submit_label | string | null | Label of the button the subscriber pressed (null when not observed, e.g. Stripe-hosted pages). |
stripe | ReceiptStripeRecord | null | The Checkout Session as Stripe recorded it (Stripe-hosted checkouts and Payment Links). |
flags | array of strings | Things a reviewer should know: unbound, token_invalid, token_expired, token_used_up, token_site_mismatch, low_confidence_detection, disclosure_not_found, below_fold, not_above_submit, far_from_submit, low_contrast, small_font, hidden, hidden_text (text was present but not visibly rendered; left out), typed_text_masked, checkbox_unchecked, no_consent_checkbox, client_clock_skew, implausible_render_gap, reconstructed, no_pre_checkout_snapshot, linked_by_client_nonce, multiple_pre_checkout_candidates, stripe_mode_mismatch, no_terms_of_service_consent, no_custom_text, test_mode, unsigned. |
integrity | object | Tamper evidence. verifyReceipt checks all of it. |
integrity.receipt_sha256 | string | SHA-256 of the canonical JSON receipt (keys sorted, no whitespace) — also the evidence record's content hash. |
integrity.evidence | string | null | ev_… record that chains the receipt into the site's evidence vault. |
integrity.chain_hash | string | null | Chain hash of that record. |
integrity.public_key_id | string | null | Ed25519 key that signed the receipt (published at /.well-known/checkbox-keys.json). |
integrity.signed | integer | null | When it was signed (unix seconds). |
retain_until | integer | When the receipt is deleted under your retention setting. Unix timestamp in seconds. |
render_url | string | PNG of what the subscriber saw, re-rendered from the snapshot (renderReceipt, same auth). |
next_action | NextAction | What the caller (often an AI agent) should do next. |
Mint a capture token for a checkout
POST /v1/receipt_tokens
Call from your server when you render a checkout page: returns a short-lived token (rct_…) to put into the page (data-receipt-token on the disclosure or form, or receipt-token on <checkbox-disclosure>). Receipts that present it are "bound" to this checkout and carry your customer_reference; receipts without one are accepted but flagged "unbound". For Stripe-hosted checkouts also set metadata.checkbox_receipt_token=<id> on the Checkout Session (or pass checkout_session here).
Scope receipts:write · Idempotent (send Idempotency-Key) · Operation createReceiptToken
SDK: checkbox.receiptTokens.create() (Node) · checkbox.receipt_tokens.create() (Python). MCP / agents: operation createReceiptToken.
Body
| Field | Type | Description |
|---|---|---|
site required | string | site_… whose checkout page will present the token. |
customer_reference | string | Your id for the subscriber (Stripe customer id, your user id, order id). Copied onto the receipt as the verified customer_reference. |
checkout_session | string | Stripe Checkout Session id when you already created it (links the Stripe record to this receipt). Or put the token id in the session's metadata.checkbox_receipt_token. |
email | string | The subscriber's email address; only an HMAC-SHA-256 under your account's own key is stored (listReceipts?email= finds receipts by it). Still personal data: covered by retention and eraseReceipts. |
expires_in | integer | Seconds until the token expires (default 3600). |
max_uses | integer | Receipts the token may bind (default 1: the subscriber can read the token in the page, so mint one per checkout attempt; raise it for checkouts that legitimately submit several times). |
metadata | object | Up to 20 key/value string pairs you can attach to the object. |
Returns
Returns a ReceiptToken object.
Errors use the standard envelope with a stable code and, where useful, next_action.
curl https://app.checkbox.site/v1/receipt_tokens \
-u "$CHECKBOX_SECRET_KEY:" \
-H "Content-Type: application/json" \
-d '{
"site": "site_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
"customer_reference": "cus_Q2x9"
}'import Checkbox from '@checkbox-site/cli';
const checkbox = new Checkbox(process.env.CHECKBOX_SECRET_KEY);
const receiptToken = await checkbox.receiptTokens.create({
site: 'site_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b',
customer_reference: 'cus_Q2x9',
});
console.log(receiptToken);import os
from checkbox import Checkbox
checkbox = Checkbox(os.environ["CHECKBOX_SECRET_KEY"])
receipt_token = checkbox.receipt_tokens.create(
site="site_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
customer_reference="cus_Q2x9",
)
print(receipt_token){
"id": "rtok_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
"object": "receipt_token",
"livemode": false,
"created": 1793318400,
"expires": 1793318400,
"site": "site_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
"token": "rct_test_8Jq2k3…",
"customer_reference": "cus_Q2x9",
"checkout_session": null,
"max_uses": 3,
"html_attribute": "data-receipt-token=\"rct_test_8Jq2k3…\"",
"metadata": {},
"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/concepts/activation#verify"
}
}List consent receipts
GET /v1/receipts
Receipts newest first. Filter by site, customer_reference (find the receipt behind a disputed subscription), checkout_session, email (hashed before the lookup), source, binding or creation time.
Scope receipts:read · Operation listReceipts
SDK: checkbox.receipts.list() (Node) · checkbox.receipts.list() (Python). MCP / agents: operation listReceipts.
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 receipts of this site. |
customer_reference | query | string | Only receipts for this subscriber reference. |
checkout_session | query | string | Only receipts of this Stripe Checkout Session. |
email | query | string | Only receipts whose email hash matches this address (hashed before the lookup). |
source | query | enum | Only this source. One of elements, snippet, api, stripe_checkout, stripe_payment_link. |
binding | query | enum | Only this binding. One of bound, unbound, server. |
created_gte | query | integer | null | Created at or after (unix seconds). |
created_lte | query | integer | null | Created at or before (unix seconds). |
Returns
Returns a list ({ object: "list", data, has_more, url }) of Receipt objects.
Errors use the standard envelope with a stable code and, where useful, next_action.
curl -G https://app.checkbox.site/v1/receipts \
-u "$CHECKBOX_SECRET_KEY:" \
-d limit=3import Checkbox from '@checkbox-site/cli';
const checkbox = new Checkbox(process.env.CHECKBOX_SECRET_KEY);
const receipts = await checkbox.receipts.list({ limit: 3 });
console.log(receipts.data);import os
from checkbox import Checkbox
checkbox = Checkbox(os.environ["CHECKBOX_SECRET_KEY"])
receipts = checkbox.receipts.list(limit=3)
print(receipts.data){
"object": "list",
"data": [
{
"id": "rcpt_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
"object": "receipt",
"livemode": false,
"created": 1793318400,
"site": "site_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
"source": "snippet",
"binding": "bound",
"reconstructed": false,
"evidentiary_weight": "direct_capture",
"detection": {
"method": "marked",
"confidence": "high"
},
"customer_reference": "cus_Q2x9",
"client_reference": null,
"email_sha256": null,
"checkout_session": null,
"subscription": null,
"consent": null,
"pre_checkout_receipt": null,
"page_url": "https://acme.example/checkout",
"language": "en-US",
"user_agent_class": "desktop-chrome",
"viewport": {
"width": 1280,
"height": 800
},
"client_timestamp": 1793318399,
"disclosure": {
"text": "Your Acme subscription renews automatically every month at $19.99 until you cancel.",
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"presentation": {
"font_size_px": 16,
"font_weight": 400,
"color": "rgb(17, 24, 39)",
"background_color": "rgb(255, 255, 255)",
"contrast_ratio": 17.74,
"visible": true,
"set_off": true,
"above_fold": true,
"above_submit": true,
"distance_to_submit_px": 12
}
},
"checkbox": {
"present": true,
"checked": true,
"label": "I agree to the automatic renewal terms.",
"required": true
},
"submit_label": "Start subscription",
"stripe": {
"checkout_session": "cs_live_a1b2c3",
"payment_link": null,
"session_created": 1793318100,
"completed": 1793318400,
"terms_of_service": "required",
"terms_of_service_accepted": true,
"custom_text": {
"submit": "Renews monthly at $19.99 until you cancel.",
"terms_of_service_acceptance": null,
"after_submit": null
},
"amount": 1999,
"currency": "usd",
"interval": "month",
"interval_count": 1,
"trial_days": 14,
"subscription": "sub_1Q2"
},
"flags": [],
"integrity": {
"receipt_sha256": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
"evidence": "ev_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
"chain_hash": "2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae",
"public_key_id": "1a2b3c4d5e6f7a8b",
"signed": 1793318400
},
"retain_until": 1793318400,
"render_url": "/v1/receipts/rcpt_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b/render.png",
"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/concepts/activation#verify"
}
}
],
"has_more": false,
"url": "/v1/sites"
}Record a consent receipt
POST /v1/receipts
Records one receipt from the structured snapshot the browser captured at submit (the /r.js snippet add-on calls this with the publishable key, Origin-checked; servers may call it with a secret key and site). The snapshot is validated strictly and re-sanitized. Next: retrieveReceipt / renderReceipt to see it, verifyReceipt to check its integrity.
Key publishable (pk_…) or secret · Idempotent (send Idempotency-Key) · Operation createReceipt
SDK: checkbox.receipts.create() (Node) · checkbox.receipts.create() (Python). MCP / agents: operation createReceipt.
Body
| Field | Type | Description |
|---|---|---|
site | string | site_… the receipt belongs to. Optional with a publishable key (resolved from the page Origin); required with a secret key. |
snapshot required | object | The structured snapshot captured by /r.js or <checkbox-disclosure> (version 1): the disclosure region's text, sanitized subtree and computed styles, the consent checkbox state and label, the submit label, layout, viewport, page URL without query string, language and user-agent class. Validated strictly and re-sanitized by the server; it never contains form-field values. |
token | string | Capture token (rct_…) your server minted for this checkout with createReceiptToken. Binds the receipt to that checkout; without it the receipt is accepted but flagged "unbound". |
client_reference | string | A reference the page claims for the subscriber (unverified; kept apart from customer_reference). |
capture_nonce | string | Random nonce the snippet also put in a Payment Link's client_reference_id (ckbx_<nonce>), linking this pre-checkout snapshot to the Stripe record. |
customer_reference | string | Secret keys only: your id for the subscriber (Stripe customer / subscription / Checkout Session id). Not an email address. |
checkout_session | string | Secret keys only: the Stripe Checkout Session id this receipt belongs to. |
email | string | Secret keys only: the subscriber's email address. Only an HMAC-SHA-256 of it (lowercased, trimmed; keyed per account) is stored. |
Returns
Returns a Receipt object.
Errors use the standard envelope with a stable code and, where useful, next_action.
curl https://app.checkbox.site/v1/receipts \
-u "$CHECKBOX_SECRET_KEY:" \
-H "Content-Type: application/json" \
-d '{
"snapshot": {
"v": 1,
"phase": "submit",
"captured_at": 1793318400000,
"page_url": "https://acme.example/checkout",
"viewport": {
"width": 1280,
"height": 800
},
"scroll": {
"x": 0,
"y": 0
},
"language": "en-US",
"user_agent_class": "desktop-chrome",
"detection": {
"method": "marked",
"confidence": "high",
"reasons": [
"marked_by_merchant"
]
},
"region": {
"text": "Your Acme subscription renews automatically every month at $19.99 until you cancel.",
"tree": [
{
"t": "p",
"s": {
"font-size": "16px"
},
"c": [
"Your Acme subscription renews automatically every month at $19.99 until you cancel."
]
}
],
"rect": {
"x": 40,
"y": 520,
"width": 480,
"height": 72
},
"style": {
"font_size_px": 16,
"font_weight": 400,
"color": "rgb(17, 24, 39)",
"background_color": "rgb(255, 255, 255)",
"contrast_ratio": 17.74,
"opacity": 1,
"visible": true
},
"set_off": true,
"above_fold": true,
"above_submit": true,
"distance_to_submit_px": 12,
"masked_fields": 0
},
"checkbox": {
"present": true,
"checked": true,
"label": "I agree to the automatic renewal terms.",
"required": true
},
"submit": {
"label": "Start subscription",
"rect": {
"x": 40,
"y": 604,
"width": 480,
"height": 44
},
"kind": "button"
},
"render": null
},
"site": "site_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b"
}'import Checkbox from '@checkbox-site/cli';
const checkbox = new Checkbox(process.env.CHECKBOX_SECRET_KEY);
const receipt = await checkbox.receipts.create({
snapshot: {
v: 1,
phase: 'submit',
captured_at: 1793318400000,
page_url: 'https://acme.example/checkout',
viewport: { width: 1280, height: 800 },
scroll: { x: 0, y: 0 },
language: 'en-US',
user_agent_class: 'desktop-chrome',
detection: {
method: 'marked',
confidence: 'high',
reasons: ['marked_by_merchant'],
},
region: {
text: 'Your Acme subscription renews automatically every month at $19.99 until you cancel.',
tree: [
{
t: 'p',
s: { 'font-size': '16px' },
c: [
'Your Acme subscription renews automatically every month at $19.99 until you cancel.',
],
},
],
rect: { x: 40, y: 520, width: 480, height: 72 },
style: {
font_size_px: 16,
font_weight: 400,
color: 'rgb(17, 24, 39)',
background_color: 'rgb(255, 255, 255)',
contrast_ratio: 17.74,
opacity: 1,
visible: true,
},
set_off: true,
above_fold: true,
above_submit: true,
distance_to_submit_px: 12,
masked_fields: 0,
},
checkbox: {
present: true,
checked: true,
label: 'I agree to the automatic renewal terms.',
required: true,
},
submit: {
label: 'Start subscription',
rect: { x: 40, y: 604, width: 480, height: 44 },
kind: 'button',
},
render: null,
},
site: 'site_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b',
});
console.log(receipt);import os
from checkbox import Checkbox
checkbox = Checkbox(os.environ["CHECKBOX_SECRET_KEY"])
receipt = checkbox.receipts.create(
snapshot={
"v": 1,
"phase": "submit",
"captured_at": 1793318400000,
"page_url": "https://acme.example/checkout",
"viewport": {"width": 1280, "height": 800},
"scroll": {"x": 0, "y": 0},
"language": "en-US",
"user_agent_class": "desktop-chrome",
"detection": {
"method": "marked",
"confidence": "high",
"reasons": ["marked_by_merchant"],
},
"region": {
"text": "Your Acme subscription renews automatically every month at $19.99 until you cancel.",
"tree": [
{
"t": "p",
"s": {"font-size": "16px"},
"c": [
"Your Acme subscription renews automatically every month at $19.99 until you cancel.",
],
},
],
"rect": {"x": 40, "y": 520, "width": 480, "height": 72},
"style": {
"font_size_px": 16,
"font_weight": 400,
"color": "rgb(17, 24, 39)",
"background_color": "rgb(255, 255, 255)",
"contrast_ratio": 17.74,
"opacity": 1,
"visible": True,
},
"set_off": True,
"above_fold": True,
"above_submit": True,
"distance_to_submit_px": 12,
"masked_fields": 0,
},
"checkbox": {
"present": True,
"checked": True,
"label": "I agree to the automatic renewal terms.",
"required": True,
},
"submit": {
"label": "Start subscription",
"rect": {"x": 40, "y": 604, "width": 480, "height": 44},
"kind": "button",
},
"render": None,
},
site="site_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
)
print(receipt){
"id": "rcpt_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
"object": "receipt",
"livemode": false,
"created": 1793318400,
"site": "site_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
"source": "snippet",
"binding": "bound",
"reconstructed": false,
"evidentiary_weight": "direct_capture",
"detection": {
"method": "marked",
"confidence": "high"
},
"customer_reference": "cus_Q2x9",
"client_reference": null,
"email_sha256": null,
"checkout_session": null,
"subscription": null,
"consent": null,
"pre_checkout_receipt": null,
"page_url": "https://acme.example/checkout",
"language": "en-US",
"user_agent_class": "desktop-chrome",
"viewport": {
"width": 1280,
"height": 800
},
"client_timestamp": 1793318399,
"disclosure": {
"text": "Your Acme subscription renews automatically every month at $19.99 until you cancel.",
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"presentation": {
"font_size_px": 16,
"font_weight": 400,
"color": "rgb(17, 24, 39)",
"background_color": "rgb(255, 255, 255)",
"contrast_ratio": 17.74,
"visible": true,
"set_off": true,
"above_fold": true,
"above_submit": true,
"distance_to_submit_px": 12
}
},
"checkbox": {
"present": true,
"checked": true,
"label": "I agree to the automatic renewal terms.",
"required": true
},
"submit_label": "Start subscription",
"stripe": {
"checkout_session": "cs_live_a1b2c3",
"payment_link": null,
"session_created": 1793318100,
"completed": 1793318400,
"terms_of_service": "required",
"terms_of_service_accepted": true,
"custom_text": {
"submit": "Renews monthly at $19.99 until you cancel.",
"terms_of_service_acceptance": null,
"after_submit": null
},
"amount": 1999,
"currency": "usd",
"interval": "month",
"interval_count": 1,
"trial_days": 14,
"subscription": "sub_1Q2"
},
"flags": [],
"integrity": {
"receipt_sha256": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
"evidence": "ev_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
"chain_hash": "2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae",
"public_key_id": "1a2b3c4d5e6f7a8b",
"signed": 1793318400
},
"retain_until": 1793318400,
"render_url": "/v1/receipts/rcpt_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b/render.png",
"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/concepts/activation#verify"
}
}Erase a subscriber's receipts
POST /v1/receipts/erase
For a subscriber's deletion request: deletes every receipt that matches customer_reference, email (hashed before the lookup) or checkout_session (optionally only for one site). The signed copy in the write-once evidence vault is kept but holds only a salted commitment to the subscriber's identifiers; erasing destroys the salt, so it can no longer be tied to the person. Pending attorney review.
Scope receipts:write · Idempotent (send Idempotency-Key) · Operation eraseReceipts
SDK: checkbox.receipts.erase() (Node) · checkbox.receipts.erase() (Python). MCP / agents: operation eraseReceipts.
Body
| Field | Type | Description |
|---|---|---|
customer_reference | string | Erase receipts with this customer_reference. |
email | string | Erase receipts recorded with this email (hashed before the lookup). |
checkout_session | string | Erase receipts of this Stripe Checkout Session. |
site | string | Only this site_…. |
Returns
Returns a ReceiptErasure object.
Errors use the standard envelope with a stable code and, where useful, next_action.
curl https://app.checkbox.site/v1/receipts/erase \
-u "$CHECKBOX_SECRET_KEY:" \
-H "Content-Type: application/json" \
-d '{
"customer_reference": "cus_Q2x9"
}'import Checkbox from '@checkbox-site/cli';
const checkbox = new Checkbox(process.env.CHECKBOX_SECRET_KEY);
const receipt = await checkbox.receipts.erase({ customer_reference: 'cus_Q2x9' });
console.log(receipt);import os
from checkbox import Checkbox
checkbox = Checkbox(os.environ["CHECKBOX_SECRET_KEY"])
receipt = checkbox.receipts.erase(customer_reference="cus_Q2x9")
print(receipt){
"object": "receipt_erasure",
"livemode": false,
"erased": 2
}Retrieve a consent receipt
GET /v1/receipts/{receipt}
What one subscriber saw and agreed to at checkout: disclosure text and presentation, checkbox state and label, button label, page, time, binding, flags and integrity data. renderReceipt shows it as an image; exportReceipt downloads a self-verifying bundle.
Scope receipts:read · Operation retrieveReceipt
SDK: checkbox.receipts.retrieve() (Node) · checkbox.receipts.retrieve() (Python). MCP / agents: operation retrieveReceipt.
Parameters
| Parameter | In | Type | Description |
|---|---|---|---|
receipt required | path | string | Unique identifier of the receipt (rcpt_…). |
Returns
Returns a Receipt object.
Errors use the standard envelope with a stable code and, where useful, next_action.
curl https://app.checkbox.site/v1/receipts/rcpt_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b \
-u "$CHECKBOX_SECRET_KEY:"import Checkbox from '@checkbox-site/cli';
const checkbox = new Checkbox(process.env.CHECKBOX_SECRET_KEY);
const receipt = await checkbox.receipts.retrieve('rcpt_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b');
console.log(receipt);import os
from checkbox import Checkbox
checkbox = Checkbox(os.environ["CHECKBOX_SECRET_KEY"])
receipt = checkbox.receipts.retrieve("rcpt_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b")
print(receipt){
"id": "rcpt_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
"object": "receipt",
"livemode": false,
"created": 1793318400,
"site": "site_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
"source": "snippet",
"binding": "bound",
"reconstructed": false,
"evidentiary_weight": "direct_capture",
"detection": {
"method": "marked",
"confidence": "high"
},
"customer_reference": "cus_Q2x9",
"client_reference": null,
"email_sha256": null,
"checkout_session": null,
"subscription": null,
"consent": null,
"pre_checkout_receipt": null,
"page_url": "https://acme.example/checkout",
"language": "en-US",
"user_agent_class": "desktop-chrome",
"viewport": {
"width": 1280,
"height": 800
},
"client_timestamp": 1793318399,
"disclosure": {
"text": "Your Acme subscription renews automatically every month at $19.99 until you cancel.",
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"presentation": {
"font_size_px": 16,
"font_weight": 400,
"color": "rgb(17, 24, 39)",
"background_color": "rgb(255, 255, 255)",
"contrast_ratio": 17.74,
"visible": true,
"set_off": true,
"above_fold": true,
"above_submit": true,
"distance_to_submit_px": 12
}
},
"checkbox": {
"present": true,
"checked": true,
"label": "I agree to the automatic renewal terms.",
"required": true
},
"submit_label": "Start subscription",
"stripe": {
"checkout_session": "cs_live_a1b2c3",
"payment_link": null,
"session_created": 1793318100,
"completed": 1793318400,
"terms_of_service": "required",
"terms_of_service_accepted": true,
"custom_text": {
"submit": "Renews monthly at $19.99 until you cancel.",
"terms_of_service_acceptance": null,
"after_submit": null
},
"amount": 1999,
"currency": "usd",
"interval": "month",
"interval_count": 1,
"trial_days": 14,
"subscription": "sub_1Q2"
},
"flags": [],
"integrity": {
"receipt_sha256": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
"evidence": "ev_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
"chain_hash": "2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae",
"public_key_id": "1a2b3c4d5e6f7a8b",
"signed": 1793318400
},
"retain_until": 1793318400,
"render_url": "/v1/receipts/rcpt_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b/render.png",
"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/concepts/activation#verify"
}
}Render a receipt as an image
GET /v1/receipts/{receipt}/render.png
A PNG of "what the subscriber saw", rendered on demand in an isolated Chromium (no network, scripts disabled) from the sanitized snapshot — a reconstruction, labeled as such, not a screenshot taken from the browser.
Scope receipts:read · Operation renderReceipt
SDK: checkbox.receipts.render() (Node) · checkbox.receipts.render() (Python). MCP / agents: operation renderReceipt.
Parameters
| Parameter | In | Type | Description |
|---|---|---|---|
receipt required | path | string | Unique identifier of the receipt (rcpt_…). |
Returns
Returns the file (image/png).
Errors use the standard envelope with a stable code and, where useful, next_action.
curl https://app.checkbox.site/v1/receipts/rcpt_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b/render.png \
-u "$CHECKBOX_SECRET_KEY:"import Checkbox from '@checkbox-site/cli';
const checkbox = new Checkbox(process.env.CHECKBOX_SECRET_KEY);
const file = await checkbox.receipts.render('rcpt_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b');
console.log(file);import os
from checkbox import Checkbox
checkbox = Checkbox(os.environ["CHECKBOX_SECRET_KEY"])
receipt = checkbox.receipts.render("rcpt_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b")
print(receipt)Verify a receipt's integrity
GET /v1/receipts/{receipt}/verify
Re-hashes the stored receipt, checks Checkbox's Ed25519 signature over its hash and chain position, and verifies the site's evidence chain. For a check that does not rely on Checkbox, download exportReceipt and run its verify.mjs.
Scope receipts:read · Operation verifyReceipt
SDK: checkbox.receipts.verify() (Node) · checkbox.receipts.verify() (Python). MCP / agents: operation verifyReceipt.
Parameters
| Parameter | In | Type | Description |
|---|---|---|---|
receipt required | path | string | Unique identifier of the receipt (rcpt_…). |
Returns
Returns a ReceiptVerification object.
Errors use the standard envelope with a stable code and, where useful, next_action.
curl https://app.checkbox.site/v1/receipts/rcpt_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b/verify \
-u "$CHECKBOX_SECRET_KEY:"import Checkbox from '@checkbox-site/cli';
const checkbox = new Checkbox(process.env.CHECKBOX_SECRET_KEY);
const receipt = await checkbox.receipts.verify('rcpt_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b');
console.log(receipt);import os
from checkbox import Checkbox
checkbox = Checkbox(os.environ["CHECKBOX_SECRET_KEY"])
receipt = checkbox.receipts.verify("rcpt_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b")
print(receipt){
"object": "receipt_verification",
"receipt": "rcpt_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
"verified": true,
"checks": {
"content": {
"ok": true,
"message": "sha256 matches"
},
"signature": {
"ok": true,
"message": "signed by 1a2b3c4d5e6f7a8b"
},
"chain": {
"ok": true,
"message": "42 records checked"
},
"index": {
"ok": true,
"message": "the receipt as shown matches the signed document",
"linked_after_signing": []
}
},
"receipt_sha256": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
"verified_at": 1793318400,
"offline": "GET /v1/receipts/rcpt_…/export, unzip, node verify.mjs --keys checkbox-keys.json"
}Download a self-verifying receipt bundle
GET /v1/receipts/{receipt}/export
ZIP for a lawyer, insurer or court expert: manifest.json (the receipt's evidence record with its chain links) + manifest.sig.json, the canonical receipt JSON under records/, receipt.sig.json (Checkbox's signature over the receipt hash), receipt.png and render.html (the reconstruction), README.md and verify.mjs (zero-dependency offline verifier: node verify.mjs --keys checkbox-keys.json).
Scope receipts:read · Operation exportReceipt
SDK: checkbox.receipts.export() (Node) · checkbox.receipts.export() (Python). MCP / agents: operation exportReceipt.
Parameters
| Parameter | In | Type | Description |
|---|---|---|---|
receipt required | path | string | Unique identifier of the receipt (rcpt_…). |
Returns
Returns the file (application/zip).
Errors use the standard envelope with a stable code and, where useful, next_action.
curl https://app.checkbox.site/v1/receipts/rcpt_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b/export \
-u "$CHECKBOX_SECRET_KEY:"import Checkbox from '@checkbox-site/cli';
const checkbox = new Checkbox(process.env.CHECKBOX_SECRET_KEY);
const file = await checkbox.receipts.export('rcpt_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b');
console.log(file);import os
from checkbox import Checkbox
checkbox = Checkbox(os.environ["CHECKBOX_SECRET_KEY"])
receipt = checkbox.receipts.export("rcpt_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b")
print(receipt)Retrieve receipt settings
GET /v1/receipt_settings
Whether consent receipts are collected for this account (in this mode) and how long they are kept.
Scope receipts:read · Operation retrieveReceiptSettings
SDK: checkbox.receiptSettings.retrieve() (Node) · checkbox.receipt_settings.retrieve() (Python). MCP / agents: operation retrieveReceiptSettings.
Returns
Returns a ReceiptSettings object.
Errors use the standard envelope with a stable code and, where useful, next_action.
curl https://app.checkbox.site/v1/receipt_settings \
-u "$CHECKBOX_SECRET_KEY:"import Checkbox from '@checkbox-site/cli';
const checkbox = new Checkbox(process.env.CHECKBOX_SECRET_KEY);
const receiptSetting = await checkbox.receiptSettings.retrieve();
console.log(receiptSetting);import os
from checkbox import Checkbox
checkbox = Checkbox(os.environ["CHECKBOX_SECRET_KEY"])
receipt_setting = checkbox.receipt_settings.retrieve()
print(receipt_setting){
"object": "receipt_settings",
"livemode": false,
"enabled": true,
"retention_days": 1461,
"updated": 1793318400
}Update receipt settings
POST /v1/receipt_settings
Turn receipts on or off and set the retention in days (default 1461 = 4 years, the California UCL limitation period; pending attorney review). A new retention applies to receipts recorded afterwards.
Scope receipts:write · Idempotent (send Idempotency-Key) · Operation updateReceiptSettings
SDK: checkbox.receiptSettings.update() (Node) · checkbox.receipt_settings.update() (Python). MCP / agents: operation updateReceiptSettings.
Body
| Field | Type | Description |
|---|---|---|
enabled | boolean | Turn receipt collection on or off. |
retention_days | integer | Days to keep receipts (30–3653). Applies to new receipts. |
Returns
Returns a ReceiptSettings object.
Errors use the standard envelope with a stable code and, where useful, next_action.
curl https://app.checkbox.site/v1/receipt_settings \
-u "$CHECKBOX_SECRET_KEY:" \
-H "Content-Type: application/json" \
-d '{
"retention_days": 1461
}'import Checkbox from '@checkbox-site/cli';
const checkbox = new Checkbox(process.env.CHECKBOX_SECRET_KEY);
const receiptSetting = await checkbox.receiptSettings.update({ retention_days: 1461 });
console.log(receiptSetting);import os
from checkbox import Checkbox
checkbox = Checkbox(os.environ["CHECKBOX_SECRET_KEY"])
receipt_setting = checkbox.receipt_settings.update(retention_days=1461)
print(receipt_setting){
"object": "receipt_settings",
"livemode": false,
"enabled": true,
"retention_days": 1461,
"updated": 1793318400
}Checkbox is compliance software, not a law firm; nothing in these docs is legal advice (details). You remain solely responsible for your subscription program, and Checkbox takes on no legal liability for it (Terms). Checkbox is a System1 company; Trust center · Status. For AI agents: every page is available as Markdown at the same URL plus .md; the index is /llms.txt.