Consent Manager endpoints
The versioned contract a registered Consent Manager implements to send consent and withdrawals, review an artefact and receive signed notifications.
The endpoints a Consent Manager registered with the Data Protection Board calls to send you consent and withdrawals, and the notifications it receives. Paths are relative to https://console.dpconsent.com/consent/sdk/v1/consent-managers. Setting one up is in Accept consent from a Consent Manager.
Shared rules#
| Rule | Detail |
|---|---|
| Key | The cmk_… key you issued in Consent Managers, as Authorization: Bearer. It is stored only as a hash. An unknown key gets a flat 401. |
| Servers only | A call with an Origin or Sec-Fetch-Site header gets 403 KEY_IN_BROWSER. |
| Suspended | A suspended Consent Manager gets 403 CONSENT_MANAGER_SUSPENDED on every endpoint except withdrawals. |
| Budget | 600 calls a minute for each Consent Manager, set by CONSENT_MANAGER_CALLS_PER_MINUTE. Above it: 429 with Retry-After. |
| Test and live | Records are written in the environment chosen when the Consent Manager was added. |
| Identifiers | EMAIL, PHONE in E.164 such as +919876543210, or USER_ID. Hashed on arrival with the deployment pepper, as for any write from your server, and never stored as sent. |
| Country | Every record is written for India, IN, under your DPDP region rule if you have one. |
| Repeats | The same artefact sent again with the same givenAt answers 200 with "duplicate": true and writes nothing. |
Endpoints#
GET /schema#
The JSON Schemas of the contract, version 1.0. Public; no key. Cached for an hour.
{
"version": "1.0",
"schemas": {
"artefact": { "$id": "urn:piedap-consent:piedap-cm-artefact/1.0" },
"withdrawal": { "$id": "urn:piedap-consent:piedap-cm-withdrawal/1.0" },
"notification": { "$id": "urn:piedap-consent:piedap-cm-notification/1.0" }
},
"signature": {
"headers": ["X-Piedap-Event", "X-Piedap-Timestamp", "X-Piedap-Signature"],
"algorithm": "HMAC-SHA256",
"signedContent": "<X-Piedap-Timestamp>.<raw body>",
"format": "sha256=<lower-case hex>"
}
}Each schema is shown here with its $id only.
POST /consents#
Records consent the person gave through the Consent Manager, through the same sealed write path as any other consent.
| Field | Type | Required | Description |
|---|---|---|---|
schemaVersion |
string | Yes | piedap-cm-artefact/1.0. |
artefactId |
string, 1 to 128 | Yes | The Consent Manager's own id for the artefact: letters, digits and . _ : -. Never personal data. |
dataPrincipal.identifierType |
string | Yes | EMAIL, PHONE or USER_ID. |
dataPrincipal.identifier |
string, up to 255 | Yes | The identifier you know the person by. |
notice.formKey or notice.formId |
string | No | The form whose notice the person was shown. Left out, the Default notice of the Consent Manager is used. |
notice.version |
integer | No | The version of that notice the person was shown. Default: the published one. |
purposes[] |
array, 1 to 100 | Yes | { "purposeKey", "granted" } for every purpose of the notice that rests on consent. |
givenAt |
time | Yes | When the person gave consent. Up to five minutes ahead of the server clock is read as now. |
validUntil |
time | No | When the consent ends. Left out, the notice's consent term applies. |
locale |
string | No | The language the person used. |
Response
201 for a new record, 200 for a change to the person's record on that form:
{
"schemaVersion": "piedap-cm-artefact/1.0",
"artefactId": "art_01JB7Q4X2M9K",
"consentId": "cns_4f1c2a9b7e3d4c55a1b2c3d4e5f60718",
"status": "PARTIAL",
"purposes": [
{ "purposeKey": "offers-by-sms", "granted": true },
{ "purposeKey": "offers-by-email", "granted": false }
],
"collectedOn": "2026-11-20T10:15:00.000Z",
"expiresOn": "2027-11-20T00:00:00.000Z",
"withdrawnOn": null,
"proofHash": "9be2c4…",
"test": false
}The record's source is CONSENT_MANAGER. Its seal carries consentManagerId and consentManagerArtefactId, so a later change to either breaks the chain. Your endpoints get consent.granted or consent.updated as usual, with source and consentManager in data.
An artefact never replaces a newer answer on the form, or an erasure made after givenAt. It does replace a withdrawal made before givenAt: that is fresh consent.
GET /consents/{artefactId}#
The current state of what one artefact gave, in the shape above, so the Consent Manager can show the person. A record erased since, or an artefact of another Consent Manager, answers 404 ARTEFACT_NOT_FOUND.
POST /withdrawals#
Withdraws, through the normal withdrawal path, consent given through this Consent Manager. Your endpoints get consent.withdrawn and your Data Processors get stop tasks.
| Field | Type | Required | Description |
|---|---|---|---|
schemaVersion |
string | Yes | piedap-cm-withdrawal/1.0. |
artefactId |
string | One of the two | Withdraws that artefact. |
dataPrincipal |
object | One of the two | Withdraws every artefact this Consent Manager sent for the person. Consent the person gave you elsewhere is left alone. |
reason |
string, up to 500 | No | Kept with the withdrawal event. |
{
"schemaVersion": "piedap-cm-withdrawal/1.0",
"withdrawn": [
{
"artefactId": "art_01JB7Q4X2M9K",
"consentId": "cns_4f1c2a9b7e3d4c55a1b2c3d4e5f60718",
"withdrawnOn": "2026-11-21T09:00:00.412Z"
}
],
"alreadyWithdrawn": 0
}withdrawnOn is when the withdrawal reached you. Each call is written to the audit log.
Notifications#
When a person changes or withdraws consent given through a Consent Manager anywhere else, it is told. That covers your website, your server, the console, an erasure, and an artefact from another Consent Manager taking over the record. A change it sent itself is not sent back.
A notification is a POST to its Notification address, signed with its signing secret exactly as webhooks are signed. Verify it as in Verify webhook signatures.
{
"schemaVersion": "piedap-cm-notification/1.0",
"id": "5c1e9a40-7b2d-4f3e-9a61-0d8c2b4f7e15",
"type": "artefact.withdrawn",
"createdOn": "2026-11-21T09:00:00.512Z",
"artefactId": "art_01JB7Q4X2M9K",
"consentId": "cns_4f1c2a9b7e3d4c55a1b2c3d4e5f60718",
"status": "WITHDRAWN",
"purposes": [
{ "purposeKey": "offers-by-sms", "granted": false },
{ "purposeKey": "offers-by-email", "granted": false }
],
"changedOn": "2026-11-21T09:00:00.512Z",
"withdrawnOn": "2026-11-21T09:00:00.412Z",
"expiresOn": "2027-11-20T00:00:00.000Z",
"test": false
}type |
Sent when |
|---|---|
artefact.updated |
The person's answers changed. |
artefact.withdrawn |
The consent was withdrawn, or the person was erased. |
artefact.superseded |
An artefact from another Consent Manager now holds the record. |
The body names the artefact and the record, never the person. Any 2xx answer counts as delivered. Others are retried after 1 minute, 5 minutes, 30 minutes, 2 hours, 6 hours and 12 hours. A suspended Consent Manager, or one without an address, gets none.
consent_manager.notification_failed#
The webhook your endpoints get when a notification still failed after the last retry, so you can tell the Consent Manager another way. Send failed notifications again in the console queues them again.
Field in data |
Type | Description |
|---|---|---|
consentManagerId |
UUID | The Consent Manager's id. |
consentManagerName |
string | Its name. |
registrationNumber |
string | Its Board registration number. |
consentId |
string | The record that changed. |
artefactId |
string | The artefact. |
notice |
string | The notification type. |
attempts |
integer | Attempts made. |
lastError |
string | The last answer or error. |
test |
boolean | true for a test Consent Manager. |
Codes#
| Status | Code | Meaning |
|---|---|---|
| 400 | PHONE_NOT_E164 |
The phone number is not in international format. |
| 400 | GIVEN_AT_IN_FUTURE |
givenAt is more than five minutes ahead. |
| 400 | ARTEFACT_EXPIRED |
validUntil has passed. |
| 400 | NOTICE_REQUIRED |
No notice named and no default notice set. |
| 400 | AMBIGUOUS_FORM_KEY |
The form key is on more than one property. Send notice.formId. |
| 400 | INVALID_PURPOSE_DECISIONS |
A purpose of the notice has no answer, or an answer names no purpose of it. |
| 400 | CONSENT_FORM_NOT_PUBLISHED |
The form has no published version. |
| 400 | ARTEFACT_OR_PRINCIPAL |
A withdrawal needs exactly one of artefactId and dataPrincipal. |
| 403 | PURPOSE_NOT_ACCEPTED |
It grants a purpose you do not accept through this Consent Manager. purposeKeys lists them. |
| 403 | CONSENT_MANAGER_SUSPENDED |
You suspended this Consent Manager. |
| 403 | KEY_IN_BROWSER |
The call came from a browser. |
| 404 | NOTICE_NOT_FOUND |
No form has that key or id. |
| 404 | ARTEFACT_NOT_FOUND |
No record carries that artefact from this Consent Manager. |
| 409 | ARTEFACT_ID_IN_USE |
The artefact id already belongs to another person, notice or erased record. |
| 409 | CONSENT_WITHDRAWN_ON_RECORD |
The person withdrew after givenAt. |
| 409 | CONSENT_NEWER_ON_RECORD |
A newer answer is on record. |
| 409 | CONSENT_ERASED_ON_RECORD |
The person was erased after givenAt. |
Sources and design choices#
No national interface between Consent Managers and Data Fiduciaries had been published by 9 October 2026. This contract is the product's own design, built on what these sources say:
| Source | What it says |
|---|---|
| DPDP Act, 2023, section 2(g) | A Consent Manager is a single point of contact to give, manage, review and withdraw consent through an accessible, transparent and interoperable platform. |
| Section 6(7) to (9) | A person may give, manage, review or withdraw consent to you through a Consent Manager. It acts on the person's behalf and is registered with the Board. |
| Sections 6(4) and 6(10) | Withdrawal must be as easy as consent. You must be able to prove notice and consent. |
| DPDP Rules, 2025, rule 4 and First Schedule | Registration and obligations. Part B: the platform makes personal data unreadable to the Consent Manager; it keeps consents given, denied or withdrawn for at least seven years. |
| G.S.R. 843(E) | Section 6(9) applies from 13 November 2026; sections 6(1) to (8) and (10) from 13 May 2027. |
| MeitY's Business Requirement Document for consent management, June 2025 | A guideline, not binding. An artefact carries the purposes, a timestamp, the user and a status; renewed consent states how long it is valid; a withdrawal is passed to those who rely on it. |
These are the product's own choices:
- the artefact id, the field names and the versions
- naming the notice by form, and the three identifier types
- signed notifications and their retries
- withdrawals accepted from a suspended Consent Manager
- fresh consent accepted after an earlier withdrawal
Data items are not repeated in the artefact: the notice it names itemises them (rule 3(b)(i)).