API reference

Scanner

Start scans and read what they found: cookies, requests, findings, reports.

  1. PATCH/v1/cookies/{id}
  2. GET/v1/domains/{domainId}/cookies
  3. GET/v1/domains/{domainId}/declaration
  4. PUT/v1/domains/{domainId}/deep-scan
  5. GET/v1/domains/{domainId}/enforcement
  6. GET/v1/domains/{domainId}/enforcement.pdf
  7. GET/v1/domains/{domainId}/enforcement/history
  8. GET/v1/domains/{domainId}/scan-settings
  9. GET/v1/domains/{domainId}/scans
  10. POST/v1/domains/{domainId}/scans
  11. GET/v1/enforcement/summary
  12. GET/v1/scans/{id}
  13. GET/v1/scans/{id}/findings
PATCH/v1/cookies/{id}

Override a cookie’s category

Needs: A signed-in panel session, sent as Authorization: Bearer <access token>.

Parameters
NameWhereTypeWhat it is
id requiredpathstring
Request body · OverrideCookieDto
FieldTypeWhat it is
category required"unknown" | "marketing" | "preferences" | "analytics" | "necessary"
vendorstringThe vendor, if you know better than the scan. Optional; an empty string clears it.
Responses
StatusBodyWhen
200CookieDto
200 body · CookieDto
FieldTypeWhat it is
id requiredstring
name requiredstring
domainScope requiredstring
party required"first" | "third"
category required"unknown" | "marketing" | "preferences" | "analytics" | "necessary"
vendor requiredstring | null
purpose requiredstring | null
expiry requiredstring | null
overridden requiredboolean
underReview requiredbooleanTrue when the category is unknown because a suggestion is waiting for a person to confirm it, rather than because nothing is known. A suggestion below the confidence threshold is never written into a cookie declaration as settled.
GET/v1/domains/{domainId}/cookies

Cookies from a domain’s latest completed scan (filterable)

Needs: A signed-in panel session, sent as Authorization: Bearer <access token>.

Parameters
NameWhereTypeWhat it is
domainId requiredpathstring
categoryquery"necessary" | "analytics" | "marketing" | "preferences" | "unknown"
partyquery"first" | "third"
qquerystringCase-insensitive cookie-name search.
Responses
StatusBodyWhen
200CookieDto[]
200 body · CookieDto[]
FieldTypeWhat it is
id requiredstring
name requiredstring
domainScope requiredstring
party required"first" | "third"
category required"unknown" | "marketing" | "preferences" | "analytics" | "necessary"
vendor requiredstring | null
purpose requiredstring | null
expiry requiredstring | null
overridden requiredboolean
underReview requiredbooleanTrue when the category is unknown because a suggestion is waiting for a person to confirm it, rather than because nothing is known. A suggestion below the confidence threshold is never written into a cookie declaration as settled.
GET/v1/domains/{domainId}/declaration

Cookie declaration (JSON by default, ?format=html)

Needs: A signed-in panel session, sent as Authorization: Bearer <access token>.

Parameters
NameWhereTypeWhat it is
domainId requiredpathstring
formatquery"json" | "html"
Responses
StatusBodyWhen
200DeclarationDto
200 body · DeclarationDto
FieldTypeWhat it is
generatedAt requiredstring
totalCookies requirednumber
categories requiredDeclarationCategoryDto[]
categories[].category required"unknown" | "marketing" | "preferences" | "analytics" | "necessary"
categories[].count requirednumber
categories[].cookies requiredDeclarationCookieDto[]
PUT/v1/domains/{domainId}/deep-scan

Nominate this domain for the monthly deep scan

Agency and above. The plan’s allowance is enforced here, not in the panel. Turning one off is always permitted, even while over the allowance — a plan downgrade can leave a workspace above its own limit.

Needs: A signed-in panel session, sent as Authorization: Bearer <access token>.

Parameters
NameWhereTypeWhat it is
domainId requiredpathstring
Request body · SetDeepScanDto
FieldTypeWhat it is
enabled requiredboolean
Responses
StatusBodyWhen
200DomainScanSettingsDto
200 body · DomainScanSettingsDto
FieldTypeWhat it is
domainId requiredstring
hostname requiredstring
cadence required"weekly" | "monthly"How often a routine scan runs for this plan.
routinePages requirednumberPages a routine run may visit.
deepPages requirednumberPages a deep run may visit. Zero on a plan with no deep scan.
routineEstimatedMs requirednumberThe longest a routine scan of this domain can take, in milliseconds, and the same for a deep scan where the plan has one. A ceiling, not a prediction.
deepEstimatedMs requirednumberZero where deepPages is zero.
deepDomainsAllowed requirednumberHow many domains this workspace may nominate. Zero below Agency.
deepDomainsUsed requirednumberHow many it has nominated, including this one.
deepScan requiredbooleanIs this domain one of them?
nextRunAt requiredstring | nullNull until the domain is verified — an unverified domain is never crawled.
nextRunBlockedBy required"unverified" | "in_flight" | nullWhy there is no next run, when there is none.
nextRunDeep requiredbooleanWhether the next scheduled run is the monthly deep scan. It takes the place of that period's routine run; between deep runs the routine schedule carries on.
lastRunAt requiredstring | null
lastStatus required"queued" | "failed" | "blocked" | "running" | "completed" | null
lastVerdict required"blocked" | "not_run" | "assessed" | "undetermined" | null
lastBlockedReason requiredstring | nullThe reason the site turned us away, verbatim, for the panel to explain — taken from the latest scan that reached the site, so a later failure on our side does not hide it. Null when that scan got through.
lastRegion requiredstring | null
scannerUserAgent requiredstringHow the crawler identifies itself — what a WAF rule allows.
egressAddresses requiredstring[]The addresses the scanner connects from, for a firewall rule. Empty when they have not been published.
scannerInfoUrl requiredstringThe public page that explains the crawler to a site owner.
routineAllowance requiredScanAllowanceDtoThe routine allowance, which Scan now spends.
routineAllowance.total requirednumberPages the plan gives per period.
routineAllowance.used requirednumberPages spent in the current window.
routineAllowance.remaining requirednumberWhat a scan started now may visit; zero when spent.
routineAllowance.nextAvailableAt requiredstring | nullWhen a scan becomes possible again, ISO-8601; null unless spent.
routineAllowance.windowDays requirednumberThe rolling window, in days: 30 for monthly, 7 for weekly.
deepAllowance requiredScanAllowanceDto | nullThe deep-scan allowance, or null where the plan has no deep scans.
deepAllowance.total requirednumberPages the plan gives per period.
deepAllowance.used requirednumberPages spent in the current window.
deepAllowance.remaining requirednumberWhat a scan started now may visit; zero when spent.
deepAllowance.nextAvailableAt requiredstring | nullWhen a scan becomes possible again, ISO-8601; null unless spent.
deepAllowance.windowDays requirednumberThe rolling window, in days: 30 for monthly, 7 for weekly.
GET/v1/domains/{domainId}/enforcement

The enforcement report for a domain

The latest version, or a named one with ?version=. A report is a fixed snapshot of what one scan proved, plus the diff against the previous version: what appeared, what was fixed, and what came back after being fixed — which is a different conversation from what is merely new.

Needs: A signed-in panel session, sent as Authorization: Bearer <access token>.

Parameters
NameWhereTypeWhat it is
domainId requiredpathstring
versionquerynumber
Responses
StatusBodyWhen
200EnforcementReportDto
200 body · EnforcementReportDto
FieldTypeWhat it is
id requiredstring
domainId requiredstring
hostname requiredstring
scanId requiredstring
version requirednumberPer domain, 1..n. What a customer cites.
verdict required"blocked" | "not_run" | "assessed" | "undetermined"
critical requirednumber
warnings requirednumber
passes requirednumberFindings that are good news: correct gating, compliant denied pings.
pagesVisited requirednumber
pageBudget requirednumber | nullPages the plan allowed. Null when the scan predates budgets.
region requiredstring | null
added requiredstring[]Fingerprints new since the previous version — never seen before.
fixed requiredstring[]In the previous version, gone now.
regressed requiredstring[]Fixed once and back again, which is a different conversation from added.
scannedAt requiredstring | nullWhen the scan ran, as against when the report row was written.
createdAt requiredstring
findings requiredScanFindingDto[]
findings[].id requiredstring
findings[].type required"consent_state_mismatch" | "leak" | "failed_to_fire" | "fired_correctly" | "cookieless_ping"
findings[].severity required"info" | "warning" | "critical"
findings[].vendorKey requiredstring | nullCatalogue key where the host matched a known vendor, else null.
findings[].vendorName requiredstring
findings[].host requiredstring
findings[].pageUrl requiredstring
findings[].passKind required"accept" | "reject" | "no_interaction"Which pass proves it — before any choice, after reject, or after accept.
findings[].title requiredstring
findings[].detail requiredstring
findings[].fingerprint requiredstringStable across scans for the same problem on the same page.
findings[].evidence requiredobject[]
findings[].explanation requiredScanFindingExplanationDto | nullAn AI-written explanation of the same finding, or null. It is labelled as generated wherever it is shown, and nothing in the verdict, the counts or the evidence comes from it.
GET/v1/domains/{domainId}/enforcement.pdf

The enforcement report as a PDF an agency can forward

Cover, summary, every finding with the request lines it rests on, and a methodology page — because the reader was not in the room and their first question is how we know. Contains no visitor data: the recorder stores cookie names, never values, and this quotes only those.

Needs: A signed-in panel session, sent as Authorization: Bearer <access token>.

Parameters
NameWhereTypeWhat it is
domainId requiredpathstring
versionquerynumber
Responses
StatusBodyWhen
200—
GET/v1/domains/{domainId}/enforcement/history

Every report version for a domain, newest first

Needs: A signed-in panel session, sent as Authorization: Bearer <access token>.

Parameters
NameWhereTypeWhat it is
domainId requiredpathstring
Responses
StatusBodyWhen
200EnforcementHistoryEntryDto[]
200 body · EnforcementHistoryEntryDto[]
FieldTypeWhat it is
id requiredstring
version requirednumber
scanId requiredstring
verdict required"blocked" | "not_run" | "assessed" | "undetermined"
critical requirednumber
warnings requirednumber
added requirednumber
fixed requirednumber
regressed requirednumber
createdAt requiredstring
GET/v1/domains/{domainId}/scan-settings

When this domain is next scanned, how many pages it gets, and why not

Worked out from the plan and the schedule. nextRunAt is an estimate: due scans are started every hour, so the run happens at or after that time.

Needs: A signed-in panel session, sent as Authorization: Bearer <access token>.

Parameters
NameWhereTypeWhat it is
domainId requiredpathstring
Responses
StatusBodyWhen
200DomainScanSettingsDto
200 body · DomainScanSettingsDto
FieldTypeWhat it is
domainId requiredstring
hostname requiredstring
cadence required"weekly" | "monthly"How often a routine scan runs for this plan.
routinePages requirednumberPages a routine run may visit.
deepPages requirednumberPages a deep run may visit. Zero on a plan with no deep scan.
routineEstimatedMs requirednumberThe longest a routine scan of this domain can take, in milliseconds, and the same for a deep scan where the plan has one. A ceiling, not a prediction.
deepEstimatedMs requirednumberZero where deepPages is zero.
deepDomainsAllowed requirednumberHow many domains this workspace may nominate. Zero below Agency.
deepDomainsUsed requirednumberHow many it has nominated, including this one.
deepScan requiredbooleanIs this domain one of them?
nextRunAt requiredstring | nullNull until the domain is verified — an unverified domain is never crawled.
nextRunBlockedBy required"unverified" | "in_flight" | nullWhy there is no next run, when there is none.
nextRunDeep requiredbooleanWhether the next scheduled run is the monthly deep scan. It takes the place of that period's routine run; between deep runs the routine schedule carries on.
lastRunAt requiredstring | null
lastStatus required"queued" | "failed" | "blocked" | "running" | "completed" | null
lastVerdict required"blocked" | "not_run" | "assessed" | "undetermined" | null
lastBlockedReason requiredstring | nullThe reason the site turned us away, verbatim, for the panel to explain — taken from the latest scan that reached the site, so a later failure on our side does not hide it. Null when that scan got through.
lastRegion requiredstring | null
scannerUserAgent requiredstringHow the crawler identifies itself — what a WAF rule allows.
egressAddresses requiredstring[]The addresses the scanner connects from, for a firewall rule. Empty when they have not been published.
scannerInfoUrl requiredstringThe public page that explains the crawler to a site owner.
routineAllowance requiredScanAllowanceDtoThe routine allowance, which Scan now spends.
routineAllowance.total requirednumberPages the plan gives per period.
routineAllowance.used requirednumberPages spent in the current window.
routineAllowance.remaining requirednumberWhat a scan started now may visit; zero when spent.
routineAllowance.nextAvailableAt requiredstring | nullWhen a scan becomes possible again, ISO-8601; null unless spent.
routineAllowance.windowDays requirednumberThe rolling window, in days: 30 for monthly, 7 for weekly.
deepAllowance requiredScanAllowanceDto | nullThe deep-scan allowance, or null where the plan has no deep scans.
deepAllowance.total requirednumberPages the plan gives per period.
deepAllowance.used requirednumberPages spent in the current window.
deepAllowance.remaining requirednumberWhat a scan started now may visit; zero when spent.
deepAllowance.nextAvailableAt requiredstring | nullWhen a scan becomes possible again, ISO-8601; null unless spent.
deepAllowance.windowDays requirednumberThe rolling window, in days: 30 for monthly, 7 for weekly.
GET/v1/domains/{domainId}/scans

A domain’s recent scans, with the pages each one visited

Newest first, capped at 50. Each scan carries its three passes per page, how many requests and storage entries each pass saw, and whether the pass could operate the site’s consent controls at all.

Needs: A signed-in panel session, sent as Authorization: Bearer <access token>.

Parameters
NameWhereTypeWhat it is
domainId requiredpathstring
Responses
StatusBodyWhen
200ScanDetailDto[]
200 body · ScanDetailDto[]
FieldTypeWhat it is
id requiredstring
domainId requiredstring
status required"queued" | "failed" | "blocked" | "running" | "completed"
pagesCrawled requirednumber
pageBudget requirednumber | nullPages this run was allowed to visit, from the plan as it was when the scan was queued. Sent so a report can say "25 of 25 pages" rather than leaving a reader to assume it saw the whole site — which is the assumption that makes a clean result on a partial crawl dangerous.
pagesPlanned requirednumber | nullPages this run actually set out to visit — discovery's result, capped by the budget. The denominator for progress, and null on scans that predate it. Separate from pageBudget because the budget is what the plan allows: a five-page site on a fifty-page plan is at 100% after five, not 10%.
estimatedMs requirednumberThe longest this scan can take, in milliseconds. A ceiling, not a prediction: most scans finish in well under a third of it.
deep requiredbooleanA deep scan: the monthly, far larger look at a nominated domain.
region requiredstring | nullThe region the scan ran from. Null until the scan has started.
verdict required"blocked" | "not_run" | "assessed" | "undetermined"What the scan concluded. The findings endpoint has the detail.
error requiredstring | null
startedAt requiredstring | null
finishedAt requiredstring | null
createdAt requiredstring
passes requiredScanPassSummaryDto[]
passes[].pageUrl requiredstring
passes[].kind required"accept" | "reject" | "no_interaction"
passes[].reachedDecision requiredbooleanWhether the scanner could operate the site's consent controls. Read this first: a pass that did not reach a decision proves nothing about the site.
passes[].reason requiredstring | nullWhy not, when not: "no reject control found", "blocked by a bot challenge".
passes[].requests requirednumber
passes[].storage requirednumber
passes[].ms requirednumber
pagesVisited requirednumberDistinct pages visited, across the three passes.
pagesUndetermined requirednumberPages where at least one pass could not be operated.
criticalFindings requirednumber
warningFindings requirednumber
POST/v1/domains/{domainId}/scans

Queue a cookie scan for a domain

Needs: A signed-in panel session, sent as Authorization: Bearer <access token>.

Parameters
NameWhereTypeWhat it is
domainId requiredpathstring
Responses
StatusBodyWhen
201ScanDto
201 body · ScanDto
FieldTypeWhat it is
id requiredstring
domainId requiredstring
status required"queued" | "failed" | "blocked" | "running" | "completed"
pagesCrawled requirednumber
pageBudget requirednumber | nullPages this run was allowed to visit, from the plan as it was when the scan was queued. Sent so a report can say "25 of 25 pages" rather than leaving a reader to assume it saw the whole site — which is the assumption that makes a clean result on a partial crawl dangerous.
pagesPlanned requirednumber | nullPages this run actually set out to visit — discovery's result, capped by the budget. The denominator for progress, and null on scans that predate it. Separate from pageBudget because the budget is what the plan allows: a five-page site on a fifty-page plan is at 100% after five, not 10%.
estimatedMs requirednumberThe longest this scan can take, in milliseconds. A ceiling, not a prediction: most scans finish in well under a third of it.
deep requiredbooleanA deep scan: the monthly, far larger look at a nominated domain.
region requiredstring | nullThe region the scan ran from. Null until the scan has started.
verdict required"blocked" | "not_run" | "assessed" | "undetermined"What the scan concluded. The findings endpoint has the detail.
error requiredstring | null
startedAt requiredstring | null
finishedAt requiredstring | null
createdAt requiredstring
GET/v1/enforcement/summary

What moved across the workspace this week

Sums the diffs of reports written in the window rather than counting findings open now: the question is what happened while you were not looking, and the compliance score already answers the other one.

Needs: A signed-in panel session, sent as Authorization: Bearer <access token>.

Responses
StatusBodyWhen
200EnforcementWeekDto
200 body · EnforcementWeekDto
FieldTypeWhat it is
windowDays requirednumberHow many days the window covers.
added requirednumberProblems that appeared, across every domain.
fixed requirednumberProblems that went away.
regressed requirednumberProblems that were fixed and came back.
domainsWithCritical requirednumberDomains with at least one critical finding in their latest report.
domainsReported requirednumberDomains that have a report at all — the denominator for the above.
latest requiredEnforcementLatestDto | nullThe report to link to: the newest one in the window that found something new or returning, otherwise the newest report of all. Null when no domain has a report.
latest.domainId requiredstring
latest.hostname requiredstring
latest.version requirednumber
GET/v1/scans/{id}

Get a scan, with the pages it visited and what each pass did

Needs: A signed-in panel session, sent as Authorization: Bearer <access token>.

Parameters
NameWhereTypeWhat it is
id requiredpathstring
Responses
StatusBodyWhen
200ScanDetailDto
200 body · ScanDetailDto
FieldTypeWhat it is
id requiredstring
domainId requiredstring
status required"queued" | "failed" | "blocked" | "running" | "completed"
pagesCrawled requirednumber
pageBudget requirednumber | nullPages this run was allowed to visit, from the plan as it was when the scan was queued. Sent so a report can say "25 of 25 pages" rather than leaving a reader to assume it saw the whole site — which is the assumption that makes a clean result on a partial crawl dangerous.
pagesPlanned requirednumber | nullPages this run actually set out to visit — discovery's result, capped by the budget. The denominator for progress, and null on scans that predate it. Separate from pageBudget because the budget is what the plan allows: a five-page site on a fifty-page plan is at 100% after five, not 10%.
estimatedMs requirednumberThe longest this scan can take, in milliseconds. A ceiling, not a prediction: most scans finish in well under a third of it.
deep requiredbooleanA deep scan: the monthly, far larger look at a nominated domain.
region requiredstring | nullThe region the scan ran from. Null until the scan has started.
verdict required"blocked" | "not_run" | "assessed" | "undetermined"What the scan concluded. The findings endpoint has the detail.
error requiredstring | null
startedAt requiredstring | null
finishedAt requiredstring | null
createdAt requiredstring
passes requiredScanPassSummaryDto[]
passes[].pageUrl requiredstring
passes[].kind required"accept" | "reject" | "no_interaction"
passes[].reachedDecision requiredbooleanWhether the scanner could operate the site's consent controls. Read this first: a pass that did not reach a decision proves nothing about the site.
passes[].reason requiredstring | nullWhy not, when not: "no reject control found", "blocked by a bot challenge".
passes[].requests requirednumber
passes[].storage requirednumber
passes[].ms requirednumber
pagesVisited requirednumberDistinct pages visited, across the three passes.
pagesUndetermined requirednumberPages where at least one pass could not be operated.
criticalFindings requirednumber
warningFindings requirednumber
GET/v1/scans/{id}/findings

What a scan proved about enforcement

The deterministic findings for a scan: leaks, consent-state mismatches, vendors that fired correctly and vendors that never fired. verdict is the part to read first — undetermined means the crawler reached the site but could not operate its consent controls, which is emphatically not "clean".

Needs: A signed-in panel session, sent as Authorization: Bearer <access token>.

Parameters
NameWhereTypeWhat it is
id requiredpathstring
Responses
StatusBodyWhen
200ScanFindingsDto
200 body · ScanFindingsDto
FieldTypeWhat it is
scanId requiredstring
verdict required"blocked" | "not_run" | "assessed" | "undetermined"
assessedAt requiredstring | nullWhen the assertions last ran, or null when they have not.
critical requirednumber
warnings requirednumber
passes requirednumberFindings that are good news: correct gating and compliant denied pings.
findings requiredScanFindingDto[]
findings[].id requiredstring
findings[].type required"consent_state_mismatch" | "leak" | "failed_to_fire" | "fired_correctly" | "cookieless_ping"
findings[].severity required"info" | "warning" | "critical"
findings[].vendorKey requiredstring | nullCatalogue key where the host matched a known vendor, else null.
findings[].vendorName requiredstring
findings[].host requiredstring
findings[].pageUrl requiredstring
findings[].passKind required"accept" | "reject" | "no_interaction"Which pass proves it — before any choice, after reject, or after accept.
findings[].title requiredstring
findings[].detail requiredstring
findings[].fingerprint requiredstringStable across scans for the same problem on the same page.
findings[].evidence requiredobject[]
findings[].explanation requiredScanFindingExplanationDto | nullAn AI-written explanation of the same finding, or null. It is labelled as generated wherever it is shown, and nothing in the verdict, the counts or the evidence comes from it.

Next groupAnalytics