PactbasePactbase DocsDocs
API

Consent data

Read consent records, a person's current consent, purposes and collection points.

All four calls need a key with consent:read.

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
periodtoday, 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, toInstead 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
emailOne data principal (identified consent)
subject_idInstead of email: any subject, for example an anonymous browser id anon_...
subject_typeidentified or anonymous
collection_pointA form's slug or id. Unknown: 404 UNKNOWN_COLLECTION_POINT
purpose_idOne purpose
sourcebanner, form, preference_center, api, gpc, parental
websiteOne of your websites, by hostname. Unknown: 404 UNKNOWN_WEBSITE
grantedtrue or false
orderdesc (newest first, default) or asc (oldest first, best for syncing)
limit1 to 1000, default 100
cursorThe 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_id were saved together. That id is the Consent ID on the person's receipt.
  • kind is decision (the person chose) or notice_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_more is true, call again with cursor=NEXT_CURSOR and 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 null

Records 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 /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 with NO_NOTICE.

On this page