Webhooks
Receive CookieBeam events as signed HTTP POST requests on your own endpoints.
Webhooks deliver CookieBeam events to an HTTPS endpoint you control. Webhooks are a plan-gated feature, and endpoint management is currently restricted: endpoints are configured for your team by CookieBeam rather than self-served in the dashboard. Each endpoint subscribes to a set of event types and has a signing secret, which starts with whsec_.
There are no public REST endpoints for webhook management. The webhooks:read and webhooks:write API key scopes exist on keys but currently gate nothing.
Event types
Six event types are currently emitted:
consent.given— a visitor recorded an explicit consent choicescan.completed— a cookie scan finishedscan.policy_update_needed— scan results require a policy reviewdsar.request_submitted— a data subject access request was submittedbanner.published— a banner configuration was publishedcompliance.report_generated— a compliance report was generated
The endpoint configuration form may list additional event types that are registered but not yet emitted by any code path — subscribing to one of those is accepted but delivers nothing. Subscribe only to the six above.
Delivery format
Each delivery is an HTTP POST with a JSON envelope:
{
"event": "consent.given",
"data": {
"consentLogId": "9b2f7c14-8b3a-4e21-9d5c-1a2b3c4d5e6f",
"bannerId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"consentGiven": { "necessary": true, "analytics": false },
"pageUrl": "https://example.com/pricing",
"timestamp": "2026-08-13T12:00:00.000Z"
},
"timestamp": "2026-08-13T12:00:01.000Z",
"deliveryId": "d290f1ee-6c54-4b01-90e6-d701748f0851"
}
The data payload is the example above for consent.given; other event types carry their own payloads. For dsar.request_submitted, data.receivedAt is the ISO 8601 time the submission was received and starts the deadline clock. data.submittedAt is the later verification time. The payload also includes deadlineAt and slaPolicy.
Request headers:
| Header | Value |
|---|---|
Content-Type | application/json |
User-Agent | CookieBeam-Webhooks/1.0 |
X-CookieBeam-Timestamp | Unix time in seconds when the delivery was signed |
X-CookieBeam-Signature-V1 | One or more v1=<hex> entries, comma-separated: HMAC-SHA256 of timestamp + "." + raw body, keyed with an endpoint secret |
X-CookieBeam-Event | The event type, for example consent.given |
X-CookieBeam-Delivery | The delivery ID, matching deliveryId in the body |
X-CookieBeam-Signature | Legacy, only for endpoints created before timestamped signatures (see below): HMAC-SHA256 hex digest of the raw body |
Verify the signature
Verify X-CookieBeam-Signature-V1 before you trust a delivery:
- Read
X-CookieBeam-Timestamp. Reject the request if it is missing, not a whole number, or more than 300 seconds away from your clock. This stops a captured request from being replayed later. - Build the signed string: the timestamp, a dot, then the raw request body exactly as received.
- For each secret you accept (normally one; two while you rotate), compute the HMAC-SHA256 hex digest of that string.
- Split
X-CookieBeam-Signature-V1on commas and keep the entries that start withv1=. Accept the request if any entry equals one of your digests, compared in constant time. Otherwise reject it.
timestamp = header("X-CookieBeam-Timestamp")
if timestamp is not digits or abs(now_seconds() - int(timestamp)) > 300: reject
signed = timestamp + "." + raw_body
candidates = [e[3:] for e in header("X-CookieBeam-Signature-V1").split(",") if e starts with "v1="]
for secret in accepted_secrets:
expected = hex(hmac_sha256(key = secret, message = signed))
if any(constant_time_equal(expected, c) for c in candidates): accept
reject
Use the raw body as received. Parsing and re-serializing the JSON can change the bytes and produce a false mismatch.
The v1= prefix names the signature scheme. A later scheme would be added as another prefix in the same header, so ignore entries you do not recognise rather than failing on them.
The legacy X-CookieBeam-Signature header
Endpoints created before timestamped signatures still receive X-CookieBeam-Signature, unchanged in name and format: the HMAC-SHA256 hex digest of the raw body alone, keyed with the endpoint secret. Receivers that verify it today keep working.
It has no timestamp, so it does not protect against replays. It is sent for each existing endpoint until that endpoint's end date, which is announced by email at least six months ahead, with a reminder one month before. Once your receiver verifies X-CookieBeam-Signature-V1, you can ask CookieBeam to switch it off earlier for that endpoint; switching it off cannot be undone. Endpoints created now receive only the v1 headers.
If your receiver checks both during the move, check them separately. Never accept a request on the legacy digest after the v1 check failed: that would let an old captured request through.
Signing secrets
The signing secret is shown once, when the endpoint is created or the secret is rotated, and is stored encrypted. It cannot be displayed again; if you lose it, ask CookieBeam to rotate it.
Rotating creates a new secret. You can keep the old one signing for up to 72 hours so you can update your receiver without missing events. During that overlap X-CookieBeam-Signature-V1 carries one v1= entry per secret (the new one first), and the legacy header, where it is still sent, stays signed with the old secret until the overlap ends.
Duplicates
A delivery can arrive more than once, for example when your endpoint answered after the 10-second timeout. Use X-CookieBeam-Delivery (or deliveryId in the body) to ignore deliveries you have already processed.
Receiving requirements
Your endpoint must:
- Use HTTPS with a publicly resolvable host. Private, loopback, and reserved IP addresses are rejected when the endpoint is created and again at delivery time.
- Answer directly. Redirects are refused and count as failed deliveries.
- Respond within 10 seconds. The response body is recorded only up to 4,000 characters.
- Return a 2xx status to acknowledge a delivery. Any other outcome follows the retry policy below.
Retries and disabling
Deliveries are queued and sent by a background runner that processes pending deliveries every few minutes. Each delivery is claimed by one run at a time, so overlapping runs do not send it twice.
A failed delivery is tried up to 8 times in total over about 27 hours: immediately, then after 1 minute, 5 minutes, 30 minutes, 2 hours, 5 hours, 10 hours and 10 hours. Each delay includes random variation so that queued deliveries do not all arrive at once when your endpoint recovers.
Network errors, timeouts, HTTP 5xx, 408, and 429 responses are retried. Other 4xx responses are treated as permanent rejections and are not retried, because the endpoint rejected the request itself.
An endpoint that accumulates 10 consecutive delivery failures is disabled automatically, and the team owner is notified by email. Ask CookieBeam to re-enable the endpoint once it is healthy again.