API reference

Public endpoints

What the tag calls from a visitor's browser, and the free audit: no credentials, rate-limited.

  1. POST/v1/public/audits
  2. DELETE/v1/public/audits/{id}
  3. GET/v1/public/audits/{id}
  4. POST/v1/public/consent
  5. GET/v1/public/scanner
  6. GET/v1/public/sites/{siteId}/config
  7. POST/v1/public/tag-errors
  8. POST/v1/public/vitals
POST/v1/public/audits

Audit one domain, once, without an account

Visits up to three pages of the domain three times each — no choice, after reject, after accept — and reports what it proves. Rate-limited per requester and per domain. A domain gets one unverified audit; a second requires proving control (409 carries the challenge). Results exist for 24 hours and are readable only with the returned token.

Needs: Nothing. This route is public.

Request body · RequestPublicAuditDto
FieldTypeWhat it is
hostname requiredstringA hostname or URL. Normalised server-side. Example: "example.com"
verify"dns_txt" | "meta_tag" | "file"Only for a domain that has already had its one unverified audit: how you have published the challenge from the previous 409, so we can check it.
Responses
StatusBodyWhen
202PublicAuditCreatedDto
409—verification_required — the body carries challenge
429—A limit was reached; retryAfterSeconds says when
202 body · PublicAuditCreatedDto
FieldTypeWhat it is
id requiredstring
token requiredstringShown once. The only way to read or delete this audit — keep it for the session. It is stored as a hash; we cannot show it again.
hostname requiredstring
status required"queued"
pageBudget requirednumber
expiresAt requiredstringWhen the result stops existing.
DELETE/v1/public/audits/{id}

Delete your audit now, rather than in 24 hours

Needs: Nothing. This route is public.

Parameters
NameWhereTypeWhat it is
id requiredpathstring
X-Audit-Token requiredheaderstring
Responses
StatusBodyWhen
204—
GET/v1/public/audits/{id}

Progress and result of your audit

Needs: Nothing. This route is public.

Parameters
NameWhereTypeWhat it is
id requiredpathstring
X-Audit-Token requiredheaderstringThe token POST returned
Responses
StatusBodyWhen
200PublicAuditDto
404—Unknown id, wrong token, or older than 24 hours — indistinguishable on purpose
200 body · PublicAuditDto
FieldTypeWhat it is
id requiredstring
hostname requiredstring
status required"queued" | "running" | "completed" | "blocked" | "failed"
verdict required"assessed" | "undetermined" | "blocked" | "not_run" | nullOnly assessed established anything. Read it before any count.
reason requiredstring | nullWhy nothing could be established.
pageBudget requirednumber
pagesVisited requirednumber
ahead requirednumber | nullAudits ahead of this one, while queued.
findings requiredPublicFindingDto[]
findings[].type required"consent_state_mismatch" | "leak" | "failed_to_fire" | "fired_correctly" | "cookieless_ping"
findings[].severity required"critical" | "warning" | "info"
findings[].vendor requiredstring
findings[].host requiredstring
findings[].pageUrl requiredstring
findings[].passKind required"no_interaction" | "reject" | "accept"
findings[].title requiredstring
findings[].detail requiredstring
findings[].evidence requiredPublicEvidenceDto[]
choices requiredAuditChoicesDto | nullWhether "after reject" and "after accept" could be reached. A choice that was not reached was not checked — say "could not reach a decision", never "fine".
choices.reject requiredChoiceResultDto
choices.accept requiredChoiceResultDto
createdAt requiredstring
finishedAt requiredstring | null
expiresAt requiredstring
POST/v1/public/consent

Record a banner view or a consent decision (sent by the tag)

Needs: Nothing. This route is public.

Request body · ConsentBeaconDto
FieldTypeWhat it is
siteIdstring
event"shown" | "decision"
stateConsentStateDto
state.necessarybooleanAlways true: necessary cookies cannot be declined.
state.analyticsboolean
state.marketingboolean
state.preferencesboolean
act"accept" | "reject" | "save"Which control the visitor actually used. Sent by current scripts; older cached ones send only state, for which the action is derived from the state against the categories the banner offers.
source"banner" | "gpc"Where the decision came from: a person pressing a button, or the browser's Global Privacy Control signal. gpc is a real, legally binding opt-out in California, Colorado and Connecticut, to be honoured exactly as a click would be. It is recorded distinctly because it is not the same evidence: nobody was shown a banner, so there is no wording to snapshot and no impression it belongs to. Absent means banner, which is what every script cached in a browser today sends.
localestringThe language the banner was shown in, as returned in the config's locale. The receipt records the wording in this language, which is what the visitor could read. Absent means the default language.
tsnumber
Responses
StatusBodyWhen
204—
GET/v1/public/scanner

How the scanner identifies itself, and the addresses it connects from

Needs: Nothing. This route is public.

Responses
StatusBodyWhen
200ScannerIdentityDto
200 body · ScannerIdentityDto
FieldTypeWhat it is
userAgent requiredstring
egressAddresses requiredstring[]IPs or CIDR ranges; empty when the deployment has not published them.
regions requiredstring[]Where scans run from: eu, us.
GET/v1/public/sites/{siteId}/config

The banner a visitor should see on a site

What the tag fetches to show the banner: its wording in the visitor’s language (lang), and the rules for their country. 404 when the site has no published banner.

Needs: Nothing. This route is public.

Parameters
NameWhereTypeWhat it is
siteId requiredpathstring
langqueryany
Responses
StatusBodyWhen
200SiteConfigDto
404—No published banner for this site id — it was deleted, never existed, or has never been published. The tag treats this as "show nothing".
200 body · SiteConfigDto
FieldTypeWhat it is
siteId requiredstring
cookieDomainstringRoot domain for cross-subdomain cookie scoping (optional; script infers otherwise).
optOutSiteOptOutDtoThe US opt-out notice's wording. Present only for visitors under the US opt-out rules. In the banner's language when that translation has its own notice, and in the default language otherwise.
optOut.title requiredstring
optOut.message requiredstring
optOut.optOut requiredstringThe prominent action: deny every sale/share category at once.
optOut.dismiss requiredstringDismiss without changing anything. Processing continues, lawfully.
optOut.link requiredstringThe persistent link that outlives the notice — the part the law requires.
noticeSiteNoticeDtoThe button label of the rest-of-world notice. Present only when mode is notice; the notice's title and text are the banner's own.
notice.dismiss requiredstring
gpcbooleanThe visitor sent Sec-GPC: 1 with this request. For integrations that fetch this config on a server, where there is no navigator.globalPrivacyControl to read. The tag reads the browser API itself and ignores this. Present only when the header was sent.
mode required"grant" | "banner" | "opt_out" | "notice"Geo directive: banner = show the consent banner; grant = the visitor's region doesn't require consent, so imply consent without showing a banner.
jurisdiction required"eea_uk" | "us" | "row"Which rules apply to this visitor: eea_uk, us or row (rest of world). Worked out on the server from the visitor's country.
usStatestringTheir US state, when the CDN gave us one. Only present for US visitors.
usStateHasLawbooleanTrue when that state has a comprehensive privacy law in force.
vitalsbooleanMeasure real-user performance on this page view. Absent unless switched on — its absence is what stops the tag fetching the measurement bundle.
region requiredstring | nullThe visitor's resolved country (ISO-3166-1 alpha-2), or null if unknown.
locale requiredstringThe language this response is written in — a BCP-47 tag. Resolved server-side from the lang the tag asked for against the languages the banner actually has, so it is not necessarily what was requested: a visitor asking for de-AT on a banner translated into de gets de here. The tag sets it on the banner's lang attribute (screen readers need it to pronounce the text) and sends it back on the consent beacon, so the receipt records the wording the visitor actually agreed to rather than whatever the default language happened to be.
blockingobject[]
blocking[].match requiredstring
blocking[].category required"marketing" | "preferences" | "analytics"
banner requiredBannerConfigDto
banner.content requiredBannerContentDto
banner.style requiredBannerStyleDto
banner.targeting requiredBannerTargetingDto
banner.behavior requiredBannerBehaviorDto
banner.categories requiredBannerCategoryDto[]
banner.cookieDomainstringRoot domain for sharing the consent cookie across subdomains, e.g. .example.com. Must be a hostname.
banner.hideBrandingbooleanHide the "Powered by CMPStack" mark. Requires a plan that includes it; saving it on another plan returns 409.
banner.optOutBannerOptOutDtoThe US opt-out notice's wording, shown only to visitors under the US opt-out rules. When it is absent, default wording is used.
banner.noticeBannerNoticeDtoThe rest-of-world notice's button. Optional; defaults to "OK".
banner.customCssstringCustom CSS for the banner. Requires a plan that includes custom CSS. Its length and content are checked when it is saved and again when it is served.
banner.blockingBlockingRuleDto[]Third-party scripts held back until their category is allowed. At most 60.
banner.vitalsbooleanMeasure real-user performance on this site: LCP, INP and CLS per page view. Off unless switched on, because it loads one more small file on the visitor's page.
banner.defaultLocalestringThe language content and categories are written in, and the one shown to any visitor whose language has no translation. Absent means en.
banner.translationsBannerTranslationDto[]The same banner in other languages, at most 40. The default language is not repeated here: it is content, and a translation for it is refused.
layout required"bar" | "modal" | "popup"Where the banner sits: bar pinned to the bottom edge, modal centred over a dimmed page, popup in a corner.
categories requiredCategoryConfigDto[]
categories[].id required"marketing" | "preferences" | "analytics" | "necessary"
categories[].label requiredstring
categories[].description requiredstring
categories[].requiredboolean
categories[].sensitivebooleanSensitive data: stays opt-in even under an opt-out regime, because US state laws carve it out of the "run until told to stop" default.
style requiredStyleConfigDto
style.background requiredstring
style.foreground requiredstring
style.accent requiredstring
style.font requiredstring
style.titleSize requirednumber
style.bodySize requirednumber
style.radius requirednumber
style.buttonRadius requirednumber
style.shadow required"none" | "sm" | "md" | "lg"
behavior requiredBehaviorConfigDto
behavior.delaySeconds requirednumberSeconds to wait before showing the banner.
behavior.scrollTrigger requiredbooleanHold the banner back until the visitor has scrolled a quarter of the page.
behavior.exitIntent requiredbooleanShow when the cursor leaves the viewport.
behavior.blockScroll requiredbooleanLock page scrolling until a choice is made. Modal layout only.
behavior.autoAccept requiredbooleanTreat a scroll as acceptance. Never sent to a visitor who is covered by GDPR — the server strips it rather than trusting the script to apply the exemption.
behavior.consentExpiryMonths requirednumberHow long a decision is honoured before the visitor is asked again. 12 unless the site says otherwise; the tag writes it as the cookie's max-age.
behavior.rejectionMemoryMonths requirednumberSix-month rejection memory: a visitor who rejected is not re-asked for this many months even if consent expires sooner. 0 (off, the default) or 6.
themeThemeConfigDto
theme.primarystring
hideBrandingbooleanSuppress the "Powered by CMPStack" mark. Only ever present when the workspace's plan includes it — the script shows the mark whenever this is absent, so a failed or unreadable config falls back to branded.
customCssstringThe banner's custom CSS, already checked and made safe. Present only on plans that include custom CSS.
POST/v1/public/tag-errors

Report a banner error (sent by the tag)

The banner reporting that it failed on a customer’s page. Carries the site id, the error’s message and its stack and nothing else: no identifier, no IP, no user agent, no page address, and every address in the text is reduced to its origin and file. Refused unless the site has a published banner and the Origin matches its domain, and rate-limited per site and overall.

Needs: Nothing. This route is public.

Responses
StatusBodyWhen
204—
POST/v1/public/vitals

Record one page view’s performance (sent by the tag)

One page view’s LCP, INP and CLS, from the sites that switched real-user measurement on. Carries a path and three rounded numbers and nothing else: no identifier, no IP, no user agent, no referrer. Refused unless the domain has vitals enabled in its published banner and the Origin matches that domain.

Needs: Nothing. This route is public.

Responses
StatusBodyWhen
204—

Next groupDomains