Domains
Add, verify and remove the sites a workspace covers, and read their install instructions.
- GET/v1/domains
- POST/v1/domains
- GET/v1/domains/availability
- GET/v1/domains/transfers
- GET/v1/domains/transfers/{id}
- POST/v1/domains/transfers/{id}/accept
- POST/v1/domains/transfers/{id}/cancel
- POST/v1/domains/transfers/{id}/decline
- POST/v1/domains/{domainId}/transfer
- GET/v1/domains/{domainId}/transfer/preflight
- DELETE/v1/domains/{id}
- GET/v1/domains/{id}
- PATCH/v1/domains/{id}
- GET/v1/domains/{id}/instructions
- POST/v1/domains/{id}/verify
- GET/v1/install/instructions
/v1/domainsDomains in this workspace, filterable, with a count per status
Needs: A signed-in panel session, sent as Authorization: Bearer <access token>.
| Name | Where | Type | What it is |
|---|---|---|---|
| status | query | "pending" | "verified" | "failed" | Verification status. |
| groupId | query | string (uuid) | Only domains in this group. |
| q | query | string | Case-insensitive hostname search. |
| sort | query | "createdAt" | "hostname" | "status" | Sort field (default createdAt). |
| order | query | "asc" | "desc" | Sort direction (default desc). |
| 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 |
/v1/domainsAdd a domain (starts pending verification)
Needs: A signed-in panel session, sent as Authorization: Bearer <access token>.
| Field | Type | What it is |
|---|---|---|
| hostname required | string | A hostname or URL; normalized server-side (scheme/path/port stripped). |
| verifyMethod | "script" | "dns_txt" | "meta_tag" | "file" | Preferred ownership-verification method (defaults to dns_txt). |
| region | "EU" | "US-CA" | "UK" | "GLOBAL" | The compliance region the owner says this domain serves. Informational: it does not change what the banner does. |
| installMethod | "script" | "wordpress" | "gtm" | "shopify" | "wix" | How the owner will put the tag on the site. Picks which install instructions the panel shows; defaults to a plain script tag. |
| Status | Body | When |
|---|---|---|
| 201 | DomainDto |
| Field | Type | What it is |
|---|---|---|
| id required | string | |
| hostname required | string | |
| rootDomain required | string | The registrable domain this hostname belongs to — the unit the plan bills. |
| 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. |
| status required | "pending" | "verified" | "failed" | |
| verifyMethod required | "script" | "dns_txt" | "meta_tag" | "file" | |
| verifyToken required | string | |
| verifiedAt required | string | null | ISO timestamp, or null if not yet verified. |
| 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. |
| lastCheckedAt required | string | null | ISO timestamp of the last verification attempt, or null if never attempted. |
| sslOk required | boolean | |
| groupId required | string | null | Group this domain belongs to, or null if ungrouped. |
| region required | "EU" | "US-CA" | "UK" | "GLOBAL" | null | Compliance jurisdiction the owner declared for this domain, or null if unset. |
| 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. |
| createdAt required | string | |
| 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. |
| 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. |
| 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. |
| activeScan required | object | |
| activeScan.status required | "queued" | "running" | |
| activeScan.pagesCrawled required | number | |
| activeScan.pagesPlanned required | number | null |
/v1/domains/availabilityCheck whether a hostname can be added to this workspace (advisory)
Needs: A signed-in panel session, sent as Authorization: Bearer <access token>.
| Name | Where | Type | What it is |
|---|---|---|---|
| hostname required | query | string | A hostname or full URL |
| Status | Body | When |
|---|---|---|
| 200 | DomainAvailabilityDto |
| Field | Type | What it is |
|---|---|---|
| hostname required | string | The normalised form the check was actually run against; "" if nothing was left. |
| available required | boolean | |
| reason required | "taken" | "invalid" | null | |
| message required | string | null | A sentence to show the customer, or null when the domain is available. |
| existingDomainId required | string | null | The id of the existing domain when reason is "taken" — otherwise null. |
/v1/domains/transfersDomain transfers in both directions
Needs: A signed-in panel session, sent as Authorization: Bearer <access token>.
| Status | Body | When |
|---|---|---|
| 200 | DomainTransfersDto |
| Field | Type | What it is |
|---|---|---|
| outgoing required | DomainTransferDto[] | Offers made by this workspace. |
| outgoing[].id required | string | |
| outgoing[].domainId required | string | |
| outgoing[].hostname required | string | The hostname as it was when offered. |
| outgoing[].status required | "pending" | "accepted" | "declined" | "cancelled" | |
| outgoing[].note | string | null | |
| outgoing[].fromWorkspaceName required | string | |
| outgoing[].fromWorkspaceSlug required | string | |
| outgoing[].toWorkspaceName required | string | |
| outgoing[].toWorkspaceSlug required | string | |
| outgoing[].expiresAt required | string | After this, the offer lapses and the link stops working. |
| outgoing[].respondedAt | string | null | |
| outgoing[].createdAt required | string | |
| incoming required | DomainTransferDto[] | Offers made to this workspace. |
| incoming[].id required | string | |
| incoming[].domainId required | string | |
| incoming[].hostname required | string | The hostname as it was when offered. |
| incoming[].status required | "pending" | "accepted" | "declined" | "cancelled" | |
| incoming[].note | string | null | |
| incoming[].fromWorkspaceName required | string | |
| incoming[].fromWorkspaceSlug required | string | |
| incoming[].toWorkspaceName required | string | |
| incoming[].toWorkspaceSlug required | string | |
| incoming[].expiresAt required | string | After this, the offer lapses and the link stops working. |
| incoming[].respondedAt | string | null | |
| incoming[].createdAt required | string |
/v1/domains/transfers/{id}Review an offer from its emailed link
Needs: A signed-in panel session, sent as Authorization: Bearer <access token>.
| Name | Where | Type | What it is |
|---|---|---|---|
| id required | path | string | |
| token required | query | string |
| Status | Body | When |
|---|---|---|
| 200 | DomainTransferDto | |
| 403 | — | The token does not match this transfer. |
| Field | Type | What it is |
|---|---|---|
| id required | string | |
| domainId required | string | |
| hostname required | string | The hostname as it was when offered. |
| status required | "pending" | "accepted" | "declined" | "cancelled" | |
| note | string | null | |
| fromWorkspaceName required | string | |
| fromWorkspaceSlug required | string | |
| toWorkspaceName required | string | |
| toWorkspaceSlug required | string | |
| expiresAt required | string | After this, the offer lapses and the link stops working. |
| respondedAt | string | null | |
| createdAt required | string |
/v1/domains/transfers/{id}/acceptAccept a domain into this workspace
Moves the domain, its banner configuration and its entire consent log. The domain keeps its id, so the tag on the site is unaffected and the banner does not go down.
Needs: A signed-in panel session, sent as Authorization: Bearer <access token>.
| Name | Where | Type | What it is |
|---|---|---|---|
| id required | path | string |
| Field | Type | What it is |
|---|---|---|
| token required | string | The secret half of the link, after the dot. |
| Status | Body | When |
|---|---|---|
| 201 | DomainTransferDto | |
| 409 | — | Already answered, expired, the domain has moved on, this workspace already has that hostname, or its plan has no room for another domain. |
| Field | Type | What it is |
|---|---|---|
| id required | string | |
| domainId required | string | |
| hostname required | string | The hostname as it was when offered. |
| status required | "pending" | "accepted" | "declined" | "cancelled" | |
| note | string | null | |
| fromWorkspaceName required | string | |
| fromWorkspaceSlug required | string | |
| toWorkspaceName required | string | |
| toWorkspaceSlug required | string | |
| expiresAt required | string | After this, the offer lapses and the link stops working. |
| respondedAt | string | null | |
| createdAt required | string |
/v1/domains/transfers/{id}/cancelWithdraw an offer this workspace made
Needs: A signed-in panel session, sent as Authorization: Bearer <access token>.
| Name | Where | Type | What it is |
|---|---|---|---|
| id required | path | string |
| Status | Body | When |
|---|---|---|
| 201 | DomainTransferDto |
| Field | Type | What it is |
|---|---|---|
| id required | string | |
| domainId required | string | |
| hostname required | string | The hostname as it was when offered. |
| status required | "pending" | "accepted" | "declined" | "cancelled" | |
| note | string | null | |
| fromWorkspaceName required | string | |
| fromWorkspaceSlug required | string | |
| toWorkspaceName required | string | |
| toWorkspaceSlug required | string | |
| expiresAt required | string | After this, the offer lapses and the link stops working. |
| respondedAt | string | null | |
| createdAt required | string |
/v1/domains/transfers/{id}/declineRefuse an offered domain
Needs: A signed-in panel session, sent as Authorization: Bearer <access token>.
| Name | Where | Type | What it is |
|---|---|---|---|
| id required | path | string |
| Field | Type | What it is |
|---|---|---|
| token required | string | The secret half of the link, after the dot. |
| Status | Body | When |
|---|---|---|
| 201 | DomainTransferDto |
| Field | Type | What it is |
|---|---|---|
| id required | string | |
| domainId required | string | |
| hostname required | string | The hostname as it was when offered. |
| status required | "pending" | "accepted" | "declined" | "cancelled" | |
| note | string | null | |
| fromWorkspaceName required | string | |
| fromWorkspaceSlug required | string | |
| toWorkspaceName required | string | |
| toWorkspaceSlug required | string | |
| expiresAt required | string | After this, the offer lapses and the link stops working. |
| respondedAt | string | null | |
| createdAt required | string |
/v1/domains/{domainId}/transferOffer a domain to another workspace
Owner-level: from this workspace's point of view the outcome is the same as deleting the domain. Nothing moves until the destination accepts from the one-time link emailed to its owners and admins — which is not returned here.
Needs: A signed-in panel session, sent as Authorization: Bearer <access token>.
| Name | Where | Type | What it is |
|---|---|---|---|
| domainId required | path | string |
| Field | Type | What it is |
|---|---|---|
| toWorkspaceSlug required | string | The destination workspace's address — the subdomain it answers on. Named by slug rather than by email so the offer can only reach people who could act on it, and so "which of their workspaces?" has one answer. Example: "acme" |
| note | string | Shown to the recipient on the acceptance screen and in the email. |
| Status | Body | When |
|---|---|---|
| 201 | DomainTransferDto | |
| 409 | — | Already offered, or the destination cannot take it. |
| Field | Type | What it is |
|---|---|---|
| id required | string | |
| domainId required | string | |
| hostname required | string | The hostname as it was when offered. |
| status required | "pending" | "accepted" | "declined" | "cancelled" | |
| note | string | null | |
| fromWorkspaceName required | string | |
| fromWorkspaceSlug required | string | |
| toWorkspaceName required | string | |
| toWorkspaceSlug required | string | |
| expiresAt required | string | After this, the offer lapses and the link stops working. |
| respondedAt | string | null | |
| createdAt required | string |
/v1/domains/{domainId}/transfer/preflightWhat moving this domain would take with it
Needs: A signed-in panel session, sent as Authorization: Bearer <access token>.
| Name | Where | Type | What it is |
|---|---|---|---|
| domainId required | path | string |
| Status | Body | When |
|---|---|---|
| 200 | DomainTransferPreflightDto |
| Field | Type | What it is |
|---|---|---|
| hostname required | string | |
| consentRecords required | number | Consent receipts that move — the evidence. |
| impressions required | number | Impressions recorded against this domain. |
| scans required | number | |
| cookies required | number | |
| published required | boolean | Whether a banner is currently live on the site. |
| exportComplete required | boolean | False when there are more consent records than one export can carry, so the copy the sender keeps would be incomplete. |
| pendingTransferId | string | null | An offer already open for this domain, if there is one. |
/v1/domains/{id}Remove a domain
Needs: A signed-in panel session, sent as Authorization: Bearer <access token>.
| Name | Where | Type | What it is |
|---|---|---|---|
| id required | path | string |
| Status | Body | When |
|---|---|---|
| 204 | — |
/v1/domains/{id}Get a single domain
Needs: A signed-in panel session, sent as Authorization: Bearer <access token>.
| Name | Where | Type | What it is |
|---|---|---|---|
| id required | path | string |
| Status | Body | When |
|---|---|---|
| 200 | DomainDto |
| Field | Type | What it is |
|---|---|---|
| id required | string | |
| hostname required | string | |
| rootDomain required | string | The registrable domain this hostname belongs to — the unit the plan bills. |
| 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. |
| status required | "pending" | "verified" | "failed" | |
| verifyMethod required | "script" | "dns_txt" | "meta_tag" | "file" | |
| verifyToken required | string | |
| verifiedAt required | string | null | ISO timestamp, or null if not yet verified. |
| 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. |
| lastCheckedAt required | string | null | ISO timestamp of the last verification attempt, or null if never attempted. |
| sslOk required | boolean | |
| groupId required | string | null | Group this domain belongs to, or null if ungrouped. |
| region required | "EU" | "US-CA" | "UK" | "GLOBAL" | null | Compliance jurisdiction the owner declared for this domain, or null if unset. |
| 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. |
| createdAt required | string | |
| 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. |
| 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. |
| 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. |
| activeScan required | object | |
| activeScan.status required | "queued" | "running" | |
| activeScan.pagesCrawled required | number | |
| activeScan.pagesPlanned required | number | null |
/v1/domains/{id}Update a domain (assign/clear its group, change its region)
Needs: A signed-in panel session, sent as Authorization: Bearer <access token>.
| Name | Where | Type | What it is |
|---|---|---|---|
| id required | path | string |
| Field | Type | What it is |
|---|---|---|
| groupId | string (uuid) | null | Assign the domain to a group (uuid), or null to ungroup. |
| region | "EU" | "US-CA" | "UK" | "GLOBAL" | Compliance jurisdiction the owner says this domain serves. |
| installMethod | "script" | "wordpress" | "gtm" | "shopify" | "wix" | Change how the tag is installed — the panel's instructions follow it. |
| deepScan | boolean | Nominate this domain for the monthly deep scan. Agency and above. The plan caps how many may be nominated at once — an agency with fifty client sites gets five, because the deep scan is the answer to "which one matters this month" and a deep scan of everything is just a very expensive routine scan. |
| Status | Body | When |
|---|---|---|
| 200 | DomainDto |
| Field | Type | What it is |
|---|---|---|
| id required | string | |
| hostname required | string | |
| rootDomain required | string | The registrable domain this hostname belongs to — the unit the plan bills. |
| 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. |
| status required | "pending" | "verified" | "failed" | |
| verifyMethod required | "script" | "dns_txt" | "meta_tag" | "file" | |
| verifyToken required | string | |
| verifiedAt required | string | null | ISO timestamp, or null if not yet verified. |
| 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. |
| lastCheckedAt required | string | null | ISO timestamp of the last verification attempt, or null if never attempted. |
| sslOk required | boolean | |
| groupId required | string | null | Group this domain belongs to, or null if ungrouped. |
| region required | "EU" | "US-CA" | "UK" | "GLOBAL" | null | Compliance jurisdiction the owner declared for this domain, or null if unset. |
| 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. |
| createdAt required | string | |
| 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. |
| 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. |
| 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. |
| activeScan required | object | |
| activeScan.status required | "queued" | "running" | |
| activeScan.pagesCrawled required | number | |
| activeScan.pagesPlanned required | number | null |
/v1/domains/{id}/instructionsOwnership-verification instructions for a domain
Needs: A signed-in panel session, sent as Authorization: Bearer <access token>.
| Name | Where | Type | What it is |
|---|---|---|---|
| id required | path | string | |
| method | query | "dns_txt" | "meta_tag" | "file" | "script" |
| Status | Body | When |
|---|---|---|
| 200 | VerificationInstructionsDto |
| Field | Type | What it is |
|---|---|---|
| method required | "script" | "dns_txt" | "meta_tag" | "file" | |
| recordName required | string | DNS record name (dns_txt), e.g. "_cmpstack.example.com". |
| recordType required | string | |
| recordValue required | string | The exact value to publish (TXT value / meta content / file contents). |
| humanInstructions required | string |
/v1/domains/{id}/verifyAttempt to verify domain ownership now
Needs: A signed-in panel session, sent as Authorization: Bearer <access token>.
| Name | Where | Type | What it is |
|---|---|---|---|
| id required | path | string |
| Field | Type | What it is |
|---|---|---|
| verifyMethod | "script" | "dns_txt" | "meta_tag" | "file" | Override the method to verify with; also persisted as the domain's method. |
| Status | Body | When |
|---|---|---|
| 200 | VerifyResultDto |
| Field | Type | What it is |
|---|---|---|
| verified required | boolean | |
| status required | "pending" | "verified" | "failed" | |
| reason required | string | null | Specific, actionable failure reason. Null when verified is true. |
| target required | string | The URL or DNS record name we actually checked. |
| checkedAt required | string | ISO timestamp of this attempt. |
/v1/install/instructionsInstall instructions per route, including the denied-first Consent Mode snippet
Needs: A signed-in panel session, sent as Authorization: Bearer <access token>.
| Status | Body | When |
|---|---|---|
| 200 | InstallInstructionsDto |
| Field | Type | What it is |
|---|---|---|
| deniedFirst required | DeniedFirstDto | |
| deniedFirst.snippet required | string | The whole block, <script> element included, ready to paste above the tag. |
| deniedFirst.js required | string | The two lines inside it, for routes that wrap them in their own element. |
| deniedFirst.explanation required | string | One line on what it does, shown beside it. |
| deniedFirst.limitation required | string | Google tags only — stated wherever the snippet is. |
| routes required | InstallRouteDto[] | |
| routes[].download required | InstallDownloadDto | null | A file to fetch first, or null. |
| routes[].tagForm required | "attributes" | "url" | How the tag carries its Site ID on this route: attributes (data-site, data-api) or url (?site=…&api=…), for places that drop attributes. |
| routes[].method required | "script" | "wordpress" | "gtm" | "shopify" | "wix" | |
| routes[].name required | string | The route as a person names it: "WordPress". |
| routes[].summary required | string | One line saying what installing this way involves. |
| routes[].steps required | string[] | What to do, in order. Three marks and no others: **what to press**, *a value to choose* and ` something typed `. |
| routes[].snippetLabel required | string | null | What the box holding the snippet is called, or null where nothing is pasted. |
| routes[].deniedFirst required | "paste" | "emitted" | "template" | How this route gets the call onto the page: paste shows the snippet above the tag, emitted means the integration prints it, template means the GTM template sets the same default itself. |
| routes[].placement required | string | Where the call goes on this route. |
| routes[].cannotReach required | string | null | Where it cannot reach on this route and what covers that instead, or null. |
Next groupClients