Answer an access request
Export one package with a signed receipt for every record a person has, link a rights request to your own id, and check the receipts.
When a person asks what you hold about them, export one package from your server. It holds a signed receipt for each consent record, a summary of the processing and whom to contact.
Before you start#
- A secret key with the scope
subjects:export. To link requests from the hosted form, it also needsrequests:write. - The
clientfrom Record consent from your server.
Export the person's records#
-
If the request came from the hosted request form, email the
verificationUrlfrom thesubject_request.receivedwebhook toreplyTo. The request waits until the person follows it. -
If you keep records under your own id, link the request to that id. The webhook carries the request's
referenceand thesubjectIdthe person typed.Node.jsJavaScript const linked = await client.linkRequest( 'DSR-2026-4F2A91', 'usr_8f2K1xQz', 'USER_ID', ); // { reference: 'DSR-2026-4F2A91', linkedRecordCount: 2 }Python linked = client.link_request( "DSR-2026-4F2A91", "usr_8f2K1xQz", "USER_ID" ) # {"reference": "DSR-2026-4F2A91", "linkedRecordCount": 2}cURLShell curl -X POST https://console.dpconsent.com/consent/sdk/v1/requests/link \ -H "Authorization: Bearer $PIEDAP_SECRET_KEY" \ -H "Content-Type: application/json" \ -d '{ "reference": "DSR-2026-4F2A91", "subjectId": "usr_8f2K1xQz", "subjectType": "USER_ID" }' -
Export the package with
exportSubject.Node.jsJavaScript import { writeFile } from 'node:fs/promises'; const pack = await client.exportSubject('usr_8f2K1xQz', 'USER_ID'); await writeFile( 'access-usr_8f2K1xQz.json', JSON.stringify(pack, null, 2), );Python import json pack = client.export_subject("usr_8f2K1xQz", "USER_ID") with open("access-usr_8f2K1xQz.json", "w", encoding="utf-8") as file: json.dump(pack, file, indent=2)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 -
Add your own account data. The package names the person only by hashes.
-
Send the package to the person, with the
contactdetails from it in your reply.
The package holds these parts:
| Part | What it holds |
|---|---|
summary.purposes |
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 |
Each partner the data could be shared with, including under consent since withdrawn |
contact |
organisation, email and dataProtectionOfficer, from the console's settings |
receipts |
One signed receipt per record: the notice version, every decision, the times and the proof chain |
generatedOn |
When the package was made |
Each export is noted in the history of every record it covers. A linked request shows how many consent records it matched. When your team carries out the request in the console, it acts on those records.
Check the receipts#
Every receipt is signed with the deployment's Ed25519 key, so anyone can check it without an account.
import { verifyReceipt } from '@piedap/consent/server';
const keys = await client.publicKeys();
for (const signed of pack.receipts) {
const ok = verifyReceipt(signed, keys);
console.log(signed.receipt.consentId, ok ? 'verified' : 'NOT verified');
}curl -X POST https://console.dpconsent.com/consent/sdk/v1/receipts/verify \
-H "Content-Type: application/json" \
-d @receipt.jsonverifyReceipt makes no network call once you hold the keys. The cURL call needs no key. It takes one receipt, saved as receipt.json with its receipt, signature and algorithm. The Python package has no offline check, so post each receipt to the same endpoint.
If a call is refused#
403 SCOPE_REQUIRED: the key lacks the scope for the call.404 REQUEST_NOT_FOUND: no request has that reference.