API reference
REST API
For your own servers, with an API key from Integrations: read consent decisions and domains.
GET
/v1/api/consent-eventsConsent decisions for this workspace
Same records, filters and proof fields the panel shows. Impressions are excluded, as they are everywhere else — an impression is not a decision.
Needs: An API key, sent as Authorization: Bearer <key>. Create one under Integrations → API keys.
| Name | Where | Type | What it is |
|---|---|---|---|
| domainId | query | string (uuid) | |
| action | query | "accept_all" | "reject_all" | "custom" | Decision type (impressions are always excluded from the log). |
| source | query | "banner" | "gpc" | Where the decision came from: a banner, or the browser's Global Privacy Control signal. Filterable because the two answer different questions. "How many people chose this?" is about banners; "how many browsers told us to stop?" is about GPC, and a log that mixes them silently answers neither. |
| country | query | string | ISO-3166-1 alpha-2 country. |
| from | query | string | Only events at/after this ISO timestamp. |
| to | query | string | Only events at/before this ISO timestamp. |
| device | query | "desktop" | "mobile" | "tablet" | Device class, derived from the stored user-agent. Filtered in the database rather than in the panel. The browser used to classify only the rows it had already fetched, so asking for "Mobile" searched the newest 500 decisions instead of the log, and answered "no results" for a workspace with plenty of them further back. |
| status | query | "valid" | "expired" | Whether the decision is still within the ~12 months a consent is treated as current. Same cutoff the receipt's own status is derived from, applied as a date range so the two can never disagree. |
| q | query | string | Free-text search: a consent id (a UUID, matched exactly) or a visitor hash (matched as a prefix/substring). Those are the only two identifiers a receipt has — there is no name or email in a consent record, by design. |
| limit | query | number | Page size for the log. Exports ignore the default and take everything up to EXPORT_MAX_ROWS — an audit trail that stops at a page boundary is not one. |
| Status | Body | When |
|---|---|---|
| 200 | ConsentReceiptDto[] |
| Field | Type | What it is |
|---|---|---|
| id required | string | |
| domainId required | string | null | Null once the domain has been deleted; the record is kept until its retention ends. |
| hostname required | string | The site the record was collected on — kept even after the domain is deleted. |
| domainDeleted required | boolean | True when the domain has been deleted since: the record outlives it. |
| action required | string | accept_all | reject_all | custom (impressions are excluded from the log). |
| source required | string | banner when a person pressed a button, gpc when their browser signalled. A GPC opt-out is a binding decision in California, Colorado and Connecticut and belongs in the log beside the others — but it is different evidence, with no wording snapshot and no impression behind it, and the receipt says so rather than letting a reader assume somebody read a banner. |
| analytics required | boolean | |
| marketing required | boolean | |
| preferences required | boolean | |
| country required | string | null | ISO-3166-1 alpha-2, or null when the region is unknown. |
| ipHash required | string | null | Salted hash of the visitor IP (never the raw IP), or null. |
| userAgent required | string | null | |
| createdAt required | string | |
| status required | "valid" | "expired" | valid or expired. A decision expires after about 12 months, the same time the tag keeps the decision cookie, so a record and the browser state it describes expire together. |
| bannerVersion required | number | null | The banner's publish counter at the moment of this decision, or null for a decision recorded before this was captured. |
| policyUrl required | string | null | The privacy/cookie policy URL the visitor was shown, or null if none was set or this decision predates capturing it. |
| bannerTitle required | string | null | The banner's title/description exactly as shown, or null if this decision predates capturing it. |
| bannerDescription required | string | null | |
| bannerLocale required | string | null | The language the banner was shown in — a BCP-47 tag, or null for a decision made before the banner had more than one, which means the default language. Part of the proof, not decoration: bannerTitle and bannerDescription above are only demonstrably the text this visitor could read if the record also says which language they are in. |
| device required | "desktop" | "mobile" | "tablet" | Device class (desktop, mobile or tablet), worked out on the server from userAgent. |
GET
/v1/api/domainsDomains in this workspace
Needs: An API key, sent as Authorization: Bearer <key>. Create one under Integrations → API keys.
| Status | Body | When |
|---|---|---|
| 200 | DomainListDto |
| Field | Type | What it is |
|---|---|---|
| items required | DomainDto[] | |
| items[].id required | string | |
| items[].hostname required | string | |
| items[].rootDomain required | string | The registrable domain this hostname belongs to — the unit the plan bills. |
| items[].billedAs required | "root" | "included" | "extra" | How this hostname is billed. A billed domain is the root domain, www and up to two further subdomains: root is the billed unit, included is covered by its root at no extra cost, and extra is a third or later subdomain that counts as another domain. |
| items[].status required | "pending" | "verified" | "failed" | |
| items[].verifyMethod required | "script" | "dns_txt" | "meta_tag" | "file" | |
| items[].verifyToken required | string | |
| items[].verifiedAt required | string | null | ISO timestamp, or null if not yet verified. |
| items[].verifyReason required | string | null | Why the last check failed, in words meant for the customer. Null when the domain is verified or has never been checked. |
| items[].lastCheckedAt required | string | null | ISO timestamp of the last verification attempt, or null if never attempted. |
| items[].sslOk required | boolean | |
| items[].groupId required | string | null | Group this domain belongs to, or null if ungrouped. |
| items[].region required | "EU" | "US-CA" | "UK" | "GLOBAL" | null | Compliance jurisdiction the owner declared for this domain, or null if unset. |
| items[].installMethod required | "script" | "wordpress" | "gtm" | "shopify" | "wix" | How the tag is installed on this site. Decides which instructions the panel shows; the tag itself is the same on every route. |
| items[].createdAt required | string | |
| items[].score required | number | Live compliance score (0–100) for this domain's banner: the same score the Compliance screen shows. It uses the published banner, else the draft, else the default banner. |
| items[].overAllowance required | boolean | True when the workspace's plan does not cover this domain. Its banner is held unpublished, and publishing is refused until the plan allows more domains or another domain is removed. This happens only after a downgrade: adding a domain over the limit is refused. |
| items[].lastScanAt required | string | null | When this domain was last scanned, whether the scan completed, was blocked or failed. Null when it has never been scanned. |
| items[].activeScan required | object | |
| facets required | DomainFacetsDto | |
| facets.all required | number | |
| facets.pending required | number | |
| facets.verified required | number | |
| facets.failed required | number | |
| total required | number | Rows matching ALL active filters, i.e. the size of items before any paging. |
| allowance required | DomainAllowanceDto | Computed over every domain in the workspace, whatever the filters. |
| allowance.limit required | number | null | |
| allowance.used required | number |
Next groupPublic endpoints