CheckboxDocs

Webhooks

Signed events for findings, scores, compliance plans, test runs, consents, certificates, notices and law changes — the event types, the payload, signature verification and retries.

Checkbox POSTs an Event to each of your webhook endpoints that subscribed to its type. Use them to react without polling: open a ticket when a finding appears, tell your agent when a verification passes, or alert when the law changes.

Event types

EventWhendata.object
plan.updatedA site's compliance plan changed (its status or a task's status); data.object is the plan without task content — GET the plan and follow next_action.CompliancePlanSummary
task.verifiedA compliance-plan task verification resolved (passed or failed); data.object is the verification with expected vs observed per check.Verification
test_run.completedA lifecycle test run finished; data.object is the test_run with every step's observations, check results and fixes.TestRun
test_run.failedA lifecycle test run stopped early; data.object.failure has the code, message and fix.TestRun
site.score_changedA site's compliance score changed (data.previous_attributes.score has the old score).Site
site.insurable_changedA site became insurable or stopped being insurable.Site
finding.createdA check started failing (new finding, or a resolved / passing one failing again).Finding
finding.resolvedA failing or undecided check now passes or no longer applies.Finding
scan.completedA scan finished (the evaluation may follow in a separate site.score_changed event).Scan
scan.failedA scan failed; data.object.error says why.Scan
law.changedA law change Checkbox applies affected one of your sites (data.object is the law update with that site's impact).LawUpdate
certificate.issuedA monitoring certificate was issued.Certificate
certificate.lapsedA certificate lapsed (the site stopped meeting the requirements).Certificate
certificate.revokedA certificate was revoked.Certificate
notice.sentCheckbox sent a legally required notice (acknowledgment, reminder, price change) for the merchant.Notice
consent.createdA subscriber gave affirmative consent to the automatic-renewal terms through Checkbox Elements (or your server recorded one); data.object is the consent.Consent
site.compliantA site's compliance plan became compliant: nothing is left to apply, verify or answer; data.object is the site.Site

Create an endpoint

enabled_events takes event types from the table above, or ["*"] for all of them. The response includes the endpoint's secret (whsec_…) once:

curl https://app.checkbox.site/v1/webhook_endpoints \
  -u "$CHECKBOX_SECRET_KEY:" \
  -H "Content-Type: application/json" \
  -d '{ "url": "https://joybox.example/hooks/checkbox", "enabled_events": ["finding.created", "finding.resolved", "task.verified"] }'

Endpoints must be public HTTPS URLs; redirects are not followed. rollWebhookEndpointSecret replaces the secret immediately and returns the new one (only then), so deploy the new secret right after rolling it.

The payload

JSON
{
  "id": "evt_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
  "object": "event",
  "type": "finding.created",
  "api_version": "2026-10-30",
  "created": 1793318400,
  "livemode": true,
  "data": {
    "object": { "id": "fnd_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b", "object": "finding", "check_id": "signup.consent.not_prechecked", "status": "fail" },
    "previous_attributes": {}
  },
  "pending_webhooks": 1,
  "request": { "id": null }
}

Headers: Checkbox-Signature, Checkbox-Event-Id, Checkbox-Event-Type. Events are kept for 30 days and listed by listEvents (type=finding.*, created_gte…).

Verify signatures

Checkbox-Signature: t=<unix seconds>,v1=<hex>, where v1 is HMAC-SHA256 of "<t>.<raw body>" with the endpoint secret. Reject anything older than 5 minutes, and accept the delivery if any v1 entry matches (a header may carry more than one). Use the raw body exactly as received — not re-serialized JSON.

Without an SDK:

Node
import { createHmac, timingSafeEqual } from 'node:crypto';

function verify(rawBody, header, secret, toleranceSec = 300) {
  const parts = header.split(',');
  const t = Number(parts.find((p) => p.startsWith('t='))?.slice(2));
  if (!t || Math.abs(Date.now() / 1000 - t) > toleranceSec) return false;
  const expected = createHmac('sha256', secret).update(`${t}.${rawBody}`).digest();
  return parts
    .filter((p) => p.startsWith('v1='))
    .some((p) => {
      const got = Buffer.from(p.slice(3), 'hex');
      return got.length === expected.length && timingSafeEqual(got, expected);
    });
}

// Self-check with a header built the same way Checkbox builds it.
const secret = 'whsec_test_secret';
const body = '{"id":"evt_123","type":"site.compliant"}';
const t = Math.floor(Date.now() / 1000);
const header = `t=${t},v1=${createHmac('sha256', secret).update(`${t}.${body}`).digest('hex')}`;
console.log(verify(body, header, secret), verify(body, header, 'whsec_wrong'));

With the SDKs: checkbox.webhooks.constructEvent(rawBody, header, secret) (Node) and checkbox.webhook.construct_event(raw_body, header, secret) (Python).

A complete handler (Node, no framework):

Node
import { createServer } from 'node:http';
import Checkbox from '@checkbox-site/cli';

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

createServer((req, res) => {
  const chunks = [];
  req.on('data', (c) => chunks.push(c));
  req.on('end', () => {
    let event;
    try {
      event = checkbox.webhooks.constructEvent(Buffer.concat(chunks).toString('utf8'), req.headers['checkbox-signature'], process.env.CHECKBOX_WEBHOOK_SECRET);
    } catch {
      res.writeHead(400).end();
      return;
    }
    if (event.type === 'task.verified') console.log('verified', event.data.object.id);
    res.writeHead(200).end();
  });
}).listen(3000);

Delivery and retries

Any 2xx response acknowledges a delivery; answer quickly and do the work afterwards. Failed deliveries are retried after 1 minute, 5 minutes, 30 minutes, 2 hours, 5 hours, 10 hours, 24 hours and 24 hours (about three days), then marked failed. Deliveries to private or loopback addresses are refused. Events can arrive more than once or out of order: use id to deduplicate and created to order.

Checkbox is compliance software, not a law firm; nothing in these docs is legal advice (details). For AI agents: every page is available as Markdown at the same URL plus .md; the index is /llms.txt.