Consent events
The webhook events sent when a consent record is created, changed, withdrawn or expires, and when an import ends, with payload fields and examples.
Four webhook events report a change to one consent record, and one reports an import from a file. Each arrives in the envelope described in Webhook format.
Payload fields#
data is the same for the four record events: the consent record as the API returns it, plus subject.
Field in data |
Type | Description |
|---|---|---|
consentId |
string | The record's id. The simplest way to match the event to your user. |
status |
string | GRANTED, PARTIAL, DENIED, WITHDRAWN, EXPIRED or PENDING. |
formId, formVersion, formContentHash |
UUID, integer, string | The form version the person answered. |
decisions |
array | One entry for each purpose: purposeKey, purposeName, category, granted and, when the purpose is allowed but a partner was refused, partners. |
countryCode, subdivisionCode |
string or null |
Where the person was. |
lawCode |
string or null |
The law that applied, such as DPDP. |
collectedOn, expiresOn |
time | When the current answer was given and when it ends. |
withdrawnOn |
time or null |
When the record was withdrawn. |
proofHash |
string | The seal of the record's current state. |
test |
boolean | true for a record written with a test key. |
subject.type |
string | The type of identifier the record was recorded under. |
subject.fingerprint |
string or null |
The subject fingerprint of that identifier. null for records made before fingerprints existed, and for records already erased. |
subject.reference |
string or null |
The identifier as you sent it, only when your project stores a readable copy. |
basis, noticeReference, noticeSha256 |
string | For consent brought in from earlier, when set. Present only then. |
captureContext |
object | On consent.granted and consent.updated, when the write sent capture. attestedByStaffOnly is true when only your staff attest the consent. See Capture fields. |
actingAs |
object | On consent.withdrawn, when a parent, lawful guardian or nominee made the rights request behind it. See Who acted for the person. |
processors |
array | On consent.updated, consent.withdrawn and consent.expired: the instructions for your Data Processors. Often empty. See Processor instructions. |
The three ways to tell whom an event is about are in Match events to users.
Events#
consent.granted#
Sent when
A record is created: the first answer from a person on a form, from the banner, an app, your server or an import. The event is sent whatever the answer was, so read data.status.
Payload
The payload fields, with these values:
Field in data |
Value |
|---|---|
status |
GRANTED, PARTIAL or DENIED. PENDING for an email opt-in that is not yet confirmed. |
withdrawnOn |
null |
Example
{
"id": "9a3d7c12-4b6e-4f0a-8c2d-1e5f7a9b3c40",
"type": "consent.granted",
"createdOn": "2026-09-20T08:41:10.305Z",
"projectId": "c2a6f0de-51b3-4f0a-9d7e-8b1f3a6c4e90",
"test": false,
"data": {
"consentId": "cns_3f1c9a7e2b8d4c6fa0e5d1b7c9f2a4e8",
"status": "PARTIAL",
"formId": "0b9c6f0e-6a55-4c1e-9d0a-2f6f0f4f7a11",
"formVersion": 3,
"formContentHash": "9b1f04c7…",
"decisions": [
{
"purposeKey": "course-updates-by-email",
"purposeName": "Course updates by email",
"category": "MARKETING",
"granted": true
},
{
"purposeKey": "class-reminders-by-sms",
"purposeName": "Class reminders by SMS",
"category": "MARKETING",
"granted": false
}
],
"countryCode": "IN",
"subdivisionCode": null,
"lawCode": "DPDP",
"collectedOn": "2026-09-20T08:41:10.000Z",
"expiresOn": "2027-09-20T08:41:10.000Z",
"withdrawnOn": null,
"proofHash": "5d1f6e0a…",
"test": false,
"subject": {
"type": "USER_ID",
"fingerprint": "8c41…",
"reference": null
}
}
}consent.updated#
Sent when
An existing record gets a new answer: a change in the preference centre, a second POST /consent for the same person and form, a PATCH, or an email opt-in being confirmed. Linking identifiers alone sends no event.
Payload
The payload fields, with these values:
Field in data |
Value |
|---|---|
status |
GRANTED, PARTIAL or DENIED, worked out from the new answers. PENDING when an email opt-in is started again before it is confirmed. |
collectedOn, expiresOn |
Counted again from the change. |
Example
{
"id": "b1e84f07-2c3a-4d59-9e61-7f0a2b4c6d81",
"type": "consent.updated",
"createdOn": "2026-09-22T14:05:31.118Z",
"projectId": "c2a6f0de-51b3-4f0a-9d7e-8b1f3a6c4e90",
"test": false,
"data": {
"consentId": "cns_3f1c9a7e2b8d4c6fa0e5d1b7c9f2a4e8",
"status": "GRANTED",
"formId": "0b9c6f0e-6a55-4c1e-9d0a-2f6f0f4f7a11",
"formVersion": 3,
"formContentHash": "9b1f04c7…",
"decisions": [
{
"purposeKey": "course-updates-by-email",
"purposeName": "Course updates by email",
"category": "MARKETING",
"granted": true
},
{
"purposeKey": "class-reminders-by-sms",
"purposeName": "Class reminders by SMS",
"category": "MARKETING",
"granted": true
}
],
"countryCode": "IN",
"subdivisionCode": null,
"lawCode": "DPDP",
"collectedOn": "2026-09-22T14:05:31.000Z",
"expiresOn": "2027-09-22T14:05:31.000Z",
"withdrawnOn": null,
"proofHash": "c07a91d3…",
"test": false,
"subject": {
"type": "USER_ID",
"fingerprint": "8c41…",
"reference": null
}
}
}consent.withdrawn#
Sent when
A record becomes WITHDRAWN. The cause can be a withdraw call for the record or the person, the banner, an unsubscribe link or the Your consents page. It can also be the console, a rights request your team carried out, or a notice campaign link. Erasing a record that was still in force sends it too. Withdrawing a record that is already withdrawn sends nothing.
Payload
The payload fields, with these values:
Field in data |
Value |
|---|---|
status |
WITHDRAWN |
decisions[].granted |
false for every optional purpose. |
withdrawnOn |
The time of the withdrawal. |
subject |
When the withdrawal is part of an erasure, taken immediately before the erasure. The fingerprint and the reference are still there. |
processors |
A STOP for each Data Processor the person had allowed. [] when the withdrawal is part of an erasure: the subject.erased event carries the ERASE tasks. |
Example
{
"id": "4f1c2b9e-7d0a-4c55-9a51-2d8e6f0b3a17",
"type": "consent.withdrawn",
"createdOn": "2026-09-24T10:12:03.412Z",
"projectId": "c2a6f0de-51b3-4f0a-9d7e-8b1f3a6c4e90",
"test": false,
"data": {
"consentId": "cns_3f1c9a7e2b8d4c6fa0e5d1b7c9f2a4e8",
"status": "WITHDRAWN",
"formId": "0b9c6f0e-6a55-4c1e-9d0a-2f6f0f4f7a11",
"formVersion": 3,
"formContentHash": "9b1f04c7…",
"decisions": [
{
"purposeKey": "course-updates-by-email",
"purposeName": "Course updates by email",
"category": "MARKETING",
"granted": false
},
{
"purposeKey": "class-reminders-by-sms",
"purposeName": "Class reminders by SMS",
"category": "MARKETING",
"granted": false
}
],
"countryCode": "IN",
"subdivisionCode": null,
"lawCode": "DPDP",
"collectedOn": "2026-09-22T14:05:31.000Z",
"expiresOn": "2027-09-22T14:05:31.000Z",
"withdrawnOn": "2026-09-24T10:12:03.000Z",
"proofHash": "5d1f6e0a…",
"test": false,
"subject": {
"type": "USER_ID",
"fingerprint": "8c41…",
"reference": null
},
"processors": [
{
"taskId": "2b9e4f61-0c3d-4a7e-8f15-6d2c9b0a7e34",
"key": "nilgiri-mailers",
"name": "Nilgiri Mailers",
"action": "STOP",
"purposes": ["course-updates-by-email"],
"dueOn": "2026-10-01T10:12:03.412Z",
"retainLogsUntil": null
}
]
}
}consent.expired#
Sent when
A record reaches expiresOn while it is still GRANTED, PARTIAL or PENDING. The check runs every hour. Treat the event like a withdrawal, and ask again if you still need the consent.
Payload
The payload fields, with these values:
Field in data |
Value |
|---|---|
status |
EXPIRED |
decisions[].granted |
false for every optional purpose. |
expiresOn |
The time that has passed. |
Example
{
"id": "e2c5a8f1-6d7b-4a39-b0c4-3f8e1d2a5b96",
"type": "consent.expired",
"createdOn": "2027-09-22T15:00:04.871Z",
"projectId": "c2a6f0de-51b3-4f0a-9d7e-8b1f3a6c4e90",
"test": false,
"data": {
"consentId": "cns_3f1c9a7e2b8d4c6fa0e5d1b7c9f2a4e8",
"status": "EXPIRED",
"formId": "0b9c6f0e-6a55-4c1e-9d0a-2f6f0f4f7a11",
"formVersion": 3,
"formContentHash": "9b1f04c7…",
"decisions": [
{
"purposeKey": "course-updates-by-email",
"purposeName": "Course updates by email",
"category": "MARKETING",
"granted": false
},
{
"purposeKey": "class-reminders-by-sms",
"purposeName": "Class reminders by SMS",
"category": "MARKETING",
"granted": false
}
],
"countryCode": "IN",
"subdivisionCode": null,
"lawCode": "DPDP",
"collectedOn": "2026-09-22T14:05:31.000Z",
"expiresOn": "2027-09-22T14:05:31.000Z",
"withdrawnOn": null,
"proofHash": "71be40c9…",
"test": false,
"subject": {
"type": "USER_ID",
"fingerprint": "8c41…",
"reference": null
}
}
}consent.imported#
Sent when
An import from a file in the console finishes or is stopped: one event for the whole file. A check that was never written sends none. No event is sent for each record the import writes.
Payload
Field in data |
Type | Description |
|---|---|---|
importId |
UUID | The import run. Open its records in the console at Records with Show records under Past imports. |
status |
string | COMPLETED, or STOPPED when it was stopped or left idle for 30 minutes. |
complete |
boolean | true when every row of the file was handled. |
test |
boolean | true for an import of test records. |
propertyId, formId, formKey, formVersion |
string, integer | The form version the records were made against. |
basis, noticeReference, noticeSha256 |
string or null |
What the import stated for every row. |
fileSha256 |
string | The SHA-256 of the file, as the browser reported it. |
rowsInFile |
integer | Rows in the file. |
created, updated, skipped, failed |
integer | Row counts. |
reasons |
object | Each skip or failure reason with its count. See Consent import file. |
startedOn, finishedOn |
time | When the import started and ended. |
The event carries no file name, no person and no identifier.
Example
{
"id": "0c7e1a94-3b2d-4f68-a5e1-9d4c2b7f0a36",
"type": "consent.imported",
"createdOn": "2026-11-02T06:22:48.511Z",
"projectId": "c2a6f0de-51b3-4f0a-9d7e-8b1f3a6c4e90",
"test": false,
"data": {
"importId": "6f0d8a52-2c7e-4b8e-9f4d-0c1a7e3b5d21",
"status": "COMPLETED",
"complete": true,
"test": false,
"propertyId": "8c2a5e10-3f4b-4c6d-9e7f-1a2b3c4d5e6f",
"formId": "0b9c6f0e-6a55-4c1e-9d0a-2f6f0f4f7a11",
"formKey": "kaveri-account-opening",
"formVersion": 1,
"basis": "PRE_COMMENCEMENT",
"noticeReference": "AOF-2019 rev 3",
"noticeSha256": null,
"fileSha256": "9c1e5b2f…",
"rowsInFile": 48210,
"created": 47002,
"updated": 830,
"skipped": 312,
"failed": 66,
"reasons": {
"CONSENT_NEWER_ON_RECORD": 280,
"CONSENT_WITHDRAWN_ON_RECORD": 30,
"CONSENT_ERASED_ON_RECORD": 2,
"PHONE_NOT_E164": 41,
"UNREADABLE_DATE": 25
},
"startedOn": "2026-11-02T05:31:44.000Z",
"finishedOn": "2026-11-02T06:22:48.000Z"
}
}Processor instructions#
processors lists one instruction for each partner you registered as a Data Processor that must stop or erase. Pass each one to the processor and report back. See Pass instructions to processors.
| Field | Type | Description |
|---|---|---|
taskId |
UUID | The task. Report against it. |
key, name |
string | The processor's partner key and name. |
action |
string | STOP, or ERASE in erasure events. |
purposes |
array | The purposes concerned. |
dueOn |
time | When the task is due, by the days in the register. |
retainLogsUntil |
time or null |
On an ERASE: until when the processor may keep processing logs. |
consent.granted carries no processors. A person known only by a cookie or session id gets none.
Related#
- Receive webhooks
- Match events to your users
- People events for
subject.erased