Consent data
Read consent records, a person's current consent, purposes and collection points.
All four calls need a key with consent:read.
List consent records
GET /api/v1/consent-records
The consent ledger: one row per purpose per decision, from every channel (cookie banner, forms, Manage my data, API, parental consent, re-consent links).
| Parameter | |
|---|---|
period | today, yesterday, last_24_hours, last_7_days, last_30_days. Days are counted in IST. With neither period nor from and to, the default is last_24_hours |
from, to | Instead of period. A date (2026-10-01 means that whole IST day) or a timestamp with a timezone (2026-10-01T09:00:00+05:30, or ...Z). from is included and to is not, so to=2026-10-07 includes all of 7 October. Either can be left out |
email | One data principal (identified consent) |
subject_id | Instead of email: any subject, for example an anonymous browser id anon_... |
subject_type | identified or anonymous |
collection_point | A form's slug or id. Unknown: 404 UNKNOWN_COLLECTION_POINT |
purpose_id | One purpose |
source | banner, form, preference_center, api, gpc, parental |
website | One of your websites, by hostname. Unknown: 404 UNKNOWN_WEBSITE |
granted | true or false |
order | desc (newest first, default) or asc (oldest first, best for syncing) |
limit | 1 to 1000, default 100 |
cursor | The next_cursor of the previous page |
{
"data": [
{
"id": "5f0c...",
"consent_event_id": "0b6c2f9e-...",
"recorded_at": "2026-10-10T19:02:11.483201+00:00",
"subject": { "id": "asha@example.in", "type": "identified" },
"purpose": { "id": "36ff...", "name": "Newsletter", "type": "data" },
"granted": false,
"kind": "decision",
"source": "form",
"collection_point": { "id": "be88...", "slug": "newsletter", "name": "Newsletter signup" },
"website": "shop.example.in",
"notice_version": "3"
}
],
"has_more": true,
"next_cursor": "WyIyMDI2LTEwLTEw...",
"period": { "name": "yesterday", "from": "2026-10-09T18:30:00.000Z", "to": "2026-10-10T18:30:00.000Z", "timezone": "Asia/Kolkata" }
}- Grouping. Rows that share a
consent_event_idwere saved together. That id is the Consent ID on the person's receipt. kindisdecision(the person chose) ornotice_shown(a legitimate-use purpose on a form: they were informed, nothing was agreed).- Erased people appear as
erased_HASH. The decisions stay, as proof of consent. - Not included: IP address and browser. They are evidence for disputes, available in the admin app and the CSV export, not for syncing.
- Paging. While
has_moreistrue, call again withcursor=NEXT_CURSORand the same filters. Paging is by time and id, so rows are never skipped or repeated, even if new consent arrives in between.
A daily sync
Run once a day after midnight IST:
curl -s "https://v1.pactbase.in/api/v1/consent-records?period=yesterday&order=asc&limit=1000" \
-H "Authorization: Bearer $PACTBASE_API_KEY"
# then repeat with &cursor=NEXT_CURSOR until next_cursor is nullRecords never change once written, so a day fetched once stays complete. For a rolling window use period=last_7_days, or from set to the time of your last run.
// Node 18+: every page of the last 7 days
let cursor = null, all = []
do {
const url = new URL("https://v1.pactbase.in/api/v1/consent-records")
url.searchParams.set("period", "last_7_days")
url.searchParams.set("order", "asc")
url.searchParams.set("limit", "1000")
if (cursor) url.searchParams.set("cursor", cursor)
const res = await fetch(url, { headers: { Authorization: `Bearer ${process.env.PACTBASE_API_KEY}` } })
if (!res.ok) throw new Error(`Pactbase API ${res.status}`)
const page = await res.json()
all.push(...page.data)
cursor = page.next_cursor
} while (cursor)Get one person's current consent
GET /api/v1/consent-state?email=asha@example.in (or ?subject_id=anon_...)
The newest decision per purpose. Use it right before sending a message.
{
"subject": { "id": "asha@example.in" },
"purposes": [
{ "purpose_id": "36ff...", "purpose": "Newsletter", "granted": false, "updated_at": "...", "consent_event_id": "...", "source": "preference_center" }
]
}List purposes
GET /api/v1/purposes
Every purpose of your organisation, active and paused: id, name, type (data or cookie), category, lawful_basis, active and description.
Get a collection point
GET /api/v1/collection-points/{cp}?lang=hi
What to show a person before asking, for an app screen or a form you build yourself. cp is the collection point's slug or id. lang (default en) returns approved translations.
{
"collection_point": { "id": "...", "slug": "newsletter", "name": "Newsletter signup" },
"lang": "hi",
"available_langs": ["en", "hi"],
"notice_version": "4",
"org_slug": "acme",
"purposes": [
{ "id": "36ff...", "name": "Newsletter", "description": "Product news by email", "basis": "consent" },
{ "id": "8a1c...", "name": "Order delivery", "description": null, "basis": "legitimate_use" }
]
}basis: consent: show an unticked checkbox. DPDP section 6 needs an affirmative act.basis: legitimate_use: show the text as information only, with no checkbox.notice_version: null: the Purpose Registry is not published yet, so do not ask. Recording would be refused withNO_NOTICE.