consent.recordedConsent decisions were recorded
Batched: at most one POST a minute per site, covering every record since the last one.
Delivery
We POST this to your endpoint, signed with its secret. Check the signature before trusting the body.
Headers
X-TagSentry-Signaturestringin headerrequiredt=<unix seconds>,v1=<hex HMAC-SHA256 of "t.body">, keyed with the endpoint's secret.X-TagSentry-Eventstringin headerrequiredThe event type; equal to the body's
type.Always
consent.recordedX-TagSentry-Deliverystringin headerrequiredThe delivery id; equal to the body's
id. Stable across retries.
Body
idstringrequiredThe delivery id. Stable across retries and equal to
X-TagSentry-Delivery: dedupe on it.typestringrequiredAlways
consent.recordedversionintegerrequiredMoves only when a receiver could notice. A new optional field does not move it.
createdAtstringrequiredISO-8601 timestamp, UTC.
accountIdstringrequiredsiteobjectrequiredsite.idstringrequiredsite.domainstringrequiredsite.regionstringrequireddataobjectrequireddata.afterstring | nullrequiredRecords strictly after this were counted. Null on an endpoint's first batch.
data.throughstringrequiredRecords up to and including this were counted.
data.totalintegerrequireddata.byMethodmap of integerrequireddata.recordIdsarray of stringrequiredOldest first, capped. Fetch a record with GET /sites/{siteId}/consent-records.
data.recordIdsTruncatedbooleanrequired
Example body
{ "id": "3f6c1b8e-2d4a-4c7e-9a51-0b8f2e6d7c10", "type": "consent.recorded", "version": 0, "createdAt": "2026-09-26T14:02:00.000Z", "accountId": "3f6c1b8e-2d4a-4c7e-9a51-0b8f2e6d7c10", "site": { "id": "3f6c1b8e-2d4a-4c7e-9a51-0b8f2e6d7c10", "domain": "silverpine.example", "region": "…" }, "data": { "after": "2026-09-26T14:02:00.000Z", "through": "2026-09-26T14:02:00.000Z", "total": 0, "byMethod": { "key": 0 }, "recordIds": [ "3f6c1b8e-2d4a-4c7e-9a51-0b8f2e6d7c10" ], "recordIdsTruncated": false }}Answer with any 2xx. Anything else counts as a failed attempt.
Delivery and signing
Counts and record ids, never visitor data.
Verifying a delivery. Every POST carries X-TagSentry-Signature: t=<unix seconds>,v1=<hex>, where v1 is the HMAC-SHA256, keyed with the endpoint's secret (whsec_…, shown once when the endpoint is added), of the string t + "." + rawBody: the timestamp, a full stop, then the body EXACTLY as received, before any JSON parsing. Compare in constant time and refuse a t more than 300 seconds from your clock.
const crypto = require("node:crypto");
function verify(secret, header, rawBody, nowSeconds = Math.floor(Date.now() / 1000)) {
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
const t = Number(parts.t);
if (!Number.isInteger(t) || Math.abs(nowSeconds - t) > 300) return false;
const expected = crypto.createHmac("sha256", secret).update(`${t}.${rawBody}`).digest();
const given = Buffer.from(parts.v1 ?? "", "hex");
return given.length === expected.length && crypto.timingSafeEqual(given, expected);
}X-TagSentry-Event names the event; X-TagSentry-Delivery is the delivery id, stable across retries. Answer 2xx within 10 seconds. A timeout, 408, 429 or 5xx is retried with backoff, 8 tries in all; any other answer, and a redirect, fails at once. We never follow redirects.
From the OpenAPI document, version 2026-08-26. Raw OpenAPI