API Reference
Complete REST API for DPDPA Shield. All endpoints return JSON. Requires HTTPS.
Base URL
https://api.dpdpashield.inResponse Envelope
Every response is wrapped in a consistent envelope.
// Success
{ "data": { ... } }
// Error
{
"error": {
"message": "Human-readable description",
"code": "MACHINE_READABLE_CODE",
"fields": { "fieldName": "validation message" }
// fields only present on VALIDATION_ERROR responses
}
}Authentication
DPDPA Shield uses two authentication methods depending on the endpoint type.
Method 1 - Bearer Token (Dashboard API)
Used for all protected dashboard endpoints. Obtain a token by logging in via POST /api/v1/auth/login. Access tokens expire in 15 minutes; use the refresh token to obtain a new one.
// Send on every request:
Authorization: Bearer <your-jwt-token>
// Obtain a token:
POST /api/v1/auth/login
{
"email": "dpo@yourcompany.com",
"password": "..."
}
// Response: { "data": { "token": "eyJ...", "refreshToken": "..." } }
// Refresh when expired:
POST /api/v1/auth/refresh
{ "refreshToken": "..." }
// Response: { "data": { "token": "eyJ..." } }Method 2 - API Key (SDK / Public Endpoints)
Used by the JavaScript SDK and public-facing endpoints. Passed as a request body field or query parameter. Generate keys in Settings β API Keys.
// As a request body field (SDK endpoints):
{
"apiKey": "dpdpa_live_YOUR_KEY",
...
}
// As a query parameter (GET endpoints):
GET /api/v1/consent/public-notice?apiKey=dpdpa_live_YOUR_KEYSDK Domain Allowlist & Rate Limits
Because SDK keys are public (embedded in browser HTML), three layers prevent abuse:
- 1. Origin allowlist. Every public SDK call must come from a domain registered under the API key. Register domains in Settings β API Keys β Domains. The request
Originheader (falling back toReferer) is matched against the list. Mismatches return403 ORIGIN_NOT_ALLOWED. Keys with zero registered domains are unenforced (allow-all) until the first domain is added - a one-time grace state for new tenants. - 2. Per-apiKey rate limits. 600 requests / minute per (apiKey, IP) and 5000 requests / minute per apiKey globally, applied to
/public-notice,/sdk-record,/withdraw-public. Excess returns429 SDK_RATE_LIMITwith aRetry-Afterheader. - 3. localhost exempt.
localhostand127.0.0.1are always allowed for local development. Vercel / Amplify preview hosts must be registered explicitly.
Manage domains: GET / POST /api/v1/api-keys/:keyId/domains Β·DELETE /api/v1/api-keys/:keyId/domains/:domainId.
Which auth method for which endpoint?
| Auth method | Endpoints |
|---|---|
| Bearer token | All /api/v1/* dashboard endpoints |
| API key | /consent/public-notice/consent/sdk-record/consent/withdraw-public |
| No auth | /consent/portal/:token/consent/portal/:token/withdraw |
Rate Limits
| Endpoint group | Limit |
|---|---|
| Bearer token endpoints | 100 req/min per tenant |
| SDK endpoints (sdk-record, public-notice) | 1,000 req/min per API key |
| Withdrawal endpoint (withdraw-public) | 5 req/hour per IP |
Consent Endpoints
/api/v1/consent/analyticsReturns aggregate consent metrics for the dashboard.
{
"data": {
"totalConsents": 4821,
"consentGiven": 3960,
"consentWithdrawn": 146,
"withdrawalRate": 3.0,
"recentConsents": 312,
"byPurpose": [
{ "purposeId": "uuid", "name": "Analytics", "given": 2100, "withdrawn": 40 }
]
}
}/api/v1/consent/recordsPaginated list of consent records. Each record includes the notice version at time of consent - use this to identify stale consents for re-consent campaigns.
| Parameter | Type | Required | Description |
|---|---|---|---|
page | number | Optional | Page number (1-indexed)Default: 1 |
limit | number | Optional | Records per page (max 50)Default: 20 |
search | string | Optional | Full email address to search by. Hashed and matched against dataPrincipalHash. Partial email search is not supported - only exact email lookups work. |
status | string | Optional | Filter by consent status: active (withdrawnAt is null and no purposes withdrawn) | partial (withdrawnPurposes set, withdrawnAt still null) | withdrawn (withdrawnAt is set) |
sortBy | string | Optional | Sort field: createdAt | withdrawnAtDefault: createdAt |
sortOrder | string | Optional | Sort direction: asc | descDefault: desc |
{
"data": {
"items": [
{
"id": "uuid",
"dataPrincipalHash": "sha256hex",
"consentGiven": true,
"languageShown": "HI",
"positionShown": "bottom-right",
"isWithdrawn": false,
"withdrawnAt": null,
"withdrawnPurposes": [],
"isPartiallyWithdrawn": false,
"createdAt": "2026-05-01T10:00:00.000Z",
"noticeId": "uuid",
"noticeTitle": "Acme Privacy Notice",
"noticeVersionNumber": 2,
"currentNoticeVersion": 3
}
],
"total": 4821,
"page": 1,
"limit": 20
}
}/api/v1/consent/status/:hashLook up consent status by SHA-256 hash of the data principal identifier.
| Parameter | Type | Required | Description |
|---|---|---|---|
:hash | string | Required | SHA-256 hex hash of the data principal email or identifier |
{
"data": [
{
"consentRecordId": "uuid",
"noticeId": "uuid",
"noticeTitle": "Acme Privacy Notice",
"noticeVersion": 3,
"consentGiven": { "purpose-id-1": true, "purpose-id-2": false },
"isWithdrawn": false,
"withdrawnAt": null,
"withdrawnPurposes": [],
"isPartiallyWithdrawn": false,
"createdAt": "2026-05-01T10:00:00.000Z",
"proofHash": "sha256hex"
}
]
}
// data is an array sorted newest-first.
// Use data[0] for the current state; empty array = no consent on record.
// isWithdrawn is only true on FULL withdrawal - check isPartiallyWithdrawn too,
// since withdrawnAt stays null when only some purposes were revoked./api/v1/consent/recordRecord a new consent decision via the dashboard API. For widget-based collection use POST /sdk-record instead.
| Parameter | Type | Required | Description |
|---|---|---|---|
noticeId | string (UUID) | Required | The consent notice UUID to record consent against |
dataPrincipalIdentifier | string | Required | Email or unique identifier of the data principal (max 512 chars) |
consentGiven | Record<string, boolean> | Required | Map of purpose UUID β consent decision (true = granted) |
ipAddress | string | Optional | Data principal IP address (auto-detected if omitted) |
userAgent | string | Optional | Browser user-agent string |
{
"noticeId": "550e8400-e29b-41d4-a716-446655440000",
"dataPrincipalIdentifier": "user@example.com",
"consentGiven": {
"purpose-uuid-1": true,
"purpose-uuid-2": false
}
}{
"data": {
"consentRecordId": "uuid",
"proofHash": "sha256-hash-of-canonical-consent-payload",
"status": "recorded"
}
}/api/v1/consent/withdrawWithdraw consent on behalf of a data principal (dashboard/DPO action). Creates an immutable WithdrawalRecord and a 7-day ComplianceTask. Use POST /withdraw-public for principal self-service.
| Parameter | Type | Required | Description |
|---|---|---|---|
dataPrincipalIdentifier | string | Required | Email or unique identifier of the data principal |
noticeId | string (UUID) | Required | The consent notice UUID to withdraw consent for |
reason | string | Optional | Optional withdrawal reason (max 500 chars) |
/api/v1/consent/withdrawal-tokenGenerate a single-use, 7-day withdrawal portal token. The token URL can be emailed to the data principal so they can self-serve withdrawal without authentication.
| Parameter | Type | Required | Description |
|---|---|---|---|
dataPrincipalIdentifier | string | Required | Email or unique identifier of the data principal |
noticeId | string (UUID) | Required | The consent notice UUID |
{
"data": {
"token": "wt_abc123...",
"portalUrl": "https://dpdpashield.in/portal/wt_abc123...",
"expiresAt": "2026-05-08T10:00:00.000Z"
}
}/api/v1/consent/portal/:tokenRetrieve portal context for a withdrawal token. Returns the notice and purpose details so the portal can render the consent state.
/api/v1/consent/portal/:token/withdrawRedeem a withdrawal token. Used by the /portal/[token] page. Token is consumed after redemption.
| Parameter | Type | Required | Description |
|---|---|---|---|
purposeIds | string[] | Optional | Specific purpose IDs to withdraw (UUID array); omit to withdraw all |
withdrawAll | boolean | Optional | Set false to do partial withdrawal via purposeIdsDefault: true |
reason | string | Optional | Optional reason for withdrawal (max 500 chars) |
/api/v1/consent/public-notice?apiKey=dpdpa_live_...Fetches the active published consent notice and its purposes. Called by the SDK on widget initialisation. Returns DPDPA Rule 3 disclosure fields and all available translations.
{
"data": {
"id": "uuid",
"title": "Acme Privacy Notice",
"description": "We collect your data to provide services...",
"privacyPolicyUrl": "https://acme.com/privacy",
"contactEmail": "dpo@acme.com",
"complaintText": "Contact us at dpo@acme.com or DPB",
"withdrawalUrl": "https://dpdpashield.in/rights/acme?apiKey=dpdpa_live_...",
"purposes": [
{
"id": "purpose-uuid",
"name": "Analytics",
"description": "We use analytics to improve your experience.",
"required": false,
"dataCategories": ["usage_data", "device_info"]
}
],
"translations": {
"HI": {
"title": "ΰ€ΰ₯ΰ€ͺΰ€¨ΰ₯ΰ€―ΰ€€ΰ€Ύ ΰ€Έΰ₯ΰ€ΰ€¨ΰ€Ύ",
"introText": "...",
"purposes": { "purpose-uuid": { "name": "ΰ€΅ΰ€Ώΰ€Άΰ₯ΰ€²ΰ₯ΰ€·ΰ€£", "description": "..." } }
}
},
"availableLanguages": ["EN", "HI", "TA"]
}
}/api/v1/consent/sdk-recordRecords a consent decision. Called automatically by the embedded SDK, or directly from your backend (server-to-server). The monthly consent volume limit applies.
identifierHash- 64-char SHA-256 hex pre-computed by your backend. Used directly (no further hashing).identifier- Raw email / phone / userId. Hashed server-side by DPDPA Shield.- Neither provided β real client IP used as fallback (anonymous visitors).
| Parameter | Type | Required | Description |
|---|---|---|---|
apiKey | string | Required | Your SDK API key (dpdpa_live_...) |
status | string | Required | Consent outcome: ACCEPTED | REJECTED | PARTIAL |
purposes | Record<string, boolean> | Required | Map of purpose UUID β consent decision |
userAgent | string | Optional | Browser user-agent string (auto-set by SDK) |
url | string | Optional | Page URL where consent was collected |
language | string | Optional | Language code shown to user (e.g. EN, HI)Default: EN |
positionShown | string | Optional | Widget position: bottom-right | bottom-left | bottom-center | top-centerDefault: bottom-right |
noticeId | string (UUID) | Optional | Specific notice UUID to record against. Defaults to the most recently published notice if omitted. |
identifierHash | string | Optional | Path 1 - Pre-computed SHA-256 hex hash of the data principal identifier, produced by your backend. Must be exactly 64 lowercase hex characters. Used directly without further hashing. Recommended for logged-in users on client-side SDK. |
identifier | string | Optional | Path 2 - Raw identifier (email / phone / userId). Hashed server-side by DPDPA Shield before storage. Intended for server-to-server calls from your backend. Never send raw PII from the browser. |
{
"apiKey": "dpdpa_live_YOUR_KEY",
"status": "ACCEPTED",
"purposes": { "purpose-uuid-1": true, "purpose-uuid-2": false },
"language": "HI",
"positionShown": "bottom-right",
"url": "https://yoursite.com/pricing"
// no identifierHash / identifier β attributed to client IP
}{
"apiKey": "dpdpa_live_YOUR_KEY",
"status": "ACCEPTED",
"purposes": { "purpose-uuid-1": true, "purpose-uuid-2": false },
"language": "EN",
"positionShown": "bottom-right",
"identifierHash": "a3f9c2e1d8b74f..." // 64-char hex, hashed by your server
}{
"apiKey": "dpdpa_live_YOUR_KEY",
"status": "ACCEPTED",
"purposes": { "purpose-uuid-1": true, "purpose-uuid-2": false },
"language": "EN",
"positionShown": "api",
"identifier": "priya@example.com", // raw - we hash it
"noticeId": "your-notice-uuid" // optional
}{
"data": {
"consentRecordId": "uuid",
"proofHash": "sha256-hash-of-canonical-consent-payload",
"status": "recorded"
}
}/api/v1/consent/withdraw-publicPublic withdrawal by data principal email. Called by the rights portal withdrawal flow. Creates a WithdrawalRecord, a 7-day ComplianceTask, and sends confirmation emails to the principal and DPO.
| Parameter | Type | Required | Description |
|---|---|---|---|
apiKey | string | Required | SDK API key for tenant identification |
email | string | Required | Data principal email address |
purposeIds | string[] | Optional | Specific purpose IDs to withdraw; empty array = full withdrawalDefault: [] |
withdrawalReason | string | Optional | Optional reason for withdrawal (max 500 chars) |
{
"apiKey": "dpdpa_live_YOUR_KEY",
"email": "user@example.com",
"purposeIds": [],
"withdrawalReason": "No longer using this service"
}{
"data": {
"withdrawalId": "uuid" | null,
"withdrawnAt": "2026-05-08T10:00:00.000Z",
"withdrawnCount": 1,
"isFullWithdrawal": true,
"purposesWithdrawn": [],
"tenant": { "name": "Acme Technologies", "slug": "acme" }
}
}
// withdrawalId is null and withdrawnCount is 0 when the email has no matching
// active consent record - this still returns 200 (not 404) to avoid letting an
// unauthenticated caller enumerate which emails have ever given consent.Notice Endpoints
Manage consent notices - the legal disclosure documents shown to data principals. Notices follow a state machine: DRAFT β PUBLISHED β ARCHIVED. Publishing auto-archives the previous published notice.
/api/v1/noticesList all notices for the tenant. Includes consent count per notice.
| Parameter | Type | Required | Description |
|---|---|---|---|
includeArchived | boolean | Optional | Include ARCHIVED notices in resultsDefault: false |
/api/v1/noticesCreate a new DRAFT consent notice.
| Parameter | Type | Required | Description |
|---|---|---|---|
title | string | Required | Notice title (3β200 chars) |
introText | string | Required | Introduction text explaining data processing (20β1000 chars) |
contactEmail | string | Required | DPO or support email for data principal queries |
contactUrl | string | Optional | Privacy policy URL |
rightsText | string | Required | Text explaining data principal rights (20β500 chars) |
complaintText | string | Required | Instructions for raising grievances (20β500 chars) |
withdrawalUrl | string | Optional | URL for consent withdrawal (DPDPA Rule 3 disclosure) |
purposeIds | string[] | Required | Array of processing purpose UUIDs to include (min 1) |
purposeConfig | PurposeConfig[] | Optional | Per-purpose display config: { purposeId, isRequired, displayOrder } |
translations | Record<lang, TranslationFields> | Optional | Language translations map. Keys are language codes (HI, TA, β¦) |
/api/v1/notices/activeReturns the currently published notice. Used by the dashboard notice card.
/api/v1/notices/:idFetch a single notice by ID with full purpose config and translation data.
/api/v1/notices/:idUpdate a DRAFT notice. All fields are optional (partial update). Returns 409 if notice is PUBLISHED or ARCHIVED - duplicate to a new draft instead.
/api/v1/notices/:id/publishPublish a DRAFT notice. Atomically archives any existing published notice. Returns materialChangeDetected flag for re-consent campaign prompting.
| Parameter | Type | Required | Description |
|---|---|---|---|
changeNote | string | Optional | Internal change summary stored in the version snapshot (max 500 chars) |
{
"data": {
"id": "uuid",
"status": "PUBLISHED",
"versionNumber": 3,
"materialChangeDetected": true,
"materialChanges": [
{ "field": "purposes", "type": "added", "isMaterial": true, "newValue": "Marketing" }
],
"fromVersionNumber": 2,
"toVersionNumber": 3
}
}/api/v1/notices/:id/archiveArchive or delete a notice. Behaviour depends on consent history: DRAFT with no consents is hard-deleted; notices with consent records are soft-archived to preserve DPDPA Section 6(10) proof evidence.
{
"data": {
"action": "ARCHIVED", // or "DELETED"
"noticeId": "uuid",
"consentCount": 1240,
"message": "Notice archived and preserved for compliance records."
}
}/api/v1/notices/:id/versionsReturns the full version history (immutable snapshots) for a notice, ordered by version number descending.
/api/v1/notices/:id/versions/diffField-by-field diff between two notice versions. Returns hasMaterialChange: true if any purpose, data category, or legal basis changed - signals re-consent is needed.
| Parameter | Type | Required | Description |
|---|---|---|---|
from | number | Required | Version number to compare from (older) |
to | number | Required | Version number to compare to (newer) |
{
"data": {
"from": 2,
"to": 3,
"hasMaterialChange": true,
"totalChanges": 2,
"changes": [
{
"field": "purposes",
"type": "added",
"isMaterial": true,
"newValue": "Marketing Communications"
},
{
"field": "title",
"type": "modified",
"isMaterial": false,
"oldValue": "Privacy Notice v2",
"newValue": "Privacy Notice v3"
}
]
}
}/api/v1/notices/:id/previewPreview how a notice will render in a given language. Returns translated fields if available, falls back to English.
| Parameter | Type | Required | Description |
|---|---|---|---|
lang | string | Optional | Language code for translated preview (e.g. HI, TA)Default: EN |
/api/v1/notices/:id/translationsAdd a translation for a specific language. Supports all 22 scheduled Indian languages per DPDPA.
| Parameter | Type | Required | Description |
|---|---|---|---|
language | string | Required | Language code (EN, HI, TA, TE, β¦) |
title | string | Optional | Translated notice title |
introText | string | Optional | Translated introduction text |
rightsText | string | Optional | Translated rights text |
complaintText | string | Optional | Translated complaint text |
/api/v1/notices/:id/translationsList all translations for a notice.
/api/v1/notices/:id/translations/:langUpdate an existing translation. :lang is the uppercase language code (e.g. HI, TA).
/api/v1/notices/:id/translations/:langRemove a translation for a specific language.
/api/v1/notices/:id/auto-translateOne-click auto-translate to all 22 scheduled Indian languages via google-translate-api-x. Translations can be reviewed and edited before publishing.
/api/v1/notices/:id/withdrawal-urlSet or clear the DPDPA Rule 3 withdrawal URL on a notice - works on PUBLISHED notices without requiring a full draft-and-publish cycle.
| Parameter | Type | Required | Description |
|---|---|---|---|
withdrawalUrl | string | null | Optional | Full URL to the withdrawal page. Pass null to clear. |
Webhook Endpoints
Webhooks deliver real-time event notifications to your server via signed HTTP POST requests. Each delivery is signed with X-DPDPAShield-Signature (HMAC-SHA256) for verification. Failed deliveries are retried with exponential backoff.
/api/v1/webhooksList all webhook configurations for the tenant.
{
"data": [
{
"id": "uuid",
"label": "Production Events",
"url": "https://yourserver.com/webhooks/dpdpa",
"events": ["consent.recorded", "consent.withdrawn"],
"isActive": true,
"createdAt": "2026-01-15T00:00:00.000Z"
}
]
}/api/v1/webhooksCreate a new webhook. The secret is used to sign deliveries - store it securely and never expose it client-side.
| Parameter | Type | Required | Description |
|---|---|---|---|
label | string | Required | Human-readable label (1β100 chars) |
url | string | Required | HTTPS endpoint to receive events |
secret | string | Required | Signing secret for HMAC-SHA256 signature verification (16β256 chars) |
events | string[] | Required | Event types to subscribe: consent.recorded | consent.withdrawn | notice.published | rights.submitted | breach.created |
consent.recorded // New consent decision recorded
consent.withdrawn // Consent withdrawn by data principal
notice.published // Consent notice published or updated
rights.submitted // Data principal rights request submitted
breach.created // Breach incident created/api/v1/webhooks/:idUpdate an existing webhook. All fields are optional. Use isActive: false to temporarily disable.
| Parameter | Type | Required | Description |
|---|---|---|---|
label | string | Optional | New label |
url | string | Optional | New endpoint URL |
secret | string | Optional | New signing secret (16β256 chars) |
events | string[] | Optional | New event types array |
isActive | boolean | Optional | Enable or disable the webhook |
/api/v1/webhooks/:idPermanently delete a webhook configuration and its delivery history.
/api/v1/webhooks/:id/deliveriesView delivery history for a webhook - useful for debugging failed deliveries.
{
"data": [
{
"id": "uuid",
"event": "consent.recorded",
"statusCode": 200,
"deliveredAt": "2026-05-01T10:00:00.000Z",
"attempts": 1,
"success": true
}
]
}/api/v1/webhooks/:id/testSend a test event to the webhook URL to verify connectivity and signature verification. Sends a sample consent.recorded payload.
Verifying webhook signatures
// Each delivery includes:
// X-DPDPAShield-Signature: sha256=<hmac-hex>
// X-DPDPAShield-Event: consent.recorded
// X-DPDPAShield-Timestamp: 1746720000000
import { createHmac } from 'crypto'
function verifySignature(payload: string, signature: string, secret: string): boolean {
const expected = createHmac('sha256', secret)
.update(payload)
.digest('hex')
return `sha256=${expected}` === signature
}Cookie Consent (Shield CMP) Endpoints
A separate SDK and endpoint family from the consent-notice widget above - covers cookies, tracking pixels, and third-party scripts. Full integration guide: /docs/cookie-consent. Bundled free on every standard plan; not available on the standalone Shield Collect plans.
/api/v1/cmp/config/:tenantIdFetches the tenant's category config, tracker registry, and label sets. Called by the CMP SDK on load. Cached for 5 minutes (Cache-Control: public, max-age=300).
{
"data": {
"tenantId": "uuid",
"categories": {
"NECESSARY": { "enabled": true, "toggleable": false },
"ANALYTICS": { "enabled": true, "toggleable": true }
},
"defaultDeny": true,
"childDirected": false,
"bannerTheme": { "position": "bottom" },
"languages": ["en"],
"reconsentDays": 180,
"scanEnabled": false,
"scanUrls": [],
"trackers": [],
"labelSets": { "en": { "labels": { "...": "..." }, "status": "REVIEWED" } },
"showPoweredBy": true,
"brandName": null,
"logoUrl": null
}
}/api/v1/cmp/web-eventRecords a cookie-consent decision into the same audit trail (ConsentAuditLog, channel: WEB_COOKIE) used by notice-based consent. Rate limited to 60 requests/minute per (tenantId, IP).
| Parameter | Type | Required | Description |
|---|---|---|---|
tenantId | string (UUID) | Required | Tenant identifying the banner - sent from the data-tenant-id attribute |
consentRecordId | string (UUID) | Optional | The visitor ID cookie value, if one already exists |
event | string | Required | COOKIE_CONSENT_GIVEN | COOKIE_CONSENT_UPDATED | COOKIE_CONSENT_WITHDRAWN |
categories | Record<string, boolean> | Required | Per-category grant/deny map (max 20 keys) |
widgetVersion | string | Optional | SDK build version string |
{ "data": { "recorded": true, "consentRecordId": "visitor-uuid" } }/api/v1/cmp/configUpsert the tenant's CMP config. Validates the merged state (existing + incoming fields) against child-directed rules before saving - see CHILD_DIRECTED_VIOLATION below.
| Parameter | Type | Required | Description |
|---|---|---|---|
categories | Record<string, {enabled, toggleable}> | Optional | Keys: NECESSARY | FUNCTIONAL | ANALYTICS | MARKETING | UNCLASSIFIED |
defaultDeny | boolean | Optional | Toggleable categories start unchecked until the visitor opts inDefault: true |
childDirected | boolean | Optional | Hard-blocks ANALYTICS and MARKETING (DPDPA S.9(3)) - rejects the request if either is set toggleable:true |
bannerTheme | object | Optional | position, primaryColor, backgroundColor, textColor, borderRadius, showLogo |
languages | string[] | Optional | Enabled Eighth Schedule language codes (max 22)Default: ['en'] |
reconsentDays | number | Optional | Days before the SDK re-prompts a returning visitor (30β730)Default: 180 |
scanEnabled | boolean | Optional | Turns on the automated tracker scanner for this tenantDefault: false |
scanUrls | string[] | Optional | URLs the scanner crawls (max 5). Falls back to Tenant.website if emptyDefault: [] |
/api/v1/cmp/trackers/api/v1/cmp/trackersList or batch-upsert (max 200 per call) the declared tracker registry. Trackers found by the automated scanner set declaredByAdmin: false; manually declared trackers set it true.
| Parameter | Type | Required | Description |
|---|---|---|---|
trackers | Tracker[] | Required | Array (max 200) of { domain, cookieName?, category, vendor?, location?, declaredByAdmin? } |
/api/v1/cmp/analyticsAccept/reject/partial rates, per-category opt-in rate, and a daily time series for the dashboard chart. Query params: from, to (max 365-day range).
/api/v1/cmp/consent-statusCheck a visitor's consent decision before firing a backend-triggered tag. X-API-Key header required. Rate limited to 600 requests/minute per API key.
| Parameter | Type | Required | Description |
|---|---|---|---|
visitorId | string (UUID) | Required | The visitor's browser-cookie identifier (query parameter) |
{
"data": {
"visitorId": "uuid",
"found": true,
"categories": { "NECESSARY": true, "ANALYTICS": true, "MARKETING": false },
"lastUpdated": "2026-07-01T10:00:00.000Z",
"withdrawn": false
}
}
// found: false β treat every non-necessary category as false (do not fire)/api/v1/cmp/server-event-logLog that your backend checked consent and whether it fired or suppressed a server-side tracker - produces audit evidence that server-side firing was consent-gated.
| Parameter | Type | Required | Description |
|---|---|---|---|
visitorId | string (UUID) | Required | Same visitor ID checked via /cmp/consent-status |
trackerName | string | Required | Identifier for the tracker fired or suppressed (max 200 chars) |
fired | boolean | Required | Whether the tracker was actually fired |
category | string | Required | NECESSARY | FUNCTIONAL | ANALYTICS | MARKETING | UNCLASSIFIED |
/api/v1/cmp/scan-nowTriggers an on-demand headless crawl of your configured scanUrls. Returns 202 immediately with a scanRunId - poll GET /api/v1/cmp/scan-runs to check completion. A DPO alert email fires automatically only when the scan finds genuinely new, previously-undeclared trackers.
/api/v1/cmp/translations/api/v1/cmp/translations/draft/api/v1/cmp/translations/draft-all/api/v1/cmp/translations/:languageGET /cmp/translations lists label sets; POST /cmp/translations/draft (body: { language }) AI-drafts one language; POST /cmp/translations/draft-all drafts all 22 in one call (capped at 3 runs/tenant/day); PUT /cmp/translations/:language saves edited labels and marks status: REVIEWED.
Error Codes
All errors follow the envelope format: { "error": { "code": "...", "message": "..." } }. On validation errors, a fields object is included with per-field messages.
| HTTP | Code | When it occurs |
|---|---|---|
| 400 | VALIDATION_ERROR | Request body failed Zod schema validation. Check the fields object for per-field errors. |
| 401 | UNAUTHORIZED | Missing, malformed, or expired Authorization header or API key. |
| 401 | TOKEN_INVALID | JWT access token is invalid or expired. Refresh using /auth/refresh. |
| 403 | FORBIDDEN | Authenticated but insufficient role permissions for this operation. |
| 403 | PLAN_LIMIT_REACHED | Resource limit for your current plan has been reached (webhooks, notices, team members, etc.). |
| 403 | FEATURE_NOT_AVAILABLE | Feature is not available on your current plan. Upgrade required. |
| 403 | PLAN_REQUIRED | Phase 2 feature gate: plan or feature override required to access this endpoint. |
| 404 | NOT_FOUND | The requested resource does not exist or belongs to a different tenant. |
| 409 | CONFLICT | State conflict - e.g. publishing an already-published notice, or duplicate resource. |
| 409 | ASSET_LINKED_TO_PUBLISHED_NOTICE | Cannot delete a data asset that is linked to a published consent notice. |
| 409 | PROCESSOR_HAS_LINKED_ASSETS | Cannot delete a processor that has data assets linked via the ProcessorAssets relation. |
| 403 | ORIGIN_NOT_ALLOWED | The request Origin/Referer hostname is not in the apiKey's SDK domain allowlist. Register the domain under Settings β API Keys. |
| 403 | ORIGIN_MISSING | The request did not send an Origin or Referer header. Required for browser-based SDK endpoints. |
| 429 | RATE_LIMIT | Too many requests from this IP. Applies to /consent/withdraw-public (5 req/hour). |
| 429 | SDK_RATE_LIMIT | Per-apiKey rate limit exceeded on a public SDK endpoint. Limits: 600/min per (apiKey + IP), 5000/min per apiKey global. Returns Retry-After header. |
| 429 | MONTHLY_LIMIT_REACHED | Monthly consent volume limit for the account has been reached. Resets next month. |
| 404 | CMP_NOT_CONFIGURED | No Cookie Manager config exists yet for this tenant, or the tenant is on a Shield Collect-only plan (Shield CMP is not included on those plans). |
| 400 | CHILD_DIRECTED_VIOLATION | PUT /cmp/config set childDirected:true together with ANALYTICS or MARKETING toggleable:true - not allowed under DPDPA S.9(3). |
| 429 | CMP_RATE_LIMIT | POST /cmp/web-event exceeded 60 requests/minute for this (tenantId, IP) pair. |
| 409 | SCAN_ALREADY_RUNNING | POST /cmp/scan-now called while a scan for this tenant is already in progress (within the last 10 minutes). |
| 429 | SCAN_RATE_LIMIT | POST /cmp/scan-now called more than once per tenant per 10 minutes. |
| 400 | NO_SCAN_URLS | POST /cmp/scan-now called with scanUrls empty and no Tenant.website set. |
| 500 | INTERNAL_ERROR | Unexpected server error. Contact support if this persists. |