Consent Docs

People

The endpoints that find, export, withdraw, erase and link everything held for one person, with parameters, examples, responses and errors.

Reference5 min readFor developers calling the REST API

These endpoints act on everything held for one identifier: the records recorded under it and the records it is linked to. Paths are relative to https://console.dpconsent.com/consent/sdk/v1. The tasks are in Find a person and Withdraw and erase.

Shared rules#

Rule Detail
Key A secret key with the endpoint's own scope. A publishable key gets 403 SCOPE_REQUIRED.
Identifier Sent in the body, never in the address. subjectType is required.
Match A record matches when the identifier is the one it was recorded under, or one linked to it.
Test and live A test key reaches test records only. A live key reaches live records only.
Audit log Every call is logged with the key's name and the first 12 characters of the identifier's hash.
Budget 60 calls a minute for each key by default, on top of the key's own budget.

subjectType is EMAIL, PHONE, USER_ID, DEVICE_ID, SESSION_ID or COOKIE_ID.

Endpoints#

POST /subjects/find#

Lists every record held for the person, oldest first.

Key Scope Idempotency-Key Webhook sent
Secret subjects:read Ignored None

Parameters

Name Type Required Description
subjectId string, up to 255 Yes The identifier, as you recorded or linked it.
subjectType string Yes The type of the identifier.

Request

cURLShell
curl -X POST https://console.dpconsent.com/consent/sdk/v1/subjects/find \
  -H "Authorization: Bearer $PIEDAP_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "subjectId": "+919846011223", "subjectType": "PHONE" }'
Node.jsJavaScript
const found = await client.findSubject('+919846011223', 'PHONE');
Python
found = client.find_subject("+919846011223", "PHONE")

Response

200, trimmed:

JSON
{
  "subjectType": "PHONE",
  "fingerprint": "41d2c7…",
  "records": [
    {
      "consentId": "cns_3f1c9a7e2b8d4c6fa0e5d1b7c9f2a4e8",
      "status": "PARTIAL",
      "decisions": [],
      "collectedOn": "2026-09-24T10:12:03.000Z",
      "test": false,
      "erasedOn": null,
      "matchedBy": ["PHONE"]
    }
  ]
}
Field Type Description
subjectType string The type you sent.
fingerprint string The subject fingerprint of the identifier you sent. See Identifiers.
records array Each entry is a consent record plus erasedOn and matchedBy. An empty list means nothing is held. It is not an error.
records[].erasedOn time or null Set on records that have been erased. They stay in the list.
records[].matchedBy array The identifier types that matched this record, such as ["PHONE"].

Errors

Only the shared errors, explained in Errors.

POST /subjects/export#

Returns the package to give a person who asks what you hold: a signed receipt for each record, a summary, their nominations and whom to contact.

Key Scope Idempotency-Key Webhook sent
Secret subjects:export Ignored None

Parameters

Name Type Required Description
subjectId string, up to 255 Yes The identifier, as you recorded or linked it.
subjectType string Yes The type of the identifier.

Request

cURLShell
curl -X POST https://console.dpconsent.com/consent/sdk/v1/subjects/export \
  -H "Authorization: Bearer $PIEDAP_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "subjectId": "usr_8f2K1xQz", "subjectType": "USER_ID" }' \
  -o access-usr_8f2K1xQz.json
Node.jsJavaScript
const pack = await client.exportSubject('usr_8f2K1xQz', 'USER_ID');
Python
pack = client.export_subject("usr_8f2K1xQz", "USER_ID")

Response

200, trimmed:

JSON
{
  "subjectType": "USER_ID",
  "generatedOn": "2026-09-24T10:20:11.000Z",
  "summary": {
    "purposes": [
      {
        "purposeKey": "course-updates-by-email",
        "purposeName": "Course updates by email",
        "granted": true,
        "dataItems": ["Email address"]
      }
    ],
    "sharedWith": [
      {
        "name": "Example Mail Pvt Ltd",
        "purposes": ["Course updates by email"],
        "dataShared": ["Email address"]
      }
    ]
  },
  "nominations": [],
  "contact": {
    "organisation": "Kalpa Learning",
    "email": "grievance@kalpalearning.example.in",
    "dataProtectionOfficer": "Meera Iyer",
    "phone": null,
    "postalAddress": null,
    "grievanceOfficer": null
  },
  "receipts": []
}
Field Type Description
generatedOn time When the package was made.
summary.purposes array Each purpose the person was asked about: its key and name, whether it is allowed now, any partner answers and the data it uses.
summary.sharedWith array Each partner the data could be shared with, including under consent since withdrawn.
summary.processorNotices array Each instruction a Data Processor was given for this person: partner, partnerKey, action, purposes, issuedOn, dueOn, status, outcome, and completedOn, reportedOn and reportedVia of the latest report. No notes or names of your staff.
nominations array Each nomination the person made: nomineeName, relationship, status (ACTIVE or REVOKED), nominatedOn, revokedOn and requestReference. Never the nominee's email address or phone number.
contact object Whom to contact, from the console's settings. See The contact object.
receipts array One signed receipt for each record, in the shape GET /consent/{consentId}/verify returns.

The package names the person only by hashes. Each export is noted in the history of every record it covers.

Errors

Only the shared errors, explained in Errors.

POST /subjects/withdraw#

Withdraws every consent the person has given, on every form.

Key Scope Idempotency-Key Webhook sent
Secret subjects:withdraw Checked, not replayed consent.withdrawn, once for each record withdrawn

Parameters

Name Type Required Description
subjectId string, up to 255 Yes The identifier, as you recorded or linked it.
subjectType string Yes The type of the identifier.
reason string, up to 500 No Kept on each withdrawal event.

Request

cURLShell
curl -X POST https://console.dpconsent.com/consent/sdk/v1/subjects/withdraw \
  -H "Authorization: Bearer $PIEDAP_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: close-usr_8f2K1xQz" \
  -d '{
    "subjectId": "usr_8f2K1xQz",
    "subjectType": "USER_ID",
    "reason": "Account closed at the learner’s request"
  }'
Node.jsJavaScript
const { withdrawn } = await client.withdrawSubject(
  'usr_8f2K1xQz',
  'USER_ID',
  {
    reason: 'Account closed at the learner’s request',
    idempotencyKey: 'close-usr_8f2K1xQz',
  },
);
Python
withdrawn = client.withdraw_subject(
    "usr_8f2K1xQz",
    "USER_ID",
    reason="Account closed at the learner’s request",
    idempotency_key="close-usr_8f2K1xQz",
)

Response

200:

JSON
{ "withdrawn": 2 }

withdrawn is how many records changed. Every record that is not erased and not already withdrawn becomes WITHDRAWN. A second call returns 0. The Python call returns the number itself.

Errors

Only the shared errors, explained in Errors.

POST /subjects/erase#

Removes what identifies the person from every record, and withdraws any consent still in force first.

Key Scope Idempotency-Key Webhook sent
Secret subjects:erase Checked, not replayed consent.withdrawn for each record still in force, then subject.erased for each record erased

Parameters

Name Type Required Description
subjectId string, up to 255 Yes The identifier, as you recorded or linked it.
subjectType string Yes The type of the identifier.
reason string, up to 500 No Kept on the erasure event and sent in subject.erased.

Request

cURLShell
curl -X POST https://console.dpconsent.com/consent/sdk/v1/subjects/erase \
  -H "Authorization: Bearer $PIEDAP_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: erase-usr_8f2K1xQz" \
  -d '{
    "subjectId": "usr_8f2K1xQz",
    "subjectType": "USER_ID",
    "reason": "Erasure requested by the learner"
  }'
Node.jsJavaScript
const { erased } = await client.eraseSubject(
  'usr_8f2K1xQz',
  'USER_ID',
  {
    reason: 'Erasure requested by the learner',
    idempotencyKey: 'erase-usr_8f2K1xQz',
  },
);
Python
erased = client.erase_subject(
    "usr_8f2K1xQz",
    "USER_ID",
    reason="Erasure requested by the learner",
    idempotency_key="erase-usr_8f2K1xQz",
)

Response

200:

JSON
{ "erased": 2 }

erased is how many records this call erased. A second call returns 0. The Python call returns the number itself. What is cleared, and the one-year hold under the DPDP Act, are in Withdraw and erase.

Errors

Only the shared errors, explained in Errors.

POST /subjects/link#

Links further identifiers to every record of the person that is not erased.

Key Scope Idempotency-Key Webhook sent
Secret subjects:link Checked, not replayed None

Parameters

Name Type Required Description
subjectId string, up to 255 Yes The identifier, as you recorded or linked it.
subjectType string Yes The type of the identifier.
identifiers array, 1 to 5 Yes Each entry has subjectType and subjectId. Entries must not repeat.

Request

cURLShell
curl -X POST https://console.dpconsent.com/consent/sdk/v1/subjects/link \
  -H "Authorization: Bearer $PIEDAP_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: phone-usr_8f2K1xQz-919846099887" \
  -d '{
    "subjectId": "usr_8f2K1xQz",
    "subjectType": "USER_ID",
    "identifiers": [
      { "subjectType": "PHONE", "subjectId": "+919846099887" }
    ]
  }'
Node.jsJavaScript
const result = await client.linkIdentifiers(
  'usr_8f2K1xQz',
  'USER_ID',
  [{ subjectType: 'PHONE', subjectId: '+919846099887' }],
  { idempotencyKey: 'phone-usr_8f2K1xQz-919846099887' },
);
Python
from piedap_consent import Identifier

result = client.link_identifiers(
    "usr_8f2K1xQz",
    "USER_ID",
    [Identifier("PHONE", "+919846099887")],
    idempotency_key="phone-usr_8f2K1xQz-919846099887",
)

Response

200:

JSON
{ "records": 2, "added": 2 }
Field Type Description
records integer How many records the person has that are not erased.
added integer How many new links were made. An identifier already linked is skipped.

Errors

Only the shared errors, explained in Errors.

Shared errors#

Status Code When
403 SCOPE_REQUIRED The key lacks the scope, or the call was made with a publishable key.
403 SECRET_KEY_IN_BROWSER The request came from a browser. The key is flagged as exposed.
400 None. The message is a list A field is missing or not valid: no subjectType, an unknown type, an empty identifiers list, more than five entries or a repeat.
429 None. The message is Too many requests The budget for calls on a person, or the key's own budget, is used up. Wait for Retry-After.

Every code is explained in Errors.

↑↓ to move, Enter to open