@cookiemunch/sdk 0.4.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/consent.ts","../src/webhooks.ts"],"sourcesContent":["/**\n * @cookiemunch/sdk — a small, typed REST client for the Cookie Munch Developer API\n * (the /v1 surface). Construct it with an API key; the org is derived server-side\n * from the key, so callers never pass an orgId. `fetch` is injectable for testing\n * and non-browser/non-Node runtimes; it defaults to the global fetch.\n *\n * const fc = createCookieMunch({ apiKey: 'fck_…', baseUrl: 'https://api.cookiemunch.net' });\n * const sites = await fc.sites.list();\n */\n\nexport * from './types.js';\nexport * from './consent.js';\nexport * from './webhooks.js';\nimport type {\n Identity,\n AccountOrgs,\n Site,\n SiteCreate,\n SiteConfig,\n SiteUpdate,\n SiteUpdateResult,\n IntegrationHealthReport,\n ProvisionRequest,\n ProvisionResult,\n PartnerLink,\n PartnerLinkCreate,\n PartnerCatalogEntry,\n ConsentDay,\n ConsentLogRow,\n RangeQuery,\n LogQuery,\n DsarRequest,\n DsarCreate,\n DsarStatus,\n ScoredVendor,\n VendorInput,\n VendorRecord,\n RiskScore,\n RopaEntry,\n RopaInput,\n SignedReceipt,\n PreferenceRecord,\n} from './types.js';\n\n/**\n * This package's version, sent in the default User-Agent. Cloudflare answers a request with\n * no recognisable User-Agent with `403 error code: 1010`; a test pins this to package.json.\n */\nexport const SDK_VERSION = '0.4.1';\n\nexport interface CookieMunchOptions {\n apiKey: string;\n /** \"https://api.cookiemunch.net\" — the /v1 prefix is appended automatically. */\n baseUrl: string;\n /** Injectable for tests / custom runtimes. Defaults to the global fetch. */\n fetch?: typeof fetch;\n /**\n * Send `Prefer: strict-unknown-fields` on every write: a body field the operation does not\n * declare is refused (`400 unknown_field`, with `field`) instead of ignored. Catches typos.\n */\n strictUnknownFields?: boolean;\n /** The `User-Agent` sent on every request. Default `cookiemunch-js/<version>`. */\n userAgent?: string;\n}\n\n/**\n * Thrown for any non-2xx response. `message` is the server's `error` field when present;\n * `code` is populated from the server's `code` field whenever the error body carries one\n * (e.g. banner-library conflicts, docs-admin checks) — not only for scoped-key\n * (`insufficient_scope`) responses. It's `undefined` when the body has no `code`.\n */\nexport class CookieMunchApiError extends Error {\n readonly status: number;\n readonly code?: string;\n /** The parsed JSON error body, when there was one (e.g. `issues` on a 422). */\n readonly body?: unknown;\n constructor(status: number, message: string, code?: string, body?: unknown) {\n super(message);\n this.name = 'CookieMunchApiError';\n this.status = status;\n if (code !== undefined) this.code = code;\n if (body !== undefined) this.body = body;\n }\n}\n\n/*\n * Additional /v1 response shapes. These live here (rather than in types.ts) but are\n * still re-exported from the package root via `export * from './types.js'` above plus\n * these top-level `export interface` declarations.\n */\n\n/** A cookie discovered on a site, classified into a consent category (wire shape of the\n * cookie-declaration feed). Matches the server's CategorizedCookie schema. */\nexport interface CategorizedCookie {\n name: string;\n category: 'necessary' | 'preferences' | 'statistics' | 'marketing' | 'unclassified' | (string & {});\n domain?: string;\n provider?: string;\n purpose?: string;\n /**\n * The purpose by language (lowercase BCP-47 tag). On a declared row, the owner's; on a\n * row the cookie database classified, the database's (empty: none yet).\n */\n purposes?: Record<string, string>;\n expiry?: string;\n /** Page URLs (multi-page scan) that set this cookie, earliest first. */\n foundOnPages?: string[];\n /** The site owner declared this cookie; it overrides the scan. */\n declared?: boolean;\n /** A declared `prefix*` wildcard row that no observed cookie matched yet. */\n pattern?: boolean;\n /**\n * Why a person should look at it. Visitors see unclassified and category ones under\n * \"Unclassified\"; `retired_but_seen` = retired, but a crawl or a visit saw it since, so the\n * site still sets it and it is listed again. `awaiting_corroboration` / `suspicious_report` =\n * only visitors reported it (not enough of them yet, or in one injection-shaped report), owner\n * views only; `foreign_cmp` = another consent platform's cookie, filed unclassified;\n * `not_seen_recently` = unseen for the site's `staleAfterDays`, owner views only.\n */\n review?: 'unclassified' | 'category_disabled' | 'category_unknown' | 'retired_but_seen' | 'awaiting_corroboration' | 'suspicious_report' | 'foreign_cmp' | 'not_seen_recently';\n /** Epoch-ms a crawl or a real visit last saw it. Absent on older rows. */\n lastSeenAt?: number;\n /** Owner views only: the site owner retired this cookie (see `sites.retireCookie`). */\n retired?: { at: number; renamedTo?: string; note?: string; by?: string; pattern?: string };\n /** A cookie (default, also when absent) or a key in browser storage. */\n type?: StorageType;\n /** Where the row came from: a real visit, a crawl, the owner (`api`) or a vendor preset (`import`). */\n source?: 'embed' | 'scan' | 'api' | 'import';\n /** Epoch-ms it was first stored. Absent on rows from before provenance was recorded. */\n firstSeen?: number;\n /** Distinct visits that reported it. */\n visits?: number;\n /** Owner views only: reported by a real visit, but not yet by enough distinct visits to be listed publicly. */\n quarantined?: boolean;\n /** \"Declared by <vendor>\": the vendor preset that declared it. */\n vendor?: string;\n}\n\n/** What a declaration row describes. */\nexport type StorageType = 'cookie' | 'localStorage' | 'sessionStorage' | 'indexedDB';\n\n/** A cookie the site owner retired: the site renamed or stopped setting it. */\nexport interface RetiredCookie {\n /** Exact name, or a `prefix*` wildcard. */\n name: string;\n /** Epoch-ms it was retired; anything seen after this brings it back. */\n at: number;\n renamedTo?: string;\n note?: string;\n /** `apikey:<prefix>` or a dashboard user id. */\n by?: string;\n pattern?: boolean;\n}\n\n/** A site's cookie declaration: the latest scan and observed visits with the site's own\n * declarations on top. This is what GET /v1/sites/:cbid/cookies returns. */\nexport interface CookieDeclaration {\n /** Epoch-ms of the latest scan or declaration change, or 0 if none. */\n updatedAt: number;\n cookies: CategorizedCookie[];\n /** The site owner's declarations, as stored. */\n declared?: DeclaredCookie[];\n /** The site's retirements, as stored (owner views only). */\n retired?: RetiredCookie[];\n}\n\n/** What the site owner says a cookie is (PUT /v1/sites/:cbid/cookies/:name). */\nexport interface DeclaredCookieInput {\n /** A standard category or one of the site's `categories.custom` ids. Not `unclassified`. */\n category: string;\n /** Shown to visitors, and the fallback for any language `purposes` lacks. */\n purpose: string;\n /**\n * The purpose in other languages: `{ es: '…', 'pt-BR': '…' }` (BCP-47 language or\n * language-region tags, stored lowercased; ≤50 languages, ≤1000 characters each).\n * The banner, `data-cc=\"declaration\"` and /cookie-declaration.js draw the visitor's.\n */\n purposes?: Record<string, string> | null;\n provider?: string;\n expiry?: string;\n domain?: string;\n /** Treat the name as a prefix wildcard (a trailing `*` does the same). */\n pattern?: boolean;\n /** A cookie (default) or a key in localStorage, sessionStorage or IndexedDB. */\n type?: StorageType;\n}\n\n/** A stored declaration. */\nexport interface DeclaredCookie extends DeclaredCookieInput {\n name: string;\n updatedAt: number;\n /** `import` when a vendor preset wrote it. */\n source?: 'import';\n /** The vendor whose preset declared it. */\n vendor?: string;\n}\n\n/** A platform whose cookies and storage a preset declares. */\nexport type CookiePresetVendor = 'atlas' | 'forumulate' | 'chatly' | 'cookiemunch' | 'referally';\n\n/** Where a vendor's live manifest will come from, and whether it exists yet. */\nexport interface CookiePresetManifest {\n status: 'builtin' | 'waiting_on_vendor';\n waitingOn?: string;\n item?: string;\n contract?: string;\n}\n\n/** Preset parameters: Atlas `frontendApiHost`/`crossSubdomainCookies`; Chatly `publicId`/`dragging`/`category`; Referally `cookieDomain`/`cookieDays`/`category`. */\nexport interface CookiePresetParams {\n frontendApiHost?: string;\n crossSubdomainCookies?: boolean;\n publicId?: string;\n dragging?: boolean;\n category?: string;\n cookieDomain?: string;\n cookieDays?: number;\n}\n\n/** One preset in GET /v1/sites/:cbid/cookies/presets. */\nexport interface CookiePreset {\n vendor: CookiePresetVendor;\n params: Array<{ name: string; type: string; required: boolean; description?: string }>;\n applied: boolean;\n declared: string[];\n manifest: CookiePresetManifest;\n}\n\n/** What applying a preset declared. */\nexport interface CookiePresetResult {\n vendor: CookiePresetVendor;\n cookies: DeclaredCookie[];\n removed: string[];\n skipped?: Array<{ name: string; reason: string }>;\n note?: string;\n manifest: CookiePresetManifest;\n dryRun?: true;\n declared?: DeclaredCookie[];\n diff?: DeclarationDiff;\n}\n\n/** What removing observed rows removed (or, with `dryRun`, would). */\nexport interface ObservedPurgeResult {\n removed: string[];\n count: number;\n since?: number;\n until?: number;\n dryRun?: true;\n}\n\n/** What a declaration write would change (`dryRun`). */\nexport interface DeclarationDiff {\n added: DeclaredCookie[];\n removed: DeclaredCookie[];\n changed: Array<{ name: string; before: DeclaredCookie; after: DeclaredCookie }>;\n}\n\n/** The answer to a `dryRun` declaration write: nothing was stored. */\nexport interface DeclarationDryRun {\n dryRun: true;\n /** Every declaration the site would have. */\n declared: DeclaredCookie[];\n diff: DeclarationDiff;\n}\n\n/** A warning about a write: e.g. the category is switched off for the site. */\nexport interface DeclarationWarning {\n field: string;\n reason: string;\n}\n\n/** What an import with `apply` declared, on top of what it read. */\nexport interface AppliedImport extends ImportedDeclaration {\n applied: number;\n skipped: Array<{ name: string; reason: string }>;\n warnings?: DeclarationWarning[];\n}\n\n/** @deprecated The server returns {@link CookieDeclaration}; kept only for back-compat. */\nexport interface SiteCookie {\n name: string;\n domain: string;\n category: string;\n provider?: string;\n purpose?: string;\n expiry?: string;\n firstSeen?: number;\n}\n\n/** Cookie-scan status, from GET /v1/sites/:cbid/scan. */\nexport interface ScanStatus {\n /** `scanning` while the last scan is queued or crawling. Kept for older clients; read `state`. */\n status: 'idle' | 'scanning';\n /** Epoch-ms of the last completed scan, or null. */\n lastScannedAt: number | null;\n /** What the last scan saw of the live banner's Accept all / Reject all. Absent until measured. */\n buttonParity?: ButtonParityScan;\n /** Where the last scan is. Absent when the site was never scanned. */\n state?: 'queued' | 'scanning' | 'done' | 'failed';\n scanId?: string;\n startedAt?: number;\n finishedAt?: number;\n durationMs?: number;\n /** Cookies this crawl saw (the stored list also keeps earlier finds: `keptFromEarlier`). */\n cookieCount?: number;\n keptFromEarlier?: number;\n pagesVisited?: number;\n /** Where the site's address redirected to (its canonical host), when it did. */\n finalUrl?: string;\n /** Why it failed. */\n error?: string;\n}\n\n/** Consent-button parity of the banner a real browser saw on the site (see ScanStatus). */\nexport interface ButtonParityScan {\n url: string;\n /** False when no visible consent accept control was found in a recognisable banner. */\n bannerFound: boolean;\n /**\n * True when the checks ran: our embed, or a recognised banner with both Accept and Reject.\n * False = not measured — no findings, `score` null. Absent on scans stored before it existed.\n */\n measured?: boolean;\n /** `cookiemunch` = our embed; `other` = found by its button text. */\n cmp: 'cookiemunch' | 'other' | null;\n acceptFound: boolean;\n rejectFound: boolean;\n designWarnings: ButtonParityFinding[];\n /** 0–100; null when no banner was found. */\n score: number | null;\n checkedAt?: number;\n}\n\n/** @deprecated The server returns {@link ScanStatus}; kept only for back-compat. */\nexport interface ScanResult {\n scanId: string;\n status: 'queued' | 'running' | 'complete' | 'failed';\n startedAt?: number;\n finishedAt?: number;\n pagesScanned?: number;\n cookiesFound?: number;\n}\n\n/** A/B banner experiment results for a single variant. */\nexport interface AbResult {\n variant: string;\n impressions: number;\n optIns: number;\n optInRate: number;\n}\n\n/** The response shape of GET /v1/sites/:cbid/snippet. */\n// Keep in sync with packages/server/src/snippet.ts InstallSnippet\nexport interface InstallSnippet {\n /** The exact `<script>` tag to paste into `<head>`. */\n snippet: string;\n /** URL of the consent.js embed file (the embed/CDN origin). */\n src: string;\n /** The API origin (beacons + consent-tcf.js). Present when the server reports it. */\n api?: string;\n /** The site identifier. */\n cbid: string;\n /** The blocking mode on the tag — the site's `blocking.mode` unless overridden. */\n blockingMode: string;\n /** Where `blockingMode` came from: the site's config, or the `blockingMode` option. */\n blockingModeSource?: 'config' | 'query';\n /** The tag's data-culture, when it has one. */\n culture?: string;\n /** CSP sources this site needs, by directive, plus a paste-able `policy`. */\n csp?: CspSources;\n /** Present when an override disagrees with the stored config. */\n warnings?: ConfigWarning[];\n}\n\n/** Content Security Policy sources the embed needs. */\nexport interface CspSources {\n script: string[];\n connect: string[];\n img: string[];\n /** Always includes 'unsafe-inline' (the banner's shadow-root <style>), unless you use a nonce. */\n style: string[];\n font: string[];\n frame: string[];\n /** The same as one policy fragment. */\n policy: string;\n}\n\n/** Something a config write did not store as sent. */\nexport interface ConfigWarning {\n field: string;\n reason: string;\n}\n\n/** One leaf a config write would change. */\nexport interface ConfigChange {\n path: string;\n from?: unknown;\n to?: unknown;\n}\n\n/** `PUT …/config` options. `keep: 'unspecified'` keeps the stored value of every top-level field the body omits. */\nexport interface PutConfigOptions {\n keep?: 'unspecified';\n}\n\n/** What a config write would do (`?dryRun=1`) — nothing is saved. */\nexport interface ConfigDryRun {\n dryRun: true;\n /** The config as it would be saved. */\n config: SiteConfig;\n /** Every leaf that would change. */\n diff: ConfigChange[];\n warnings?: ConfigWarning[];\n /** Consent-button parity of the would-be config (see {@link ButtonParityFinding}). */\n designWarnings: ButtonParityFinding[];\n}\n\n/** The effective style of an Accept all / Reject all control. */\nexport interface ParityControlSummary {\n id?: string;\n label: string;\n element: 'button' | 'link';\n background?: string;\n color?: string;\n borderColor?: string;\n borderWidth?: number;\n fontSize?: number;\n fontWeight?: number;\n width?: number;\n height?: number;\n}\n\n/**\n * One way Accept all is pushed harder than Reject all (colour, size, placement, copy, custom\n * CSS). Returned as `designWarnings` on every config read and write, and by `lintConfig`.\n * A warning, never an error — the config is saved regardless. See /docs/fair-consent-buttons.\n */\nexport interface ButtonParityFinding {\n code:\n | 'reject_missing_first_layer' | 'reject_missing_same_layer' | 'reject_hidden' | 'reject_below_fold' | 'reject_isolated'\n | 'reject_low_emphasis' | 'saturation_gap' | 'color_mismatch' | 'reject_text_low_contrast' | 'reject_styled_as_link'\n | 'font_size_gap' | 'font_weight_gap' | 'size_gap' | 'padding_gap' | 'border_mismatch' | 'reject_label_missing'\n | 'reject_label_long' | 'confirmshaming' | 'custom_css_asymmetric';\n severity: 'warning' | 'notice';\n layer: 'banner' | 'preferences' | 'modal' | 'popup' | 'bar' | 'inline' | 'custom';\n accept: ParityControlSummary | null;\n reject: ParityControlSummary | null;\n metric: { name: string; accept?: number; reject?: number; value: number; threshold: number };\n detail: string;\n fix: string;\n where: { view?: string; variant?: string; theme?: 'light' | 'dark'; breakpoint?: 'desktop' | 'mobile'; language?: string; url?: string };\n}\n\n/** Reject all style values that would match the Accept all on the same layer. */\nexport interface SuggestedRejectStyle {\n layer: string;\n view?: string;\n background?: string;\n color?: string;\n borderColor?: string;\n borderWidth?: number;\n fontSize?: number;\n fontWeight?: number;\n width?: number;\n height?: number;\n}\n\n/** `POST /v1/sites/:cbid/config/lint` — nothing is saved. */\nexport interface ButtonParityLint {\n /** `merged` = what PATCH would store (merged over the stored config); `replaced` = what PUT would store (`replace: true`). */\n mode: 'merged' | 'replaced';\n designWarnings: ButtonParityFinding[];\n /** 100 minus 20 per warning and 5 per notice, floored at 0. */\n score: number;\n suggestedReject: SuggestedRejectStyle[];\n}\n\n/**\n * Scrollbars on the surfaces Cookie Munch renders — the same shape on the banner theme (`theme.scrollbar`)\n * and the rights-page theme. Colours are #hex, rgb() or hsl(); width is clamped to 4–16, radius to 0–999.\n */\nexport interface ScrollbarTheme {\n style: 'themed' | 'thin' | 'native' | 'hidden';\n thumb?: string;\n thumbHover?: string;\n track?: string;\n width?: number;\n radius?: number;\n}\n\n/** The hosted rights page document. See docs/rights-page for each section type's props. */\nexport interface RightsPageDocument {\n v: 1;\n theme: { preset: 'brand' | 'light' | 'dark' | 'custom'; width: 'narrow' | 'normal' | 'wide' | 'full'; density: 'compact' | 'comfortable' | 'spacious'; colorScheme: 'light' | 'dark' | 'auto'; scrollbar?: ScrollbarTheme; [k: string]: unknown };\n sections: Array<{ id: string; type: string; props: Record<string, unknown>; hidden?: { phone?: boolean; desktop?: boolean }; style?: Record<string, unknown> }>;\n form: { requestTypes: Array<Record<string, unknown>>; fields: Array<Record<string, unknown>>; jurisdiction: 'auto' | 'ask' | 'hidden'; consentIdField: 'hidden' | 'optional' | 'required'; success: { title?: string; body?: string }; submitLabel?: string };\n i18n: { defaultLocale: string; autoDetect: boolean; overrides: Record<string, unknown> };\n seo: { title?: string; description?: string; favicon?: string; image?: string; index: boolean };\n customCss?: string;\n}\n/** `message` is English; `code` (+ `params`) is stable, for your own wording or language. */\nexport interface RightsPageIssue { path: string; message: string; code?: string; params?: Record<string, string | number> }\nexport interface RightsPageDraft { cbid: string; draft: RightsPageDocument; version: number; publishedAt: number | null; updatedAt: number | null; warnings: RightsPageIssue[] }\nexport interface RightsPageDryRun { dryRun: true; cbid: string; draft: RightsPageDocument; warnings: RightsPageIssue[] }\n/** Who published a version: `publishedBy` is an actor id; `publishedByEmail` names a dashboard user (null for an API key). */\nexport interface RightsPagePublisher { publishedBy: string; publishedByEmail: string | null; publishedVia: 'dashboard' | 'api_key' }\nexport interface RightsPagePublished { cbid: string; page: RightsPageDocument; version: number; publishedAt: number; publishedBy?: string | null; publishedByEmail?: string | null; publishedVia?: 'dashboard' | 'api_key' | null }\n/**\n * Optimistic concurrency for a draft write: the draft's `updatedAt` as you last read it (epoch ms or\n * an ISO date; null = you saw no saved draft). A mismatch rejects with a 409 CookieMunchApiError whose\n * body is RightsPageDraftConflict. Omitted: a full write (put) is last-write-wins; a patch merges onto the draft\n * as it is when written, so a concurrent edit is not lost.\n */\nexport interface RightsPageWriteOptions { baseUpdatedAt?: number | string | null }\nexport interface RightsPageDraftConflict { error: 'draft_conflict'; draft: RightsPageDraft }\n/** `draftUpdatedAt`: the draft's updatedAt after the call — pass it as the next `baseUpdatedAt`. */\nexport interface RightsPagePublishResult { cbid: string; version: number; publishedAt: number; draftUpdatedAt: number; rolledBackTo?: number }\nexport interface RightsPageVersions { cbid: string; versions: Array<{ version: number; publishedAt: number } & RightsPagePublisher> }\n\n/** The draft body with `baseUpdatedAt` added when the caller passed one (null included). */\nconst withBase = (doc: object, opts?: RightsPageWriteOptions): object =>\n opts?.baseUpdatedAt !== undefined ? { ...doc, baseUpdatedAt: opts.baseUpdatedAt } : doc;\n\n/** Options for POST /v1/sites/:cbid/preview-token. All optional. */\nexport interface PreviewTokenOptions {\n /** Lifetime in seconds, 1..3600. Default 900. */\n ttlSeconds?: number;\n /** The page to preview. Default: the site's registered domain. */\n url?: string;\n /** Region to force: a country or subdivision code (`de`, `us-ca`) or `eu`. */\n region?: string;\n /** Language to force, as a BCP 47 tag (`de`, `pt-BR`). */\n lang?: string;\n}\n\n/** A banner preview token, and the page URL that opens the preview. */\nexport interface PreviewToken {\n cbid: string;\n token: string;\n /** Epoch milliseconds. */\n expiresAt: number;\n region?: string;\n lang?: string;\n /** The page with `cm_preview` / `cm_region` / `cm_lang` added. */\n previewUrl?: string;\n}\n\n/** A right a rights link may file (the hosted rights page's request-type kinds). */\nexport type RightKind =\n | 'access' | 'erasure' | 'rectification' | 'portability' | 'restriction'\n | 'objection' | 'opt_out_sale' | 'opt_out_targeted' | 'limit_sensitive';\n\n/** Input for POST /v1/sites/:cbid/rights-links. */\nexport interface RightsLinkInput {\n /** The signed-in person. `email` must be an address you have verified. */\n subject: { email: string; userId?: string };\n /** Limit the link to these rights. Default: every right the page offers. */\n rights?: RightKind[];\n /** Lifetime in seconds, 60..3600. Default 900. */\n ttlSeconds?: number;\n}\n\n/** A signed, single-use link to the hosted rights page for one signed-in person. */\nexport interface RightsLink {\n /** `https://<customDomain>/?t=…`, or `https://cookiemunch.net/r/<cbid>?t=…` when `customDomain` is null. */\n url: string;\n /** Epoch milliseconds. */\n expiresAt: number;\n /** The site's active custom rights-page host the link opens on, or null. */\n customDomain: string | null;\n}\n\n/** One DNS record to create, exactly as written. */\nexport interface DnsRecordInstruction {\n type: 'CNAME' | 'TXT';\n name: string;\n value: string;\n}\n\n/** A custom hostname serving a site's rights page. */\nexport interface RightsPageDomain {\n /** Lowercase ASCII form (punycode for an internationalised name). */\n hostname: string;\n /** As a person reads it. */\n unicodeHostname: string;\n cbid: string;\n /** pending_dns → verifying → issuing → active, or error (see lastError). */\n status: 'pending_dns' | 'verifying' | 'issuing' | 'active' | 'error';\n certStatus: 'none' | 'pending' | 'issued' | 'failed';\n createdAt: number;\n verifiedAt?: number;\n activatedAt?: number;\n lastCheckedAt?: number;\n /** dns_not_found | wrong_target | hostname_in_use | router_write_failed | certificate_not_issued | dns_changed | probe_failed */\n lastError?: string;\n url: string;\n /** The records to create: the CNAME, or (apex / flattened / proxied) the TXT plus an ALIAS to the same target. */\n /** `cname.value` is this domain's own `<token>.cname.cookiemunch.net` — unique to the claim; a CNAME to anything else does not verify it. */\n dns: { recommended: 'cname' | 'flattened'; cname: DnsRecordInstruction; txt: DnsRecordInstruction };\n /** Only on a verify that found no proof: what DNS answered. */\n dnsSeen?: { cnames: string[]; addresses: string[]; txt: string[] };\n}\n\n/** The site's custom hostnames, the plan allowance (null = unlimited) and the org's usage. */\nexport interface RightsPageDomainList {\n domains: RightsPageDomain[];\n limit: number | null;\n used: number;\n target: string;\n}\n\n/** Options for the snippet endpoint. */\nexport interface SnippetOptions {\n /** Override the site's configured `blocking.mode`. Default: the config's. */\n blockingMode?: 'auto' | 'manual';\n /** Language culture override, e.g. \"en\" or \"fr\". */\n culture?: string;\n}\n\n/**\n * The result of domain verification.\n * Keep in sync with the POST /v1/sites/:cbid/verify response in\n * packages/server/src/dev-api.ts (~L267) — note the server adds `method` on success,\n * so this wire shape is wider than domain-verify.ts's internal VerifyResult.\n */\nexport interface VerifyResult {\n verified: boolean;\n method?: 'dns' | 'meta' | 'file';\n reason?: string;\n}\n\n/** A site brand suggestion (\"Match my site\"). Keep in sync with packages/server/src/brand-extract.ts BrandSuggestion. */\nexport interface BrandSuggestion {\n background?: string;\n text?: string;\n highlight?: string;\n fontFamily?: string;\n fontUrl?: string;\n palette?: string[];\n}\n\n/** The response shape of POST /v1/sites/:cbid/brand. */\nexport interface BrandExtractionResult {\n suggestion: BrandSuggestion;\n}\n\n/** A validation/lint issue on a v2 banner flow. */\nexport interface FlowIssue {\n code: string;\n message: string;\n path?: string;\n}\n\n/** A site's v2 banner flow + lint issues, from GET /v1/sites/:cbid/flow. */\nexport interface SiteFlow {\n v: 2;\n flow: Record<string, unknown>;\n categories: Record<string, unknown>;\n customCss?: string;\n lint: FlowIssue[];\n}\n\n/**\n * A single structured flow edit operation. `op` selects the kind; remaining fields are\n * that op's args (e.g. { op: 'addView', id, surface, layoutMode }).\n */\nexport type FlowOp = { op: string } & Record<string, unknown>;\n\n/**\n * Result of editFlow/setFlow. On success the new flow is persisted and returned;\n * on a validation/lint failure the config is NOT saved and `issues` says why.\n */\nexport type FlowOpResponse =\n | { ok: true; flow: Record<string, unknown>; /** Consent-button parity of the saved design. */ designWarnings?: ButtonParityFinding[] }\n | { ok: false; failedAt?: number; op?: unknown; issues: FlowIssue[] };\n\n/** A reusable brand kit (colors/logo/typography) for banner theming. */\nexport interface BrandKit {\n id: string;\n orgId: string;\n name: string;\n /** Banner theme tokens (opaque design object). */\n theme: Record<string, unknown>;\n /** Optional banner content overrides (opaque). */\n content?: Record<string, unknown>;\n logoUrl?: string;\n customCss?: string;\n}\n\n/** A configurable consent preference/purpose item. */\nexport interface PreferenceItem {\n id: string;\n orgId: string;\n cbid?: string;\n label: string;\n description?: string;\n category?: string;\n}\n\n/** An organization member. */\nexport interface Member {\n userId: string;\n email: string;\n role: 'owner' | 'admin' | 'member' | 'viewer' | (string & {});\n}\n\n/** Display metadata for an issued key — never the secret. Returned by keys.list(). */\nexport interface ApiKeyPrefix {\n prefix: string;\n createdAt?: number;\n /** Epoch ms the key stops working; null = never. */\n expiresAt?: number | null;\n name?: string | null;\n /** null = full access. */\n scopes?: string[] | null;\n cbids?: string[] | null;\n /** Epoch ms it last authenticated a request (recorded at most hourly); null = not since recording began. */\n lastUsedAt?: number | null;\n}\n\n/** GET /v1/keys/report — key hygiene. */\nexport interface ApiKeyReport {\n generatedAt: number;\n unusedDays: number;\n expiringDays: number;\n unused: Array<ApiKeyPrefix & { reason: string }>;\n broad: Array<ApiKeyPrefix & { reason: string }>;\n expiringSoon: Array<ApiKeyPrefix & { reason: string }>;\n noExpiry: number;\n total: number;\n}\n\n/** A newly issued key. `key` is returned ONCE and never again. Returned by keys.issue(). */\nexport interface ApiKeyIssued {\n key: string;\n prefix: string;\n}\n\n/** @deprecated No endpoint returns this shape; use {@link ApiKeyPrefix} / {@link ApiKeyIssued}. */\nexport interface ApiKey {\n id: string;\n orgId: string;\n prefix: string;\n name?: string;\n createdAt: number;\n lastUsedAt?: number;\n secret?: string;\n}\n\n/**\n * Input for issuing a new API key.\n *\n * NOTE: the server's POST /v1/keys currently accepts no body and always mints an\n * unscoped key — `name` is not yet honoured server-side. It's kept here (rather than\n * dropped) so a future scoped/named-key endpoint can start reading it without an SDK\n * major version bump; see {@link CookieMunchClient.keys}.\n */\nexport interface ApiKeyIssueInput {\n name?: string;\n /** Least privilege: grant only these scopes. Omit for a full-access key. */\n scopes?: string[];\n /**\n * Lock the key to these properties. A locked key works only on those sites and on no\n * org-wide endpoint — see \"Keys locked to properties\" in the client reference.\n */\n cbids?: string[];\n /** Expire the key after this many days (1–3650). */\n expiresInDays?: number;\n /** Or when it expires: epoch ms or an ISO date-time (not with expiresInDays). Omit both: never. */\n expiresAt?: number | string;\n}\n\n/** Org usage/quota summary. Matches the server's Usage schema. */\nexport interface Usage {\n domains: number;\n seats: number;\n monthlyEvents: number;\n}\n\n/** A webhook subscription. `secret` is present only in the create response. */\nexport interface WebhookSubscription {\n id: string;\n orgId: string;\n url: string;\n secret?: string;\n events: string[];\n cbid: string | null;\n active: boolean;\n createdAt: number;\n}\n\n/** One cookie read from another CMP's export. */\nexport interface ImportedCookie {\n name: string;\n domain: string;\n /** Ours, or `unclassified` when their category did not map to one. */\n category: 'necessary' | 'preferences' | 'statistics' | 'marketing' | 'unclassified';\n /** What the previous vendor called it, kept for review. */\n sourceCategory: string;\n purpose: string;\n provider: string;\n expiry: string;\n}\n\n/** What a competitor's cookie-declaration export contained, in our vocabulary. */\nexport interface ImportedDeclaration {\n source: 'onetrust' | 'cookiebot' | 'cookieyes' | 'unknown';\n cookies: ImportedCookie[];\n /** Rows whose category could not be placed — review these first. */\n unmapped: Array<{ name: string; category: string }>;\n}\n\n/**\n * A page that refused to load the banner renderer. The site is live and configured, so\n * every other signal looks healthy; this is the one that says nobody can be asked.\n */\nexport interface BlockedReport {\n cbid: string;\n /** Origin of the page the embed was running on. */\n page: string;\n /** The bundle URL that page refused. */\n url: string;\n reason: string;\n firstSeen: number;\n lastSeen: number;\n /** How many times this page has reported. Repeats are counted, not stored. */\n count: number;\n}\n\n/** A webhook delivery that failed every retry. Replay it by `id`. */\nexport interface WebhookDeadLetter {\n id: string;\n orgId: string;\n subscriptionId: string;\n url: string;\n eventType: string;\n cbid: string | null;\n payload: unknown;\n attempts: number;\n lastStatus?: number;\n lastError?: string;\n failedAt: number;\n}\n\n/** What a test delivery's endpoint answered. */\nexport interface WebhookTestResult {\n ok: boolean;\n status?: number;\n error?: string;\n}\n\n/** The key's organisation. */\nexport interface Org {\n id: string;\n name: string;\n plan: string;\n logoUrl: string | null;\n}\n\nexport type MailProviderKind = 'brevo' | 'sendgrid' | 'postmark' | 'resend' | 'ses' | 'smtp';\n\n/**\n * The org's own mail provider for rights-request mail. The credential is never returned —\n * `secretLast4` only. `sender` says whether verification codes go through it right now\n * (`own`) or are not mailed at all (`none`): there is no shared sender for rights mail.\n */\nexport interface MailProvider {\n configured: boolean;\n sender: 'own' | 'none';\n provider?: MailProviderKind;\n fromEmail?: string;\n fromName?: string;\n replyTo?: string;\n ses?: { region: string; accessKeyId: string };\n smtp?: { host: string; port: number; security: 'tls' | 'starttls'; username: string };\n secretLast4?: string;\n status?: 'untested' | 'verified' | 'failing';\n verifiedAt?: number | null;\n lastTest?: { ok: boolean; at: number; error?: string } | null;\n updatedAt?: number;\n /** Only on `set`: no verification codes are mailed until a test succeeds. */\n note?: string;\n}\n\n/** Save a provider. `secret` is write-only; omit it to keep the stored one (same provider only). */\nexport interface MailProviderInput {\n provider: MailProviderKind;\n fromEmail: string;\n fromName?: string | null;\n replyTo?: string | null;\n secret?: string;\n ses?: { region: string; accessKeyId: string };\n smtp?: { host: string; port: 25 | 465 | 587 | 2525; security: 'tls' | 'starttls'; username: string };\n}\n\nexport type RightsCaptchaProvider = 'turnstile' | 'hcaptcha' | 'recaptcha_v2' | 'recaptcha_v3';\n\n/**\n * The rights page's captcha in force: your own keys (`own`, recommended) or none (`off`, the default).\n * Cookie Munch's own captcha is never used on rights pages. The secret is never returned.\n */\nexport interface RightsCaptcha {\n /** `site`: the site's own setting; `org`: the organisation default; `default`: neither (off). */\n source: 'site' | 'org' | 'default';\n mode: 'own' | 'off';\n provider?: RightsCaptchaProvider;\n siteKey?: string;\n /** reCAPTCHA v3 only. */\n minScore?: number;\n secretSet: boolean;\n secretLast4?: string;\n status: 'verified' | 'disabled';\n verifiedAt: number | null;\n updatedAt: number | null;\n /** Only when a `set` turned the captcha off: what that costs. */\n warning?: string;\n}\n\n/**\n * `own` needs provider, siteKey and (the first time) secret; the provider is asked to accept the secret before\n * anything is saved (422 captcha_secret_invalid / 502 captcha_unreachable). `off` removes spam protection beyond\n * the hidden spam-trap field, the rate limits and the email code.\n */\nexport interface RightsCaptchaInput {\n mode: 'own' | 'off';\n provider?: RightsCaptchaProvider;\n siteKey?: string;\n /** Write-only. Omit to keep the stored one (same provider, same place). */\n secret?: string;\n /** reCAPTCHA v3 only: 0.1–0.9, default 0.5. */\n minScore?: number;\n}\n\nexport type RightsIdentityType = 'clerk' | 'atlas' | 'oidc_jwt';\n\n/**\n * The sign-in provider rights requests are verified with: a visitor signed in to your site is\n * verified by their session, with no code mail. Nothing here is secret.\n */\nexport interface RightsIdentity {\n /** `site`: the site's own setting; `org`: the organisation default; `default`: neither (none). */\n source: 'site' | 'org' | 'default';\n configured: boolean;\n type?: RightsIdentityType;\n issuer?: string;\n jwksUrl?: string;\n audience?: string;\n authorizedParties?: string[];\n emailClaim?: string;\n emailVerifiedClaim?: string;\n sessionCookieName?: string;\n /** Present (true) only when set: clerk/atlas tokens without the verified claim are accepted. */\n trustEmailClaim?: true;\n status: 'verified' | 'not_configured';\n verifiedAt: number | null;\n updatedAt: number | null;\n}\n\n/**\n * The JWKS is fetched and checked before anything is saved (502 identity_jwks_unreachable /\n * 422 identity_jwks_invalid). `authorizedParties` (your own origins) is required for clerk and atlas;\n * oidc_jwt needs an `audience` or `authorizedParties`. Issuer and JWKS URL are https on a host name,\n * never an IP address. The token must carry the email and a verified-email claim set to `true`\n * (clerk/atlas with `trustEmailClaim`: the verified claim may be absent).\n */\nexport interface RightsIdentityInput {\n type: RightsIdentityType;\n /** Exactly the token's `iss` (Clerk: your Frontend API URL). */\n issuer: string;\n /** Required for oidc_jwt; clerk/atlas default to `<issuer>/.well-known/jwks.json`. */\n jwksUrl?: string;\n audience?: string;\n authorizedParties?: string[];\n /** Default `email`. */\n emailClaim?: string;\n /** Default `email_verified`. */\n emailVerifiedClaim?: string;\n /** Default `__session`: the cookie the hosted page on your custom domain reads. */\n sessionCookieName?: string;\n /**\n * clerk/atlas only, default false. True when your provider has no verified-email claim and every\n * address it puts in the email claim is verified: a token without the verified claim is accepted.\n */\n trustEmailClaim?: boolean;\n}\n\nexport interface MailProviderTestResult {\n ok: boolean;\n sentTo: string;\n error?: string;\n mailProvider: MailProvider;\n}\n\n/** Change an org's name or logo. `logoUrl: null` removes the logo. */\nexport interface OrgUpdate {\n name?: string;\n logoUrl?: string | null;\n}\n\n/** One entry in the org's audit log. */\nexport interface AuditEntry {\n id: string;\n orgId: string;\n actorUserId: string;\n actorEmail?: string;\n action: string;\n target?: string;\n meta?: Record<string, unknown>;\n at: number;\n}\n\n/** Rename a key, or replace its scopes or the properties it is locked to. */\nexport interface ApiKeyUpdate {\n name?: string;\n scopes?: string[];\n cbids?: string[];\n /** A new expiry (epoch ms or ISO date-time), or null to remove it. */\n expiresAt?: number | string | null;\n}\n\n/** An image to upload: base64 (or a `data:` URL) plus its type. */\nexport interface AssetUpload {\n data: string;\n contentType: 'image/png' | 'image/jpeg' | 'image/webp' | 'image/gif' | 'image/svg+xml';\n}\n\n/** Result of erasing a subject's consent for a deletion request. */\nexport interface DsarEraseResult {\n erased: number;\n encryptionEnabled: boolean;\n /** Present when no KEK is configured, so nothing was cryptographically erased. */\n warning?: string;\n request: DsarRequest;\n}\n\n/** Result of exporting a subject's consent for an access or portability request. */\nexport interface DsarExportResult {\n records: unknown[];\n count: number;\n request: DsarRequest;\n}\n\n/** Input for creating a webhook subscription. */\nexport interface WebhookCreate {\n url: string;\n events: string[];\n cbid?: string;\n}\n\n/** Fields to change on a webhook subscription. Only those present are sent. */\nexport interface WebhookUpdate {\n url?: string;\n events?: string[];\n /** One property, or null for every property in the org. */\n cbid?: string | null;\n /** false pauses delivery without deleting the subscription. */\n active?: boolean;\n}\n\n/** One site to create in a bulk call. */\nexport interface BulkSiteInput {\n domain: string;\n cbid?: string;\n platform?: string;\n}\n\n/** Per-item outcome of a bulk create, in input order. */\nexport interface BulkSiteResult {\n ok: boolean;\n cbid?: string;\n domain?: string;\n error?: string;\n}\n\n/** Input for creating a brand kit. */\nexport interface BrandKitCreate {\n name: string;\n theme: unknown;\n content?: unknown;\n logoUrl?: string;\n customCss?: string;\n}\n\n/** A lightweight summary of an account-level banner, as returned by GET /v1/banners. */\nexport interface BannerSummary {\n id: string;\n name: string;\n updatedAt: number;\n assignedCbids: string[];\n /** How many warning-severity consent-button parity findings the design has (0 = fair). */\n designWarningCount: number;\n}\n\n/** A full account-level banner record, including its config JSON. */\nexport interface BannerRecord {\n id: string;\n orgId: string;\n name: string;\n json: SiteConfig;\n createdAt: number;\n updatedAt: number;\n /** Consent-button parity findings in the design. Never blocks a save. */\n designWarnings?: ButtonParityFinding[];\n}\n\n/**\n * Input for the public consent-log ingest endpoint (POST /api/v1/consent) — the\n * tamper-evident, hash-chained ledger write the browser embed makes. Mirrors the\n * server's ConsentPayload; optional fields are sent only when set. Records are\n * anonymised (IP truncated) and hash-chained server-side.\n */\nexport interface ConsentIngestInput {\n /** The registered site id the consent belongs to. */\n cbid: string;\n /** Stable per-subject consent-receipt id (used for erase/export). */\n stamp: string;\n /** The subject's cookie-category choices. */\n choices: { preferences: boolean; statistics: boolean; marketing: boolean };\n /** How consent was captured. */\n method: 'explicit' | 'implied';\n /** Consent/notice version number. */\n ver: number;\n /** Epoch-ms the decision was made. */\n utc: number;\n /** The URL / screen where consent was captured. */\n url: string;\n /** Optional IAB TCF consent string (TCF mode only). */\n tcString?: string;\n /** Optional GPP string (GPP mode only). */\n gppString?: string;\n /** Optional named-purpose map beyond the standard categories. */\n purposes?: Record<string, boolean>;\n /** Optional opaque digest of the exact notice text shown to the subject. */\n subjectPolicyHash?: string;\n /** Optional map: named purpose → RoPA entry id. */\n purposeRopa?: Record<string, string>;\n /** Optional A/B variant id. */\n variant?: string;\n /**\n * Optional, app-supplied STABLE cross-surface subject id. Lets an org retrieve one\n * subject's consent history across ALL its sites/surfaces (web + mobile + desktop).\n * Opaque — stored and bound into the record hash server-side, never interpreted.\n */\n subjectId?: string;\n}\n\n/** One of a person's identifiers: an identity-space code plus the normalised value. */\nexport interface Identifier {\n space: string;\n value: string;\n}\n\nexport interface ConsentDecisionInput {\n purpose: string;\n allowed: boolean;\n legalBasis?: string;\n jurisdiction?: string;\n provenance?: string;\n collectedAt?: number;\n}\n\nexport interface ProfileAttributeInput {\n value: string;\n purpose?: string;\n collectedAt?: number;\n}\n\nexport interface SubscriptionTopicInput {\n code: string;\n name?: string;\n channels: string[];\n downstream?: Record<string, string>;\n}\n\n/** A warehouse-native enforcement rule. See POST /v1/discovery/enforcement. */\nexport interface EnforcementRuleInput {\n id: string;\n action: 'mask' | 'restrict';\n /** Select columns at or above this sensitivity. */\n minSensitivity?: 'personal' | 'sensitive';\n /** Select columns in any of these categories. */\n categories?: string[];\n /** mask: roles that still see the raw value. */\n allowRoles?: string[];\n /** restrict: the purpose a subject must have consented to. */\n purpose?: string;\n /** restrict: the column holding the subject id. Default SUBJECT_ID. */\n subjectColumn?: string;\n}\n\nexport interface RegulationSummary {\n id: string;\n jurisdiction: string;\n name: string;\n /** ISO date the law takes (or took) effect. */\n effective: string;\n model: 'opt-in' | 'opt-out';\n obligations: string[];\n /** Primary source — the statute or the regulator. */\n source: string;\n}\n\nexport interface RegulatoryFeedResult {\n /** When a human last checked the dataset against its sources. */\n reviewedAt: string;\n /** Days since that review. */\n ageDays: number;\n /** How long a review is considered good for. */\n reviewAfterDays: number;\n /** True once the review interval has passed — treat the answer with caution. */\n stale: boolean;\n regulations: RegulationSummary[];\n}\n\nexport interface AiInspectInput {\n prompt: string;\n purpose: string;\n model?: string;\n actor?: string;\n direction?: 'prompt' | 'response';\n /** The subject's current permits, when the call concerns an identified person. */\n consent?: Record<string, boolean>;\n}\n\nexport interface AiInspectResult {\n action: 'allow' | 'redact' | 'block';\n /** The text to forward: redacted when `redact`, empty when `block`. */\n prompt: string;\n findings: unknown[];\n reasons: string[];\n policyIds: string[];\n blocked: boolean;\n direction: 'prompt' | 'response';\n}\n\nexport type FulfillmentOp = 'locate' | 'export' | 'erase' | 'optOut';\n\n/** A child org provisioned by a reseller. */\nexport interface ResellerChild {\n id: string;\n name: string;\n status: 'active' | 'suspended';\n delegatedAccess?: boolean;\n /** Per-child DSAR routing override; unset defers to the reseller's policy. */\n dsarRouting?: 'reseller' | 'child';\n createdAt?: number;\n usage?: unknown;\n}\n\nexport interface ResellerChildList {\n children: ResellerChild[];\n /** Pooled usage across the reseller and all its children, when metering is wired. */\n aggregate?: { childOrgs: number; sites: number; monthlyEvents: number; monthlyEventsCap: number | null; overCap: boolean };\n}\n\nexport interface ResellerChildCreate {\n name: string;\n ownerEmail?: string;\n /** legalName, contactEmail, address, dpoContact. */\n controller?: Record<string, unknown>;\n /** Branding overrides, merged onto the reseller's white-label defaults. */\n whiteLabel?: Record<string, unknown>;\n delegatedAccess?: boolean;\n /** Also issue an API key for the new child; it is returned once, as `apiKey`. */\n mintKey?: boolean;\n /** Scopes for that key. Defaults to [\"sites:read\"]. */\n keyScopes?: string[];\n}\n\nexport interface ResellerChildPatch {\n status?: 'active' | 'suspended';\n delegatedAccess?: boolean;\n /** Pin this child's DSAR routing; null clears the override. */\n dsarRouting?: 'reseller' | 'child' | null;\n controller?: Record<string, unknown>;\n}\n\nexport interface ChildKeyInput {\n name?: string;\n scopes?: string[];\n /** Lock the key to these properties of the child org. */\n cbids?: string[];\n}\n\nexport interface PolicyOptions {\n /** Contact email shown in the policy. Falls back to the org controller's, then the owner's. */\n contactEmail?: string;\n /** YYYY-MM-DD. Defaults to today. */\n effectiveDate?: string;\n /** Override the jurisdictions the policy addresses. */\n jurisdictions?: string[];\n}\n\nexport interface AdPersonalizationInput {\n enabled: boolean;\n /** Whether personalised ads start on before the visitor chooses. */\n default?: boolean;\n label?: string;\n}\n\nexport interface SessionAnalysisInput {\n /** A HAR export of the captured session. */\n har?: unknown;\n /** Or the requests directly. */\n requests?: unknown[];\n /** The consent state the session ran under. */\n consent?: Record<string, boolean>;\n /** Whether Global Privacy Control was set. */\n gpc?: boolean;\n}\n\n/**\n * How to drive one system's rights API, as data. This is what makes a connection specific to\n * a platform — Cookie Munch names none of them, so connecting a new system is a profile\n * rather than a release. Paths are relative to `baseUrl`; placeholders are `{email}`,\n * `{subject}`, `{operation}`, `{correlation}` and `{id}`.\n */\nexport interface ExecutorProfile {\n /** How requests carry the credential. Defaults to `Authorization: Bearer {secret}`. */\n auth?: { header: string; format: string };\n /**\n * Optional first step: turn the subject's email into the id this system uses. `noMatch`\n * names how the system says \"no record of that person\" in its body — a sentinel id is\n * never read as a person when it is set.\n */\n subjectLookup?: {\n method: 'GET' | 'POST';\n path: string;\n body?: unknown;\n idPath: string;\n noMatch?: { statusPath: string; values: string[] };\n /** How this system says the person was already removed; closes the task as `already_erased`. Checked before `noMatch`. */\n alreadyErased?: { statusPath: string; values: string[] };\n };\n /** Response header carrying the system's version, recorded in the task log. Default `X-Version`. */\n versionHeader?: string;\n /** How to open a request. An operation left out of `operations` is one this system does not run. */\n open: {\n method: 'POST' | 'PUT';\n path: string;\n body?: unknown;\n operations: { export?: string; erase?: string };\n idPath: string;\n statusPath?: string;\n };\n /** How to ask how it is going. Polling is the truth; a webhook only prompts a check. */\n poll: {\n method?: 'GET';\n path: string;\n statusPath: string;\n resultPath?: string;\n /** Whether `resultPath` holds the bundle itself or a URL to fetch it from. */\n resultIs?: 'inline' | 'url';\n /** Where a date the work is waiting for lives (an erasure in a grace period). */\n scheduledPath?: string;\n };\n /** That system's status words → the three answers a sub-task can have. */\n statuses: { running: string[]; done: string[]; failed: string[] };\n idempotencyHeader?: string;\n /**\n * An identity platform's app grant, revoked on erasure before anything else: look it up by\n * `{email}`, then end it with `{grant}`. A test looks it up but never revokes it.\n */\n grantRevoke?: {\n lookup: { method: 'GET' | 'POST'; path: string; body?: unknown; grantIdPath: string };\n revoke: { method: 'POST' | 'DELETE'; path: string; body?: unknown };\n };\n /** How that system signs its webhooks, if it sends any. */\n webhook?: {\n algorithm: 'hmac-sha256';\n /** Must include `{body}`, or the signature proves nothing about it. */\n signedPayload: string;\n headers: { signature: string; timestamp?: string; id?: string };\n encoding?: 'base64' | 'hex';\n prefix?: string;\n toleranceSeconds?: number;\n timestampUnit?: 'seconds' | 'milliseconds' | 'auto';\n };\n}\n\n/** A system connected to run part of a rights request. Credentials are never included. */\nexport interface DsarExecutor {\n id: string;\n /** The name it answers to in a fulfillment plan, e.g. `identity` or `crm`. */\n system: string;\n profile: ExecutorProfile;\n baseUrl: string;\n keyPrefix: string;\n hasWebhookSecret: boolean;\n /** Open a sub-task automatically when a request reaches fulfilment. */\n auto: boolean;\n /** What the subject reads on this system's notice line when it holds nothing about them. */\n subjectNote?: string;\n createdAt: number;\n /** Where that system should send its completion webhooks. On every connection, secret or not. */\n webhookUrl: string;\n /**\n * The last canary health check (hourly, and on `testExecutor(id, { canary: true })`). null\n * until the first; cleared when the address, profile or key changes.\n */\n health: ExecutorHealth | null;\n /** The preset it was built from; absent when hand-written. */\n preset?: ExecutorPresetName;\n presetVersion?: number;\n /** Newer than `presetVersion`: `upgradeExecutorPreset` applies it. */\n presetLatestVersion?: number;\n /** The non-secret params it was built with, e.g. `{ orgId }`. */\n presetParams?: Record<string, string>;\n /** How the stored address and profile differ from what its preset version builds. Empty: unmodified. */\n presetDiff?: ConfigChange[];\n}\n\n/** A connection's last canary health check. */\nexport interface ExecutorHealth {\n lastCheckedAt: number;\n ok: boolean;\n verdict: string;\n /** e.g. `base_url_redirects`, `credential_unavailable`. */\n code?: string;\n}\n\n/** A preset's name, as `executorPresets()` lists them. Not enumerated here: presets are data. */\nexport type ExecutorPresetName = string;\n\n/**\n * Connect a system from a versioned preset instead of a hand-written profile. Each preset\n * lists the params it takes (`executorPresets()`); a preset waiting on another platform is\n * refused with `preset_waiting_on_platform`.\n */\nexport interface DsarExecutorPresetInput {\n preset: ExecutorPresetName;\n presetVersion?: number;\n secretKey: string;\n system?: string;\n baseUrl?: string;\n orgId?: string;\n atlasIdAppId?: string;\n webhookSecret?: string;\n auto?: boolean;\n cbid?: string;\n}\n\n/** What `upgradeExecutorPreset` takes: a version, a preset to adopt, or params to add. */\nexport interface ExecutorPresetUpgrade {\n preset?: ExecutorPresetName;\n presetVersion?: number;\n orgId?: string;\n atlasIdAppId?: string;\n}\n\n/** The presets, which are usable now, and this org's canary address. */\nexport interface ExecutorPresets {\n canaryEmail: string;\n presets: Array<{\n name: ExecutorPresetName;\n title: string;\n latestVersion: number;\n defaultSystem?: string;\n params: Array<{ name: string; required: boolean; description: string }>;\n available: boolean;\n waitingOn?: { platform: string; items: string[]; operations: string[] };\n }>;\n}\n\n/** What a connection write would do (`?dryRun=1`). Nothing was stored. */\nexport interface DsarExecutorDryRun {\n dryRun: true;\n /** The connection as it would be, credentials excluded. A create has no `id` or `webhookUrl` yet. */\n executor: Partial<DsarExecutor> & Record<string, unknown>;\n /** Each field that would change. */\n diff: ConfigChange[];\n warnings?: Array<{ field: string; reason: string }>;\n}\n\n/** Where a webhook check stopped. */\nexport type WebhookStage = 'configuration' | 'headers' | 'timestamp' | 'prefix' | 'signature';\n\n/** Which key a verified delivery matched: a `whsec_` secret's base64-decoded remainder, or the secret as typed. */\nexport type WebhookKeyForm = 'decoded' | 'raw';\n\n/** Whether a supplied delivery verified (`stage` and `reason` are null when it did). */\nexport interface ExecutorWebhookTestResult {\n verified: boolean;\n stage: WebhookStage | null;\n reason: string | null;\n /** Which key form matched; null when refused. */\n keyForm: WebhookKeyForm | null;\n}\n\n/** One delivery to a connection's webhook. Never the secret, the signature or the body. */\nexport interface ExecutorWebhookDelivery {\n id: string;\n executorId: string;\n at: number;\n verified: boolean;\n stage: WebhookStage | null;\n reason: string | null;\n /** The HTTP status we answered with. */\n status: number;\n /** The first 16 hex characters of SHA-256 over the raw body. */\n bodySha256: string;\n bodyBytes: number;\n /** Which key form matched; null when refused (or logged before this was kept). Never the key. */\n keyForm: WebhookKeyForm | null;\n}\n\nexport interface DsarExecutorInput {\n /** The name this connection answers to in a plan, e.g. `identity` or `crm`. */\n system: string;\n /** The system's public https origin. */\n baseUrl: string;\n /** Its API key. Stored encrypted; never returned. */\n secretKey: string;\n /**\n * The signing secret of its webhook endpoint. Optional — polling closes tasks without it.\n * A `whsec_<base64>` secret (standard base64 or base64url, padded or not) verifies a delivery\n * signed with either the base64-decoded bytes or the literal string; anything else only with\n * its UTF-8 bytes.\n */\n webhookSecret?: string;\n auto?: boolean;\n /** What the subject reads when this system holds nothing about them (≤500 chars, never a key). */\n subjectNote?: string;\n /** How to drive it. */\n profile: ExecutorProfile;\n}\n\nexport type DsarExecutorCreated = DsarExecutor;\n\n/** The fields `updateExecutor` may change. */\nexport interface DsarExecutorPatch {\n profile?: unknown;\n baseUrl?: string;\n secretKey?: string;\n webhookSecret?: string | null;\n auto?: boolean;\n /** null removes it. */\n subjectNote?: string | null;\n}\n\n/** One entry in a fulfilment task's status history. */\nexport interface DsarTaskEvent {\n at: number;\n kind: 'created' | 'changed' | 'version_changed';\n status: 'pending' | 'running' | 'done' | 'failed';\n detail?: string;\n outcome?: DsarTaskOutcome;\n scheduledFor?: number;\n externalRef?: string;\n}\n\n/** What a task's answer means for the person. `exported` only once the export was fetched. */\nexport type DsarTaskOutcome = 'located' | 'exported' | 'erased' | 'no_record' | 'done_without_artifact' | 'not_applicable' | 'already_erased' | 'external_identity';\n\n/** One request made to a connected system on a task's behalf, credentials removed. Kept 30 days. */\nexport interface DsarTaskLogEntry {\n id?: string;\n system?: string;\n step: 'lookup' | 'open' | 'poll' | 'export' | 'grant';\n method: string;\n path: string;\n /** Absent when the system never answered. */\n status?: number;\n latencyMs: number;\n requestHeaders?: Record<string, string>;\n /** The first ~2 KB of what was sent. */\n request?: string;\n /** The first ~2 KB of what came back. */\n response?: string;\n error?: string;\n /** The system's version header on this answer. */\n version?: string;\n /** This answer was a redirect (origin and path; never the query). Only same-origin, method-keeping ones are followed. */\n redirect?: { status: number; location: string };\n at: number;\n}\n\n/** One fulfilment task with its history and its upstream request log. */\nexport interface DsarTask {\n id: string;\n requestId: string;\n system: string;\n operation: FulfillmentOp;\n status: 'pending' | 'running' | 'done' | 'failed';\n detail?: string;\n outcome?: DsarTaskOutcome;\n attempts: number;\n executor?: string;\n externalRef?: string;\n scheduledFor?: number;\n artifacts?: Array<{ kind: string; system: string; ref: string }>;\n createdAt: number;\n updatedAt: number;\n events: DsarTaskEvent[];\n log: DsarTaskLogEntry[];\n /** Versions the system reported while working on this task, in order of change. */\n upstreamVersions?: string[];\n /** The system was redeployed while this task was in flight. */\n versionChanged: boolean;\n}\n\n/** What testing a connection found. Nothing was filed. */\nexport interface ExecutorTestResult {\n system: string;\n /** Did the lookup find the person; null when there is no lookup step or it failed. */\n matched: boolean | null;\n subject?: string;\n operationWord?: string;\n /** The open request a real request would send, when it was not sent. */\n wouldSend?: { method: string; path: string; body: unknown };\n /** One sentence: what a real request would have done. */\n verdict: string;\n ok: boolean;\n /** Why not, when there is a code: `base_url_redirects` — the baseUrl redirected; the verdict names the status and target. */\n code?: string;\n /** The subject's app grant at an identity platform, when the profile has a grant step. Never revoked by a test. */\n grant?: string;\n /** Present when the test used the org canary address, with the address in `email`. */\n canary?: boolean;\n email?: string;\n /** Every upstream answer, as the system gave it (credentials removed). */\n exchanges: DsarTaskLogEntry[];\n}\n\nexport interface ExecutorTestInput {\n /** The address to look up. Required unless `canary` is true. */\n email?: string;\n /** Use this org's canary address — no real person. A canary `locate` also updates `health`. */\n canary?: boolean;\n /** Default `locate`. */\n operation?: 'locate' | 'export' | 'erase';\n /** For `export` only: open a real export and poll it once. An erasure is never sent. */\n open?: boolean;\n}\n\nexport interface DsarTaskExport {\n system: string;\n ref: string;\n /** The bundle as that system returned it. Not stored here. */\n bundle: unknown;\n}\n\n/** A language the banner already has copy for. */\nexport interface SupportedLanguage {\n /** BCP-47 tag, lowercase. Matched exactly, then by language prefix. */\n code: string;\n /** English name. */\n name: string;\n /** The language's name in itself. */\n endonym: string;\n /** Written right to left; the banner flips for these. */\n rtl: boolean;\n /** `bundled` ships in consent.js; `extended` ships in the renderer, fetched when a banner is first drawn. */\n source: 'bundled' | 'extended';\n}\n\nexport interface CookieMunchClient {\n me(): Promise<Identity>;\n /**\n * Languages the banner already speaks. Diff this against the locales your visitors\n * use to see which ones you still need to write copy for in `banner.i18n`.\n */\n languages(): Promise<SupportedLanguage[]>;\n orgs: {\n /**\n * Every organisation on this key's account, each with its effective plan and where that\n * comes from, plus the per-account org cap and the upgrade URL. Needs an unscoped key that\n * is not locked to properties.\n */\n list(): Promise<AccountOrgs>;\n /**\n * Create a sibling organisation owned by the same account — for a separate business of\n * your own. Needs an unscoped key. Not reseller provisioning, which is for your customers.\n */\n create(\n name: string,\n opts?: { mintKey?: boolean; keyScopes?: string[] },\n ): Promise<{ org: { id: string; name: string; role: string }; apiKey?: string; keyError?: string }>;\n };\n sites: {\n list(): Promise<Site[]>;\n create(input: SiteCreate): Promise<Site>;\n get(cbid: string): Promise<Site>;\n /**\n * Rename or move a site: domain, display name, aliases. The cbid and the consent ledger stay; a domain\n * move clears verification. `{ dryRun: true }` answers with the diff and stores nothing.\n */\n update(cbid: string, patch: SiteUpdate, opts?: { dryRun?: boolean }): Promise<SiteUpdateResult>;\n delete(cbid: string): Promise<void>;\n /** Partner platforms granted this site's consent events, and the partners on offer. */\n listPartners(cbid: string): Promise<{ links: PartnerLink[]; partners: PartnerCatalogEntry[] }>;\n /** Grant a partner (Forumulate, Atlas) the site's consent events — no secret is created. Idempotent. */\n linkPartner(cbid: string, input: PartnerLinkCreate, opts?: { dryRun?: boolean }): Promise<{ change: 'created' | 'updated' | 'unchanged'; link: PartnerLink; dryRun?: true }>;\n /** Revoke a partner link: deliveries stop at once. */\n unlinkPartner(cbid: string, linkId: string): Promise<{ link: PartnerLink }>;\n getConfig(cbid: string): Promise<SiteConfig>;\n /**\n * Replaces the whole config — a top-level field you omit reverts to its default. With\n * `{ keep: 'unspecified' }` (`?keep=unspecified`) an omitted top-level field keeps its\n * stored value instead; each field you send is still replaced whole, and one sent as\n * `null` reverts to its default.\n */\n putConfig(cbid: string, config: SiteConfig, opts?: PutConfigOptions): Promise<SiteConfig>;\n /**\n * Changes some of the config — a JSON Merge Patch (RFC 7386): omitted fields keep their\n * stored value, nested objects merge, and `null` removes a field (it takes its default):\n * `{ banner: { i18n: { es: null } } }` drops the Spanish override. Arrays replace whole.\n */\n patchConfig(cbid: string, config: Partial<SiteConfig>): Promise<SiteConfig>;\n /**\n * Preview a config write without saving it: the merged result and a diff. `mode: 'put'`\n * previews a replace (the diff then lists everything that would revert to its default);\n * add `keep: 'unspecified'` to preview a replace that keeps omitted fields.\n */\n dryRunConfig(cbid: string, config: Partial<SiteConfig>, opts?: { mode?: 'patch' | 'put' } & PutConfigOptions): Promise<ConfigDryRun>;\n /**\n * Check a proposed config for consent-button parity — an Accept all pushed harder than\n * Reject all — without saving it. A partial v1 config is merged over the stored one (like\n * PATCH); a v2 design, or `replace: true`, is linted as sent. Needs only `sites:read`.\n */\n lintConfig(cbid: string, config: Partial<SiteConfig>, opts?: { replace?: boolean }): Promise<ButtonParityLint>;\n cookies(cbid: string): Promise<CookieDeclaration>;\n /**\n * Start a cookie crawl. Answers at once with `scanId` and `state: 'queued'`; `wait`\n * (seconds, ≤60) holds the request until the scan is done or failed.\n */\n scan(cbid: string, opts?: { wait?: number }): Promise<ScanStatus>;\n /** The last scan's state. `wait` (seconds, ≤60) holds the request until a running scan settles. */\n scanStatus(cbid: string, opts?: { wait?: number }): Promise<ScanStatus>;\n /**\n * Resolve once the site's scan is done or failed (read `state` and `error`), long-polling\n * the server. At `timeoutMs` (default 120 000) it resolves with the still-running state.\n */\n waitForScan(cbid: string, opts?: { timeoutMs?: number }): Promise<ScanStatus>;\n ab(cbid: string): Promise<AbResult[]>;\n /** Generate the site's privacy and cookie policy, as Markdown. */\n policy(cbid: string, opts?: PolicyOptions): Promise<string>;\n /** Turn on the personalised-ads split on whichever banner form the site uses. */\n setAdPersonalization(cbid: string, input: AdPersonalizationInput): Promise<unknown>;\n /** Analyse a captured session: trackers that fired after opt-out, personal data leaving the page. */\n analyzeSession(cbid: string, input: SessionAnalysisInput): Promise<unknown>;\n /** Which banner design this site uses, or null when none is assigned. */\n banner(cbid: string): Promise<{ bannerId: string | null }>;\n /**\n * Pages where the embed could not load its banner renderer — the host page's CSP or\n * Trusted Types policy refused it, so nobody on that page can be asked. An empty list\n * is the healthy answer.\n */\n blocked(cbid: string): Promise<{ reports: BlockedReport[] }>;\n /**\n * Read a cookie declaration exported from another CMP and translate its categories\n * into ours. Nothing is applied: their vocabulary is not ours, and a cookie in the\n * wrong category is a tag firing against a refusal, so the result is for review.\n */\n importDeclaration(cbid: string, data: string): Promise<ImportedDeclaration>;\n /**\n * Import AND declare: every row that mapped to one of our categories and has a purpose\n * is declared; the rest come back in `skipped`. Already-declared cookies are kept\n * unless `overwrite`.\n */\n applyImport(cbid: string, data: string, opts?: { overwrite?: boolean }): Promise<AppliedImport>;\n /**\n * Declare what one cookie is. Overrides scanned, observed and database classification\n * in the API, the preference modal and `data-cc=\"declaration\"`. A name ending in `*`\n * declares a prefix wildcard. `dryRun` validates and returns the would-be list and a\n * diff without storing anything.\n */\n declareCookie(\n cbid: string,\n name: string,\n input: DeclaredCookieInput,\n opts?: { dryRun?: boolean },\n ): Promise<{ cookie: DeclaredCookie; warnings?: DeclarationWarning[] } & Partial<DeclarationDryRun>>;\n /**\n * Retire a cookie the site renamed or stopped setting: it leaves the banner's list,\n * `data-cc=\"declaration\"` and the public feed, and stays listed for you with `retired`.\n * A name ending in `*` retires every cookie with that prefix. If a crawl or a visit sees\n * it again afterwards it is listed again, flagged `review: 'retired_but_seen'`.\n */\n retireCookie(\n cbid: string,\n name: string,\n input?: { renamedTo?: string; note?: string },\n opts?: { dryRun?: boolean },\n ): Promise<{ cookie: RetiredCookie; matched: string[]; dryRun?: true; retired?: RetiredCookie[] }>;\n /** Un-retire a cookie: it is listed again everywhere. */\n unretireCookie(cbid: string, name: string): Promise<void>;\n /** What un-retiring would leave; nothing is removed. */\n unretireCookie(cbid: string, name: string, opts: { dryRun: true }): Promise<{ dryRun: true; retired: RetiredCookie[] }>;\n /** Every declaration preset (atlas, forumulate, chatly, cookiemunch, referally): params, what it declared here, manifest status. */\n cookiePresets(cbid: string): Promise<{ presets: CookiePreset[] }>;\n /**\n * Apply (or re-sync) a vendor preset: declares what that platform writes, marked\n * `vendor`. Names you declared yourself are kept (`skipped`). `dryRun` stores nothing.\n */\n applyCookiePreset(cbid: string, vendor: CookiePresetVendor, params?: CookiePresetParams, opts?: { dryRun?: boolean }): Promise<CookiePresetResult>;\n /** Remove every declaration a vendor preset made. */\n removeCookiePreset(cbid: string, vendor: CookiePresetVendor, opts?: { dryRun?: boolean }): Promise<{ vendor: CookiePresetVendor; removed: string[]; dryRun?: true; declared?: DeclaredCookie[]; diff?: DeclarationDiff }>;\n /** Remove every stored (scanned or observed) row of this name. A declaration of it stays. */\n deleteObservedCookie(cbid: string, name: string): Promise<void>;\n /** What deleting it would remove; nothing is removed. */\n deleteObservedCookie(cbid: string, name: string, opts: { dryRun: true }): Promise<ObservedPurgeResult>;\n /**\n * Purge observed since T: remove every row a real visit first reported at or after\n * `since` (and before `until`). Crawled rows and declarations stay. ISO-8601 or epoch ms.\n */\n purgeObservedCookies(cbid: string, input: { since: string | number; until?: string | number }, opts?: { dryRun?: boolean }): Promise<ObservedPurgeResult>;\n /** Remove a declaration; the scanned classification applies again. */\n undeclareCookie(cbid: string, name: string): Promise<void>;\n /** What removing a declaration would leave; nothing is removed. */\n undeclareCookie(cbid: string, name: string, opts: { dryRun: true }): Promise<DeclarationDryRun>;\n /**\n * Declare many at once (all-or-nothing). `replace` also removes declarations not listed;\n * `dryRun` answers with what would be stored and a diff, storing nothing.\n */\n declareCookies(\n cbid: string,\n cookies: Array<DeclaredCookieInput & { name: string }>,\n opts?: { replace?: boolean; dryRun?: boolean },\n ): Promise<{ declared: DeclaredCookie[]; saved: number; removed: number; warnings?: DeclarationWarning[]; dryRun?: true; diff?: DeclarationDiff }>;\n snippet(cbid: string, opts?: SnippetOptions): Promise<InstallSnippet>;\n /**\n * Mint a short-lived signed token to see the banner as a visitor in a given region and\n * language sees it — even from a headless browser, and without recording anything.\n */\n previewToken(cbid: string, opts?: PreviewTokenOptions): Promise<PreviewToken>;\n verify(cbid: string, method: 'dns' | 'meta' | 'file' | 'embed'): Promise<VerifyResult>;\n /** Exactly what to publish to prove control of the domain, for each verification method. */\n verifyChallenge(cbid: string): Promise<unknown>;\n /** Create up to 100 sites. Partial success: a bad or duplicate item fails only itself. */\n createBulk(sites: BulkSiteInput[]): Promise<{ results: BulkSiteResult[] }>;\n brand(cbid: string): Promise<BrandExtractionResult>;\n /** Read a site's v2 banner flow (views, categories) plus any lint issues. */\n getFlow(cbid: string): Promise<SiteFlow>;\n /** Apply an ordered batch of structured edit ops; validated + persisted if lint-clean. */\n editFlow(cbid: string, operations: FlowOp[]): Promise<FlowOpResponse>;\n /** Wholesale-replace the flow with a full v2 config; validated + persisted if valid. */\n setFlow(cbid: string, config: Record<string, unknown>): Promise<FlowOpResponse>;\n };\n consent: {\n /**\n * Verify the consent log's tamper-evident hash chain. Each record carries the hash of the\n * one before it, so an edited, reordered or removed record answers `false`. This is the\n * evidence behind the log: proof that what it says today is what it said when written.\n */\n verify(cbid: string): Promise<{ valid: boolean }>;\n stats(cbid: string, query?: RangeQuery): Promise<ConsentDay[]>;\n log(cbid: string, query?: LogQuery): Promise<ConsentLogRow[]>;\n export(cbid: string, query?: RangeQuery): Promise<string>;\n receipt(cbid: string, stamp: string): Promise<SignedReceipt>;\n /** Crypto-erase a subject's consent records by their consent-receipt stamp. Irreversible. */\n eraseSubject(cbid: string, stamp: string): Promise<{ erased: number }>;\n /** Export a data subject's consent records by their consent-receipt stamp (GDPR access/portability). */\n exportSubject(cbid: string, stamp: string): Promise<{ cbid: string; stamp: string; records: unknown[]; count: number }>;\n /** Record a consent decision via the PUBLIC ingest endpoint (POST /api/v1/consent). No `/v1`\n * prefix and no auth is required by the server (the cbid must be a registered site); for\n * server-side / non-browser consent flows. Pass `subjectId` to correlate a subject cross-surface. */\n ingest(input: ConsentIngestInput): Promise<void>;\n };\n dsar: {\n list(): Promise<DsarRequest[]>;\n /**\n * One request, in the same shape `list` returns — note that `create` answers with it\n * wrapped as `{ request }`, so `create(...).request.id` is what you pass here.\n */\n get(id: string): Promise<DsarRequest>;\n create(input: DsarCreate): Promise<{ request: DsarRequest }>;\n /** `opts.messageToSubject` replaces the line the subject reads in the notice — for a rejection, why. */\n advance(id: string, toStatus: DsarStatus, opts?: { messageToSubject?: string }): Promise<{ request: DsarRequest }>;\n /** The subject-facing response notice for a request, as plain text. */\n response(id: string): Promise<string>;\n /**\n * Erase a subject's consent records on one site, for a deletion request past identity\n * verification. Noted on the request. Requires dsar:write and consent:write.\n */\n erase(id: string, cbid: string, stamp: string): Promise<DsarEraseResult>;\n /**\n * A subject's consent records on one site, for an access or portability request past\n * identity verification. Noted on the request. Requires dsar:write and consent:read.\n */\n export(id: string, cbid: string, stamp: string): Promise<DsarExportResult>;\n /**\n * Delete a TEST request (created with `test: true`), with its tasks and their log. A\n * real request cannot be deleted (409 `not_a_test_request`); the deletion is audited.\n */\n delete(id: string): Promise<void>;\n /**\n * Mark a CLOSED request (completed or rejected) as a test, after the fact — for trials\n * filed before `test` existed. An open one is refused (409 `request_not_closed`). Audited\n * as `dsar.marked_test`; afterwards `delete` works on it. `changed` is false on a repeat.\n */\n markTest(id: string): Promise<{ request: DsarRequest; changed: boolean }>;\n };\n vendors: {\n list(): Promise<ScoredVendor[]>;\n create(input: VendorInput): Promise<{ vendor: VendorRecord; risk: RiskScore }>;\n };\n ropa: {\n list(): Promise<RopaEntry[]>;\n create(input: RopaInput): Promise<{ entry: RopaEntry }>;\n /** The org's RoPA (GDPR Art. 30) as CSV. */\n exportCsv(): Promise<string>;\n };\n brandKits: {\n list(): Promise<BrandKit[]>;\n create(input: BrandKitCreate): Promise<{ kit: BrandKit }>;\n delete(id: string): Promise<void>;\n };\n /** The hosted rights page (cookiemunch.net/r/<cbid> or the site's own domain). */\n rightsPage: {\n get(cbid: string): Promise<RightsPageDraft>;\n /** Replace the draft. Nothing is live until publish(). `opts.baseUpdatedAt` refuses to overwrite a newer save (409 draft_conflict). */\n put(cbid: string, page: RightsPageDocument, opts?: RightsPageWriteOptions): Promise<RightsPageDraft>;\n /** Merge into the draft: objects merge, arrays replace, null removes. `opts.baseUpdatedAt` as on put(). */\n patch(cbid: string, patch: Record<string, unknown>, opts?: RightsPageWriteOptions): Promise<RightsPageDraft>;\n /** Validate a write without saving it. */\n dryRun(cbid: string, page: Record<string, unknown>, opts?: { mode?: 'patch' | 'put' }): Promise<RightsPageDryRun>;\n getPublished(cbid: string): Promise<RightsPagePublished>;\n /**\n * Publish the draft; it goes live at once. Pass `opts.baseUpdatedAt` (the draft's updatedAt\n * as you read it) so only the draft you saw goes live: 409 draft_conflict otherwise.\n */\n publish(cbid: string, opts?: RightsPageWriteOptions): Promise<RightsPagePublishResult>;\n versions(cbid: string): Promise<RightsPageVersions>;\n /**\n * Publish an earlier version again; it goes live at once and REPLACES the current draft\n * (unpublished edits are lost). `opts.baseUpdatedAt` refuses with 409 if the draft moved.\n */\n rollback(cbid: string, version: number, opts?: RightsPageWriteOptions): Promise<RightsPagePublishResult>;\n /** Custom hostnames serving the rights page, with the plan allowance. */\n listDomains(cbid: string): Promise<RightsPageDomainList>;\n /** Add a custom hostname (e.g. privacy.example.com); the answer carries the DNS records to create. */\n addDomain(cbid: string, hostname: string): Promise<RightsPageDomain>;\n /** Check it now: DNS while pending, the HTTPS answer once routed. Always the domain as it stands. */\n verifyDomain(cbid: string, hostname: string): Promise<RightsPageDomain>;\n /** Remove it: it stops being served and its certificate is not renewed. */\n removeDomain(cbid: string, hostname: string): Promise<void>;\n };\n /**\n * Identity resolution. Reads are POSTs on purpose: a person's identifiers travel in the\n * body, never a URL where they would land in access logs.\n */\n identity: {\n /** Canonical subject id for any known identifier, else null. */\n resolve(identifiers: Identifier[]): Promise<{ subjectId: string | null }>;\n /** Stitch identifiers into one subject (merging clusters when a link bridges them). */\n link(identifiers: Identifier[]): Promise<{ subjectId: string }>;\n cluster(subjectId: string): Promise<{ subjectId: string; identifiers: Identifier[] }>;\n };\n /** The Permission Vault: a resolved person's current consent state. */\n vault: {\n record(identifiers: Identifier[], decisions: ConsentDecisionInput[]): Promise<{ subjectId: string; permits: unknown[] }>;\n current(identifiers: Identifier[]): Promise<{ purposes: Record<string, boolean> }>;\n permits(identifiers: Identifier[]): Promise<{ permits: unknown[] }>;\n };\n /** The unified profile, with consent enforced at ACTIVATION time. */\n profile: {\n get(identifiers: Identifier[]): Promise<unknown>;\n setAttributes(identifiers: Identifier[], attributes: Record<string, ProfileAttributeInput>): Promise<{ subjectId: string }>;\n /** Attribute values usable for `purpose` — {} when the person hasn't consented to it. */\n activate(identifiers: Identifier[], purpose: string): Promise<{ attributes: Record<string, string> }>;\n };\n /** Marketing preferences as topics x channels. */\n subscriptions: {\n /** The org's topic catalog — what a subject can subscribe TO. */\n topics(): Promise<{ topics: SubscriptionTopicInput[] }>;\n /** Replace the catalog. It is authored whole, not patched. */\n setTopics(topics: SubscriptionTopicInput[]): Promise<{ topics: SubscriptionTopicInput[] }>;\n get(subjectId: string): Promise<unknown>;\n set(subjectId: string, topic: string, channel: string, optedIn: boolean): Promise<unknown>;\n unsubscribeAll(subjectId: string): Promise<unknown>;\n resubscribe(subjectId: string): Promise<unknown>;\n activation(subjectId: string, topics: SubscriptionTopicInput[]): Promise<{ entries: unknown[] }>;\n };\n /** DPIA / PIA / LIA / TIA / AI-impact / vendor assessments. */\n assessments: {\n templates(): Promise<{ templates: unknown[] }>;\n list(): Promise<{ assessments: unknown[] }>;\n start(template: string, subject: string): Promise<unknown>;\n /** The record plus its derived score, completeness and SME routing. */\n get(id: string): Promise<unknown>;\n answer(id: string, questionId: string, value: string | number | boolean): Promise<unknown>;\n /** Fill from the org's latest data map; never overwrites a human answer. */\n autoPopulateFromMap(id: string): Promise<{ assessment: unknown; applied: string[] }>;\n /** Fill from evidence you supply. Never overwrites a human answer; records `source` as provenance. */\n autoPopulate(id: string, evidence: Record<string, unknown>, source?: string): Promise<{ assessment: unknown; applied: string[] }>;\n submit(id: string): Promise<unknown>;\n approve(id: string, by: string): Promise<unknown>;\n reject(id: string, by: string, reason: string): Promise<unknown>;\n };\n /** The data map produced by an in-environment scan (metadata only). */\n discovery: {\n ingestMap(map: unknown): Promise<{ scannedAt: number; systems: number }>;\n getMap(): Promise<unknown>;\n ropaDrafts(): Promise<{ drafts: unknown[] }>;\n evidence(): Promise<{ evidence: Record<string, unknown> }>;\n /** What changed since the last scan, and where the RoPA disagrees with reality. */\n drift(): Promise<{ comparedTo: number | null; sinceLastScan: unknown[]; againstRecord: unknown[] }>;\n /**\n * Plan warehouse-native policy from the latest map. Returns a PLAN with its own\n * revert — nothing is applied to your warehouse by calling this.\n */\n planEnforcement(\n dialect: 'postgres' | 'mysql' | 'snowflake',\n rules: EnforcementRuleInput[],\n opts?: { permitsTable?: string; policyPrefix?: string },\n ): Promise<{ plan: { statements: unknown[]; revert: unknown[]; skipped: unknown[] } }>;\n };\n /**\n * Regulatory intelligence: the curated privacy-law dataset. Every response carries\n * `reviewedAt` — the dataset ships with the product and is only as current as its\n * last review. Not legal advice.\n */\n regulatory: {\n /** The dataset, optionally narrowed to the jurisdictions you operate in. */\n feed(jurisdictions?: string[]): Promise<RegulatoryFeedResult>;\n /** What takes effect within `days` (default 180), soonest first. */\n upcoming(days?: number): Promise<RegulatoryFeedResult>;\n };\n /** AI governance: policy, the inline gateway, inventory and lineage. */\n ai: {\n getPolicy(): Promise<{ policy: unknown; configured: boolean }>;\n setPolicy(policy: unknown): Promise<{ policy: unknown }>;\n /** Enforce consent + policy on a prompt or response. Requires the ai:inspect scope. */\n inspect(input: AiInspectInput): Promise<AiInspectResult>;\n inventory(): Promise<{ systems: unknown[]; shadow: unknown[] }>;\n lineage(): Promise<{ lineage: Record<string, string[]> }>;\n registerSystem(input: { id: string; name: string; provider?: string; purpose?: string }): Promise<unknown>;\n /** The AI systems declared with registerSystem. */\n systems(): Promise<unknown>;\n audit(limit?: number): Promise<{ entries: unknown[] }>;\n };\n /** DSR fulfillment: plan work per system, poll it from an in-VPC agent, report outcomes. */\n fulfillment: {\n sla(): Promise<unknown>;\n plan(requestId: string, systems: Array<{ system: string; operation: FulfillmentOp }>, includeHistorical?: boolean): Promise<{ requestId: string; tasks: unknown[] }>;\n status(requestId: string): Promise<unknown>;\n /** In-VPC agent: poll pending tasks. */\n pendingTasks(limit?: number): Promise<{ tasks: unknown[] }>;\n /** In-VPC agent: report an outcome. Only the outcome crosses the boundary. */\n reportTask(taskId: string, ok: boolean, error?: string): Promise<unknown>;\n /**\n * Systems you have connected that run part of a request themselves — an identity\n * platform holding your users' accounts, for instance. The in-VPC agent covers the\n * systems we cannot reach; these are the ones we can.\n */\n executors(): Promise<DsarExecutor[]>;\n /**\n * Connect one. The secret is stored encrypted and never returned; the response carries\n * the URL to point that system's webhook at.\n */\n connectExecutor(input: DsarExecutorInput | DsarExecutorPresetInput): Promise<DsarExecutorCreated>;\n /**\n * Change a connection in place. Only what you send changes, and the stored credential\n * is kept unless you pass a new `secretKey` — so adding a field to a profile does not\n * cost you the connection or its id.\n */\n updateExecutor(id: string, patch: DsarExecutorPatch): Promise<DsarExecutor>;\n /** What `connectExecutor` would create, validated in full; nothing is stored (`?dryRun=1`). */\n dryRunConnectExecutor(input: DsarExecutorInput | DsarExecutorPresetInput): Promise<DsarExecutorDryRun>;\n /** The versioned presets, whether each is usable now (or waits on another platform), and the org canary address. */\n executorPresets(): Promise<ExecutorPresets>;\n /**\n * Rebuild a connection from its preset's latest version (or `presetVersion`), keeping its id\n * and credentials; or adopt a preset on a hand-written one by naming it. Answers with `diff`.\n */\n upgradeExecutorPreset(id: string, input?: ExecutorPresetUpgrade): Promise<DsarExecutor & { diff: ConfigChange[]; warnings?: Array<{ field: string; reason: string }> }>;\n /** What `upgradeExecutorPreset` would change; nothing is stored (`?dryRun=1`). */\n dryRunUpgradeExecutorPreset(id: string, input?: ExecutorPresetUpgrade): Promise<DsarExecutorDryRun>;\n /** What `updateExecutor` would change, with a diff; the stored connection is untouched (`?dryRun=1`). */\n dryRunUpdateExecutor(id: string, patch: DsarExecutorPatch): Promise<DsarExecutorDryRun>;\n /**\n * Would this webhook delivery verify? Runs the receiver's exact check on headers and a raw\n * body you supply, acts on nothing, and names the stage that failed.\n */\n testExecutorWebhook(id: string, delivery: { headers: Record<string, string>; body: string }): Promise<ExecutorWebhookTestResult>;\n /** The last 50 deliveries to a connection's webhook, newest first, each with its reason. */\n executorWebhookDeliveries(id: string): Promise<ExecutorWebhookDelivery[]>;\n /** Disconnect a system; its open sub-tasks stop being driven. */\n disconnectExecutor(id: string): Promise<void>;\n /** The export bundle a connected system produced, fetched from it on demand. */\n taskExport(requestId: string, taskId: string): Promise<DsarTaskExport>;\n /**\n * One task: its status history (`events`), every request made to a connected system on\n * its behalf (`log`, redacted, 30 days) and whether that system's version changed mid-way.\n */\n task(requestId: string, taskId: string): Promise<DsarTask>;\n /**\n * Try a connection against one email without filing anything: runs the lookup, returns\n * the raw (redacted) upstream answers and what a real request would have sent.\n */\n testExecutor(id: string, input: ExecutorTestInput): Promise<ExecutorTestResult>;\n };\n preferences: {\n list(): Promise<PreferenceItem[]>;\n /** Save/update a subject's named-purpose opt-ins. Returns the persisted record. */\n save(subjectId: string, purposes: Record<string, boolean>): Promise<{ record: PreferenceRecord }>;\n /** One subject's record. A subject with none has empty `purposes`. */\n get(subjectId: string): Promise<PreferenceRecord>;\n };\n members: {\n list(): Promise<Member[]>;\n invite(email: string, role: string): Promise<{ member: { userId: string; email: string; role: string } }>;\n setRole(userId: string, role: string): Promise<{ member: { userId: string; email: string; role: string } }>;\n remove(userId: string): Promise<{ ok: true }>;\n };\n keys: {\n list(): Promise<ApiKeyPrefix[]>;\n /**\n * Issue a new API key; the secret is returned once. Pass `scopes` and/or `cbids` for a\n * least-privilege key — omit both for full access to the whole org.\n */\n issue(input?: ApiKeyIssueInput): Promise<ApiKeyIssued>;\n /** Revoke a key by its prefix. Immediate. */\n revoke(prefix: string): Promise<void>;\n /** Rotate a key: a new secret, returned once, with the same scopes, lock and expiry. The old one stops working. */\n roll(prefix: string): Promise<ApiKeyIssued>;\n /** Rename a key, or replace its scopes or property lock. Only the fields sent change. */\n update(prefix: string, patch: ApiKeyUpdate): Promise<{ ok: true }>;\n /** Key hygiene: keys unused for `unusedDays` (default 90), broad keys, keys expiring within `expiringDays` (default 14). */\n report(opts?: { unusedDays?: number; expiringDays?: number }): Promise<ApiKeyReport>;\n };\n usage(): Promise<Usage>;\n /** Every link a site's privacy wiring depends on, one row each with `ok` and a fix hint. `cbid` may be omitted with one site. */\n integrationHealth(cbid?: string): Promise<IntegrationHealthReport>;\n /**\n * Wire a product's privacy in one call: the site, connected systems, Atlas and Forumulate. Idempotent;\n * `{ dryRun: true }` writes nothing anywhere. No secret appears in the answer.\n */\n provision(input: ProvisionRequest, opts?: { dryRun?: boolean }): Promise<ProvisionResult>;\n /** The key's organisation. Requires an unscoped key that is not property-locked. */\n org: {\n get(): Promise<Org>;\n /** Rename the org or set its logo. Deleting the org is not available through the API. */\n update(patch: OrgUpdate): Promise<Org>;\n /**\n * Your own mail provider for rights-request mail. It carries verification codes once\n * `test` succeeds; every `set` clears that. There is no shared sender: without one, requests\n * are verified by your sign-in provider (`rightsIdentity`) or filed unverified for you to confirm.\n */\n mailProvider: {\n get(): Promise<MailProvider>;\n set(input: MailProviderInput): Promise<MailProvider>;\n remove(): Promise<void>;\n /** Send a test through your provider. `to` defaults to the reply-to, else the from address. */\n test(to?: string): Promise<MailProviderTestResult>;\n };\n /**\n * The default captcha for your rights pages, for every site without its own (`rightsCaptcha`): your own\n * keys (recommended) or off. Without one, rights pages have no captcha.\n */\n rightsCaptcha: {\n get(): Promise<RightsCaptcha>;\n set(input: RightsCaptchaInput): Promise<RightsCaptcha>;\n /** Remove the default; answers the setting now in force (off). */\n remove(): Promise<RightsCaptcha>;\n };\n /** The default sign-in provider rights requests are verified with, for every site without its own. */\n rightsIdentity: {\n get(): Promise<RightsIdentity>;\n set(input: RightsIdentityInput): Promise<RightsIdentity>;\n /** Remove the default; answers the setting now in force. */\n remove(): Promise<RightsIdentity>;\n };\n };\n /** The org's audit log, newest first. API actions appear as `apikey:<prefix>`. */\n audit(limit?: number): Promise<{ entries: AuditEntry[] }>;\n assets: {\n /** Upload a banner image (≤ 1,000,000 bytes) and get its public URL. Requires sites:write. */\n upload(input: AssetUpload): Promise<{ url: string }>;\n /**\n * Delete a stored image. Takes the URL `upload` returned, or just its file name. Only\n * your own org's images are reachable — the folder comes from your API key.\n */\n delete(urlOrFileName: string): Promise<void>;\n };\n /** Provision and manage child orgs. Requires a key with the reseller:* scopes. */\n reseller: {\n list(): Promise<ResellerChildList>;\n /** Provision a child org. With `mintKey`, its first API key is returned once, as `apiKey`. */\n create(input: ResellerChildCreate): Promise<{ child: ResellerChild; apiKey?: string }>;\n get(id: string): Promise<unknown>;\n update(id: string, patch: ResellerChildPatch): Promise<unknown>;\n /**\n * Deprovision a child. Suspends it by default, which is reversible; `{ purge: true }`\n * hard-deletes it and its data, which is not.\n */\n deprovision(id: string, opts?: { purge?: boolean }): Promise<void>;\n listKeys(id: string): Promise<ApiKeyPrefix[]>;\n /** Mint an API key for a child. The secret is returned once. */\n mintKey(id: string, input?: ChildKeyInput): Promise<ApiKeyIssued>;\n revokeKey(id: string, prefix: string): Promise<void>;\n };\n webhooks: {\n list(): Promise<WebhookSubscription[]>;\n create(input: WebhookCreate): Promise<WebhookSubscription>;\n /** Change or pause a subscription. `{ active: false }` pauses without deleting it. */\n update(id: string, patch: WebhookUpdate): Promise<WebhookSubscription>;\n delete(id: string): Promise<void>;\n /** Rotate the signing secret. The new secret is returned once. */\n rollSecret(id: string): Promise<{ secret: string }>;\n /** Send a signed test event now and report what the endpoint answered. */\n test(id: string): Promise<WebhookTestResult>;\n /** Deliveries that failed every retry, newest first. */\n deadLetters(): Promise<{ deadLetters: WebhookDeadLetter[] }>;\n /** Deliver a dead letter again, to the subscription as it is now. */\n replayDeadLetter(id: string): Promise<unknown>;\n };\n /**\n * Account-linked rights requests: a single-use link to the hosted rights page for a person your\n * backend knows is signed in. The page locks their email and skips the code step. Requires dsar:write.\n */\n rightsLinks: {\n create(cbid: string, input: RightsLinkInput): Promise<RightsLink>;\n };\n /**\n * A site's rights-page captcha: your own Turnstile, hCaptcha or reCAPTCHA keys (recommended), or off. A site\n * without its own setting uses the organisation default (`org.rightsCaptcha`), else off. Requires sites:read / sites:write.\n */\n rightsCaptcha: {\n get(cbid: string): Promise<RightsCaptcha>;\n set(cbid: string, input: RightsCaptchaInput): Promise<RightsCaptcha>;\n /** Remove the site's own setting: it inherits the organisation default again. Answers the setting now in force. */\n remove(cbid: string): Promise<RightsCaptcha>;\n };\n /**\n * A site's sign-in provider (Clerk, Atlas or any JWKS issuer): a visitor signed in to your site\n * is verified by their session, with no code mail. A site without its own setting uses the\n * organisation default (`org.rightsIdentity`). Requires sites:read / sites:write.\n */\n rightsIdentity: {\n get(cbid: string): Promise<RightsIdentity>;\n set(cbid: string, input: RightsIdentityInput): Promise<RightsIdentity>;\n /** Remove the site's own setting: it inherits the organisation default again. */\n remove(cbid: string): Promise<RightsIdentity>;\n };\n /** One person's consent across every site in the org, by the subjectId your apps attach. */\n subjects: {\n /** Requires consent:read; not available to property-locked keys. */\n consent(subjectId: string): Promise<{ subjectId: string; records: unknown[]; count: number; sites: string[] }>;\n };\n banners: {\n list(): Promise<BannerSummary[]>;\n create(input: { name: string; json: SiteConfig }): Promise<BannerRecord>;\n get(id: string): Promise<BannerRecord>;\n update(id: string, patch: { name?: string; json?: SiteConfig }): Promise<BannerRecord>;\n delete(id: string): Promise<void>;\n assignments(id: string): Promise<{ cbids: string[] }>;\n setAssignments(id: string, cbids: string[]): Promise<{ cbids: string[] }>;\n publish(id: string): Promise<{ publishedCbids: string[] }>;\n };\n}\n\nfunction qs(query: Record<string, number | string | undefined> | undefined): string {\n if (!query) return '';\n const parts: string[] = [];\n for (const [k, v] of Object.entries(query)) {\n if (v !== undefined) parts.push(`${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`);\n }\n return parts.length ? `?${parts.join('&')}` : '';\n}\n\n/** Widen a typed query interface (RangeQuery/LogQuery) to the plain record qs() wants. */\nfunction toQuery(q: RangeQuery | LogQuery | undefined): Record<string, number | string | undefined> | undefined {\n return q as Record<string, number | string | undefined> | undefined;\n}\n\nexport function createCookieMunch(opts: CookieMunchOptions): CookieMunchClient {\n const base = `${opts.baseUrl.replace(/\\/+$/, '')}/v1`;\n const doFetch = opts.fetch ?? globalThis.fetch;\n if (!doFetch) throw new Error('No fetch available; pass { fetch } in CookieMunchOptions.');\n\n async function request(method: string, path: string, body?: unknown, raw = false): Promise<unknown> {\n const headers: Record<string, string> = { Authorization: `Bearer ${opts.apiKey}`, 'User-Agent': opts.userAgent ?? `cookiemunch-js/${SDK_VERSION}` };\n if (opts.strictUnknownFields && method !== 'GET') headers['Prefer'] = 'strict-unknown-fields';\n const init: RequestInit = { method, headers };\n if (body !== undefined) {\n headers['Content-Type'] = 'application/json';\n init.body = JSON.stringify(body);\n }\n const res = await doFetch(`${base}${path}`, init);\n if (!res.ok) {\n let message = `request failed with status ${res.status}`;\n let code: string | undefined;\n let parsed: unknown;\n try {\n const data = (await res.json()) as { error?: string; code?: string };\n parsed = data;\n if (typeof data.error === 'string') message = data.error;\n if (typeof data.code === 'string') code = data.code;\n } catch {\n /* non-JSON error body — keep the default message */\n }\n throw new CookieMunchApiError(res.status, message, code, parsed);\n }\n if (res.status === 204) return undefined;\n if (raw) return res.text();\n const contentType = res.headers.get('content-type') ?? '';\n return contentType.includes('application/json') ? res.json() : res.text();\n }\n\n const get = (p: string) => request('GET', p);\n const enc = encodeURIComponent;\n\n return {\n me: () => get('/me') as Promise<Identity>,\n languages: () => get('/languages') as Promise<SupportedLanguage[]>,\n orgs: {\n list: () => get('/orgs') as Promise<AccountOrgs>,\n create: (name, opts) =>\n request('POST', '/orgs', { name, ...(opts ?? {}) }) as Promise<{\n org: { id: string; name: string; role: string };\n apiKey?: string;\n keyError?: string;\n }>,\n },\n sites: {\n list: () => get('/sites') as Promise<Site[]>,\n create: (input) => request('POST', '/sites', input) as Promise<Site>,\n get: (cbid) => get(`/sites/${enc(cbid)}`) as Promise<Site>,\n update: (cbid, patch, o) => request('PATCH', `/sites/${enc(cbid)}${qs({ dryRun: o?.dryRun ? '1' : undefined })}`, patch) as Promise<SiteUpdateResult>,\n delete: async (cbid) => {\n await request('DELETE', `/sites/${enc(cbid)}`);\n },\n listPartners: (cbid) => get(`/sites/${enc(cbid)}/partners`) as Promise<{ links: PartnerLink[]; partners: PartnerCatalogEntry[] }>,\n linkPartner: (cbid, input, o) =>\n request('POST', `/sites/${enc(cbid)}/partners${qs({ dryRun: o?.dryRun ? '1' : undefined })}`, input) as Promise<{ change: 'created' | 'updated' | 'unchanged'; link: PartnerLink; dryRun?: true }>,\n unlinkPartner: (cbid, linkId) => request('DELETE', `/sites/${enc(cbid)}/partners/${enc(linkId)}`) as Promise<{ link: PartnerLink }>,\n getConfig: (cbid) => get(`/sites/${enc(cbid)}/config`) as Promise<SiteConfig>,\n putConfig: (cbid, config, opts) =>\n request('PUT', `/sites/${enc(cbid)}/config${qs({ keep: opts?.keep })}`, config) as Promise<SiteConfig>,\n patchConfig: (cbid, config) => request('PATCH', `/sites/${enc(cbid)}/config`, config) as Promise<SiteConfig>,\n dryRunConfig: (cbid, config, opts) =>\n request(\n opts?.mode === 'put' ? 'PUT' : 'PATCH',\n `/sites/${enc(cbid)}/config${qs({ dryRun: 1, keep: opts?.mode === 'put' ? opts.keep : undefined })}`,\n config,\n ) as Promise<ConfigDryRun>,\n lintConfig: (cbid, config, opts) =>\n request('POST', `/sites/${enc(cbid)}/config/lint${qs({ replace: opts?.replace ? 1 : undefined })}`, config) as Promise<ButtonParityLint>,\n cookies: (cbid) => get(`/sites/${enc(cbid)}/cookies`) as Promise<CookieDeclaration>,\n scan: (cbid, o) => request('POST', `/sites/${enc(cbid)}/scan${qs({ wait: o?.wait })}`) as Promise<ScanStatus>,\n scanStatus: (cbid, o) => get(`/sites/${enc(cbid)}/scan${qs({ wait: o?.wait })}`) as Promise<ScanStatus>,\n waitForScan: async (cbid, o) => {\n const until = Date.now() + (o?.timeoutMs ?? 120_000);\n for (;;) {\n const left = until - Date.now();\n const asked = Math.max(0, Math.min(60_000, left));\n const t0 = Date.now();\n const s = (await get(`/sites/${enc(cbid)}/scan${qs({ wait: asked / 1000 })}`)) as ScanStatus;\n if (s.status !== 'scanning' || until - Date.now() <= 0) return s;\n // A server that answered sooner than asked (one without long-polling) is not hammered.\n const early = Math.min(1000, until - Date.now()) - (Date.now() - t0);\n if (early > 0) await new Promise((r) => setTimeout(r, early));\n }\n },\n ab: (cbid) => get(`/sites/${enc(cbid)}/ab`) as Promise<AbResult[]>,\n policy: (cbid, o) =>\n request(\n 'GET',\n `/sites/${enc(cbid)}/policy${qs({ contactEmail: o?.contactEmail, effectiveDate: o?.effectiveDate, jurisdictions: o?.jurisdictions?.join(',') })}`,\n undefined,\n true,\n ) as Promise<string>,\n setAdPersonalization: (cbid, input) => request('POST', `/sites/${enc(cbid)}/elements/ad-personalization`, input),\n analyzeSession: (cbid, input) => request('POST', `/sites/${enc(cbid)}/sentry`, input),\n banner: (cbid) => get(`/sites/${enc(cbid)}/banner`) as Promise<{ bannerId: string | null }>,\n blocked: (cbid) => get(`/sites/${enc(cbid)}/blocked`) as Promise<{ reports: BlockedReport[] }>,\n importDeclaration: (cbid, data) => request('POST', `/sites/${enc(cbid)}/import`, { data }) as Promise<ImportedDeclaration>,\n applyImport: (cbid, data, o) =>\n request('POST', `/sites/${enc(cbid)}/import${qs({ apply: '1', overwrite: o?.overwrite ? '1' : undefined })}`, { data }) as Promise<AppliedImport>,\n declareCookie: (cbid, name, input, o) =>\n request('PUT', `/sites/${enc(cbid)}/cookies/${enc(name)}${qs({ dryRun: o?.dryRun ? '1' : undefined })}`, input) as Promise<\n { cookie: DeclaredCookie; warnings?: DeclarationWarning[] } & Partial<DeclarationDryRun>\n >,\n retireCookie: (cbid, name, input, o) =>\n request('POST', `/sites/${enc(cbid)}/cookies/${enc(name)}/retire${qs({ dryRun: o?.dryRun ? '1' : undefined })}`, input ?? {}) as Promise<{\n cookie: RetiredCookie;\n matched: string[];\n dryRun?: true;\n retired?: RetiredCookie[];\n }>,\n unretireCookie: (async (cbid: string, name: string, o?: { dryRun?: boolean }) => {\n const out = await request('DELETE', `/sites/${enc(cbid)}/cookies/${enc(name)}/retire${qs({ dryRun: o?.dryRun ? '1' : undefined })}`);\n return o?.dryRun ? out : undefined;\n }) as CookieMunchClient['sites']['unretireCookie'],\n cookiePresets: (cbid) => get(`/sites/${enc(cbid)}/cookies/presets`) as Promise<{ presets: CookiePreset[] }>,\n applyCookiePreset: (cbid, vendor, params, o) =>\n request('PUT', `/sites/${enc(cbid)}/cookies/presets/${enc(vendor)}${qs({ dryRun: o?.dryRun ? '1' : undefined })}`, params ?? {}) as Promise<CookiePresetResult>,\n removeCookiePreset: (cbid, vendor, o) =>\n request('DELETE', `/sites/${enc(cbid)}/cookies/presets/${enc(vendor)}${qs({ dryRun: o?.dryRun ? '1' : undefined })}`) as Promise<{\n vendor: CookiePresetVendor;\n removed: string[];\n dryRun?: true;\n declared?: DeclaredCookie[];\n diff?: DeclarationDiff;\n }>,\n deleteObservedCookie: (async (cbid: string, name: string, o?: { dryRun?: boolean }) => {\n const out = await request('DELETE', `/sites/${enc(cbid)}/observed-cookies/${enc(name)}${qs({ dryRun: o?.dryRun ? '1' : undefined })}`);\n return o?.dryRun ? (out as ObservedPurgeResult) : undefined;\n }) as CookieMunchClient['sites']['deleteObservedCookie'],\n purgeObservedCookies: (cbid, input, o) =>\n request('POST', `/sites/${enc(cbid)}/observed-cookies/purge${qs({ dryRun: o?.dryRun ? '1' : undefined })}`, input) as Promise<ObservedPurgeResult>,\n undeclareCookie: (async (cbid: string, name: string, o?: { dryRun?: boolean }) => {\n const out = await request('DELETE', `/sites/${enc(cbid)}/cookies/${enc(name)}${qs({ dryRun: o?.dryRun ? '1' : undefined })}`);\n return o?.dryRun ? (out as DeclarationDryRun) : undefined;\n }) as CookieMunchClient['sites']['undeclareCookie'],\n declareCookies: (cbid, cookies, o) =>\n request('PUT', `/sites/${enc(cbid)}/cookies${qs({ dryRun: o?.dryRun ? '1' : undefined })}`, { cookies, ...(o?.replace ? { replace: true } : {}) }) as Promise<{\n declared: DeclaredCookie[];\n saved: number;\n removed: number;\n warnings?: DeclarationWarning[];\n dryRun?: true;\n diff?: DeclarationDiff;\n }>,\n snippet: (cbid, opts) =>\n get(`/sites/${enc(cbid)}/snippet${qs({ blockingmode: opts?.blockingMode, culture: opts?.culture })}`) as Promise<InstallSnippet>,\n previewToken: (cbid, o) => request('POST', `/sites/${enc(cbid)}/preview-token`, o ?? {}) as Promise<PreviewToken>,\n verify: (cbid, method) => request('POST', `/sites/${enc(cbid)}/verify`, { method }) as Promise<VerifyResult>,\n verifyChallenge: (cbid) => get(`/sites/${enc(cbid)}/verify/challenge`),\n createBulk: (sites) => request('POST', '/sites/bulk', { sites }) as Promise<{ results: BulkSiteResult[] }>,\n brand: (cbid) => request('POST', `/sites/${enc(cbid)}/brand`, {}) as Promise<BrandExtractionResult>,\n getFlow: (cbid) => get(`/sites/${enc(cbid)}/flow`) as Promise<SiteFlow>,\n editFlow: (cbid, operations) =>\n request('POST', `/sites/${enc(cbid)}/flow/ops`, { operations }) as Promise<FlowOpResponse>,\n setFlow: (cbid, config) => request('PUT', `/sites/${enc(cbid)}/flow`, config) as Promise<FlowOpResponse>,\n },\n consent: {\n verify: (cbid) => get(`/sites/${enc(cbid)}/consent/verify`) as Promise<{ valid: boolean }>,\n stats: (cbid, query) => get(`/sites/${enc(cbid)}/consent/stats${qs(toQuery(query))}`) as Promise<ConsentDay[]>,\n log: (cbid, query) => get(`/sites/${enc(cbid)}/consent/log${qs(toQuery(query))}`) as Promise<ConsentLogRow[]>,\n export: (cbid, query) => request('GET', `/sites/${enc(cbid)}/consent/export${qs(toQuery(query))}`, undefined, true) as Promise<string>,\n receipt: (cbid, stamp) => get(`/sites/${enc(cbid)}/receipt/${enc(stamp)}`) as Promise<SignedReceipt>,\n eraseSubject: (cbid, stamp) =>\n request('POST', `/sites/${enc(cbid)}/erase-consent`, { stamp }) as Promise<{ erased: number }>,\n exportSubject: (cbid, stamp) =>\n request('GET', `/sites/${enc(cbid)}/subject-export?stamp=${enc(stamp)}`) as Promise<{ cbid: string; stamp: string; records: unknown[]; count: number }>,\n ingest: async (input) => {\n // The public ingest endpoint lives under /api/v1, not the /v1 dev-API prefix,\n // so it is called directly rather than via `request` (which prepends /v1).\n const res = await doFetch(`${opts.baseUrl.replace(/\\/+$/, '')}/api/v1/consent`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(input),\n });\n if (!res.ok) {\n let message = `consent ingest failed with status ${res.status}`;\n let code: string | undefined;\n try {\n const data = (await res.json()) as { error?: string; code?: string };\n if (typeof data.error === 'string') message = data.error;\n if (typeof data.code === 'string') code = data.code;\n } catch {\n /* non-JSON error body — keep the default message */\n }\n throw new CookieMunchApiError(res.status, message, code);\n }\n },\n },\n dsar: {\n list: () => get('/dsar') as Promise<DsarRequest[]>,\n get: (id) => get(`/dsar/${enc(id)}`) as Promise<DsarRequest>,\n create: (input) => request('POST', '/dsar', input) as Promise<{ request: DsarRequest }>,\n advance: (id, toStatus, opts) =>\n request('POST', `/dsar/${enc(id)}/advance`, { toStatus, ...(opts?.messageToSubject !== undefined ? { messageToSubject: opts.messageToSubject } : {}) }) as Promise<{ request: DsarRequest }>,\n response: (id) => request('GET', `/dsar/${enc(id)}/response`, undefined, true) as Promise<string>,\n erase: (id, cbid, stamp) => request('POST', `/dsar/${enc(id)}/erase`, { cbid, stamp }) as Promise<DsarEraseResult>,\n export: (id, cbid, stamp) => request('POST', `/dsar/${enc(id)}/export`, { cbid, stamp }) as Promise<DsarExportResult>,\n delete: async (id) => {\n await request('DELETE', `/dsar/${enc(id)}`);\n },\n markTest: (id) => request('POST', `/dsar/${enc(id)}/mark-test`) as Promise<{ request: DsarRequest; changed: boolean }>,\n },\n vendors: {\n list: () => get('/vendors') as Promise<ScoredVendor[]>,\n create: (input) => request('POST', '/vendors', input) as Promise<{ vendor: VendorRecord; risk: RiskScore }>,\n },\n ropa: {\n list: () => get('/ropa') as Promise<RopaEntry[]>,\n create: (input) => request('POST', '/ropa', input) as Promise<{ entry: RopaEntry }>,\n exportCsv: () => request('GET', '/ropa/export.csv', undefined, true) as Promise<string>,\n },\n brandKits: {\n list: () => get('/brand-kits') as Promise<BrandKit[]>,\n create: (input) => request('POST', '/brand-kits', input) as Promise<{ kit: BrandKit }>,\n delete: async (id) => {\n await request('DELETE', `/brand-kits/${enc(id)}`);\n },\n },\n rightsPage: {\n get: (cbid) => get(`/sites/${enc(cbid)}/rights-page`) as Promise<RightsPageDraft>,\n put: (cbid, page, opts) => request('PUT', `/sites/${enc(cbid)}/rights-page`, withBase(page, opts)) as Promise<RightsPageDraft>,\n patch: (cbid, patch, opts) => request('PATCH', `/sites/${enc(cbid)}/rights-page`, withBase(patch, opts)) as Promise<RightsPageDraft>,\n dryRun: (cbid, page, opts) =>\n request(opts?.mode === 'put' ? 'PUT' : 'PATCH', `/sites/${enc(cbid)}/rights-page?dryRun=1`, page) as Promise<RightsPageDryRun>,\n getPublished: (cbid) => get(`/sites/${enc(cbid)}/rights-page/published`) as Promise<RightsPagePublished>,\n publish: (cbid, opts) => request('POST', `/sites/${enc(cbid)}/rights-page/publish`, withBase({}, opts)) as Promise<RightsPagePublishResult>,\n versions: (cbid) => get(`/sites/${enc(cbid)}/rights-page/versions`) as Promise<RightsPageVersions>,\n rollback: (cbid, version, opts) => request('POST', `/sites/${enc(cbid)}/rights-page/rollback`, withBase({ version }, opts)) as Promise<RightsPagePublishResult>,\n listDomains: (cbid) => get(`/sites/${enc(cbid)}/rights-page/domains`) as Promise<RightsPageDomainList>,\n addDomain: (cbid, hostname) => request('POST', `/sites/${enc(cbid)}/rights-page/domains`, { hostname }) as Promise<RightsPageDomain>,\n verifyDomain: (cbid, hostname) =>\n request('POST', `/sites/${enc(cbid)}/rights-page/domains/${enc(hostname)}/verify`) as Promise<RightsPageDomain>,\n removeDomain: async (cbid, hostname) => {\n await request('DELETE', `/sites/${enc(cbid)}/rights-page/domains/${enc(hostname)}`);\n },\n },\n identity: {\n resolve: (identifiers) => request('POST', '/identity/resolve', { identifiers }) as Promise<{ subjectId: string | null }>,\n link: (identifiers) => request('POST', '/identity/link', { identifiers }) as Promise<{ subjectId: string }>,\n cluster: (subjectId) => get(`/identity/${enc(subjectId)}`) as Promise<{ subjectId: string; identifiers: Identifier[] }>,\n },\n vault: {\n record: (identifiers, decisions) => request('POST', '/vault/record', { identifiers, decisions }) as Promise<{ subjectId: string; permits: unknown[] }>,\n current: (identifiers) => request('POST', '/vault/current', { identifiers }) as Promise<{ purposes: Record<string, boolean> }>,\n permits: (identifiers) => request('POST', '/vault/permits', { identifiers }) as Promise<{ permits: unknown[] }>,\n },\n profile: {\n get: (identifiers) => request('POST', '/profile/get', { identifiers }),\n setAttributes: (identifiers, attributes) => request('POST', '/profile/attributes', { identifiers, attributes }) as Promise<{ subjectId: string }>,\n activate: (identifiers, purpose) => request('POST', '/profile/activate', { identifiers, purpose }) as Promise<{ attributes: Record<string, string> }>,\n },\n subscriptions: {\n topics: () => get('/subscriptions/topics') as Promise<{ topics: SubscriptionTopicInput[] }>,\n setTopics: (topics) => request('PUT', '/subscriptions/topics', { topics }) as Promise<{ topics: SubscriptionTopicInput[] }>,\n get: (subjectId) => get(`/subscriptions/${enc(subjectId)}`),\n set: (subjectId, topic, channel, optedIn) => request('PUT', `/subscriptions/${enc(subjectId)}`, { topic, channel, optedIn }),\n unsubscribeAll: (subjectId) => request('POST', `/subscriptions/${enc(subjectId)}/unsubscribe-all`),\n resubscribe: (subjectId) => request('POST', `/subscriptions/${enc(subjectId)}/resubscribe`),\n activation: (subjectId, topics) => request('POST', `/subscriptions/${enc(subjectId)}/activation`, { topics }) as Promise<{ entries: unknown[] }>,\n },\n assessments: {\n templates: () => get('/assessments/templates') as Promise<{ templates: unknown[] }>,\n list: () => get('/assessments') as Promise<{ assessments: unknown[] }>,\n start: (template, subject) => request('POST', '/assessments', { template, subject }),\n get: (id) => get(`/assessments/${enc(id)}`),\n answer: (id, questionId, value) => request('POST', `/assessments/${enc(id)}/answer`, { questionId, value }),\n autoPopulateFromMap: (id) => request('POST', `/assessments/${enc(id)}/autopopulate-from-map`) as Promise<{ assessment: unknown; applied: string[] }>,\n autoPopulate: (id, evidence, source) =>\n request('POST', `/assessments/${enc(id)}/autopopulate`, { evidence, ...(source !== undefined ? { source } : {}) }) as Promise<{ assessment: unknown; applied: string[] }>,\n submit: (id) => request('POST', `/assessments/${enc(id)}/submit`),\n approve: (id, by) => request('POST', `/assessments/${enc(id)}/approve`, { by }),\n reject: (id, by, reason) => request('POST', `/assessments/${enc(id)}/reject`, { by, reason }),\n },\n discovery: {\n ingestMap: (map) => request('POST', '/discovery/map', { map }) as Promise<{ scannedAt: number; systems: number }>,\n getMap: () => get('/discovery/map'),\n ropaDrafts: () => get('/discovery/ropa-drafts') as Promise<{ drafts: unknown[] }>,\n evidence: () => get('/discovery/evidence') as Promise<{ evidence: Record<string, unknown> }>,\n drift: () => get('/discovery/drift') as Promise<{ comparedTo: number | null; sinceLastScan: unknown[]; againstRecord: unknown[] }>,\n planEnforcement: (dialect, rules, opts) =>\n request('POST', '/discovery/enforcement', {\n dialect,\n rules,\n ...(opts?.permitsTable !== undefined ? { permitsTable: opts.permitsTable } : {}),\n ...(opts?.policyPrefix !== undefined ? { policyPrefix: opts.policyPrefix } : {}),\n }) as Promise<{ plan: { statements: unknown[]; revert: unknown[]; skipped: unknown[] } }>,\n },\n regulatory: {\n feed: (jurisdictions) =>\n get(`/regulatory/feed${qs({ jurisdictions: jurisdictions?.join(',') })}`) as Promise<RegulatoryFeedResult>,\n upcoming: (days) => get(`/regulatory/upcoming${qs({ days })}`) as Promise<RegulatoryFeedResult>,\n },\n ai: {\n getPolicy: () => get('/ai/policy') as Promise<{ policy: unknown; configured: boolean }>,\n setPolicy: (policy) => request('PUT', '/ai/policy', { policy }) as Promise<{ policy: unknown }>,\n inspect: (input) => request('POST', '/ai/inspect', input) as Promise<AiInspectResult>,\n inventory: () => get('/ai/inventory') as Promise<{ systems: unknown[]; shadow: unknown[] }>,\n lineage: () => get('/ai/lineage') as Promise<{ lineage: Record<string, string[]> }>,\n registerSystem: (input) => request('POST', '/ai/systems', input),\n systems: () => get('/ai/systems'),\n audit: (limit) => get(`/ai/audit${qs({ limit })}`) as Promise<{ entries: unknown[] }>,\n },\n fulfillment: {\n sla: () => get('/dsar/sla'),\n plan: (requestId, systems, includeHistorical) =>\n request('POST', `/dsar/${enc(requestId)}/plan`, { systems, ...(includeHistorical ? { includeHistorical: true } : {}) }) as Promise<{ requestId: string; tasks: unknown[] }>,\n status: (requestId) => get(`/dsar/${enc(requestId)}/fulfillment`),\n pendingTasks: (limit) => get(`/dsar/agent/tasks${qs({ limit })}`) as Promise<{ tasks: unknown[] }>,\n reportTask: (taskId, ok, error) => request('POST', `/dsar/agent/tasks/${enc(taskId)}/result`, { ok, ...(error !== undefined ? { error } : {}) }),\n executors: () => get('/dsar/executors') as Promise<DsarExecutor[]>,\n connectExecutor: (input) => request('POST', '/dsar/executors', input) as Promise<DsarExecutorCreated>,\n updateExecutor: (id, patch) => request('PATCH', `/dsar/executors/${enc(id)}`, patch) as Promise<DsarExecutor>,\n dryRunConnectExecutor: (input) => request('POST', '/dsar/executors?dryRun=1', input) as Promise<DsarExecutorDryRun>,\n executorPresets: () => get('/dsar/executors/presets') as Promise<ExecutorPresets>,\n upgradeExecutorPreset: (id, input = {}) =>\n request('POST', `/dsar/executors/${enc(id)}/upgrade-preset`, input) as Promise<DsarExecutor & { diff: ConfigChange[] }>,\n dryRunUpgradeExecutorPreset: (id, input = {}) =>\n request('POST', `/dsar/executors/${enc(id)}/upgrade-preset?dryRun=1`, input) as Promise<DsarExecutorDryRun>,\n dryRunUpdateExecutor: (id, patch) => request('PATCH', `/dsar/executors/${enc(id)}?dryRun=1`, patch) as Promise<DsarExecutorDryRun>,\n testExecutorWebhook: (id, delivery) =>\n request('POST', `/dsar/executors/${enc(id)}/webhook/test`, delivery) as Promise<ExecutorWebhookTestResult>,\n executorWebhookDeliveries: (id) => get(`/dsar/executors/${enc(id)}/webhook/deliveries`) as Promise<ExecutorWebhookDelivery[]>,\n disconnectExecutor: async (id) => {\n await request('DELETE', `/dsar/executors/${enc(id)}`);\n },\n taskExport: (requestId, taskId) => get(`/dsar/${enc(requestId)}/tasks/${enc(taskId)}/export`) as Promise<DsarTaskExport>,\n task: (requestId, taskId) => get(`/dsar/${enc(requestId)}/tasks/${enc(taskId)}`) as Promise<DsarTask>,\n testExecutor: (id, input) => request('POST', `/dsar/executors/${enc(id)}/test`, input) as Promise<ExecutorTestResult>,\n },\n preferences: {\n list: () => get('/preferences') as Promise<PreferenceItem[]>,\n save: (subjectId, purposes) => request('POST', '/preferences', { subjectId, purposes }) as Promise<{ record: PreferenceRecord }>,\n get: (subjectId) => get(`/preferences/${enc(subjectId)}`) as Promise<PreferenceRecord>,\n },\n members: {\n list: () => get('/members') as Promise<Member[]>,\n invite: (email, role) =>\n request('POST', '/members', { email, role }) as Promise<{ member: { userId: string; email: string; role: string } }>,\n setRole: (userId, role) =>\n request('PATCH', `/members/${enc(userId)}`, { role }) as Promise<{ member: { userId: string; email: string; role: string } }>,\n remove: (userId) => request('DELETE', `/members/${enc(userId)}`) as Promise<{ ok: true }>,\n },\n keys: {\n list: () => get('/keys') as Promise<ApiKeyPrefix[]>,\n issue: (input) => request('POST', '/keys', input ?? {}) as Promise<ApiKeyIssued>,\n revoke: async (prefix) => {\n await request('DELETE', `/keys/${enc(prefix)}`);\n },\n roll: (prefix) => request('POST', `/keys/${enc(prefix)}/roll`) as Promise<ApiKeyIssued>,\n update: (prefix, patch) => request('PATCH', `/keys/${enc(prefix)}`, patch) as Promise<{ ok: true }>,\n report: (o) => get(`/keys/report${qs({ unusedDays: o?.unusedDays, expiringDays: o?.expiringDays })}`) as Promise<ApiKeyReport>,\n },\n usage: () => get('/usage') as Promise<Usage>,\n integrationHealth: (cbid) => get(`/integration-health${qs({ cbid })}`) as Promise<IntegrationHealthReport>,\n provision: (input, o) => request('POST', `/provision${qs({ dryRun: o?.dryRun ? '1' : undefined })}`, input) as Promise<ProvisionResult>,\n org: {\n get: () => get('/org') as Promise<Org>,\n update: (patch) => request('PATCH', '/org', patch) as Promise<Org>,\n mailProvider: {\n get: () => get('/org/mail-provider') as Promise<MailProvider>,\n set: (input) => request('PUT', '/org/mail-provider', input) as Promise<MailProvider>,\n remove: async () => {\n await request('DELETE', '/org/mail-provider');\n },\n test: (to) => request('POST', '/org/mail-provider/test', to ? { to } : {}) as Promise<MailProviderTestResult>,\n },\n rightsCaptcha: {\n get: () => get('/org/rights-captcha') as Promise<RightsCaptcha>,\n set: (input) => request('PUT', '/org/rights-captcha', input) as Promise<RightsCaptcha>,\n remove: () => request('DELETE', '/org/rights-captcha') as Promise<RightsCaptcha>,\n },\n rightsIdentity: {\n get: () => get('/org/rights-identity') as Promise<RightsIdentity>,\n set: (input) => request('PUT', '/org/rights-identity', input) as Promise<RightsIdentity>,\n remove: () => request('DELETE', '/org/rights-identity') as Promise<RightsIdentity>,\n },\n },\n audit: (limit) => get(`/audit${qs({ limit })}`) as Promise<{ entries: AuditEntry[] }>,\n assets: {\n upload: (input) => request('POST', '/assets', input) as Promise<{ url: string }>,\n delete: async (urlOrFileName) => {\n const name = urlOrFileName.split('/').pop() ?? urlOrFileName;\n await request('DELETE', `/assets/${enc(name)}`);\n },\n },\n reseller: {\n list: () => get('/reseller/customers') as Promise<ResellerChildList>,\n create: (input) => request('POST', '/reseller/customers', input) as Promise<{ child: ResellerChild; apiKey?: string }>,\n get: (id) => get(`/reseller/customers/${enc(id)}`),\n update: (id, patch) => request('PATCH', `/reseller/customers/${enc(id)}`, patch),\n deprovision: async (id, o) => {\n await request('DELETE', `/reseller/customers/${enc(id)}${qs({ purge: o?.purge ? 'true' : undefined })}`);\n },\n listKeys: (id) => get(`/reseller/customers/${enc(id)}/keys`) as Promise<ApiKeyPrefix[]>,\n mintKey: (id, input) => request('POST', `/reseller/customers/${enc(id)}/keys`, input ?? {}) as Promise<ApiKeyIssued>,\n revokeKey: async (id, prefix) => {\n await request('DELETE', `/reseller/customers/${enc(id)}/keys/${enc(prefix)}`);\n },\n },\n webhooks: {\n list: () => get('/webhooks') as Promise<WebhookSubscription[]>,\n create: (input) => request('POST', '/webhooks', input) as Promise<WebhookSubscription>,\n update: (id, patch) => request('PATCH', `/webhooks/${enc(id)}`, patch) as Promise<WebhookSubscription>,\n delete: async (id) => {\n await request('DELETE', `/webhooks/${enc(id)}`);\n },\n rollSecret: (id) => request('POST', `/webhooks/${enc(id)}/roll`) as Promise<{ secret: string }>,\n test: (id) => request('POST', `/webhooks/${enc(id)}/test`) as Promise<WebhookTestResult>,\n deadLetters: () => get('/webhooks/dead-letters') as Promise<{ deadLetters: WebhookDeadLetter[] }>,\n replayDeadLetter: (id) => request('POST', `/webhooks/dead-letters/${enc(id)}/replay`),\n },\n rightsLinks: {\n create: (cbid, input) => request('POST', `/sites/${enc(cbid)}/rights-links`, input) as Promise<RightsLink>,\n },\n rightsCaptcha: {\n get: (cbid) => get(`/sites/${enc(cbid)}/rights-captcha`) as Promise<RightsCaptcha>,\n set: (cbid, input) => request('PUT', `/sites/${enc(cbid)}/rights-captcha`, input) as Promise<RightsCaptcha>,\n remove: (cbid) => request('DELETE', `/sites/${enc(cbid)}/rights-captcha`) as Promise<RightsCaptcha>,\n },\n rightsIdentity: {\n get: (cbid) => get(`/sites/${enc(cbid)}/rights-identity`) as Promise<RightsIdentity>,\n set: (cbid, input) => request('PUT', `/sites/${enc(cbid)}/rights-identity`, input) as Promise<RightsIdentity>,\n remove: (cbid) => request('DELETE', `/sites/${enc(cbid)}/rights-identity`) as Promise<RightsIdentity>,\n },\n subjects: {\n consent: (subjectId) =>\n get(`/subjects/${enc(subjectId)}/consent`) as Promise<{ subjectId: string; records: unknown[]; count: number; sites: string[] }>,\n },\n banners: {\n list: () => get('/banners') as Promise<BannerSummary[]>,\n create: (input) => request('POST', '/banners', input) as Promise<BannerRecord>,\n get: (id) => get(`/banners/${enc(id)}`) as Promise<BannerRecord>,\n update: (id, patch) => request('PUT', `/banners/${enc(id)}`, patch) as Promise<BannerRecord>,\n delete: async (id) => {\n await request('DELETE', `/banners/${enc(id)}`);\n },\n assignments: (id) => get(`/banners/${enc(id)}/assignments`) as Promise<{ cbids: string[] }>,\n setAssignments: (id, cbids) => request('PUT', `/banners/${enc(id)}/assignments`, { cbids }) as Promise<{ cbids: string[] }>,\n publish: (id) => request('POST', `/banners/${enc(id)}/publish`) as Promise<{ publishedCbids: string[] }>,\n },\n };\n}\n","/**\n * Server-side consent reading. The Cookie Munch embed stores the visitor's decision in a\n * first-party `CookieMunch` cookie (URL-encoded JSON). This lets a server / edge worker read\n * that decision from the incoming request — to gate server-rendered tags or SSR'd third-party\n * scripts BEFORE they hit the browser — without shipping the whole embed. Zero-dependency and\n * runtime-agnostic: accepts a raw Cookie header, a Node `req` (`req.headers.cookie`), or a\n * Fetch `Request` (`request.headers.get('cookie')`).\n *\n * import { readConsent, isAllowed } from '@cookiemunch/sdk/consent';\n * const consent = readConsent(req.headers.cookie); // ServerConsent | null\n * if (isAllowed(consent, 'statistics')) { …render analytics tag… }\n *\n * The contract is documented at https://cookiemunch.net/docs/consent-cookie. It accepts\n * exactly what the embed accepts, so a server never grants what the browser would refuse:\n * strict JSON (URL-encoded or not) and the relaxed object literal Cookiebot writes.\n *\n * This module imports nothing — `@cookiemunch/sdk/consent` pulls in no API client.\n */\n\n/** The first-party cookie the embed writes (mirrors @cookiemunch/core COOKIE_NAME). */\nexport const CONSENT_COOKIE_NAME = 'CookieMunch';\n\n/** A visitor's consent decision, read from the request cookie. `necessary` is always true. */\nexport interface ServerConsent {\n necessary: true;\n preferences: boolean;\n statistics: boolean;\n marketing: boolean;\n /** How consent was given. `implied` = not yet an explicit choice. */\n method: 'explicit' | 'implied';\n /** Custom (non-standard) categories, when the site defines any. */\n custom?: Record<string, boolean>;\n /** Consent-policy version the decision was made under (see `minVersion`). */\n ver: number;\n /** When the decision was made, epoch milliseconds (see `expiryDays`). */\n utc: number;\n /** Region code the decision was made in (`unknown` when undetected). */\n region: string;\n /** The anonymous consent-receipt id tying this browser to the consent log. */\n stamp: string;\n}\n\n/** The site's consent policy, so the server applies the same rules the embed does. */\nexport interface ReadConsentOptions {\n /** Cookie name. Default `CookieMunch`. */\n cookieName?: string;\n /**\n * The site's current consent version (`consent.version`, or the config `ver` when that is\n * higher). A decision made under an older version is not honoured by the embed — the\n * visitor is asked again — so it reads as `null` here too.\n */\n minVersion?: number;\n /** The site's `consent.expiryDays`: a decision older than this reads as `null`. */\n expiryDays?: number;\n /** Clock for `expiryDays`, epoch ms. Default `Date.now()`. */\n now?: number;\n}\n\n/** Anything we can pull a Cookie header off of. */\nexport type ConsentSource =\n | string\n | null\n | undefined\n | { headers?: { cookie?: string | null; get?(name: string): string | null } | Record<string, unknown> };\n\nfunction cookieHeaderOf(src: ConsentSource): string {\n if (typeof src === 'string') return src;\n if (!src || typeof src !== 'object') return '';\n const h = src.headers;\n if (!h) return '';\n // Fetch Request: headers.get('cookie'); Node: headers.cookie.\n if (typeof (h as { get?(n: string): string | null }).get === 'function') {\n return (h as { get(n: string): string | null }).get('cookie') ?? '';\n }\n const c = (h as Record<string, unknown>).cookie;\n return typeof c === 'string' ? c : '';\n}\n\n/** Extract one cookie's raw value from a Cookie header (no decoding). */\nfunction cookieValue(header: string, name: string): string | null {\n for (const part of header.split(';')) {\n const eq = part.indexOf('=');\n if (eq === -1) continue;\n if (part.slice(0, eq).trim() === name) return part.slice(eq + 1).trim();\n }\n return null;\n}\n\nfunction bool(v: unknown): v is boolean {\n return typeof v === 'boolean';\n}\n\nfunction decode(raw: string): string {\n try {\n return decodeURIComponent(raw);\n } catch {\n return raw; // not URL-encoded; use as-is\n }\n}\n\n/** The embed's own validation (core cookie.ts `normalize`), field for field. */\nfunction normalize(o: unknown): ServerConsent | null {\n if (!o || typeof o !== 'object') return null;\n const r = o as Record<string, unknown>;\n if (!bool(r.preferences) || !bool(r.statistics) || !bool(r.marketing)) return null;\n if (r.method !== 'explicit' && r.method !== 'implied') return null;\n if (typeof r.ver !== 'number' || typeof r.utc !== 'number') return null;\n if (typeof r.stamp !== 'string' && typeof r.stamp !== 'number') return null;\n if (typeof r.region !== 'string') return null;\n const out: ServerConsent = {\n necessary: true, // never trust a cookie that claims necessary:false\n preferences: r.preferences,\n statistics: r.statistics,\n marketing: r.marketing,\n method: r.method,\n ver: r.ver,\n utc: r.utc,\n region: r.region,\n stamp: String(r.stamp),\n };\n if (r.custom && typeof r.custom === 'object' && !Array.isArray(r.custom)) {\n const custom: Record<string, boolean> = {};\n for (const [k, v] of Object.entries(r.custom as Record<string, unknown>)) if (bool(v)) custom[k] = v;\n if (Object.keys(custom).length > 0) out.custom = custom;\n }\n return out;\n}\n\n/**\n * Parse a raw cookie value into a validated ServerConsent, or null. Accepts the two formats\n * the embed reads: URL-encoded (or plain) strict JSON, and Cookiebot's relaxed object\n * literal — `{stamp:'…',necessary:true,…}` — left behind by a site migrating from Cookiebot.\n */\nexport function parseConsentValue(raw: string): ServerConsent | null {\n if (!raw) return null;\n const decoded = decode(raw);\n try {\n return normalize(JSON.parse(decoded));\n } catch {\n /* not strict JSON — try the Cookiebot literal */\n }\n if (!decoded.includes('necessary')) return null;\n const json = decoded.replace(/'/g, '\"').replace(/([{,]\\s*)([A-Za-z_][\\w]*)\\s*:/g, '$1\"$2\":');\n try {\n return normalize(JSON.parse(json));\n } catch {\n return null;\n }\n}\n\nconst DAY_MS = 24 * 60 * 60 * 1000;\n\n/**\n * Read the visitor's consent from a request: a raw `Cookie` header, a Node `req`, or a Fetch\n * `Request`. Returns null when there is no decision the embed would honour — no cookie, an\n * unparseable one, or (with the options) one made under an older version or past expiry.\n */\nexport function readConsent(source: ConsentSource, opts: ReadConsentOptions = {}): ServerConsent | null {\n const raw = cookieValue(cookieHeaderOf(source), opts.cookieName ?? CONSENT_COOKIE_NAME);\n const consent = raw ? parseConsentValue(raw) : null;\n if (!consent) return null;\n if (typeof opts.minVersion === 'number' && consent.ver < opts.minVersion) return null;\n if (typeof opts.expiryDays === 'number' && opts.expiryDays > 0) {\n if ((opts.now ?? Date.now()) - consent.utc > opts.expiryDays * DAY_MS) return null;\n }\n return consent;\n}\n\n/**\n * Read the visitor's consent from a request. The original spelling of {@link readConsent},\n * kept for existing callers; the second argument is the cookie name.\n */\nexport function getConsent(source: ConsentSource, cookieName: string = CONSENT_COOKIE_NAME): ServerConsent | null {\n return readConsent(source, { cookieName });\n}\n\n/** Standard + custom consent categories. */\nexport type ConsentCategory = 'necessary' | 'preferences' | 'statistics' | 'marketing' | (string & {});\n\n/**\n * Whether a category is allowed for this visitor. `necessary` is always allowed. A missing\n * consent (`null` — visitor hasn't decided) is treated as NOT allowed for non-necessary\n * categories (fail-closed / prior-blocking posture), matching the embed.\n */\nexport function isAllowed(consent: ServerConsent | null, category: ConsentCategory): boolean {\n if (category === 'necessary') return true;\n if (!consent) return false;\n // Explicit per-literal access rather than `consent[category]`: the `(string & {})` arm of\n // ConsentCategory defeats control-flow narrowing, so a dynamic index is typed as an\n // implicit-any index into ServerConsent (TS7053, which fails the tsup DTS build).\n if (category === 'preferences') return consent.preferences === true;\n if (category === 'statistics') return consent.statistics === true;\n if (category === 'marketing') return consent.marketing === true;\n return consent.custom?.[category] === true;\n}\n","/**\n * Verify an inbound Cookie Munch webhook signature.\n *\n * Cookie Munch signs every webhook delivery with the subscription's `whsec_…` secret\n * (returned once when the webhook is created). Each POST carries:\n *\n * X-CookieMunch-Signature: sha256=<hex HMAC-SHA256(secret, `${timestamp}.${rawBody}`)>\n * X-CookieMunch-Timestamp: <unix SECONDS>\n * X-CookieMunch-Event: <event type, e.g. dsar.action_required>\n *\n * The signature covers the timestamp as well as the body, so a captured delivery cannot be\n * replayed forever — reject deliveries whose timestamp is outside `toleranceSeconds`\n * (default 300s / 5 min). This matches the platform-standard (Stripe-style) verifyWebhook shape.\n *\n * @example\n * import { verifyWebhookSignature } from '@cookiemunch/sdk';\n * // Express — capture the RAW body: app.use(express.raw({ type: 'application/json' }))\n * app.post('/hooks/cm', (req, res) => {\n * const ok = verifyWebhookSignature({\n * payload: req.body.toString('utf8'), // exact bytes, not a re-stringified object\n * signature: req.header('X-CookieMunch-Signature'),\n * timestamp: req.header('X-CookieMunch-Timestamp'),\n * secret: process.env.CM_WEBHOOK_SECRET!, // the whsec_… value\n * });\n * if (!ok) return res.sendStatus(400);\n * const event = JSON.parse(req.body.toString('utf8')); // { id, type, cbid, createdAt, data }\n * // …handle event.type…\n * res.sendStatus(200);\n * });\n *\n * Server-side only: uses `node:crypto`. Pass the EXACT raw request body — verifying against a\n * re-serialized JSON object fails whenever key order or whitespace differs from what was signed.\n */\nimport { createHmac, timingSafeEqual } from 'node:crypto';\n\nexport interface VerifyWebhookInput {\n /** The raw request body, exactly as received. Do NOT re-stringify parsed JSON. */\n payload: string;\n /** The `X-CookieMunch-Signature` header value. */\n signature: string | undefined | null;\n /** The `X-CookieMunch-Timestamp` header value (unix seconds). */\n timestamp: string | number | undefined | null;\n /** The subscription secret (`whsec_…`), shown once when the webhook was created. */\n secret: string;\n /**\n * Maximum age (in seconds) of a delivery before it is rejected as stale/replayed.\n * Defaults to 300 (5 minutes). Pass 0 to disable the freshness check entirely.\n */\n toleranceSeconds?: number;\n /** Current time in ms (injectable for tests); defaults to `Date.now()`. */\n now?: number;\n}\n\n/**\n * Returns true only when the signature is valid AND (unless disabled) the timestamp is fresh.\n * Fail-closed: any missing/malformed input returns false rather than throwing.\n */\nexport function verifyWebhookSignature(input: VerifyWebhookInput): boolean {\n const { payload, signature, timestamp, secret } = input;\n if (!signature || timestamp == null || timestamp === '' || !secret) return false;\n\n const tsRaw = String(timestamp);\n const tsNum = Number(tsRaw);\n if (!Number.isFinite(tsNum)) return false;\n\n const tolerance = input.toleranceSeconds ?? 300;\n if (tolerance > 0) {\n const nowSec = Math.floor((input.now ?? Date.now()) / 1000);\n if (Math.abs(nowSec - tsNum) > tolerance) return false; // stale or replayed\n }\n\n // HMAC over the EXACT timestamp bytes received, joined to the raw body with a dot — matching\n // the server's `sign(secret, timestamp, body)` (packages/server/src/webhook-delivery.ts).\n const expected = `sha256=${createHmac('sha256', secret).update(`${tsRaw}.${payload}`).digest('hex')}`;\n\n // Constant-time compare; timingSafeEqual throws on length mismatch, so guard first.\n const got = Buffer.from(signature);\n const want = Buffer.from(expected);\n if (got.length !== want.length) return false;\n return timingSafeEqual(got, want);\n}\n"],"mappings":"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","names":["opts"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/consent.ts","../src/webhooks.ts","../src/consent-sync.ts"],"sourcesContent":["/**\n * @cookiemunch/sdk — a small, typed REST client for the Cookie Munch Developer API\n * (the /v1 surface). Construct it with an API key; the org is derived server-side\n * from the key, so callers never pass an orgId. `fetch` is injectable for testing\n * and non-browser/non-Node runtimes; it defaults to the global fetch.\n *\n * const fc = createCookieMunch({ apiKey: 'fck_…', baseUrl: 'https://api.cookiemunch.net' });\n * const sites = await fc.sites.list();\n */\n\nexport * from './types.js';\nexport * from './consent.js';\nexport * from './webhooks.js';\nexport * from './consent-sync.js';\nimport type {\n Identity,\n AccountOrgs,\n Site,\n SiteCreate,\n SiteConfig,\n SiteUpdate,\n SiteUpdateResult,\n IntegrationHealthReport,\n ProvisionRequest,\n ProvisionResult,\n PartnerLink,\n PartnerLinkCreate,\n PartnerCatalogEntry,\n ConsentDay,\n ConsentLogRow,\n RangeQuery,\n LogQuery,\n DsarRequest,\n DsarCreate,\n DsarStatus,\n ScoredVendor,\n VendorInput,\n VendorRecord,\n RiskScore,\n RopaEntry,\n RopaInput,\n SignedReceipt,\n PreferenceRecord,\n} from './types.js';\n\n/**\n * This package's version, sent in the default User-Agent. Cloudflare answers a request with\n * no recognisable User-Agent with `403 error code: 1010`; a test pins this to package.json.\n */\nexport const SDK_VERSION = '0.5.0';\n\nexport interface CookieMunchOptions {\n apiKey: string;\n /** \"https://api.cookiemunch.net\" — the /v1 prefix is appended automatically. */\n baseUrl: string;\n /** Injectable for tests / custom runtimes. Defaults to the global fetch. */\n fetch?: typeof fetch;\n /**\n * Send `Prefer: strict-unknown-fields` on every write: a body field the operation does not\n * declare is refused (`400 unknown_field`, with `field`) instead of ignored. Catches typos.\n */\n strictUnknownFields?: boolean;\n /** The `User-Agent` sent on every request. Default `cookiemunch-js/<version>`. */\n userAgent?: string;\n}\n\n/**\n * Thrown for any non-2xx response. `message` is the server's `error` field when present;\n * `code` is populated from the server's `code` field whenever the error body carries one\n * (e.g. `forbidden` on a scope failure, whose `message` is `insufficient_scope`,\n * or `banner_in_use` on a banner-library conflict). It's `undefined` when the body has no `code`.\n */\nexport class CookieMunchApiError extends Error {\n readonly status: number;\n readonly code?: string;\n /** The parsed JSON error body, when there was one (e.g. `issues` on a 422). */\n readonly body?: unknown;\n constructor(status: number, message: string, code?: string, body?: unknown) {\n super(message);\n this.name = 'CookieMunchApiError';\n this.status = status;\n if (code !== undefined) this.code = code;\n if (body !== undefined) this.body = body;\n }\n}\n\n/*\n * Additional /v1 response shapes. These live here (rather than in types.ts) but are\n * still re-exported from the package root via `export * from './types.js'` above plus\n * these top-level `export interface` declarations.\n */\n\n/** A cookie discovered on a site, classified into a consent category (wire shape of the\n * cookie-declaration feed). Matches the server's CategorizedCookie schema. */\nexport interface CategorizedCookie {\n name: string;\n category: 'necessary' | 'preferences' | 'statistics' | 'marketing' | 'unclassified' | (string & {});\n domain?: string;\n provider?: string;\n purpose?: string;\n /**\n * The purpose by language (lowercase BCP-47 tag). On a declared row, the owner's; on a\n * row the cookie database classified, the database's (empty: none yet).\n */\n purposes?: Record<string, string>;\n expiry?: string;\n /** Page URLs (multi-page scan) that set this cookie, earliest first. */\n foundOnPages?: string[];\n /** The site owner declared this cookie; it overrides the scan. */\n declared?: boolean;\n /** A declared `prefix*` wildcard row that no observed cookie matched yet. */\n pattern?: boolean;\n /**\n * Why a person should look at it. Visitors see unclassified and category ones under\n * \"Unclassified\"; `retired_but_seen` = retired, but a crawl or a visit saw it since, so the\n * site still sets it and it is listed again. `awaiting_corroboration` / `suspicious_report` =\n * only visitors reported it (not enough of them yet, or in one injection-shaped report), owner\n * views only; `foreign_cmp` = another consent platform's cookie, filed unclassified;\n * `not_seen_recently` = unseen for the site's `staleAfterDays`, owner views only.\n */\n review?: 'unclassified' | 'category_disabled' | 'category_unknown' | 'retired_but_seen' | 'awaiting_corroboration' | 'suspicious_report' | 'foreign_cmp' | 'not_seen_recently';\n /** Epoch-ms a crawl or a real visit last saw it. Absent on older rows. */\n lastSeenAt?: number;\n /** Owner views only: the site owner retired this cookie (see `sites.retireCookie`). */\n retired?: { at: number; renamedTo?: string; note?: string; by?: string; pattern?: string };\n /** A cookie (default, also when absent) or a key in browser storage. */\n type?: StorageType;\n /** Where the row came from: a real visit, a crawl, the owner (`api`) or a vendor preset (`import`). */\n source?: 'embed' | 'scan' | 'api' | 'import';\n /** Epoch-ms it was first stored. Absent on rows from before provenance was recorded. */\n firstSeen?: number;\n /** Distinct visits that reported it. */\n visits?: number;\n /** Owner views only: reported by a real visit, but not yet by enough distinct visits to be listed publicly. */\n quarantined?: boolean;\n /** \"Declared by <vendor>\": the vendor preset that declared it. */\n vendor?: string;\n}\n\n/** What a declaration row describes. */\nexport type StorageType = 'cookie' | 'localStorage' | 'sessionStorage' | 'indexedDB';\n\n/** A cookie the site owner retired: the site renamed or stopped setting it. */\nexport interface RetiredCookie {\n /** Exact name, or a `prefix*` wildcard. */\n name: string;\n /** Epoch-ms it was retired; anything seen after this brings it back. */\n at: number;\n renamedTo?: string;\n note?: string;\n /** `apikey:<prefix>` or a dashboard user id. */\n by?: string;\n pattern?: boolean;\n}\n\n/** A site's cookie declaration: the latest scan and observed visits with the site's own\n * declarations on top. This is what GET /v1/sites/:cbid/cookies returns. */\nexport interface CookieDeclaration {\n /** Epoch-ms of the latest scan or declaration change, or 0 if none. */\n updatedAt: number;\n cookies: CategorizedCookie[];\n /** The site owner's declarations, as stored. */\n declared?: DeclaredCookie[];\n /** The site's retirements, as stored (owner views only). */\n retired?: RetiredCookie[];\n}\n\n/** What the site owner says a cookie is (PUT /v1/sites/:cbid/cookies/:name). */\nexport interface DeclaredCookieInput {\n /** A standard category or one of the site's `categories.custom` ids. Not `unclassified`. */\n category: string;\n /** Shown to visitors, and the fallback for any language `purposes` lacks. */\n purpose: string;\n /**\n * The purpose in other languages: `{ es: '…', 'pt-BR': '…' }` (BCP-47 language or\n * language-region tags, stored lowercased; ≤50 languages, ≤1000 characters each).\n * The banner, `data-cc=\"declaration\"` and /cookie-declaration.js draw the visitor's.\n */\n purposes?: Record<string, string> | null;\n provider?: string;\n expiry?: string;\n domain?: string;\n /** Treat the name as a prefix wildcard (a trailing `*` does the same). */\n pattern?: boolean;\n /** A cookie (default) or a key in localStorage, sessionStorage or IndexedDB. */\n type?: StorageType;\n}\n\n/** A stored declaration. */\nexport interface DeclaredCookie extends DeclaredCookieInput {\n name: string;\n updatedAt: number;\n /** `import` when a vendor preset wrote it. */\n source?: 'import';\n /** The vendor whose preset declared it. */\n vendor?: string;\n}\n\n/** A platform whose cookies and storage a preset declares. */\nexport type CookiePresetVendor = 'atlas' | 'forumulate' | 'chatly' | 'cookiemunch' | 'referally';\n\n/** Where a vendor's live manifest will come from, and whether it exists yet. */\nexport interface CookiePresetManifest {\n status: 'builtin' | 'waiting_on_vendor';\n waitingOn?: string;\n item?: string;\n contract?: string;\n}\n\n/** Preset parameters: Atlas `frontendApiHost`/`crossSubdomainCookies`; Chatly `publicId`/`dragging`/`category`; Referally `cookieDomain`/`cookieDays`/`category`. */\nexport interface CookiePresetParams {\n frontendApiHost?: string;\n crossSubdomainCookies?: boolean;\n publicId?: string;\n dragging?: boolean;\n category?: string;\n cookieDomain?: string;\n cookieDays?: number;\n}\n\n/** One preset in GET /v1/sites/:cbid/cookies/presets. */\nexport interface CookiePreset {\n vendor: CookiePresetVendor;\n params: Array<{ name: string; type: string; required: boolean; description?: string }>;\n applied: boolean;\n declared: string[];\n manifest: CookiePresetManifest;\n}\n\n/** What applying a preset declared. */\nexport interface CookiePresetResult {\n vendor: CookiePresetVendor;\n cookies: DeclaredCookie[];\n removed: string[];\n skipped?: Array<{ name: string; reason: string }>;\n note?: string;\n manifest: CookiePresetManifest;\n dryRun?: true;\n declared?: DeclaredCookie[];\n diff?: DeclarationDiff;\n}\n\n/** What removing observed rows removed (or, with `dryRun`, would). */\nexport interface ObservedPurgeResult {\n removed: string[];\n count: number;\n since?: number;\n until?: number;\n dryRun?: true;\n}\n\n/** What a declaration write would change (`dryRun`). */\nexport interface DeclarationDiff {\n added: DeclaredCookie[];\n removed: DeclaredCookie[];\n changed: Array<{ name: string; before: DeclaredCookie; after: DeclaredCookie }>;\n}\n\n/** The answer to a `dryRun` declaration write: nothing was stored. */\nexport interface DeclarationDryRun {\n dryRun: true;\n /** Every declaration the site would have. */\n declared: DeclaredCookie[];\n diff: DeclarationDiff;\n}\n\n/** A warning about a write: e.g. the category is switched off for the site. */\nexport interface DeclarationWarning {\n field: string;\n reason: string;\n}\n\n/** What an import with `apply` declared, on top of what it read. */\nexport interface AppliedImport extends ImportedDeclaration {\n applied: number;\n skipped: Array<{ name: string; reason: string }>;\n warnings?: DeclarationWarning[];\n}\n\n/** @deprecated The server returns {@link CookieDeclaration}; kept only for back-compat. */\nexport interface SiteCookie {\n name: string;\n domain: string;\n category: string;\n provider?: string;\n purpose?: string;\n expiry?: string;\n firstSeen?: number;\n}\n\n/** Cookie-scan status, from GET /v1/sites/:cbid/scan. */\nexport interface ScanStatus {\n /** `scanning` while the last scan is queued or crawling. Kept for older clients; read `state`. */\n status: 'idle' | 'scanning';\n /** Epoch-ms of the last completed scan, or null. */\n lastScannedAt: number | null;\n /** What the last scan saw of the live banner's Accept all / Reject all. Absent until measured. */\n buttonParity?: ButtonParityScan;\n /** Where the last scan is. Absent when the site was never scanned. */\n state?: 'queued' | 'scanning' | 'done' | 'failed';\n scanId?: string;\n startedAt?: number;\n finishedAt?: number;\n durationMs?: number;\n /** Cookies this crawl saw (the stored list also keeps earlier finds: `keptFromEarlier`). */\n cookieCount?: number;\n keptFromEarlier?: number;\n pagesVisited?: number;\n /** Where the site's address redirected to (its canonical host), when it did. */\n finalUrl?: string;\n /** Why it failed. */\n error?: string;\n}\n\n/** Consent-button parity of the banner a real browser saw on the site (see ScanStatus). */\nexport interface ButtonParityScan {\n url: string;\n /** False when no visible consent accept control was found in a recognisable banner. */\n bannerFound: boolean;\n /**\n * True when the checks ran: our embed, or a recognised banner with both Accept and Reject.\n * False = not measured — no findings, `score` null. Absent on scans stored before it existed.\n */\n measured?: boolean;\n /** `cookiemunch` = our embed; `other` = found by its button text. */\n cmp: 'cookiemunch' | 'other' | null;\n acceptFound: boolean;\n rejectFound: boolean;\n designWarnings: ButtonParityFinding[];\n /** 0–100; null when no banner was found. */\n score: number | null;\n checkedAt?: number;\n}\n\n/** @deprecated The server returns {@link ScanStatus}; kept only for back-compat. */\nexport interface ScanResult {\n scanId: string;\n status: 'queued' | 'running' | 'complete' | 'failed';\n startedAt?: number;\n finishedAt?: number;\n pagesScanned?: number;\n cookiesFound?: number;\n}\n\n/** A/B banner experiment results for a single variant. */\nexport interface AbResult {\n variant: string;\n impressions: number;\n optIns: number;\n optInRate: number;\n}\n\n/** The response shape of GET /v1/sites/:cbid/snippet. */\n// Keep in sync with packages/server/src/snippet.ts InstallSnippet\nexport interface InstallSnippet {\n /** The exact `<script>` tag to paste into `<head>`. */\n snippet: string;\n /** URL of the consent.js embed file (the embed/CDN origin). */\n src: string;\n /** The API origin (beacons + consent-tcf.js). Present when the server reports it. */\n api?: string;\n /** The site identifier. */\n cbid: string;\n /** The blocking mode on the tag — the site's `blocking.mode` unless overridden. */\n blockingMode: string;\n /** Where `blockingMode` came from: the site's config, or the `blockingMode` option. */\n blockingModeSource?: 'config' | 'query';\n /** The tag's data-culture, when it has one. */\n culture?: string;\n /** CSP sources this site needs, by directive, plus a paste-able `policy`. */\n csp?: CspSources;\n /** Present when an override disagrees with the stored config. */\n warnings?: ConfigWarning[];\n}\n\n/** Content Security Policy sources the embed needs. */\nexport interface CspSources {\n script: string[];\n connect: string[];\n img: string[];\n /** Always includes 'unsafe-inline' (the banner's shadow-root <style>), unless you use a nonce. */\n style: string[];\n font: string[];\n frame: string[];\n /** The same as one policy fragment. */\n policy: string;\n}\n\n/** Something a config write did not store as sent. */\nexport interface ConfigWarning {\n field: string;\n reason: string;\n}\n\n/** One leaf a config write would change. */\nexport interface ConfigChange {\n path: string;\n from?: unknown;\n to?: unknown;\n}\n\n/** `PUT …/config` options. `keep: 'unspecified'` keeps the stored value of every top-level field the body omits. */\nexport interface PutConfigOptions {\n keep?: 'unspecified';\n}\n\n/** What a config write would do (`?dryRun=1`) — nothing is saved. */\nexport interface ConfigDryRun {\n dryRun: true;\n /** The config as it would be saved. */\n config: SiteConfig;\n /** Every leaf that would change. */\n diff: ConfigChange[];\n warnings?: ConfigWarning[];\n /** Consent-button parity of the would-be config (see {@link ButtonParityFinding}). */\n designWarnings: ButtonParityFinding[];\n}\n\n/** The effective style of an Accept all / Reject all control. */\nexport interface ParityControlSummary {\n id?: string;\n label: string;\n element: 'button' | 'link';\n background?: string;\n color?: string;\n borderColor?: string;\n borderWidth?: number;\n fontSize?: number;\n fontWeight?: number;\n width?: number;\n height?: number;\n}\n\n/**\n * One way Accept all is pushed harder than Reject all (colour, size, placement, copy, custom\n * CSS). Returned as `designWarnings` on every config read and write, and by `lintConfig`.\n * A warning, never an error — the config is saved regardless. See /docs/fair-consent-buttons.\n */\nexport interface ButtonParityFinding {\n code:\n | 'reject_missing_first_layer' | 'reject_missing_same_layer' | 'reject_hidden' | 'reject_below_fold' | 'reject_isolated'\n | 'reject_low_emphasis' | 'saturation_gap' | 'color_mismatch' | 'reject_text_low_contrast' | 'reject_styled_as_link'\n | 'font_size_gap' | 'font_weight_gap' | 'size_gap' | 'padding_gap' | 'border_mismatch' | 'reject_label_missing'\n | 'reject_label_long' | 'confirmshaming' | 'custom_css_asymmetric';\n severity: 'warning' | 'notice';\n layer: 'banner' | 'preferences' | 'modal' | 'popup' | 'bar' | 'inline' | 'custom';\n accept: ParityControlSummary | null;\n reject: ParityControlSummary | null;\n metric: { name: string; accept?: number; reject?: number; value: number; threshold: number };\n detail: string;\n fix: string;\n where: { view?: string; variant?: string; theme?: 'light' | 'dark'; breakpoint?: 'desktop' | 'mobile'; language?: string; url?: string };\n}\n\n/** Reject all style values that would match the Accept all on the same layer. */\nexport interface SuggestedRejectStyle {\n layer: string;\n view?: string;\n background?: string;\n color?: string;\n borderColor?: string;\n borderWidth?: number;\n fontSize?: number;\n fontWeight?: number;\n width?: number;\n height?: number;\n}\n\n/** `POST /v1/sites/:cbid/config/lint` — nothing is saved. */\nexport interface ButtonParityLint {\n /** `merged` = what PATCH would store (merged over the stored config); `replaced` = what PUT would store (`replace: true`). */\n mode: 'merged' | 'replaced';\n designWarnings: ButtonParityFinding[];\n /** 100 minus 20 per warning and 5 per notice, floored at 0. */\n score: number;\n suggestedReject: SuggestedRejectStyle[];\n}\n\n/**\n * Scrollbars on the surfaces Cookie Munch renders — the same shape on the banner theme (`theme.scrollbar`)\n * and the rights-page theme. Colours are #hex, rgb() or hsl(); width is clamped to 4–16, radius to 0–999.\n */\nexport interface ScrollbarTheme {\n style: 'themed' | 'thin' | 'native' | 'hidden';\n thumb?: string;\n thumbHover?: string;\n track?: string;\n width?: number;\n radius?: number;\n}\n\n/** The hosted rights page document. See docs/rights-page for each section type's props. */\nexport interface RightsPageDocument {\n v: 1;\n theme: { preset: 'brand' | 'light' | 'dark' | 'custom'; width: 'narrow' | 'normal' | 'wide' | 'full'; density: 'compact' | 'comfortable' | 'spacious'; colorScheme: 'light' | 'dark' | 'auto'; scrollbar?: ScrollbarTheme; [k: string]: unknown };\n sections: Array<{ id: string; type: string; props: Record<string, unknown>; hidden?: { phone?: boolean; desktop?: boolean }; style?: Record<string, unknown> }>;\n form: { requestTypes: Array<Record<string, unknown>>; fields: Array<Record<string, unknown>>; jurisdiction: 'auto' | 'ask' | 'hidden'; consentIdField: 'hidden' | 'optional' | 'required'; success: { title?: string; body?: string }; submitLabel?: string };\n i18n: { defaultLocale: string; autoDetect: boolean; overrides: Record<string, unknown> };\n seo: { title?: string; description?: string; favicon?: string; image?: string; index: boolean };\n customCss?: string;\n}\n/** `message` is English; `code` (+ `params`) is stable, for your own wording or language. */\nexport interface RightsPageIssue { path: string; message: string; code?: string; params?: Record<string, string | number> }\nexport interface RightsPageDraft { cbid: string; draft: RightsPageDocument; version: number; publishedAt: number | null; updatedAt: number | null; warnings: RightsPageIssue[] }\nexport interface RightsPageDryRun { dryRun: true; cbid: string; draft: RightsPageDocument; warnings: RightsPageIssue[] }\n/** Who published a version: `publishedBy` is an actor id; `publishedByEmail` names a dashboard user (null for an API key). */\nexport interface RightsPagePublisher { publishedBy: string; publishedByEmail: string | null; publishedVia: 'dashboard' | 'api_key' }\nexport interface RightsPagePublished { cbid: string; page: RightsPageDocument; version: number; publishedAt: number; publishedBy?: string | null; publishedByEmail?: string | null; publishedVia?: 'dashboard' | 'api_key' | null }\n/**\n * Optimistic concurrency for a draft write: the draft's `updatedAt` as you last read it (epoch ms or\n * an ISO date; null = you saw no saved draft). A mismatch rejects with a 409 CookieMunchApiError whose\n * body is RightsPageDraftConflict. Omitted: a full write (put) is last-write-wins; a patch merges onto the draft\n * as it is when written, so a concurrent edit is not lost.\n */\nexport interface RightsPageWriteOptions { baseUpdatedAt?: number | string | null }\nexport interface RightsPageDraftConflict { error: 'draft_conflict'; draft: RightsPageDraft }\n/** `draftUpdatedAt`: the draft's updatedAt after the call — pass it as the next `baseUpdatedAt`. */\nexport interface RightsPagePublishResult { cbid: string; version: number; publishedAt: number; draftUpdatedAt: number; rolledBackTo?: number }\nexport interface RightsPageVersions { cbid: string; versions: Array<{ version: number; publishedAt: number } & RightsPagePublisher> }\n\n/** The draft body with `baseUpdatedAt` added when the caller passed one (null included). */\nconst withBase = (doc: object, opts?: RightsPageWriteOptions): object =>\n opts?.baseUpdatedAt !== undefined ? { ...doc, baseUpdatedAt: opts.baseUpdatedAt } : doc;\n\n/** Options for POST /v1/sites/:cbid/preview-token. All optional. */\nexport interface PreviewTokenOptions {\n /** Lifetime in seconds, 1..3600. Default 900. */\n ttlSeconds?: number;\n /** The page to preview. Default: the site's registered domain. */\n url?: string;\n /** Region to force: a country or subdivision code (`de`, `us-ca`) or `eu`. */\n region?: string;\n /** Language to force, as a BCP 47 tag (`de`, `pt-BR`). */\n lang?: string;\n}\n\n/** A banner preview token, and the page URL that opens the preview. */\nexport interface PreviewToken {\n cbid: string;\n token: string;\n /** Epoch milliseconds. */\n expiresAt: number;\n region?: string;\n lang?: string;\n /** The page with `cm_preview` / `cm_region` / `cm_lang` added. */\n previewUrl?: string;\n}\n\n/** A right a rights link may file (the hosted rights page's request-type kinds). */\nexport type RightKind =\n | 'access' | 'erasure' | 'rectification' | 'portability' | 'restriction'\n | 'objection' | 'opt_out_sale' | 'opt_out_targeted' | 'limit_sensitive';\n\n/** Input for POST /v1/sites/:cbid/rights-links. */\nexport interface RightsLinkInput {\n /** The signed-in person. `email` must be an address you have verified. */\n subject: { email: string; userId?: string };\n /** Limit the link to these rights. Default: every right the page offers. */\n rights?: RightKind[];\n /** Lifetime in seconds, 60..3600. Default 900. */\n ttlSeconds?: number;\n}\n\n/** A signed, single-use link to the hosted rights page for one signed-in person. */\nexport interface RightsLink {\n /** `https://<customDomain>/?t=…`, or `https://cookiemunch.net/r/<cbid>?t=…` when `customDomain` is null. */\n url: string;\n /** Epoch milliseconds. */\n expiresAt: number;\n /** The site's active custom rights-page host the link opens on, or null. */\n customDomain: string | null;\n}\n\n/** One DNS record to create, exactly as written. */\nexport interface DnsRecordInstruction {\n type: 'CNAME' | 'TXT';\n name: string;\n value: string;\n}\n\n/** A custom hostname serving a site's rights page. */\nexport interface RightsPageDomain {\n /** Lowercase ASCII form (punycode for an internationalised name). */\n hostname: string;\n /** As a person reads it. */\n unicodeHostname: string;\n cbid: string;\n /** pending_dns → verifying → issuing → active, or error (see lastError). */\n status: 'pending_dns' | 'verifying' | 'issuing' | 'active' | 'error';\n certStatus: 'none' | 'pending' | 'issued' | 'failed';\n createdAt: number;\n verifiedAt?: number;\n activatedAt?: number;\n lastCheckedAt?: number;\n /** dns_not_found | wrong_target | hostname_in_use | router_write_failed | certificate_not_issued | dns_changed | probe_failed */\n lastError?: string;\n url: string;\n /** The records to create: the CNAME, or (apex / flattened / proxied) the TXT plus an ALIAS to the same target. */\n /** `cname.value` is this domain's own `<token>.cname.cookiemunch.net` — unique to the claim; a CNAME to anything else does not verify it. */\n dns: { recommended: 'cname' | 'flattened'; cname: DnsRecordInstruction; txt: DnsRecordInstruction };\n /** Only on a verify that found no proof: what DNS answered. */\n dnsSeen?: { cnames: string[]; addresses: string[]; txt: string[] };\n}\n\n/** The site's custom hostnames, the plan allowance (null = unlimited) and the org's usage. */\nexport interface RightsPageDomainList {\n domains: RightsPageDomain[];\n limit: number | null;\n used: number;\n target: string;\n}\n\n/** Options for the snippet endpoint. */\nexport interface SnippetOptions {\n /** Override the site's configured `blocking.mode`. Default: the config's. */\n blockingMode?: 'auto' | 'manual';\n /** Language culture override, e.g. \"en\" or \"fr\". */\n culture?: string;\n}\n\n/**\n * The result of domain verification.\n * Keep in sync with the POST /v1/sites/:cbid/verify response in\n * packages/server/src/dev-api.ts (~L267) — note the server adds `method` on success,\n * so this wire shape is wider than domain-verify.ts's internal VerifyResult.\n */\nexport interface VerifyResult {\n verified: boolean;\n method?: 'dns' | 'meta' | 'file';\n reason?: string;\n}\n\n/** A site brand suggestion (\"Match my site\"). Keep in sync with packages/server/src/brand-extract.ts BrandSuggestion. */\nexport interface BrandSuggestion {\n background?: string;\n text?: string;\n highlight?: string;\n fontFamily?: string;\n fontUrl?: string;\n palette?: string[];\n}\n\n/** The response shape of POST /v1/sites/:cbid/brand. */\nexport interface BrandExtractionResult {\n suggestion: BrandSuggestion;\n}\n\n/** A validation/lint issue on a v2 banner flow. */\nexport interface FlowIssue {\n code: string;\n message: string;\n path?: string;\n}\n\n/** A site's v2 banner flow + lint issues, from GET /v1/sites/:cbid/flow. */\nexport interface SiteFlow {\n v: 2;\n flow: Record<string, unknown>;\n categories: Record<string, unknown>;\n customCss?: string;\n lint: FlowIssue[];\n}\n\n/**\n * A single structured flow edit operation. `op` selects the kind; remaining fields are\n * that op's args (e.g. { op: 'addView', id, surface, layoutMode }).\n */\nexport type FlowOp = { op: string } & Record<string, unknown>;\n\n/**\n * Result of editFlow/setFlow. On success the new flow is persisted and returned;\n * on a validation/lint failure the config is NOT saved and `issues` says why.\n */\nexport type FlowOpResponse =\n | { ok: true; flow: Record<string, unknown>; /** Consent-button parity of the saved design. */ designWarnings?: ButtonParityFinding[] }\n | { ok: false; failedAt?: number; op?: unknown; issues: FlowIssue[] };\n\n/** A reusable brand kit (colors/logo/typography) for banner theming. */\nexport interface BrandKit {\n id: string;\n orgId: string;\n name: string;\n /** Banner theme tokens (opaque design object). */\n theme: Record<string, unknown>;\n /** Optional banner content overrides (opaque). */\n content?: Record<string, unknown>;\n logoUrl?: string;\n customCss?: string;\n}\n\n/** A configurable consent preference/purpose item. */\nexport interface PreferenceItem {\n id: string;\n orgId: string;\n cbid?: string;\n label: string;\n description?: string;\n category?: string;\n}\n\n/** An organization member. */\nexport interface Member {\n userId: string;\n email: string;\n role: 'owner' | 'admin' | 'member' | 'viewer' | (string & {});\n}\n\n/** Display metadata for an issued key — never the secret. Returned by keys.list(). */\nexport interface ApiKeyPrefix {\n prefix: string;\n createdAt?: number;\n /** Epoch ms the key stops working; null = never. */\n expiresAt?: number | null;\n name?: string | null;\n /** null = full access. */\n scopes?: string[] | null;\n cbids?: string[] | null;\n /** Epoch ms it last authenticated a request (recorded at most hourly); null = not since recording began. */\n lastUsedAt?: number | null;\n}\n\n/** GET /v1/keys/report — key hygiene. */\nexport interface ApiKeyReport {\n generatedAt: number;\n unusedDays: number;\n expiringDays: number;\n unused: Array<ApiKeyPrefix & { reason: string }>;\n broad: Array<ApiKeyPrefix & { reason: string }>;\n expiringSoon: Array<ApiKeyPrefix & { reason: string }>;\n noExpiry: number;\n total: number;\n}\n\n/** A newly issued key. `key` is returned ONCE and never again. Returned by keys.issue(). */\nexport interface ApiKeyIssued {\n key: string;\n prefix: string;\n}\n\n/** @deprecated No endpoint returns this shape; use {@link ApiKeyPrefix} / {@link ApiKeyIssued}. */\nexport interface ApiKey {\n id: string;\n orgId: string;\n prefix: string;\n name?: string;\n createdAt: number;\n lastUsedAt?: number;\n secret?: string;\n}\n\n/**\n * Input for issuing a new API key.\n *\n * NOTE: the server's POST /v1/keys currently accepts no body and always mints an\n * unscoped key — `name` is not yet honoured server-side. It's kept here (rather than\n * dropped) so a future scoped/named-key endpoint can start reading it without an SDK\n * major version bump; see {@link CookieMunchClient.keys}.\n */\nexport interface ApiKeyIssueInput {\n name?: string;\n /** Least privilege: grant only these scopes. Omit for a full-access key. */\n scopes?: string[];\n /**\n * Lock the key to these properties. A locked key works only on those sites and on no\n * org-wide endpoint — see \"Keys locked to properties\" in the client reference.\n */\n cbids?: string[];\n /** Expire the key after this many days (1–3650). */\n expiresInDays?: number;\n /** Or when it expires: epoch ms or an ISO date-time (not with expiresInDays). Omit both: never. */\n expiresAt?: number | string;\n}\n\n/** Org usage/quota summary. Matches the server's Usage schema. */\nexport interface Usage {\n domains: number;\n seats: number;\n monthlyEvents: number;\n}\n\n/** A webhook subscription. `secret` is present only in the create response. */\nexport interface WebhookSubscription {\n id: string;\n orgId: string;\n url: string;\n secret?: string;\n events: string[];\n cbid: string | null;\n active: boolean;\n createdAt: number;\n}\n\n/** One cookie read from another CMP's export. */\nexport interface ImportedCookie {\n name: string;\n domain: string;\n /** Ours, or `unclassified` when their category did not map to one. */\n category: 'necessary' | 'preferences' | 'statistics' | 'marketing' | 'unclassified';\n /** What the previous vendor called it, kept for review. */\n sourceCategory: string;\n purpose: string;\n provider: string;\n expiry: string;\n}\n\n/** What a competitor's cookie-declaration export contained, in our vocabulary. */\nexport interface ImportedDeclaration {\n source: 'onetrust' | 'cookiebot' | 'cookieyes' | 'unknown';\n cookies: ImportedCookie[];\n /** Rows whose category could not be placed — review these first. */\n unmapped: Array<{ name: string; category: string }>;\n}\n\n/**\n * A page that refused to load the banner renderer. The site is live and configured, so\n * every other signal looks healthy; this is the one that says nobody can be asked.\n */\nexport interface BlockedReport {\n cbid: string;\n /** Origin of the page the embed was running on. */\n page: string;\n /** The bundle URL that page refused. */\n url: string;\n reason: string;\n firstSeen: number;\n lastSeen: number;\n /** How many times this page has reported. Repeats are counted, not stored. */\n count: number;\n}\n\n/** A webhook delivery that failed every retry. Replay it by `id`. */\nexport interface WebhookDeadLetter {\n id: string;\n orgId: string;\n subscriptionId: string;\n url: string;\n eventType: string;\n cbid: string | null;\n payload: unknown;\n attempts: number;\n lastStatus?: number;\n lastError?: string;\n failedAt: number;\n}\n\n/** What a test delivery's endpoint answered. */\nexport interface WebhookTestResult {\n ok: boolean;\n status?: number;\n error?: string;\n}\n\n/** The key's organisation. */\nexport interface Org {\n id: string;\n name: string;\n plan: string;\n logoUrl: string | null;\n}\n\nexport type MailProviderKind = 'brevo' | 'sendgrid' | 'postmark' | 'resend' | 'ses' | 'smtp';\n\n/**\n * The org's own mail provider for rights-request mail. The credential is never returned —\n * `secretLast4` only. `sender` says whether verification codes go through it right now\n * (`own`) or are not mailed at all (`none`): there is no shared sender for rights mail.\n */\nexport interface MailProvider {\n configured: boolean;\n sender: 'own' | 'none';\n provider?: MailProviderKind;\n fromEmail?: string;\n fromName?: string;\n replyTo?: string;\n ses?: { region: string; accessKeyId: string };\n smtp?: { host: string; port: number; security: 'tls' | 'starttls'; username: string };\n secretLast4?: string;\n status?: 'untested' | 'verified' | 'failing';\n verifiedAt?: number | null;\n lastTest?: { ok: boolean; at: number; error?: string } | null;\n updatedAt?: number;\n /** Only on `set`: no verification codes are mailed until a test succeeds. */\n note?: string;\n}\n\n/** Save a provider. `secret` is write-only; omit it to keep the stored one (same provider only). */\nexport interface MailProviderInput {\n provider: MailProviderKind;\n fromEmail: string;\n fromName?: string | null;\n replyTo?: string | null;\n secret?: string;\n ses?: { region: string; accessKeyId: string };\n smtp?: { host: string; port: 25 | 465 | 587 | 2525; security: 'tls' | 'starttls'; username: string };\n}\n\nexport type RightsCaptchaProvider = 'turnstile' | 'hcaptcha' | 'recaptcha_v2' | 'recaptcha_v3';\n\n/**\n * The rights page's captcha in force: your own keys (`own`, recommended) or none (`off`, the default).\n * Cookie Munch's own captcha is never used on rights pages. The secret is never returned.\n */\n/** Whether hosted consent sync is set up for a site. The secret itself is never read back. */\nexport interface ConsentSyncSecret {\n cbid: string;\n configured: boolean;\n secretLast4: string | null;\n /** Epoch ms the current secret was minted. */\n createdAt: number | null;\n}\n\n/**\n * Who a preference-centre token is for: a site of your organisation and exactly one of `email`\n * (resolved as the emailed preference link resolves it) or `subjectId`.\n */\nexport type CenterTokenInput = { cbid: string; ttlSeconds?: number } & (\n | { email: string; subjectId?: never }\n | { subjectId: string; email?: never }\n);\n\n/** A preference-centre token. A credential: hand it to `CookieMunch.setSubjectToken`, never put it in a shared URL or a log. */\nexport interface CenterToken {\n token: string;\n /** ISO 8601. */\n expiresAt: string;\n}\n\n/** A freshly minted or rotated consent-sync secret: `secret` is shown this once. */\nexport interface ConsentSyncSecretMinted extends ConsentSyncSecret {\n secret: string;\n}\n\nexport interface RightsCaptcha {\n /** `site`: the site's own setting; `org`: the organisation default; `default`: neither (off). */\n source: 'site' | 'org' | 'default';\n mode: 'own' | 'off';\n provider?: RightsCaptchaProvider;\n siteKey?: string;\n /** reCAPTCHA v3 only. */\n minScore?: number;\n secretSet: boolean;\n secretLast4?: string;\n status: 'verified' | 'disabled';\n verifiedAt: number | null;\n updatedAt: number | null;\n /** Only when a `set` turned the captcha off: what that costs. */\n warning?: string;\n}\n\n/**\n * `own` needs provider, siteKey and (the first time) secret; the provider is asked to accept the secret before\n * anything is saved (422 captcha_secret_invalid / 502 captcha_unreachable). `off` removes spam protection beyond\n * the hidden spam-trap field, the rate limits and the email code.\n */\nexport interface RightsCaptchaInput {\n mode: 'own' | 'off';\n provider?: RightsCaptchaProvider;\n siteKey?: string;\n /** Write-only. Omit to keep the stored one (same provider, same place). */\n secret?: string;\n /** reCAPTCHA v3 only: 0.1–0.9, default 0.5. */\n minScore?: number;\n}\n\nexport type RightsIdentityType = 'clerk' | 'atlas' | 'oidc_jwt';\n\n/**\n * The sign-in provider rights requests are verified with: a visitor signed in to your site is\n * verified by their session, with no code mail. Nothing here is secret.\n */\nexport interface RightsIdentity {\n /** `site`: the site's own setting; `org`: the organisation default; `default`: neither (none). */\n source: 'site' | 'org' | 'default';\n configured: boolean;\n type?: RightsIdentityType;\n issuer?: string;\n jwksUrl?: string;\n audience?: string;\n authorizedParties?: string[];\n emailClaim?: string;\n emailVerifiedClaim?: string;\n sessionCookieName?: string;\n /** Present (true) only when set: clerk/atlas tokens without the verified claim are accepted. */\n trustEmailClaim?: true;\n status: 'verified' | 'not_configured';\n verifiedAt: number | null;\n updatedAt: number | null;\n}\n\n/**\n * The JWKS is fetched and checked before anything is saved (502 identity_jwks_unreachable /\n * 422 identity_jwks_invalid). `authorizedParties` (your own origins) is required for clerk and atlas;\n * oidc_jwt needs an `audience` or `authorizedParties`. Issuer and JWKS URL are https on a host name,\n * never an IP address. The token must carry the email and a verified-email claim set to `true`\n * (clerk/atlas with `trustEmailClaim`: the verified claim may be absent).\n */\nexport interface RightsIdentityInput {\n type: RightsIdentityType;\n /** Exactly the token's `iss` (Clerk: your Frontend API URL). */\n issuer: string;\n /** Required for oidc_jwt; clerk/atlas default to `<issuer>/.well-known/jwks.json`. */\n jwksUrl?: string;\n audience?: string;\n authorizedParties?: string[];\n /** Default `email`. */\n emailClaim?: string;\n /** Default `email_verified`. */\n emailVerifiedClaim?: string;\n /** Default `__session`: the cookie the hosted page on your custom domain reads. */\n sessionCookieName?: string;\n /**\n * clerk/atlas only, default false. True when your provider has no verified-email claim and every\n * address it puts in the email claim is verified: a token without the verified claim is accepted.\n */\n trustEmailClaim?: boolean;\n}\n\n/** The organisation's own data-controller identity (GET /v1/org/controller). */\nexport interface OrgControllerIdentity {\n /** False when none is set; then only `updatedAt` (null) is present. */\n set: boolean;\n legalName?: string;\n contactEmail?: string;\n address?: string;\n /** The Data Protection Officer: a name, an email address, or both. */\n dpoContact?: string;\n updatedAt: number | null;\n}\n\n/** Replaces the identity: a field left out is removed. `legalName` is required. */\nexport interface OrgControllerInput {\n legalName: string;\n contactEmail?: string;\n address?: string;\n dpoContact?: string;\n}\n\nexport interface MailProviderTestResult {\n ok: boolean;\n sentTo: string;\n error?: string;\n mailProvider: MailProvider;\n}\n\n/** Change an org's name or logo. `logoUrl: null` removes the logo. */\nexport interface OrgUpdate {\n name?: string;\n logoUrl?: string | null;\n}\n\n/** One entry in the org's audit log. */\nexport interface AuditEntry {\n id: string;\n orgId: string;\n actorUserId: string;\n actorEmail?: string;\n action: string;\n target?: string;\n meta?: Record<string, unknown>;\n at: number;\n}\n\n/** Rename a key, or replace its scopes or the properties it is locked to. */\nexport interface ApiKeyUpdate {\n name?: string;\n scopes?: string[];\n cbids?: string[];\n /** A new expiry (epoch ms or ISO date-time), or null to remove it. */\n expiresAt?: number | string | null;\n}\n\n/** An image to upload: base64 (or a `data:` URL) plus its type. */\nexport interface AssetUpload {\n data: string;\n contentType: 'image/png' | 'image/jpeg' | 'image/webp' | 'image/gif' | 'image/svg+xml';\n}\n\n/** Result of erasing a subject's consent for a deletion request. */\nexport interface DsarEraseResult {\n erased: number;\n encryptionEnabled: boolean;\n /** Present when no KEK is configured, so nothing was cryptographically erased. */\n warning?: string;\n request: DsarRequest;\n}\n\n/** Result of exporting a subject's consent for an access or portability request. */\nexport interface DsarExportResult {\n records: unknown[];\n count: number;\n request: DsarRequest;\n}\n\n/** Input for creating a webhook subscription. */\nexport interface WebhookCreate {\n url: string;\n events: string[];\n cbid?: string;\n}\n\n/** Fields to change on a webhook subscription. Only those present are sent. */\nexport interface WebhookUpdate {\n url?: string;\n events?: string[];\n /** One property, or null for every property in the org. */\n cbid?: string | null;\n /** false pauses delivery without deleting the subscription. */\n active?: boolean;\n}\n\n/** One site to create in a bulk call. */\nexport interface BulkSiteInput {\n domain: string;\n cbid?: string;\n platform?: string;\n}\n\n/** Per-item outcome of a bulk create, in input order. */\nexport interface BulkSiteResult {\n ok: boolean;\n cbid?: string;\n domain?: string;\n error?: string;\n}\n\n/** Input for creating a brand kit. */\nexport interface BrandKitCreate {\n name: string;\n theme: unknown;\n content?: unknown;\n logoUrl?: string;\n customCss?: string;\n}\n\n/** A lightweight summary of an account-level banner, as returned by GET /v1/banners. */\nexport interface BannerSummary {\n id: string;\n name: string;\n updatedAt: number;\n assignedCbids: string[];\n /** How many warning-severity consent-button parity findings the design has (0 = fair). */\n designWarningCount: number;\n}\n\n/** A full account-level banner record, including its config JSON. */\nexport interface BannerRecord {\n id: string;\n orgId: string;\n name: string;\n json: SiteConfig;\n createdAt: number;\n updatedAt: number;\n /** Consent-button parity findings in the design. Never blocks a save. */\n designWarnings?: ButtonParityFinding[];\n}\n\n/**\n * Input for the public consent-log ingest endpoint (POST /api/v1/consent) — the\n * tamper-evident, hash-chained ledger write the browser embed makes. Mirrors the\n * server's ConsentPayload; optional fields are sent only when set. Records are\n * anonymised (IP truncated) and hash-chained server-side.\n */\nexport interface ConsentIngestInput {\n /** The registered site id the consent belongs to. */\n cbid: string;\n /** Stable per-subject consent-receipt id (used for erase/export). */\n stamp: string;\n /** The subject's cookie-category choices. */\n choices: { preferences: boolean; statistics: boolean; marketing: boolean };\n /** How consent was captured. */\n method: 'explicit' | 'implied';\n /** Consent/notice version number. */\n ver: number;\n /** Epoch-ms the decision was made. */\n utc: number;\n /** The URL / screen where consent was captured. */\n url: string;\n /** Optional IAB TCF consent string (TCF mode only). */\n tcString?: string;\n /** Optional GPP string (GPP mode only). */\n gppString?: string;\n /** Optional named-purpose map beyond the standard categories. */\n purposes?: Record<string, boolean>;\n /** Optional opaque digest of the exact notice text shown to the subject. */\n subjectPolicyHash?: string;\n /** Optional map: named purpose → RoPA entry id. */\n purposeRopa?: Record<string, string>;\n /** Optional A/B variant id. */\n variant?: string;\n /**\n * Optional, app-supplied STABLE cross-surface subject id. Lets an org retrieve one\n * subject's consent history across ALL its sites/surfaces (web + mobile + desktop).\n * Opaque — stored and bound into the record hash server-side, never interpreted.\n */\n subjectId?: string;\n}\n\n/** One of a person's identifiers: an identity-space code plus the normalised value. */\nexport interface Identifier {\n space: string;\n value: string;\n}\n\nexport interface ConsentDecisionInput {\n purpose: string;\n allowed: boolean;\n legalBasis?: string;\n jurisdiction?: string;\n provenance?: string;\n collectedAt?: number;\n}\n\nexport interface ProfileAttributeInput {\n value: string;\n purpose?: string;\n collectedAt?: number;\n}\n\nexport interface SubscriptionTopicInput {\n code: string;\n name?: string;\n channels: string[];\n downstream?: Record<string, string>;\n}\n\n/** A warehouse-native enforcement rule. See POST /v1/discovery/enforcement. */\nexport interface EnforcementRuleInput {\n id: string;\n action: 'mask' | 'restrict';\n /** Select columns at or above this sensitivity. */\n minSensitivity?: 'personal' | 'sensitive';\n /** Select columns in any of these categories. */\n categories?: string[];\n /** mask: roles that still see the raw value. */\n allowRoles?: string[];\n /** restrict: the purpose a subject must have consented to. */\n purpose?: string;\n /** restrict: the column holding the subject id. Default SUBJECT_ID. */\n subjectColumn?: string;\n}\n\nexport interface RegulationSummary {\n id: string;\n jurisdiction: string;\n name: string;\n /** ISO date the law takes (or took) effect. */\n effective: string;\n model: 'opt-in' | 'opt-out';\n obligations: string[];\n /** Primary source — the statute or the regulator. */\n source: string;\n}\n\nexport interface RegulatoryFeedResult {\n /** When a human last checked the dataset against its sources. */\n reviewedAt: string;\n /** Days since that review. */\n ageDays: number;\n /** How long a review is considered good for. */\n reviewAfterDays: number;\n /** True once the review interval has passed — treat the answer with caution. */\n stale: boolean;\n regulations: RegulationSummary[];\n}\n\nexport interface AiInspectInput {\n prompt: string;\n purpose: string;\n model?: string;\n actor?: string;\n direction?: 'prompt' | 'response';\n /** The subject's current permits, when the call concerns an identified person. */\n consent?: Record<string, boolean>;\n}\n\nexport interface AiInspectResult {\n action: 'allow' | 'redact' | 'block';\n /** The text to forward: redacted when `redact`, empty when `block`. */\n prompt: string;\n findings: unknown[];\n reasons: string[];\n policyIds: string[];\n blocked: boolean;\n direction: 'prompt' | 'response';\n}\n\nexport type FulfillmentOp = 'locate' | 'export' | 'erase' | 'optOut';\n\n/** A child org provisioned by a reseller. */\nexport interface ResellerChild {\n id: string;\n name: string;\n status: 'active' | 'suspended';\n delegatedAccess?: boolean;\n /** Per-child DSAR routing override; unset defers to the reseller's policy. */\n dsarRouting?: 'reseller' | 'child';\n createdAt?: number;\n usage?: unknown;\n}\n\nexport interface ResellerChildList {\n children: ResellerChild[];\n /** Pooled usage across the reseller and all its children, when metering is wired. */\n aggregate?: { childOrgs: number; sites: number; monthlyEvents: number; monthlyEventsCap: number | null; overCap: boolean };\n}\n\nexport interface ResellerChildCreate {\n name: string;\n ownerEmail?: string;\n /** legalName, contactEmail, address, dpoContact. */\n controller?: Record<string, unknown>;\n /** Branding overrides, merged onto the reseller's white-label defaults. */\n whiteLabel?: Record<string, unknown>;\n delegatedAccess?: boolean;\n /** Also issue an API key for the new child; it is returned once, as `apiKey`. */\n mintKey?: boolean;\n /** Scopes for that key. Defaults to [\"sites:read\"]. */\n keyScopes?: string[];\n}\n\nexport interface ResellerChildPatch {\n status?: 'active' | 'suspended';\n delegatedAccess?: boolean;\n /** Pin this child's DSAR routing; null clears the override. */\n dsarRouting?: 'reseller' | 'child' | null;\n controller?: Record<string, unknown>;\n}\n\nexport interface ChildKeyInput {\n name?: string;\n scopes?: string[];\n /** Lock the key to these properties of the child org. */\n cbids?: string[];\n}\n\nexport interface PolicyOptions {\n /** Contact email shown in the policy. Falls back to the org controller's, then the owner's. */\n contactEmail?: string;\n /** YYYY-MM-DD. Defaults to today. */\n effectiveDate?: string;\n /** Override the jurisdictions the policy addresses. */\n jurisdictions?: string[];\n}\n\nexport interface AdPersonalizationInput {\n enabled: boolean;\n /** Whether personalised ads start on before the visitor chooses. */\n default?: boolean;\n label?: string;\n}\n\nexport interface SessionAnalysisInput {\n /** A HAR export of the captured session. */\n har?: unknown;\n /** Or the requests directly. */\n requests?: unknown[];\n /** The consent state the session ran under. */\n consent?: Record<string, boolean>;\n /** Whether Global Privacy Control was set. */\n gpc?: boolean;\n}\n\n/**\n * How to drive one system's rights API, as data. This is what makes a connection specific to\n * a platform — Cookie Munch names none of them, so connecting a new system is a profile\n * rather than a release. Paths are relative to `baseUrl`; placeholders are `{email}`,\n * `{subject}`, `{operation}`, `{correlation}` and `{id}`.\n */\nexport interface ExecutorProfile {\n /** How requests carry the credential. Defaults to `Authorization: Bearer {secret}`. */\n auth?: { header: string; format: string };\n /**\n * Optional first step: turn the subject's email into the id this system uses. `noMatch`\n * names how the system says \"no record of that person\" in its body — a sentinel id is\n * never read as a person when it is set.\n */\n subjectLookup?: {\n method: 'GET' | 'POST';\n path: string;\n body?: unknown;\n idPath: string;\n noMatch?: { statusPath: string; values: string[] };\n /** How this system says the person was already removed; closes the task as `already_erased`. Checked before `noMatch`. */\n alreadyErased?: { statusPath: string; values: string[] };\n };\n /** Response header carrying the system's version, recorded in the task log. Default `X-Version`. */\n versionHeader?: string;\n /** How to open a request. An operation left out of `operations` is one this system does not run. */\n open: {\n method: 'POST' | 'PUT';\n path: string;\n body?: unknown;\n operations: { export?: string; erase?: string };\n idPath: string;\n statusPath?: string;\n };\n /** How to ask how it is going. Polling is the truth; a webhook only prompts a check. */\n poll: {\n method?: 'GET';\n path: string;\n statusPath: string;\n resultPath?: string;\n /** Whether `resultPath` holds the bundle itself or a URL to fetch it from. */\n resultIs?: 'inline' | 'url';\n /** Where a date the work is waiting for lives (an erasure in a grace period). */\n scheduledPath?: string;\n };\n /** That system's status words → the three answers a sub-task can have. */\n statuses: { running: string[]; done: string[]; failed: string[] };\n idempotencyHeader?: string;\n /**\n * An identity platform's app grant, revoked on erasure before anything else: look it up by\n * `{email}`, then end it with `{grant}`. A test looks it up but never revokes it.\n */\n grantRevoke?: {\n lookup: { method: 'GET' | 'POST'; path: string; body?: unknown; grantIdPath: string };\n revoke: { method: 'POST' | 'DELETE'; path: string; body?: unknown };\n };\n /** How that system signs its webhooks, if it sends any. */\n webhook?: {\n algorithm: 'hmac-sha256';\n /** Must include `{body}`, or the signature proves nothing about it. */\n signedPayload: string;\n headers: { signature: string; timestamp?: string; id?: string };\n encoding?: 'base64' | 'hex';\n prefix?: string;\n toleranceSeconds?: number;\n timestampUnit?: 'seconds' | 'milliseconds' | 'auto';\n };\n}\n\n/** A system connected to run part of a rights request. Credentials are never included. */\nexport interface DsarExecutor {\n id: string;\n /** The name it answers to in a fulfillment plan, e.g. `identity` or `crm`. */\n system: string;\n profile: ExecutorProfile;\n baseUrl: string;\n keyPrefix: string;\n hasWebhookSecret: boolean;\n /** Open a sub-task automatically when a request reaches fulfilment. */\n auto: boolean;\n /** What the subject reads on this system's notice line when it holds nothing about them. */\n subjectNote?: string;\n createdAt: number;\n /** Where that system should send its completion webhooks. On every connection, secret or not. */\n webhookUrl: string;\n /**\n * The last canary health check (hourly, and on `testExecutor(id, { canary: true })`). null\n * until the first; cleared when the address, profile or key changes.\n */\n health: ExecutorHealth | null;\n /** The preset it was built from; absent when hand-written. */\n preset?: ExecutorPresetName;\n presetVersion?: number;\n /** Newer than `presetVersion`: `upgradeExecutorPreset` applies it. */\n presetLatestVersion?: number;\n /** The non-secret params it was built with, e.g. `{ orgId }`. */\n presetParams?: Record<string, string>;\n /** How the stored address and profile differ from what its preset version builds. Empty: unmodified. */\n presetDiff?: ConfigChange[];\n}\n\n/** A connection's last canary health check. */\nexport interface ExecutorHealth {\n lastCheckedAt: number;\n ok: boolean;\n verdict: string;\n /** e.g. `base_url_redirects`, `credential_unavailable`. */\n code?: string;\n}\n\n/** A preset's name, as `executorPresets()` lists them. Not enumerated here: presets are data. */\nexport type ExecutorPresetName = string;\n\n/**\n * Connect a system from a versioned preset instead of a hand-written profile. Each preset\n * lists the params it takes (`executorPresets()`); a preset waiting on another platform is\n * refused with `preset_waiting_on_platform`.\n */\nexport interface DsarExecutorPresetInput {\n preset: ExecutorPresetName;\n presetVersion?: number;\n secretKey: string;\n system?: string;\n baseUrl?: string;\n orgId?: string;\n atlasIdAppId?: string;\n webhookSecret?: string;\n auto?: boolean;\n cbid?: string;\n}\n\n/** What `upgradeExecutorPreset` takes: a version, a preset to adopt, or params to add. */\nexport interface ExecutorPresetUpgrade {\n preset?: ExecutorPresetName;\n presetVersion?: number;\n orgId?: string;\n atlasIdAppId?: string;\n}\n\n/** The presets, which are usable now, and this org's canary address. */\nexport interface ExecutorPresets {\n canaryEmail: string;\n presets: Array<{\n name: ExecutorPresetName;\n title: string;\n latestVersion: number;\n defaultSystem?: string;\n params: Array<{ name: string; required: boolean; description: string }>;\n available: boolean;\n waitingOn?: { platform: string; items: string[]; operations: string[] };\n }>;\n}\n\n/** What a connection write would do (`?dryRun=1`). Nothing was stored. */\nexport interface DsarExecutorDryRun {\n dryRun: true;\n /** The connection as it would be, credentials excluded. A create has no `id` or `webhookUrl` yet. */\n executor: Partial<DsarExecutor> & Record<string, unknown>;\n /** Each field that would change. */\n diff: ConfigChange[];\n warnings?: Array<{ field: string; reason: string }>;\n}\n\n/** Where a webhook check stopped. */\nexport type WebhookStage = 'configuration' | 'headers' | 'timestamp' | 'prefix' | 'signature';\n\n/** Which key a verified delivery matched: a `whsec_` secret's base64-decoded remainder, or the secret as typed. */\nexport type WebhookKeyForm = 'decoded' | 'raw';\n\n/** Whether a supplied delivery verified (`stage` and `reason` are null when it did). */\nexport interface ExecutorWebhookTestResult {\n verified: boolean;\n stage: WebhookStage | null;\n reason: string | null;\n /** Which key form matched; null when refused. */\n keyForm: WebhookKeyForm | null;\n}\n\n/** One delivery to a connection's webhook. Never the secret, the signature or the body. */\nexport interface ExecutorWebhookDelivery {\n id: string;\n executorId: string;\n at: number;\n verified: boolean;\n stage: WebhookStage | null;\n reason: string | null;\n /** The HTTP status we answered with. */\n status: number;\n /** The first 16 hex characters of SHA-256 over the raw body. */\n bodySha256: string;\n bodyBytes: number;\n /** Which key form matched; null when refused (or logged before this was kept). Never the key. */\n keyForm: WebhookKeyForm | null;\n}\n\nexport interface DsarExecutorInput {\n /** The name this connection answers to in a plan, e.g. `identity` or `crm`. */\n system: string;\n /** The system's public https origin. */\n baseUrl: string;\n /** Its API key. Stored encrypted; never returned. */\n secretKey: string;\n /**\n * The signing secret of its webhook endpoint. Optional — polling closes tasks without it.\n * A `whsec_<base64>` secret (standard base64 or base64url, padded or not) verifies a delivery\n * signed with either the base64-decoded bytes or the literal string; anything else only with\n * its UTF-8 bytes.\n */\n webhookSecret?: string;\n auto?: boolean;\n /** What the subject reads when this system holds nothing about them (≤500 chars, never a key). */\n subjectNote?: string;\n /** How to drive it. */\n profile: ExecutorProfile;\n}\n\nexport type DsarExecutorCreated = DsarExecutor;\n\n/** The fields `updateExecutor` may change. */\nexport interface DsarExecutorPatch {\n profile?: unknown;\n baseUrl?: string;\n secretKey?: string;\n webhookSecret?: string | null;\n auto?: boolean;\n /** null removes it. */\n subjectNote?: string | null;\n}\n\n/** One entry in a fulfilment task's status history. */\nexport interface DsarTaskEvent {\n at: number;\n kind: 'created' | 'changed' | 'version_changed';\n status: 'pending' | 'running' | 'done' | 'failed';\n detail?: string;\n outcome?: DsarTaskOutcome;\n scheduledFor?: number;\n externalRef?: string;\n}\n\n/** What a task's answer means for the person. `exported` only once the export was fetched. */\nexport type DsarTaskOutcome = 'located' | 'exported' | 'erased' | 'no_record' | 'done_without_artifact' | 'not_applicable' | 'already_erased' | 'external_identity';\n\n/** One request made to a connected system on a task's behalf, credentials removed. Kept 30 days. */\nexport interface DsarTaskLogEntry {\n id?: string;\n system?: string;\n step: 'lookup' | 'open' | 'poll' | 'export' | 'grant';\n method: string;\n path: string;\n /** Absent when the system never answered. */\n status?: number;\n latencyMs: number;\n requestHeaders?: Record<string, string>;\n /** The first ~2 KB of what was sent. */\n request?: string;\n /** The first ~2 KB of what came back. */\n response?: string;\n error?: string;\n /** The system's version header on this answer. */\n version?: string;\n /** This answer was a redirect (origin and path; never the query). Only same-origin, method-keeping ones are followed. */\n redirect?: { status: number; location: string };\n at: number;\n}\n\n/** One fulfilment task with its history and its upstream request log. */\nexport interface DsarTask {\n id: string;\n requestId: string;\n system: string;\n operation: FulfillmentOp;\n status: 'pending' | 'running' | 'done' | 'failed';\n detail?: string;\n outcome?: DsarTaskOutcome;\n attempts: number;\n executor?: string;\n externalRef?: string;\n scheduledFor?: number;\n artifacts?: Array<{ kind: string; system: string; ref: string }>;\n createdAt: number;\n updatedAt: number;\n events: DsarTaskEvent[];\n log: DsarTaskLogEntry[];\n /** Versions the system reported while working on this task, in order of change. */\n upstreamVersions?: string[];\n /** The system was redeployed while this task was in flight. */\n versionChanged: boolean;\n}\n\n/** What testing a connection found. Nothing was filed. */\nexport interface ExecutorTestResult {\n system: string;\n /** Did the lookup find the person; null when there is no lookup step or it failed. */\n matched: boolean | null;\n subject?: string;\n operationWord?: string;\n /** The open request a real request would send, when it was not sent. */\n wouldSend?: { method: string; path: string; body: unknown };\n /** One sentence: what a real request would have done. */\n verdict: string;\n ok: boolean;\n /** Why not, when there is a code: `base_url_redirects` — the baseUrl redirected; the verdict names the status and target. */\n code?: string;\n /** The subject's app grant at an identity platform, when the profile has a grant step. Never revoked by a test. */\n grant?: string;\n /** Present when the test used the org canary address, with the address in `email`. */\n canary?: boolean;\n email?: string;\n /** Every upstream answer, as the system gave it (credentials removed). */\n exchanges: DsarTaskLogEntry[];\n}\n\nexport interface ExecutorTestInput {\n /** The address to look up. Required unless `canary` is true. */\n email?: string;\n /** Use this org's canary address — no real person. A canary `locate` also updates `health`. */\n canary?: boolean;\n /** Default `locate`. */\n operation?: 'locate' | 'export' | 'erase';\n /** For `export` only: open a real export and poll it once. An erasure is never sent. */\n open?: boolean;\n}\n\nexport interface DsarTaskExport {\n system: string;\n ref: string;\n /** The bundle as that system returned it. Not stored here. */\n bundle: unknown;\n}\n\n/** A language the banner already has copy for. */\nexport interface SupportedLanguage {\n /** BCP-47 tag, lowercase. Matched exactly, then by language prefix. */\n code: string;\n /** English name. */\n name: string;\n /** The language's name in itself. */\n endonym: string;\n /** Written right to left; the banner flips for these. */\n rtl: boolean;\n /** `bundled` ships in consent.js; `extended` ships in the renderer, fetched when a banner is first drawn. */\n source: 'bundled' | 'extended';\n}\n\nexport interface CookieMunchClient {\n me(): Promise<Identity>;\n /**\n * Languages the banner already speaks. Diff this against the locales your visitors\n * use to see which ones you still need to write copy for in `banner.i18n`.\n */\n languages(): Promise<SupportedLanguage[]>;\n orgs: {\n /**\n * Every organisation on this key's account, each with its effective plan and where that\n * comes from, plus the per-account org cap and the upgrade URL. Needs an unscoped key that\n * is not locked to properties.\n */\n list(): Promise<AccountOrgs>;\n /**\n * Create a sibling organisation owned by the same account — for a separate business of\n * your own. Needs an unscoped key. Not reseller provisioning, which is for your customers.\n */\n create(\n name: string,\n opts?: { mintKey?: boolean; keyScopes?: string[] },\n ): Promise<{ org: { id: string; name: string; role: string }; apiKey?: string; keyError?: string }>;\n };\n sites: {\n list(): Promise<Site[]>;\n create(input: SiteCreate): Promise<Site>;\n get(cbid: string): Promise<Site>;\n /**\n * Rename or move a site: domain, display name, aliases. The cbid and the consent ledger stay; a domain\n * move clears verification. `{ dryRun: true }` answers with the diff and stores nothing.\n */\n update(cbid: string, patch: SiteUpdate, opts?: { dryRun?: boolean }): Promise<SiteUpdateResult>;\n delete(cbid: string): Promise<void>;\n /** Partner platforms granted this site's consent events, and the partners on offer. */\n listPartners(cbid: string): Promise<{ links: PartnerLink[]; partners: PartnerCatalogEntry[] }>;\n /** Grant a partner (Forumulate, Atlas) the site's consent events — no secret is created. Idempotent. */\n linkPartner(cbid: string, input: PartnerLinkCreate, opts?: { dryRun?: boolean }): Promise<{ change: 'created' | 'updated' | 'unchanged'; link: PartnerLink; dryRun?: true }>;\n /** Revoke a partner link: deliveries stop at once. */\n unlinkPartner(cbid: string, linkId: string): Promise<{ link: PartnerLink }>;\n getConfig(cbid: string): Promise<SiteConfig>;\n /**\n * Replaces the whole config — a top-level field you omit reverts to its default. With\n * `{ keep: 'unspecified' }` (`?keep=unspecified`) an omitted top-level field keeps its\n * stored value instead; each field you send is still replaced whole, and one sent as\n * `null` reverts to its default.\n */\n putConfig(cbid: string, config: SiteConfig, opts?: PutConfigOptions): Promise<SiteConfig>;\n /**\n * Changes some of the config — a JSON Merge Patch (RFC 7386): omitted fields keep their\n * stored value, nested objects merge, and `null` removes a field (it takes its default):\n * `{ banner: { i18n: { es: null } } }` drops the Spanish override. Arrays replace whole.\n */\n patchConfig(cbid: string, config: Partial<SiteConfig>): Promise<SiteConfig>;\n /**\n * Preview a config write without saving it: the merged result and a diff. `mode: 'put'`\n * previews a replace (the diff then lists everything that would revert to its default);\n * add `keep: 'unspecified'` to preview a replace that keeps omitted fields.\n */\n dryRunConfig(cbid: string, config: Partial<SiteConfig>, opts?: { mode?: 'patch' | 'put' } & PutConfigOptions): Promise<ConfigDryRun>;\n /**\n * Check a proposed config for consent-button parity — an Accept all pushed harder than\n * Reject all — without saving it. A partial v1 config is merged over the stored one (like\n * PATCH); a v2 design, or `replace: true`, is linted as sent. Needs only `sites:read`.\n */\n lintConfig(cbid: string, config: Partial<SiteConfig>, opts?: { replace?: boolean }): Promise<ButtonParityLint>;\n cookies(cbid: string): Promise<CookieDeclaration>;\n /**\n * Start a cookie crawl. Answers at once with `scanId` and `state: 'queued'`; `wait`\n * (seconds, ≤60) holds the request until the scan is done or failed.\n */\n scan(cbid: string, opts?: { wait?: number }): Promise<ScanStatus>;\n /** The last scan's state. `wait` (seconds, ≤60) holds the request until a running scan settles. */\n scanStatus(cbid: string, opts?: { wait?: number }): Promise<ScanStatus>;\n /**\n * Resolve once the site's scan is done or failed (read `state` and `error`), long-polling\n * the server. At `timeoutMs` (default 120 000) it resolves with the still-running state.\n */\n waitForScan(cbid: string, opts?: { timeoutMs?: number }): Promise<ScanStatus>;\n ab(cbid: string): Promise<AbResult[]>;\n /** Generate the site's privacy and cookie policy, as Markdown. */\n policy(cbid: string, opts?: PolicyOptions): Promise<string>;\n /** Turn on the personalised-ads split on whichever banner form the site uses. */\n setAdPersonalization(cbid: string, input: AdPersonalizationInput): Promise<unknown>;\n /** Analyse a captured session: trackers that fired after opt-out, personal data leaving the page. */\n analyzeSession(cbid: string, input: SessionAnalysisInput): Promise<unknown>;\n /** Which banner design this site uses, or null when none is assigned. */\n banner(cbid: string): Promise<{ bannerId: string | null }>;\n /**\n * Pages where the embed could not load its banner renderer — the host page's CSP or\n * Trusted Types policy refused it, so nobody on that page can be asked. An empty list\n * is the healthy answer.\n */\n blocked(cbid: string): Promise<{ reports: BlockedReport[] }>;\n /**\n * Read a cookie declaration exported from another CMP and translate its categories\n * into ours. Nothing is applied: their vocabulary is not ours, and a cookie in the\n * wrong category is a tag firing against a refusal, so the result is for review.\n */\n importDeclaration(cbid: string, data: string): Promise<ImportedDeclaration>;\n /**\n * Import AND declare: every row that mapped to one of our categories and has a purpose\n * is declared; the rest come back in `skipped`. Already-declared cookies are kept\n * unless `overwrite`.\n */\n applyImport(cbid: string, data: string, opts?: { overwrite?: boolean }): Promise<AppliedImport>;\n /**\n * Declare what one cookie is. Overrides scanned, observed and database classification\n * in the API, the preference modal and `data-cc=\"declaration\"`. A name ending in `*`\n * declares a prefix wildcard. `dryRun` validates and returns the would-be list and a\n * diff without storing anything.\n */\n declareCookie(\n cbid: string,\n name: string,\n input: DeclaredCookieInput,\n opts?: { dryRun?: boolean },\n ): Promise<{ cookie: DeclaredCookie; warnings?: DeclarationWarning[] } & Partial<DeclarationDryRun>>;\n /**\n * Retire a cookie the site renamed or stopped setting: it leaves the banner's list,\n * `data-cc=\"declaration\"` and the public feed, and stays listed for you with `retired`.\n * A name ending in `*` retires every cookie with that prefix. If a crawl or a visit sees\n * it again afterwards it is listed again, flagged `review: 'retired_but_seen'`.\n */\n retireCookie(\n cbid: string,\n name: string,\n input?: { renamedTo?: string; note?: string },\n opts?: { dryRun?: boolean },\n ): Promise<{ cookie: RetiredCookie; matched: string[]; dryRun?: true; retired?: RetiredCookie[] }>;\n /** Un-retire a cookie: it is listed again everywhere. */\n unretireCookie(cbid: string, name: string): Promise<void>;\n /** What un-retiring would leave; nothing is removed. */\n unretireCookie(cbid: string, name: string, opts: { dryRun: true }): Promise<{ dryRun: true; retired: RetiredCookie[] }>;\n /** Every declaration preset (atlas, forumulate, chatly, cookiemunch, referally): params, what it declared here, manifest status. */\n cookiePresets(cbid: string): Promise<{ presets: CookiePreset[] }>;\n /**\n * Apply (or re-sync) a vendor preset: declares what that platform writes, marked\n * `vendor`. Names you declared yourself are kept (`skipped`). `dryRun` stores nothing.\n */\n applyCookiePreset(cbid: string, vendor: CookiePresetVendor, params?: CookiePresetParams, opts?: { dryRun?: boolean }): Promise<CookiePresetResult>;\n /** Remove every declaration a vendor preset made. */\n removeCookiePreset(cbid: string, vendor: CookiePresetVendor, opts?: { dryRun?: boolean }): Promise<{ vendor: CookiePresetVendor; removed: string[]; dryRun?: true; declared?: DeclaredCookie[]; diff?: DeclarationDiff }>;\n /** Remove every stored (scanned or observed) row of this name. A declaration of it stays. */\n deleteObservedCookie(cbid: string, name: string): Promise<void>;\n /** What deleting it would remove; nothing is removed. */\n deleteObservedCookie(cbid: string, name: string, opts: { dryRun: true }): Promise<ObservedPurgeResult>;\n /**\n * Purge observed since T: remove every row a real visit first reported at or after\n * `since` (and before `until`). Crawled rows and declarations stay. ISO-8601 or epoch ms.\n */\n purgeObservedCookies(cbid: string, input: { since: string | number; until?: string | number }, opts?: { dryRun?: boolean }): Promise<ObservedPurgeResult>;\n /** Remove a declaration; the scanned classification applies again. */\n undeclareCookie(cbid: string, name: string): Promise<void>;\n /** What removing a declaration would leave; nothing is removed. */\n undeclareCookie(cbid: string, name: string, opts: { dryRun: true }): Promise<DeclarationDryRun>;\n /**\n * Declare many at once (all-or-nothing). `replace` also removes declarations not listed;\n * `dryRun` answers with what would be stored and a diff, storing nothing.\n */\n declareCookies(\n cbid: string,\n cookies: Array<DeclaredCookieInput & { name: string }>,\n opts?: { replace?: boolean; dryRun?: boolean },\n ): Promise<{ declared: DeclaredCookie[]; saved: number; removed: number; warnings?: DeclarationWarning[]; dryRun?: true; diff?: DeclarationDiff }>;\n snippet(cbid: string, opts?: SnippetOptions): Promise<InstallSnippet>;\n /**\n * Mint a short-lived signed token to see the banner as a visitor in a given region and\n * language sees it — even from a headless browser, and without recording anything.\n */\n previewToken(cbid: string, opts?: PreviewTokenOptions): Promise<PreviewToken>;\n verify(cbid: string, method: 'dns' | 'meta' | 'file' | 'embed'): Promise<VerifyResult>;\n /** Exactly what to publish to prove control of the domain, for each verification method. */\n verifyChallenge(cbid: string): Promise<unknown>;\n /** Create up to 100 sites. Partial success: a bad or duplicate item fails only itself. */\n createBulk(sites: BulkSiteInput[]): Promise<{ results: BulkSiteResult[] }>;\n brand(cbid: string): Promise<BrandExtractionResult>;\n /** Read a site's v2 banner flow (views, categories) plus any lint issues. */\n getFlow(cbid: string): Promise<SiteFlow>;\n /** Apply an ordered batch of structured edit ops; validated + persisted if lint-clean. */\n editFlow(cbid: string, operations: FlowOp[]): Promise<FlowOpResponse>;\n /** Wholesale-replace the flow with a full v2 config; validated + persisted if valid. */\n setFlow(cbid: string, config: Record<string, unknown>): Promise<FlowOpResponse>;\n };\n consent: {\n /**\n * Verify the consent log's tamper-evident hash chain. Each record carries the hash of the\n * one before it, so an edited, reordered or removed record answers `false`. This is the\n * evidence behind the log: proof that what it says today is what it said when written.\n */\n verify(cbid: string): Promise<{ valid: boolean }>;\n stats(cbid: string, query?: RangeQuery): Promise<ConsentDay[]>;\n log(cbid: string, query?: LogQuery): Promise<ConsentLogRow[]>;\n export(cbid: string, query?: RangeQuery): Promise<string>;\n receipt(cbid: string, stamp: string): Promise<SignedReceipt>;\n /** Crypto-erase a subject's consent records by their consent-receipt stamp. Irreversible. */\n eraseSubject(cbid: string, stamp: string): Promise<{ erased: number }>;\n /** Export a data subject's consent records by their consent-receipt stamp (GDPR access/portability). */\n exportSubject(cbid: string, stamp: string): Promise<{ cbid: string; stamp: string; records: unknown[]; count: number }>;\n /** Record a consent decision via the PUBLIC ingest endpoint (POST /api/v1/consent). No `/v1`\n * prefix and no auth is required by the server (the cbid must be a registered site); for\n * server-side / non-browser consent flows. Pass `subjectId` to correlate a subject cross-surface. */\n ingest(input: ConsentIngestInput): Promise<void>;\n };\n dsar: {\n list(): Promise<DsarRequest[]>;\n /**\n * One request, in the same shape `list` returns — note that `create` answers with it\n * wrapped as `{ request }`, so `create(...).request.id` is what you pass here.\n */\n get(id: string): Promise<DsarRequest>;\n create(input: DsarCreate): Promise<{ request: DsarRequest }>;\n /** `opts.messageToSubject` replaces the line the subject reads in the notice — for a rejection, why. */\n advance(id: string, toStatus: DsarStatus, opts?: { messageToSubject?: string }): Promise<{ request: DsarRequest }>;\n /** The subject-facing response notice for a request, as plain text. */\n response(id: string): Promise<string>;\n /**\n * Erase a subject's consent records on one site, for a deletion request past identity\n * verification. Noted on the request. Requires dsar:write and consent:write.\n */\n erase(id: string, cbid: string, stamp: string): Promise<DsarEraseResult>;\n /**\n * A subject's consent records on one site, for an access or portability request past\n * identity verification. Noted on the request. Requires dsar:write and consent:read.\n */\n export(id: string, cbid: string, stamp: string): Promise<DsarExportResult>;\n /**\n * Delete a TEST request (created with `test: true`), with its tasks and their log. A\n * real request cannot be deleted (409 `not_a_test_request`); the deletion is audited.\n */\n delete(id: string): Promise<void>;\n /**\n * Mark a CLOSED request (completed or rejected) as a test, after the fact — for trials\n * filed before `test` existed. An open one is refused (409 `request_not_closed`). Audited\n * as `dsar.marked_test`; afterwards `delete` works on it. `changed` is false on a repeat.\n */\n markTest(id: string): Promise<{ request: DsarRequest; changed: boolean }>;\n };\n vendors: {\n list(): Promise<ScoredVendor[]>;\n create(input: VendorInput): Promise<{ vendor: VendorRecord; risk: RiskScore }>;\n };\n ropa: {\n list(): Promise<RopaEntry[]>;\n create(input: RopaInput): Promise<{ entry: RopaEntry }>;\n /** The org's RoPA (GDPR Art. 30) as CSV. */\n exportCsv(): Promise<string>;\n };\n brandKits: {\n list(): Promise<BrandKit[]>;\n create(input: BrandKitCreate): Promise<{ kit: BrandKit }>;\n delete(id: string): Promise<void>;\n };\n /** The hosted rights page (cookiemunch.net/r/<cbid> or the site's own domain). */\n rightsPage: {\n get(cbid: string): Promise<RightsPageDraft>;\n /** Replace the draft. Nothing is live until publish(). `opts.baseUpdatedAt` refuses to overwrite a newer save (409 draft_conflict). */\n put(cbid: string, page: RightsPageDocument, opts?: RightsPageWriteOptions): Promise<RightsPageDraft>;\n /** Merge into the draft: objects merge, arrays replace, null removes. `opts.baseUpdatedAt` as on put(). */\n patch(cbid: string, patch: Record<string, unknown>, opts?: RightsPageWriteOptions): Promise<RightsPageDraft>;\n /** Validate a write without saving it. */\n dryRun(cbid: string, page: Record<string, unknown>, opts?: { mode?: 'patch' | 'put' }): Promise<RightsPageDryRun>;\n getPublished(cbid: string): Promise<RightsPagePublished>;\n /**\n * Publish the draft; it goes live at once. Pass `opts.baseUpdatedAt` (the draft's updatedAt\n * as you read it) so only the draft you saw goes live: 409 draft_conflict otherwise.\n */\n publish(cbid: string, opts?: RightsPageWriteOptions): Promise<RightsPagePublishResult>;\n versions(cbid: string): Promise<RightsPageVersions>;\n /**\n * Publish an earlier version again; it goes live at once and REPLACES the current draft\n * (unpublished edits are lost). `opts.baseUpdatedAt` refuses with 409 if the draft moved.\n */\n rollback(cbid: string, version: number, opts?: RightsPageWriteOptions): Promise<RightsPagePublishResult>;\n /** Custom hostnames serving the rights page, with the plan allowance. */\n listDomains(cbid: string): Promise<RightsPageDomainList>;\n /** Add a custom hostname (e.g. privacy.example.com); the answer carries the DNS records to create. */\n addDomain(cbid: string, hostname: string): Promise<RightsPageDomain>;\n /** Check it now: DNS while pending, the HTTPS answer once routed. Always the domain as it stands. */\n verifyDomain(cbid: string, hostname: string): Promise<RightsPageDomain>;\n /** Remove it: it stops being served and its certificate is not renewed. */\n removeDomain(cbid: string, hostname: string): Promise<void>;\n };\n /**\n * Identity resolution. Reads are POSTs on purpose: a person's identifiers travel in the\n * body, never a URL where they would land in access logs.\n */\n identity: {\n /** Canonical subject id for any known identifier, else null. */\n resolve(identifiers: Identifier[]): Promise<{ subjectId: string | null }>;\n /** Stitch identifiers into one subject (merging clusters when a link bridges them). */\n link(identifiers: Identifier[]): Promise<{ subjectId: string }>;\n cluster(subjectId: string): Promise<{ subjectId: string; identifiers: Identifier[] }>;\n };\n /** The Permission Vault: a resolved person's current consent state. */\n vault: {\n record(identifiers: Identifier[], decisions: ConsentDecisionInput[]): Promise<{ subjectId: string; permits: unknown[] }>;\n current(identifiers: Identifier[]): Promise<{ purposes: Record<string, boolean> }>;\n permits(identifiers: Identifier[]): Promise<{ permits: unknown[] }>;\n };\n /** The unified profile, with consent enforced at ACTIVATION time. */\n profile: {\n get(identifiers: Identifier[]): Promise<unknown>;\n setAttributes(identifiers: Identifier[], attributes: Record<string, ProfileAttributeInput>): Promise<{ subjectId: string }>;\n /** Attribute values usable for `purpose` — {} when the person hasn't consented to it. */\n activate(identifiers: Identifier[], purpose: string): Promise<{ attributes: Record<string, string> }>;\n };\n /** Marketing preferences as topics x channels. */\n subscriptions: {\n /** The org's topic catalog — what a subject can subscribe TO. */\n topics(): Promise<{ topics: SubscriptionTopicInput[] }>;\n /** Replace the catalog. It is authored whole, not patched. */\n setTopics(topics: SubscriptionTopicInput[]): Promise<{ topics: SubscriptionTopicInput[] }>;\n get(subjectId: string): Promise<unknown>;\n set(subjectId: string, topic: string, channel: string, optedIn: boolean): Promise<unknown>;\n unsubscribeAll(subjectId: string): Promise<unknown>;\n resubscribe(subjectId: string): Promise<unknown>;\n activation(subjectId: string, topics: SubscriptionTopicInput[]): Promise<{ entries: unknown[] }>;\n };\n /** DPIA / PIA / LIA / TIA / AI-impact / vendor assessments. */\n assessments: {\n templates(): Promise<{ templates: unknown[] }>;\n list(): Promise<{ assessments: unknown[] }>;\n start(template: string, subject: string): Promise<unknown>;\n /** The record plus its derived score, completeness and SME routing. */\n get(id: string): Promise<unknown>;\n answer(id: string, questionId: string, value: string | number | boolean): Promise<unknown>;\n /** Fill from the org's latest data map; never overwrites a human answer. */\n autoPopulateFromMap(id: string): Promise<{ assessment: unknown; applied: string[] }>;\n /** Fill from evidence you supply. Never overwrites a human answer; records `source` as provenance. */\n autoPopulate(id: string, evidence: Record<string, unknown>, source?: string): Promise<{ assessment: unknown; applied: string[] }>;\n submit(id: string): Promise<unknown>;\n approve(id: string, by: string): Promise<unknown>;\n reject(id: string, by: string, reason: string): Promise<unknown>;\n };\n /** The data map produced by an in-environment scan (metadata only). */\n discovery: {\n ingestMap(map: unknown): Promise<{ scannedAt: number; systems: number }>;\n getMap(): Promise<unknown>;\n ropaDrafts(): Promise<{ drafts: unknown[] }>;\n evidence(): Promise<{ evidence: Record<string, unknown> }>;\n /** What changed since the last scan, and where the RoPA disagrees with reality. */\n drift(): Promise<{ comparedTo: number | null; sinceLastScan: unknown[]; againstRecord: unknown[] }>;\n /**\n * Plan warehouse-native policy from the latest map. Returns a PLAN with its own\n * revert — nothing is applied to your warehouse by calling this.\n */\n planEnforcement(\n dialect: 'postgres' | 'mysql' | 'snowflake',\n rules: EnforcementRuleInput[],\n opts?: { permitsTable?: string; policyPrefix?: string },\n ): Promise<{ plan: { statements: unknown[]; revert: unknown[]; skipped: unknown[] } }>;\n };\n /**\n * Regulatory intelligence: the curated privacy-law dataset. Every response carries\n * `reviewedAt` — the dataset ships with the product and is only as current as its\n * last review. Not legal advice.\n */\n regulatory: {\n /** The dataset, optionally narrowed to the jurisdictions you operate in. */\n feed(jurisdictions?: string[]): Promise<RegulatoryFeedResult>;\n /** What takes effect within `days` (default 180), soonest first. */\n upcoming(days?: number): Promise<RegulatoryFeedResult>;\n };\n /** AI governance: policy, the inline gateway, inventory and lineage. */\n ai: {\n getPolicy(): Promise<{ policy: unknown; configured: boolean }>;\n setPolicy(policy: unknown): Promise<{ policy: unknown }>;\n /** Enforce consent + policy on a prompt or response. Requires the ai:inspect scope. */\n inspect(input: AiInspectInput): Promise<AiInspectResult>;\n inventory(): Promise<{ systems: unknown[]; shadow: unknown[] }>;\n lineage(): Promise<{ lineage: Record<string, string[]> }>;\n registerSystem(input: { id: string; name: string; provider?: string; purpose?: string }): Promise<unknown>;\n /** The AI systems declared with registerSystem. */\n systems(): Promise<unknown>;\n audit(limit?: number): Promise<{ entries: unknown[] }>;\n };\n /** DSR fulfillment: plan work per system, poll it from an in-VPC agent, report outcomes. */\n fulfillment: {\n sla(): Promise<unknown>;\n plan(requestId: string, systems: Array<{ system: string; operation: FulfillmentOp }>, includeHistorical?: boolean): Promise<{ requestId: string; tasks: unknown[] }>;\n status(requestId: string): Promise<unknown>;\n /** In-VPC agent: poll pending tasks. */\n pendingTasks(limit?: number): Promise<{ tasks: unknown[] }>;\n /** In-VPC agent: report an outcome. Only the outcome crosses the boundary. */\n reportTask(taskId: string, ok: boolean, error?: string): Promise<unknown>;\n /**\n * Systems you have connected that run part of a request themselves — an identity\n * platform holding your users' accounts, for instance. The in-VPC agent covers the\n * systems we cannot reach; these are the ones we can.\n */\n executors(): Promise<DsarExecutor[]>;\n /**\n * Connect one. The secret is stored encrypted and never returned; the response carries\n * the URL to point that system's webhook at.\n */\n connectExecutor(input: DsarExecutorInput | DsarExecutorPresetInput): Promise<DsarExecutorCreated>;\n /**\n * Change a connection in place. Only what you send changes, and the stored credential\n * is kept unless you pass a new `secretKey` — so adding a field to a profile does not\n * cost you the connection or its id.\n */\n updateExecutor(id: string, patch: DsarExecutorPatch): Promise<DsarExecutor>;\n /** What `connectExecutor` would create, validated in full; nothing is stored (`?dryRun=1`). */\n dryRunConnectExecutor(input: DsarExecutorInput | DsarExecutorPresetInput): Promise<DsarExecutorDryRun>;\n /** The versioned presets, whether each is usable now (or waits on another platform), and the org canary address. */\n executorPresets(): Promise<ExecutorPresets>;\n /**\n * Rebuild a connection from its preset's latest version (or `presetVersion`), keeping its id\n * and credentials; or adopt a preset on a hand-written one by naming it. Answers with `diff`.\n */\n upgradeExecutorPreset(id: string, input?: ExecutorPresetUpgrade): Promise<DsarExecutor & { diff: ConfigChange[]; warnings?: Array<{ field: string; reason: string }> }>;\n /** What `upgradeExecutorPreset` would change; nothing is stored (`?dryRun=1`). */\n dryRunUpgradeExecutorPreset(id: string, input?: ExecutorPresetUpgrade): Promise<DsarExecutorDryRun>;\n /** What `updateExecutor` would change, with a diff; the stored connection is untouched (`?dryRun=1`). */\n dryRunUpdateExecutor(id: string, patch: DsarExecutorPatch): Promise<DsarExecutorDryRun>;\n /**\n * Would this webhook delivery verify? Runs the receiver's exact check on headers and a raw\n * body you supply, acts on nothing, and names the stage that failed.\n */\n testExecutorWebhook(id: string, delivery: { headers: Record<string, string>; body: string }): Promise<ExecutorWebhookTestResult>;\n /** The last 50 deliveries to a connection's webhook, newest first, each with its reason. */\n executorWebhookDeliveries(id: string): Promise<ExecutorWebhookDelivery[]>;\n /** Disconnect a system; its open sub-tasks stop being driven. */\n disconnectExecutor(id: string): Promise<void>;\n /** The export bundle a connected system produced, fetched from it on demand. */\n taskExport(requestId: string, taskId: string): Promise<DsarTaskExport>;\n /**\n * One task: its status history (`events`), every request made to a connected system on\n * its behalf (`log`, redacted, 30 days) and whether that system's version changed mid-way.\n */\n task(requestId: string, taskId: string): Promise<DsarTask>;\n /**\n * Try a connection against one email without filing anything: runs the lookup, returns\n * the raw (redacted) upstream answers and what a real request would have sent.\n */\n testExecutor(id: string, input: ExecutorTestInput): Promise<ExecutorTestResult>;\n };\n preferences: {\n list(): Promise<PreferenceItem[]>;\n /** Save/update a subject's named-purpose opt-ins. Returns the persisted record. */\n save(subjectId: string, purposes: Record<string, boolean>): Promise<{ record: PreferenceRecord }>;\n /** One subject's record. A subject with none has empty `purposes`. */\n get(subjectId: string): Promise<PreferenceRecord>;\n };\n /** The hosted preference centre, opened for a visitor your server has signed in. */\n preferenceCenter: {\n /**\n * Mint a preference-centre token (POST /v1/preference-center/tokens) for the page to pass to\n * `CookieMunch.setSubjectToken`. Server-side only: the token opens that subject's subscriptions\n * and profile until `expiresAt`. `ttlSeconds` defaults to 3600, at most 86400. Needs\n * subscriptions:write and profile:write, plus identity:read when minting by email.\n */\n mintToken(input: CenterTokenInput): Promise<CenterToken>;\n };\n members: {\n list(): Promise<Member[]>;\n invite(email: string, role: string): Promise<{ member: { userId: string; email: string; role: string } }>;\n setRole(userId: string, role: string): Promise<{ member: { userId: string; email: string; role: string } }>;\n remove(userId: string): Promise<{ ok: true }>;\n };\n keys: {\n list(): Promise<ApiKeyPrefix[]>;\n /**\n * Issue a new API key; the secret is returned once. Pass `scopes` and/or `cbids` for a\n * least-privilege key — omit both for full access to the whole org.\n */\n issue(input?: ApiKeyIssueInput): Promise<ApiKeyIssued>;\n /** Revoke a key by its prefix. Immediate. */\n revoke(prefix: string): Promise<void>;\n /** Rotate a key: a new secret, returned once, with the same scopes, lock and expiry. The old one stops working. */\n roll(prefix: string): Promise<ApiKeyIssued>;\n /** Rename a key, or replace its scopes or property lock. Only the fields sent change. */\n update(prefix: string, patch: ApiKeyUpdate): Promise<{ ok: true }>;\n /** Key hygiene: keys unused for `unusedDays` (default 90), broad keys, keys expiring within `expiringDays` (default 14). */\n report(opts?: { unusedDays?: number; expiringDays?: number }): Promise<ApiKeyReport>;\n };\n usage(): Promise<Usage>;\n /** Every link a site's privacy wiring depends on, one row each with `ok` and a fix hint. `cbid` may be omitted with one site. */\n integrationHealth(cbid?: string): Promise<IntegrationHealthReport>;\n /**\n * Wire a product's privacy in one call: the site, connected systems, Atlas and Forumulate. Idempotent;\n * `{ dryRun: true }` writes nothing anywhere. No secret appears in the answer.\n */\n provision(input: ProvisionRequest, opts?: { dryRun?: boolean }): Promise<ProvisionResult>;\n /** The key's organisation. Requires an unscoped key that is not property-locked. */\n org: {\n get(): Promise<Org>;\n /** Rename the org or set its logo. Deleting the org is not available through the API. */\n update(patch: OrgUpdate): Promise<Org>;\n /**\n * Your own mail provider for rights-request mail. It carries verification codes once\n * `test` succeeds; every `set` clears that. There is no shared sender: without one, requests\n * are verified by your sign-in provider (`rightsIdentity`) or filed unverified for you to confirm.\n */\n mailProvider: {\n get(): Promise<MailProvider>;\n set(input: MailProviderInput): Promise<MailProvider>;\n remove(): Promise<void>;\n /** Send a test through your provider. `to` defaults to the reply-to, else the from address. */\n test(to?: string): Promise<MailProviderTestResult>;\n };\n /**\n * Your organisation's own data-controller identity: who every rights notice names, before any site config\n * `controller` block. Needed for a notice on a request filed without a cbid when you have several sites.\n * An unscoped key, or `rights:write`.\n */\n controller: {\n get(): Promise<OrgControllerIdentity>;\n /** Replace it: a field left out is removed. */\n set(input: OrgControllerInput): Promise<OrgControllerIdentity>;\n remove(): Promise<void>;\n };\n /**\n * The default captcha for your rights pages, for every site without its own (`rightsCaptcha`): your own\n * keys (recommended) or off. Without one, rights pages have no captcha.\n */\n rightsCaptcha: {\n get(): Promise<RightsCaptcha>;\n set(input: RightsCaptchaInput): Promise<RightsCaptcha>;\n /** Remove the default; answers the setting now in force (off). */\n remove(): Promise<RightsCaptcha>;\n };\n /** The default sign-in provider rights requests are verified with, for every site without its own. */\n rightsIdentity: {\n get(): Promise<RightsIdentity>;\n set(input: RightsIdentityInput): Promise<RightsIdentity>;\n /** Remove the default; answers the setting now in force. */\n remove(): Promise<RightsIdentity>;\n };\n };\n /** The org's audit log, newest first. API actions appear as `apikey:<prefix>`. */\n audit(limit?: number): Promise<{ entries: AuditEntry[] }>;\n assets: {\n /** Upload a banner image (≤ 1,000,000 bytes) and get its public URL. Requires sites:write. */\n upload(input: AssetUpload): Promise<{ url: string }>;\n /**\n * Delete a stored image. Takes the URL `upload` returned, or just its file name. Only\n * your own org's images are reachable — the folder comes from your API key.\n */\n delete(urlOrFileName: string): Promise<void>;\n };\n /** Provision and manage child orgs. Requires a key with the reseller:* scopes. */\n reseller: {\n list(): Promise<ResellerChildList>;\n /** Provision a child org. With `mintKey`, its first API key is returned once, as `apiKey`. */\n create(input: ResellerChildCreate): Promise<{ child: ResellerChild; apiKey?: string }>;\n get(id: string): Promise<unknown>;\n update(id: string, patch: ResellerChildPatch): Promise<unknown>;\n /**\n * Deprovision a child. Suspends it by default, which is reversible; `{ purge: true }`\n * hard-deletes it and its data, which is not.\n */\n deprovision(id: string, opts?: { purge?: boolean }): Promise<void>;\n listKeys(id: string): Promise<ApiKeyPrefix[]>;\n /** Mint an API key for a child. The secret is returned once. */\n mintKey(id: string, input?: ChildKeyInput): Promise<ApiKeyIssued>;\n revokeKey(id: string, prefix: string): Promise<void>;\n };\n webhooks: {\n list(): Promise<WebhookSubscription[]>;\n create(input: WebhookCreate): Promise<WebhookSubscription>;\n /** Change or pause a subscription. `{ active: false }` pauses without deleting it. */\n update(id: string, patch: WebhookUpdate): Promise<WebhookSubscription>;\n delete(id: string): Promise<void>;\n /** Rotate the signing secret. The new secret is returned once. */\n rollSecret(id: string): Promise<{ secret: string }>;\n /** Send a signed test event now and report what the endpoint answered. */\n test(id: string): Promise<WebhookTestResult>;\n /** Deliveries that failed every retry, newest first. */\n deadLetters(): Promise<{ deadLetters: WebhookDeadLetter[] }>;\n /** Deliver a dead letter again, to the subscription as it is now. */\n replayDeadLetter(id: string): Promise<unknown>;\n };\n /**\n * Account-linked rights requests: a single-use link to the hosted rights page for a person your\n * backend knows is signed in. The page locks their email and skips the code step. Requires dsar:write.\n */\n rightsLinks: {\n create(cbid: string, input: RightsLinkInput): Promise<RightsLink>;\n };\n /**\n * A site's consent-sync signing secret: your server signs consent-sync tokens with it\n * (`mintConsentSyncToken`) so a visitor's decision syncs across their browsers. `rotate` mints the\n * first one or replaces it (the old one stops at once) and is the only time the secret is returned.\n * Reading needs sites:read; rotate and remove need sites:write and consent:write.\n */\n consentSyncSecret: {\n get(cbid: string): Promise<ConsentSyncSecret>;\n rotate(cbid: string): Promise<ConsentSyncSecretMinted>;\n /** Turn hosted consent sync off for the site. */\n remove(cbid: string): Promise<ConsentSyncSecret>;\n };\n /**\n * A site's rights-page captcha: your own Turnstile, hCaptcha or reCAPTCHA keys (recommended), or off. A site\n * without its own setting uses the organisation default (`org.rightsCaptcha`), else off. Requires sites:read / sites:write.\n */\n rightsCaptcha: {\n get(cbid: string): Promise<RightsCaptcha>;\n set(cbid: string, input: RightsCaptchaInput): Promise<RightsCaptcha>;\n /** Remove the site's own setting: it inherits the organisation default again. Answers the setting now in force. */\n remove(cbid: string): Promise<RightsCaptcha>;\n };\n /**\n * A site's sign-in provider (Clerk, Atlas or any JWKS issuer): a visitor signed in to your site\n * is verified by their session, with no code mail. A site without its own setting uses the\n * organisation default (`org.rightsIdentity`). Requires sites:read / sites:write.\n */\n rightsIdentity: {\n get(cbid: string): Promise<RightsIdentity>;\n set(cbid: string, input: RightsIdentityInput): Promise<RightsIdentity>;\n /** Remove the site's own setting: it inherits the organisation default again. */\n remove(cbid: string): Promise<RightsIdentity>;\n };\n /** One person's consent across every site in the org, by the subjectId your apps attach. */\n subjects: {\n /** Requires consent:read; not available to property-locked keys. */\n consent(subjectId: string): Promise<{ subjectId: string; records: unknown[]; count: number; sites: string[] }>;\n };\n banners: {\n list(): Promise<BannerSummary[]>;\n create(input: { name: string; json: SiteConfig }): Promise<BannerRecord>;\n get(id: string): Promise<BannerRecord>;\n update(id: string, patch: { name?: string; json?: SiteConfig }): Promise<BannerRecord>;\n delete(id: string): Promise<void>;\n assignments(id: string): Promise<{ cbids: string[] }>;\n setAssignments(id: string, cbids: string[]): Promise<{ cbids: string[] }>;\n publish(id: string): Promise<{ publishedCbids: string[] }>;\n };\n}\n\nfunction qs(query: Record<string, number | string | undefined> | undefined): string {\n if (!query) return '';\n const parts: string[] = [];\n for (const [k, v] of Object.entries(query)) {\n if (v !== undefined) parts.push(`${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`);\n }\n return parts.length ? `?${parts.join('&')}` : '';\n}\n\n/** Widen a typed query interface (RangeQuery/LogQuery) to the plain record qs() wants. */\nfunction toQuery(q: RangeQuery | LogQuery | undefined): Record<string, number | string | undefined> | undefined {\n return q as Record<string, number | string | undefined> | undefined;\n}\n\nexport function createCookieMunch(opts: CookieMunchOptions): CookieMunchClient {\n const base = `${opts.baseUrl.replace(/\\/+$/, '')}/v1`;\n const doFetch = opts.fetch ?? globalThis.fetch;\n if (!doFetch) throw new Error('No fetch available; pass { fetch } in CookieMunchOptions.');\n\n async function request(method: string, path: string, body?: unknown, raw = false): Promise<unknown> {\n const headers: Record<string, string> = { Authorization: `Bearer ${opts.apiKey}`, 'User-Agent': opts.userAgent ?? `cookiemunch-js/${SDK_VERSION}` };\n if (opts.strictUnknownFields && method !== 'GET') headers['Prefer'] = 'strict-unknown-fields';\n const init: RequestInit = { method, headers };\n if (body !== undefined) {\n headers['Content-Type'] = 'application/json';\n init.body = JSON.stringify(body);\n }\n const res = await doFetch(`${base}${path}`, init);\n if (!res.ok) {\n let message = `request failed with status ${res.status}`;\n let code: string | undefined;\n let parsed: unknown;\n try {\n const data = (await res.json()) as { error?: string; code?: string };\n parsed = data;\n if (typeof data.error === 'string') message = data.error;\n if (typeof data.code === 'string') code = data.code;\n } catch {\n /* non-JSON error body — keep the default message */\n }\n throw new CookieMunchApiError(res.status, message, code, parsed);\n }\n if (res.status === 204) return undefined;\n if (raw) return res.text();\n const contentType = res.headers.get('content-type') ?? '';\n return contentType.includes('application/json') ? res.json() : res.text();\n }\n\n const get = (p: string) => request('GET', p);\n const enc = encodeURIComponent;\n\n return {\n me: () => get('/me') as Promise<Identity>,\n languages: () => get('/languages') as Promise<SupportedLanguage[]>,\n orgs: {\n list: () => get('/orgs') as Promise<AccountOrgs>,\n create: (name, opts) =>\n request('POST', '/orgs', { name, ...(opts ?? {}) }) as Promise<{\n org: { id: string; name: string; role: string };\n apiKey?: string;\n keyError?: string;\n }>,\n },\n sites: {\n list: () => get('/sites') as Promise<Site[]>,\n create: (input) => request('POST', '/sites', input) as Promise<Site>,\n get: (cbid) => get(`/sites/${enc(cbid)}`) as Promise<Site>,\n update: (cbid, patch, o) => request('PATCH', `/sites/${enc(cbid)}${qs({ dryRun: o?.dryRun ? '1' : undefined })}`, patch) as Promise<SiteUpdateResult>,\n delete: async (cbid) => {\n await request('DELETE', `/sites/${enc(cbid)}`);\n },\n listPartners: (cbid) => get(`/sites/${enc(cbid)}/partners`) as Promise<{ links: PartnerLink[]; partners: PartnerCatalogEntry[] }>,\n linkPartner: (cbid, input, o) =>\n request('POST', `/sites/${enc(cbid)}/partners${qs({ dryRun: o?.dryRun ? '1' : undefined })}`, input) as Promise<{ change: 'created' | 'updated' | 'unchanged'; link: PartnerLink; dryRun?: true }>,\n unlinkPartner: (cbid, linkId) => request('DELETE', `/sites/${enc(cbid)}/partners/${enc(linkId)}`) as Promise<{ link: PartnerLink }>,\n getConfig: (cbid) => get(`/sites/${enc(cbid)}/config`) as Promise<SiteConfig>,\n putConfig: (cbid, config, opts) =>\n request('PUT', `/sites/${enc(cbid)}/config${qs({ keep: opts?.keep })}`, config) as Promise<SiteConfig>,\n patchConfig: (cbid, config) => request('PATCH', `/sites/${enc(cbid)}/config`, config) as Promise<SiteConfig>,\n dryRunConfig: (cbid, config, opts) =>\n request(\n opts?.mode === 'put' ? 'PUT' : 'PATCH',\n `/sites/${enc(cbid)}/config${qs({ dryRun: 1, keep: opts?.mode === 'put' ? opts.keep : undefined })}`,\n config,\n ) as Promise<ConfigDryRun>,\n lintConfig: (cbid, config, opts) =>\n request('POST', `/sites/${enc(cbid)}/config/lint${qs({ replace: opts?.replace ? 1 : undefined })}`, config) as Promise<ButtonParityLint>,\n cookies: (cbid) => get(`/sites/${enc(cbid)}/cookies`) as Promise<CookieDeclaration>,\n scan: (cbid, o) => request('POST', `/sites/${enc(cbid)}/scan${qs({ wait: o?.wait })}`) as Promise<ScanStatus>,\n scanStatus: (cbid, o) => get(`/sites/${enc(cbid)}/scan${qs({ wait: o?.wait })}`) as Promise<ScanStatus>,\n waitForScan: async (cbid, o) => {\n const until = Date.now() + (o?.timeoutMs ?? 120_000);\n for (;;) {\n const left = until - Date.now();\n const asked = Math.max(0, Math.min(60_000, left));\n const t0 = Date.now();\n const s = (await get(`/sites/${enc(cbid)}/scan${qs({ wait: asked / 1000 })}`)) as ScanStatus;\n if (s.status !== 'scanning' || until - Date.now() <= 0) return s;\n // A server that answered sooner than asked (one without long-polling) is not hammered.\n const early = Math.min(1000, until - Date.now()) - (Date.now() - t0);\n if (early > 0) await new Promise((r) => setTimeout(r, early));\n }\n },\n ab: (cbid) => get(`/sites/${enc(cbid)}/ab`) as Promise<AbResult[]>,\n policy: (cbid, o) =>\n request(\n 'GET',\n `/sites/${enc(cbid)}/policy${qs({ contactEmail: o?.contactEmail, effectiveDate: o?.effectiveDate, jurisdictions: o?.jurisdictions?.join(',') })}`,\n undefined,\n true,\n ) as Promise<string>,\n setAdPersonalization: (cbid, input) => request('POST', `/sites/${enc(cbid)}/elements/ad-personalization`, input),\n analyzeSession: (cbid, input) => request('POST', `/sites/${enc(cbid)}/sentry`, input),\n banner: (cbid) => get(`/sites/${enc(cbid)}/banner`) as Promise<{ bannerId: string | null }>,\n blocked: (cbid) => get(`/sites/${enc(cbid)}/blocked`) as Promise<{ reports: BlockedReport[] }>,\n importDeclaration: (cbid, data) => request('POST', `/sites/${enc(cbid)}/import`, { data }) as Promise<ImportedDeclaration>,\n applyImport: (cbid, data, o) =>\n request('POST', `/sites/${enc(cbid)}/import${qs({ apply: '1', overwrite: o?.overwrite ? '1' : undefined })}`, { data }) as Promise<AppliedImport>,\n declareCookie: (cbid, name, input, o) =>\n request('PUT', `/sites/${enc(cbid)}/cookies/${enc(name)}${qs({ dryRun: o?.dryRun ? '1' : undefined })}`, input) as Promise<\n { cookie: DeclaredCookie; warnings?: DeclarationWarning[] } & Partial<DeclarationDryRun>\n >,\n retireCookie: (cbid, name, input, o) =>\n request('POST', `/sites/${enc(cbid)}/cookies/${enc(name)}/retire${qs({ dryRun: o?.dryRun ? '1' : undefined })}`, input ?? {}) as Promise<{\n cookie: RetiredCookie;\n matched: string[];\n dryRun?: true;\n retired?: RetiredCookie[];\n }>,\n unretireCookie: (async (cbid: string, name: string, o?: { dryRun?: boolean }) => {\n const out = await request('DELETE', `/sites/${enc(cbid)}/cookies/${enc(name)}/retire${qs({ dryRun: o?.dryRun ? '1' : undefined })}`);\n return o?.dryRun ? out : undefined;\n }) as CookieMunchClient['sites']['unretireCookie'],\n cookiePresets: (cbid) => get(`/sites/${enc(cbid)}/cookies/presets`) as Promise<{ presets: CookiePreset[] }>,\n applyCookiePreset: (cbid, vendor, params, o) =>\n request('PUT', `/sites/${enc(cbid)}/cookies/presets/${enc(vendor)}${qs({ dryRun: o?.dryRun ? '1' : undefined })}`, params ?? {}) as Promise<CookiePresetResult>,\n removeCookiePreset: (cbid, vendor, o) =>\n request('DELETE', `/sites/${enc(cbid)}/cookies/presets/${enc(vendor)}${qs({ dryRun: o?.dryRun ? '1' : undefined })}`) as Promise<{\n vendor: CookiePresetVendor;\n removed: string[];\n dryRun?: true;\n declared?: DeclaredCookie[];\n diff?: DeclarationDiff;\n }>,\n deleteObservedCookie: (async (cbid: string, name: string, o?: { dryRun?: boolean }) => {\n const out = await request('DELETE', `/sites/${enc(cbid)}/observed-cookies/${enc(name)}${qs({ dryRun: o?.dryRun ? '1' : undefined })}`);\n return o?.dryRun ? (out as ObservedPurgeResult) : undefined;\n }) as CookieMunchClient['sites']['deleteObservedCookie'],\n purgeObservedCookies: (cbid, input, o) =>\n request('POST', `/sites/${enc(cbid)}/observed-cookies/purge${qs({ dryRun: o?.dryRun ? '1' : undefined })}`, input) as Promise<ObservedPurgeResult>,\n undeclareCookie: (async (cbid: string, name: string, o?: { dryRun?: boolean }) => {\n const out = await request('DELETE', `/sites/${enc(cbid)}/cookies/${enc(name)}${qs({ dryRun: o?.dryRun ? '1' : undefined })}`);\n return o?.dryRun ? (out as DeclarationDryRun) : undefined;\n }) as CookieMunchClient['sites']['undeclareCookie'],\n declareCookies: (cbid, cookies, o) =>\n request('PUT', `/sites/${enc(cbid)}/cookies${qs({ dryRun: o?.dryRun ? '1' : undefined })}`, { cookies, ...(o?.replace ? { replace: true } : {}) }) as Promise<{\n declared: DeclaredCookie[];\n saved: number;\n removed: number;\n warnings?: DeclarationWarning[];\n dryRun?: true;\n diff?: DeclarationDiff;\n }>,\n snippet: (cbid, opts) =>\n get(`/sites/${enc(cbid)}/snippet${qs({ blockingmode: opts?.blockingMode, culture: opts?.culture })}`) as Promise<InstallSnippet>,\n previewToken: (cbid, o) => request('POST', `/sites/${enc(cbid)}/preview-token`, o ?? {}) as Promise<PreviewToken>,\n verify: (cbid, method) => request('POST', `/sites/${enc(cbid)}/verify`, { method }) as Promise<VerifyResult>,\n verifyChallenge: (cbid) => get(`/sites/${enc(cbid)}/verify/challenge`),\n createBulk: (sites) => request('POST', '/sites/bulk', { sites }) as Promise<{ results: BulkSiteResult[] }>,\n brand: (cbid) => request('POST', `/sites/${enc(cbid)}/brand`, {}) as Promise<BrandExtractionResult>,\n getFlow: (cbid) => get(`/sites/${enc(cbid)}/flow`) as Promise<SiteFlow>,\n editFlow: (cbid, operations) =>\n request('POST', `/sites/${enc(cbid)}/flow/ops`, { operations }) as Promise<FlowOpResponse>,\n setFlow: (cbid, config) => request('PUT', `/sites/${enc(cbid)}/flow`, config) as Promise<FlowOpResponse>,\n },\n consent: {\n verify: (cbid) => get(`/sites/${enc(cbid)}/consent/verify`) as Promise<{ valid: boolean }>,\n stats: (cbid, query) => get(`/sites/${enc(cbid)}/consent/stats${qs(toQuery(query))}`) as Promise<ConsentDay[]>,\n log: (cbid, query) => get(`/sites/${enc(cbid)}/consent/log${qs(toQuery(query))}`) as Promise<ConsentLogRow[]>,\n export: (cbid, query) => request('GET', `/sites/${enc(cbid)}/consent/export${qs(toQuery(query))}`, undefined, true) as Promise<string>,\n receipt: (cbid, stamp) => get(`/sites/${enc(cbid)}/receipt/${enc(stamp)}`) as Promise<SignedReceipt>,\n eraseSubject: (cbid, stamp) =>\n request('POST', `/sites/${enc(cbid)}/erase-consent`, { stamp }) as Promise<{ erased: number }>,\n exportSubject: (cbid, stamp) =>\n request('GET', `/sites/${enc(cbid)}/subject-export?stamp=${enc(stamp)}`) as Promise<{ cbid: string; stamp: string; records: unknown[]; count: number }>,\n ingest: async (input) => {\n // The public ingest endpoint lives under /api/v1, not the /v1 dev-API prefix,\n // so it is called directly rather than via `request` (which prepends /v1).\n const res = await doFetch(`${opts.baseUrl.replace(/\\/+$/, '')}/api/v1/consent`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(input),\n });\n if (!res.ok) {\n let message = `consent ingest failed with status ${res.status}`;\n let code: string | undefined;\n try {\n const data = (await res.json()) as { error?: string; code?: string };\n if (typeof data.error === 'string') message = data.error;\n if (typeof data.code === 'string') code = data.code;\n } catch {\n /* non-JSON error body — keep the default message */\n }\n throw new CookieMunchApiError(res.status, message, code);\n }\n },\n },\n dsar: {\n list: () => get('/dsar') as Promise<DsarRequest[]>,\n get: (id) => get(`/dsar/${enc(id)}`) as Promise<DsarRequest>,\n create: (input) => request('POST', '/dsar', input) as Promise<{ request: DsarRequest }>,\n advance: (id, toStatus, opts) =>\n request('POST', `/dsar/${enc(id)}/advance`, { toStatus, ...(opts?.messageToSubject !== undefined ? { messageToSubject: opts.messageToSubject } : {}) }) as Promise<{ request: DsarRequest }>,\n response: (id) => request('GET', `/dsar/${enc(id)}/response`, undefined, true) as Promise<string>,\n erase: (id, cbid, stamp) => request('POST', `/dsar/${enc(id)}/erase`, { cbid, stamp }) as Promise<DsarEraseResult>,\n export: (id, cbid, stamp) => request('POST', `/dsar/${enc(id)}/export`, { cbid, stamp }) as Promise<DsarExportResult>,\n delete: async (id) => {\n await request('DELETE', `/dsar/${enc(id)}`);\n },\n markTest: (id) => request('POST', `/dsar/${enc(id)}/mark-test`) as Promise<{ request: DsarRequest; changed: boolean }>,\n },\n vendors: {\n list: () => get('/vendors') as Promise<ScoredVendor[]>,\n create: (input) => request('POST', '/vendors', input) as Promise<{ vendor: VendorRecord; risk: RiskScore }>,\n },\n ropa: {\n list: () => get('/ropa') as Promise<RopaEntry[]>,\n create: (input) => request('POST', '/ropa', input) as Promise<{ entry: RopaEntry }>,\n exportCsv: () => request('GET', '/ropa/export.csv', undefined, true) as Promise<string>,\n },\n brandKits: {\n list: () => get('/brand-kits') as Promise<BrandKit[]>,\n create: (input) => request('POST', '/brand-kits', input) as Promise<{ kit: BrandKit }>,\n delete: async (id) => {\n await request('DELETE', `/brand-kits/${enc(id)}`);\n },\n },\n rightsPage: {\n get: (cbid) => get(`/sites/${enc(cbid)}/rights-page`) as Promise<RightsPageDraft>,\n put: (cbid, page, opts) => request('PUT', `/sites/${enc(cbid)}/rights-page`, withBase(page, opts)) as Promise<RightsPageDraft>,\n patch: (cbid, patch, opts) => request('PATCH', `/sites/${enc(cbid)}/rights-page`, withBase(patch, opts)) as Promise<RightsPageDraft>,\n dryRun: (cbid, page, opts) =>\n request(opts?.mode === 'put' ? 'PUT' : 'PATCH', `/sites/${enc(cbid)}/rights-page?dryRun=1`, page) as Promise<RightsPageDryRun>,\n getPublished: (cbid) => get(`/sites/${enc(cbid)}/rights-page/published`) as Promise<RightsPagePublished>,\n publish: (cbid, opts) => request('POST', `/sites/${enc(cbid)}/rights-page/publish`, withBase({}, opts)) as Promise<RightsPagePublishResult>,\n versions: (cbid) => get(`/sites/${enc(cbid)}/rights-page/versions`) as Promise<RightsPageVersions>,\n rollback: (cbid, version, opts) => request('POST', `/sites/${enc(cbid)}/rights-page/rollback`, withBase({ version }, opts)) as Promise<RightsPagePublishResult>,\n listDomains: (cbid) => get(`/sites/${enc(cbid)}/rights-page/domains`) as Promise<RightsPageDomainList>,\n addDomain: (cbid, hostname) => request('POST', `/sites/${enc(cbid)}/rights-page/domains`, { hostname }) as Promise<RightsPageDomain>,\n verifyDomain: (cbid, hostname) =>\n request('POST', `/sites/${enc(cbid)}/rights-page/domains/${enc(hostname)}/verify`) as Promise<RightsPageDomain>,\n removeDomain: async (cbid, hostname) => {\n await request('DELETE', `/sites/${enc(cbid)}/rights-page/domains/${enc(hostname)}`);\n },\n },\n identity: {\n resolve: (identifiers) => request('POST', '/identity/resolve', { identifiers }) as Promise<{ subjectId: string | null }>,\n link: (identifiers) => request('POST', '/identity/link', { identifiers }) as Promise<{ subjectId: string }>,\n cluster: (subjectId) => get(`/identity/${enc(subjectId)}`) as Promise<{ subjectId: string; identifiers: Identifier[] }>,\n },\n vault: {\n record: (identifiers, decisions) => request('POST', '/vault/record', { identifiers, decisions }) as Promise<{ subjectId: string; permits: unknown[] }>,\n current: (identifiers) => request('POST', '/vault/current', { identifiers }) as Promise<{ purposes: Record<string, boolean> }>,\n permits: (identifiers) => request('POST', '/vault/permits', { identifiers }) as Promise<{ permits: unknown[] }>,\n },\n profile: {\n get: (identifiers) => request('POST', '/profile/get', { identifiers }),\n setAttributes: (identifiers, attributes) => request('POST', '/profile/attributes', { identifiers, attributes }) as Promise<{ subjectId: string }>,\n activate: (identifiers, purpose) => request('POST', '/profile/activate', { identifiers, purpose }) as Promise<{ attributes: Record<string, string> }>,\n },\n subscriptions: {\n topics: () => get('/subscriptions/topics') as Promise<{ topics: SubscriptionTopicInput[] }>,\n setTopics: (topics) => request('PUT', '/subscriptions/topics', { topics }) as Promise<{ topics: SubscriptionTopicInput[] }>,\n get: (subjectId) => get(`/subscriptions/${enc(subjectId)}`),\n set: (subjectId, topic, channel, optedIn) => request('PUT', `/subscriptions/${enc(subjectId)}`, { topic, channel, optedIn }),\n unsubscribeAll: (subjectId) => request('POST', `/subscriptions/${enc(subjectId)}/unsubscribe-all`),\n resubscribe: (subjectId) => request('POST', `/subscriptions/${enc(subjectId)}/resubscribe`),\n activation: (subjectId, topics) => request('POST', `/subscriptions/${enc(subjectId)}/activation`, { topics }) as Promise<{ entries: unknown[] }>,\n },\n assessments: {\n templates: () => get('/assessments/templates') as Promise<{ templates: unknown[] }>,\n list: () => get('/assessments') as Promise<{ assessments: unknown[] }>,\n start: (template, subject) => request('POST', '/assessments', { template, subject }),\n get: (id) => get(`/assessments/${enc(id)}`),\n answer: (id, questionId, value) => request('POST', `/assessments/${enc(id)}/answer`, { questionId, value }),\n autoPopulateFromMap: (id) => request('POST', `/assessments/${enc(id)}/autopopulate-from-map`) as Promise<{ assessment: unknown; applied: string[] }>,\n autoPopulate: (id, evidence, source) =>\n request('POST', `/assessments/${enc(id)}/autopopulate`, { evidence, ...(source !== undefined ? { source } : {}) }) as Promise<{ assessment: unknown; applied: string[] }>,\n submit: (id) => request('POST', `/assessments/${enc(id)}/submit`),\n approve: (id, by) => request('POST', `/assessments/${enc(id)}/approve`, { by }),\n reject: (id, by, reason) => request('POST', `/assessments/${enc(id)}/reject`, { by, reason }),\n },\n discovery: {\n ingestMap: (map) => request('POST', '/discovery/map', { map }) as Promise<{ scannedAt: number; systems: number }>,\n getMap: () => get('/discovery/map'),\n ropaDrafts: () => get('/discovery/ropa-drafts') as Promise<{ drafts: unknown[] }>,\n evidence: () => get('/discovery/evidence') as Promise<{ evidence: Record<string, unknown> }>,\n drift: () => get('/discovery/drift') as Promise<{ comparedTo: number | null; sinceLastScan: unknown[]; againstRecord: unknown[] }>,\n planEnforcement: (dialect, rules, opts) =>\n request('POST', '/discovery/enforcement', {\n dialect,\n rules,\n ...(opts?.permitsTable !== undefined ? { permitsTable: opts.permitsTable } : {}),\n ...(opts?.policyPrefix !== undefined ? { policyPrefix: opts.policyPrefix } : {}),\n }) as Promise<{ plan: { statements: unknown[]; revert: unknown[]; skipped: unknown[] } }>,\n },\n regulatory: {\n feed: (jurisdictions) =>\n get(`/regulatory/feed${qs({ jurisdictions: jurisdictions?.join(',') })}`) as Promise<RegulatoryFeedResult>,\n upcoming: (days) => get(`/regulatory/upcoming${qs({ days })}`) as Promise<RegulatoryFeedResult>,\n },\n ai: {\n getPolicy: () => get('/ai/policy') as Promise<{ policy: unknown; configured: boolean }>,\n setPolicy: (policy) => request('PUT', '/ai/policy', { policy }) as Promise<{ policy: unknown }>,\n inspect: (input) => request('POST', '/ai/inspect', input) as Promise<AiInspectResult>,\n inventory: () => get('/ai/inventory') as Promise<{ systems: unknown[]; shadow: unknown[] }>,\n lineage: () => get('/ai/lineage') as Promise<{ lineage: Record<string, string[]> }>,\n registerSystem: (input) => request('POST', '/ai/systems', input),\n systems: () => get('/ai/systems'),\n audit: (limit) => get(`/ai/audit${qs({ limit })}`) as Promise<{ entries: unknown[] }>,\n },\n fulfillment: {\n sla: () => get('/dsar/sla'),\n plan: (requestId, systems, includeHistorical) =>\n request('POST', `/dsar/${enc(requestId)}/plan`, { systems, ...(includeHistorical ? { includeHistorical: true } : {}) }) as Promise<{ requestId: string; tasks: unknown[] }>,\n status: (requestId) => get(`/dsar/${enc(requestId)}/fulfillment`),\n pendingTasks: (limit) => get(`/dsar/agent/tasks${qs({ limit })}`) as Promise<{ tasks: unknown[] }>,\n reportTask: (taskId, ok, error) => request('POST', `/dsar/agent/tasks/${enc(taskId)}/result`, { ok, ...(error !== undefined ? { error } : {}) }),\n executors: () => get('/dsar/executors') as Promise<DsarExecutor[]>,\n connectExecutor: (input) => request('POST', '/dsar/executors', input) as Promise<DsarExecutorCreated>,\n updateExecutor: (id, patch) => request('PATCH', `/dsar/executors/${enc(id)}`, patch) as Promise<DsarExecutor>,\n dryRunConnectExecutor: (input) => request('POST', '/dsar/executors?dryRun=1', input) as Promise<DsarExecutorDryRun>,\n executorPresets: () => get('/dsar/executors/presets') as Promise<ExecutorPresets>,\n upgradeExecutorPreset: (id, input = {}) =>\n request('POST', `/dsar/executors/${enc(id)}/upgrade-preset`, input) as Promise<DsarExecutor & { diff: ConfigChange[] }>,\n dryRunUpgradeExecutorPreset: (id, input = {}) =>\n request('POST', `/dsar/executors/${enc(id)}/upgrade-preset?dryRun=1`, input) as Promise<DsarExecutorDryRun>,\n dryRunUpdateExecutor: (id, patch) => request('PATCH', `/dsar/executors/${enc(id)}?dryRun=1`, patch) as Promise<DsarExecutorDryRun>,\n testExecutorWebhook: (id, delivery) =>\n request('POST', `/dsar/executors/${enc(id)}/webhook/test`, delivery) as Promise<ExecutorWebhookTestResult>,\n executorWebhookDeliveries: (id) => get(`/dsar/executors/${enc(id)}/webhook/deliveries`) as Promise<ExecutorWebhookDelivery[]>,\n disconnectExecutor: async (id) => {\n await request('DELETE', `/dsar/executors/${enc(id)}`);\n },\n taskExport: (requestId, taskId) => get(`/dsar/${enc(requestId)}/tasks/${enc(taskId)}/export`) as Promise<DsarTaskExport>,\n task: (requestId, taskId) => get(`/dsar/${enc(requestId)}/tasks/${enc(taskId)}`) as Promise<DsarTask>,\n testExecutor: (id, input) => request('POST', `/dsar/executors/${enc(id)}/test`, input) as Promise<ExecutorTestResult>,\n },\n preferences: {\n list: () => get('/preferences') as Promise<PreferenceItem[]>,\n save: (subjectId, purposes) => request('POST', '/preferences', { subjectId, purposes }) as Promise<{ record: PreferenceRecord }>,\n get: (subjectId) => get(`/preferences/${enc(subjectId)}`) as Promise<PreferenceRecord>,\n },\n preferenceCenter: {\n mintToken: (input) => request('POST', '/preference-center/tokens', input) as Promise<CenterToken>,\n },\n members: {\n list: () => get('/members') as Promise<Member[]>,\n invite: (email, role) =>\n request('POST', '/members', { email, role }) as Promise<{ member: { userId: string; email: string; role: string } }>,\n setRole: (userId, role) =>\n request('PATCH', `/members/${enc(userId)}`, { role }) as Promise<{ member: { userId: string; email: string; role: string } }>,\n remove: (userId) => request('DELETE', `/members/${enc(userId)}`) as Promise<{ ok: true }>,\n },\n keys: {\n list: () => get('/keys') as Promise<ApiKeyPrefix[]>,\n issue: (input) => request('POST', '/keys', input ?? {}) as Promise<ApiKeyIssued>,\n revoke: async (prefix) => {\n await request('DELETE', `/keys/${enc(prefix)}`);\n },\n roll: (prefix) => request('POST', `/keys/${enc(prefix)}/roll`) as Promise<ApiKeyIssued>,\n update: (prefix, patch) => request('PATCH', `/keys/${enc(prefix)}`, patch) as Promise<{ ok: true }>,\n report: (o) => get(`/keys/report${qs({ unusedDays: o?.unusedDays, expiringDays: o?.expiringDays })}`) as Promise<ApiKeyReport>,\n },\n usage: () => get('/usage') as Promise<Usage>,\n integrationHealth: (cbid) => get(`/integration-health${qs({ cbid })}`) as Promise<IntegrationHealthReport>,\n provision: (input, o) => request('POST', `/provision${qs({ dryRun: o?.dryRun ? '1' : undefined })}`, input) as Promise<ProvisionResult>,\n org: {\n get: () => get('/org') as Promise<Org>,\n update: (patch) => request('PATCH', '/org', patch) as Promise<Org>,\n mailProvider: {\n get: () => get('/org/mail-provider') as Promise<MailProvider>,\n set: (input) => request('PUT', '/org/mail-provider', input) as Promise<MailProvider>,\n remove: async () => {\n await request('DELETE', '/org/mail-provider');\n },\n test: (to) => request('POST', '/org/mail-provider/test', to ? { to } : {}) as Promise<MailProviderTestResult>,\n },\n controller: {\n get: () => get('/org/controller') as Promise<OrgControllerIdentity>,\n set: (input) => request('PUT', '/org/controller', input) as Promise<OrgControllerIdentity>,\n remove: async () => {\n await request('DELETE', '/org/controller');\n },\n },\n rightsCaptcha: {\n get: () => get('/org/rights-captcha') as Promise<RightsCaptcha>,\n set: (input) => request('PUT', '/org/rights-captcha', input) as Promise<RightsCaptcha>,\n remove: () => request('DELETE', '/org/rights-captcha') as Promise<RightsCaptcha>,\n },\n rightsIdentity: {\n get: () => get('/org/rights-identity') as Promise<RightsIdentity>,\n set: (input) => request('PUT', '/org/rights-identity', input) as Promise<RightsIdentity>,\n remove: () => request('DELETE', '/org/rights-identity') as Promise<RightsIdentity>,\n },\n },\n audit: (limit) => get(`/audit${qs({ limit })}`) as Promise<{ entries: AuditEntry[] }>,\n assets: {\n upload: (input) => request('POST', '/assets', input) as Promise<{ url: string }>,\n delete: async (urlOrFileName) => {\n const name = urlOrFileName.split('/').pop() ?? urlOrFileName;\n await request('DELETE', `/assets/${enc(name)}`);\n },\n },\n reseller: {\n list: () => get('/reseller/customers') as Promise<ResellerChildList>,\n create: (input) => request('POST', '/reseller/customers', input) as Promise<{ child: ResellerChild; apiKey?: string }>,\n get: (id) => get(`/reseller/customers/${enc(id)}`),\n update: (id, patch) => request('PATCH', `/reseller/customers/${enc(id)}`, patch),\n deprovision: async (id, o) => {\n await request('DELETE', `/reseller/customers/${enc(id)}${qs({ purge: o?.purge ? 'true' : undefined })}`);\n },\n listKeys: (id) => get(`/reseller/customers/${enc(id)}/keys`) as Promise<ApiKeyPrefix[]>,\n mintKey: (id, input) => request('POST', `/reseller/customers/${enc(id)}/keys`, input ?? {}) as Promise<ApiKeyIssued>,\n revokeKey: async (id, prefix) => {\n await request('DELETE', `/reseller/customers/${enc(id)}/keys/${enc(prefix)}`);\n },\n },\n webhooks: {\n list: () => get('/webhooks') as Promise<WebhookSubscription[]>,\n create: (input) => request('POST', '/webhooks', input) as Promise<WebhookSubscription>,\n update: (id, patch) => request('PATCH', `/webhooks/${enc(id)}`, patch) as Promise<WebhookSubscription>,\n delete: async (id) => {\n await request('DELETE', `/webhooks/${enc(id)}`);\n },\n rollSecret: (id) => request('POST', `/webhooks/${enc(id)}/roll`) as Promise<{ secret: string }>,\n test: (id) => request('POST', `/webhooks/${enc(id)}/test`) as Promise<WebhookTestResult>,\n deadLetters: () => get('/webhooks/dead-letters') as Promise<{ deadLetters: WebhookDeadLetter[] }>,\n replayDeadLetter: (id) => request('POST', `/webhooks/dead-letters/${enc(id)}/replay`),\n },\n rightsLinks: {\n create: (cbid, input) => request('POST', `/sites/${enc(cbid)}/rights-links`, input) as Promise<RightsLink>,\n },\n consentSyncSecret: {\n get: (cbid) => get(`/sites/${enc(cbid)}/consent-sync-secret`) as Promise<ConsentSyncSecret>,\n rotate: (cbid) => request('POST', `/sites/${enc(cbid)}/consent-sync-secret`) as Promise<ConsentSyncSecretMinted>,\n remove: (cbid) => request('DELETE', `/sites/${enc(cbid)}/consent-sync-secret`) as Promise<ConsentSyncSecret>,\n },\n rightsCaptcha: {\n get: (cbid) => get(`/sites/${enc(cbid)}/rights-captcha`) as Promise<RightsCaptcha>,\n set: (cbid, input) => request('PUT', `/sites/${enc(cbid)}/rights-captcha`, input) as Promise<RightsCaptcha>,\n remove: (cbid) => request('DELETE', `/sites/${enc(cbid)}/rights-captcha`) as Promise<RightsCaptcha>,\n },\n rightsIdentity: {\n get: (cbid) => get(`/sites/${enc(cbid)}/rights-identity`) as Promise<RightsIdentity>,\n set: (cbid, input) => request('PUT', `/sites/${enc(cbid)}/rights-identity`, input) as Promise<RightsIdentity>,\n remove: (cbid) => request('DELETE', `/sites/${enc(cbid)}/rights-identity`) as Promise<RightsIdentity>,\n },\n subjects: {\n consent: (subjectId) =>\n get(`/subjects/${enc(subjectId)}/consent`) as Promise<{ subjectId: string; records: unknown[]; count: number; sites: string[] }>,\n },\n banners: {\n list: () => get('/banners') as Promise<BannerSummary[]>,\n create: (input) => request('POST', '/banners', input) as Promise<BannerRecord>,\n get: (id) => get(`/banners/${enc(id)}`) as Promise<BannerRecord>,\n update: (id, patch) => request('PUT', `/banners/${enc(id)}`, patch) as Promise<BannerRecord>,\n delete: async (id) => {\n await request('DELETE', `/banners/${enc(id)}`);\n },\n assignments: (id) => get(`/banners/${enc(id)}/assignments`) as Promise<{ cbids: string[] }>,\n setAssignments: (id, cbids) => request('PUT', `/banners/${enc(id)}/assignments`, { cbids }) as Promise<{ cbids: string[] }>,\n publish: (id) => request('POST', `/banners/${enc(id)}/publish`) as Promise<{ publishedCbids: string[] }>,\n },\n };\n}\n","/**\n * Server-side consent reading. The Cookie Munch embed stores the visitor's decision in a\n * first-party `CookieMunch` cookie (URL-encoded JSON). This lets a server / edge worker read\n * that decision from the incoming request — to gate server-rendered tags or SSR'd third-party\n * scripts BEFORE they hit the browser — without shipping the whole embed. Zero-dependency and\n * runtime-agnostic: accepts a raw Cookie header, a Node `req` (`req.headers.cookie`), or a\n * Fetch `Request` (`request.headers.get('cookie')`).\n *\n * import { readConsent, isAllowed } from '@cookiemunch/sdk/consent';\n * const consent = readConsent(req.headers.cookie); // ServerConsent | null\n * if (isAllowed(consent, 'statistics')) { …render analytics tag… }\n *\n * The contract is documented at https://cookiemunch.net/docs/consent-cookie. It accepts\n * exactly what the embed accepts, so a server never grants what the browser would refuse:\n * strict JSON (URL-encoded or not) and the relaxed object literal Cookiebot writes.\n *\n * This module imports nothing — `@cookiemunch/sdk/consent` pulls in no API client.\n */\n\n/** The first-party cookie the embed writes (mirrors @cookiemunch/core COOKIE_NAME). */\nexport const CONSENT_COOKIE_NAME = 'CookieMunch';\n\n/** A visitor's consent decision, read from the request cookie. `necessary` is always true. */\nexport interface ServerConsent {\n necessary: true;\n preferences: boolean;\n statistics: boolean;\n marketing: boolean;\n /** How consent was given. `implied` = not yet an explicit choice. */\n method: 'explicit' | 'implied';\n /** Custom (non-standard) categories, when the site defines any. */\n custom?: Record<string, boolean>;\n /** Consent-policy version the decision was made under (see `minVersion`). */\n ver: number;\n /** When the decision was made, epoch milliseconds (see `expiryDays`). */\n utc: number;\n /** Region code the decision was made in (`unknown` when undetected). */\n region: string;\n /** The anonymous consent-receipt id tying this browser to the consent log. */\n stamp: string;\n}\n\n/** The site's consent policy, so the server applies the same rules the embed does. */\nexport interface ReadConsentOptions {\n /** Cookie name. Default `CookieMunch`. */\n cookieName?: string;\n /**\n * The site's current consent version (`consent.version`, or the config `ver` when that is\n * higher). A decision made under an older version is not honoured by the embed — the\n * visitor is asked again — so it reads as `null` here too.\n */\n minVersion?: number;\n /** The site's `consent.expiryDays`: a decision older than this reads as `null`. */\n expiryDays?: number;\n /** Clock for `expiryDays`, epoch ms. Default `Date.now()`. */\n now?: number;\n}\n\n/** Anything we can pull a Cookie header off of. */\nexport type ConsentSource =\n | string\n | null\n | undefined\n | { headers?: { cookie?: string | null; get?(name: string): string | null } | Record<string, unknown> };\n\nfunction cookieHeaderOf(src: ConsentSource): string {\n if (typeof src === 'string') return src;\n if (!src || typeof src !== 'object') return '';\n const h = src.headers;\n if (!h) return '';\n // Fetch Request: headers.get('cookie'); Node: headers.cookie.\n if (typeof (h as { get?(n: string): string | null }).get === 'function') {\n return (h as { get(n: string): string | null }).get('cookie') ?? '';\n }\n const c = (h as Record<string, unknown>).cookie;\n return typeof c === 'string' ? c : '';\n}\n\n/** Extract one cookie's raw value from a Cookie header (no decoding). */\nfunction cookieValue(header: string, name: string): string | null {\n for (const part of header.split(';')) {\n const eq = part.indexOf('=');\n if (eq === -1) continue;\n if (part.slice(0, eq).trim() === name) return part.slice(eq + 1).trim();\n }\n return null;\n}\n\nfunction bool(v: unknown): v is boolean {\n return typeof v === 'boolean';\n}\n\nfunction decode(raw: string): string {\n try {\n return decodeURIComponent(raw);\n } catch {\n return raw; // not URL-encoded; use as-is\n }\n}\n\n/** The embed's own validation (core cookie.ts `normalize`), field for field. */\nfunction normalize(o: unknown): ServerConsent | null {\n if (!o || typeof o !== 'object') return null;\n const r = o as Record<string, unknown>;\n if (!bool(r.preferences) || !bool(r.statistics) || !bool(r.marketing)) return null;\n if (r.method !== 'explicit' && r.method !== 'implied') return null;\n if (typeof r.ver !== 'number' || typeof r.utc !== 'number') return null;\n if (typeof r.stamp !== 'string' && typeof r.stamp !== 'number') return null;\n if (typeof r.region !== 'string') return null;\n const out: ServerConsent = {\n necessary: true, // never trust a cookie that claims necessary:false\n preferences: r.preferences,\n statistics: r.statistics,\n marketing: r.marketing,\n method: r.method,\n ver: r.ver,\n utc: r.utc,\n region: r.region,\n stamp: String(r.stamp),\n };\n if (r.custom && typeof r.custom === 'object' && !Array.isArray(r.custom)) {\n const custom: Record<string, boolean> = {};\n for (const [k, v] of Object.entries(r.custom as Record<string, unknown>)) if (bool(v)) custom[k] = v;\n if (Object.keys(custom).length > 0) out.custom = custom;\n }\n return out;\n}\n\n/**\n * Parse a raw cookie value into a validated ServerConsent, or null. Accepts the two formats\n * the embed reads: URL-encoded (or plain) strict JSON, and Cookiebot's relaxed object\n * literal — `{stamp:'…',necessary:true,…}` — left behind by a site migrating from Cookiebot.\n */\nexport function parseConsentValue(raw: string): ServerConsent | null {\n if (!raw) return null;\n const decoded = decode(raw);\n try {\n return normalize(JSON.parse(decoded));\n } catch {\n /* not strict JSON — try the Cookiebot literal */\n }\n if (!decoded.includes('necessary')) return null;\n const json = decoded.replace(/'/g, '\"').replace(/([{,]\\s*)([A-Za-z_][\\w]*)\\s*:/g, '$1\"$2\":');\n try {\n return normalize(JSON.parse(json));\n } catch {\n return null;\n }\n}\n\nconst DAY_MS = 24 * 60 * 60 * 1000;\n\n/**\n * Read the visitor's consent from a request: a raw `Cookie` header, a Node `req`, or a Fetch\n * `Request`. Returns null when there is no decision the embed would honour — no cookie, an\n * unparseable one, or (with the options) one made under an older version or past expiry.\n */\nexport function readConsent(source: ConsentSource, opts: ReadConsentOptions = {}): ServerConsent | null {\n const raw = cookieValue(cookieHeaderOf(source), opts.cookieName ?? CONSENT_COOKIE_NAME);\n const consent = raw ? parseConsentValue(raw) : null;\n if (!consent) return null;\n if (typeof opts.minVersion === 'number' && consent.ver < opts.minVersion) return null;\n if (typeof opts.expiryDays === 'number' && opts.expiryDays > 0) {\n if ((opts.now ?? Date.now()) - consent.utc > opts.expiryDays * DAY_MS) return null;\n }\n return consent;\n}\n\n/**\n * Read the visitor's consent from a request. The original spelling of {@link readConsent},\n * kept for existing callers; the second argument is the cookie name.\n */\nexport function getConsent(source: ConsentSource, cookieName: string = CONSENT_COOKIE_NAME): ServerConsent | null {\n return readConsent(source, { cookieName });\n}\n\n/** Standard + custom consent categories. */\nexport type ConsentCategory = 'necessary' | 'preferences' | 'statistics' | 'marketing' | (string & {});\n\n/**\n * Whether a category is allowed for this visitor. `necessary` is always allowed. A missing\n * consent (`null` — visitor hasn't decided) is treated as NOT allowed for non-necessary\n * categories (fail-closed / prior-blocking posture), matching the embed.\n */\nexport function isAllowed(consent: ServerConsent | null, category: ConsentCategory): boolean {\n if (category === 'necessary') return true;\n if (!consent) return false;\n // Explicit per-literal access rather than `consent[category]`: the `(string & {})` arm of\n // ConsentCategory defeats control-flow narrowing, so a dynamic index is typed as an\n // implicit-any index into ServerConsent (TS7053, which fails the tsup DTS build).\n if (category === 'preferences') return consent.preferences === true;\n if (category === 'statistics') return consent.statistics === true;\n if (category === 'marketing') return consent.marketing === true;\n return consent.custom?.[category] === true;\n}\n","/**\n * Verify an inbound Cookie Munch webhook signature.\n *\n * Cookie Munch signs every webhook delivery with the subscription's `whsec_…` secret\n * (returned once when the webhook is created). Each POST carries:\n *\n * X-CookieMunch-Signature: sha256=<hex HMAC-SHA256(secret, `${timestamp}.${rawBody}`)>\n * X-CookieMunch-Timestamp: <unix SECONDS>\n * X-CookieMunch-Event: <event type, e.g. dsar.action_required>\n *\n * The signature covers the timestamp as well as the body, so a captured delivery cannot be\n * replayed forever — reject deliveries whose timestamp is outside `toleranceSeconds`\n * (default 300s / 5 min). This matches the platform-standard (Stripe-style) verifyWebhook shape.\n *\n * @example\n * import { verifyWebhookSignature } from '@cookiemunch/sdk';\n * // Express — capture the RAW body: app.use(express.raw({ type: 'application/json' }))\n * app.post('/hooks/cm', (req, res) => {\n * const ok = verifyWebhookSignature({\n * payload: req.body.toString('utf8'), // exact bytes, not a re-stringified object\n * signature: req.header('X-CookieMunch-Signature'),\n * timestamp: req.header('X-CookieMunch-Timestamp'),\n * secret: process.env.CM_WEBHOOK_SECRET!, // the whsec_… value\n * });\n * if (!ok) return res.sendStatus(400);\n * const event = JSON.parse(req.body.toString('utf8')); // { id, type, cbid, createdAt, data }\n * // …handle event.type…\n * res.sendStatus(200);\n * });\n *\n * Server-side only: uses `node:crypto`. Pass the EXACT raw request body — verifying against a\n * re-serialized JSON object fails whenever key order or whitespace differs from what was signed.\n */\nimport { createHmac, timingSafeEqual } from 'node:crypto';\n\nexport interface VerifyWebhookInput {\n /** The raw request body, exactly as received. Do NOT re-stringify parsed JSON. */\n payload: string;\n /** The `X-CookieMunch-Signature` header value. */\n signature: string | undefined | null;\n /** The `X-CookieMunch-Timestamp` header value (unix seconds). */\n timestamp: string | number | undefined | null;\n /** The subscription secret (`whsec_…`), shown once when the webhook was created. */\n secret: string;\n /**\n * Maximum age (in seconds) of a delivery before it is rejected as stale/replayed.\n * Defaults to 300 (5 minutes). Pass 0 to disable the freshness check entirely.\n */\n toleranceSeconds?: number;\n /** Current time in ms (injectable for tests); defaults to `Date.now()`. */\n now?: number;\n}\n\n/**\n * Returns true only when the signature is valid AND (unless disabled) the timestamp is fresh.\n * Fail-closed: any missing/malformed input returns false rather than throwing.\n */\nexport function verifyWebhookSignature(input: VerifyWebhookInput): boolean {\n const { payload, signature, timestamp, secret } = input;\n if (!signature || timestamp == null || timestamp === '' || !secret) return false;\n\n const tsRaw = String(timestamp);\n const tsNum = Number(tsRaw);\n if (!Number.isFinite(tsNum)) return false;\n\n const tolerance = input.toleranceSeconds ?? 300;\n if (tolerance > 0) {\n const nowSec = Math.floor((input.now ?? Date.now()) / 1000);\n if (Math.abs(nowSec - tsNum) > tolerance) return false; // stale or replayed\n }\n\n // HMAC over the EXACT timestamp bytes received, joined to the raw body with a dot — matching\n // the server's `sign(secret, timestamp, body)` (packages/server/src/webhook-delivery.ts).\n const expected = `sha256=${createHmac('sha256', secret).update(`${tsRaw}.${payload}`).digest('hex')}`;\n\n // Constant-time compare; timingSafeEqual throws on length mismatch, so guard first.\n const got = Buffer.from(signature);\n const want = Buffer.from(expected);\n if (got.length !== want.length) return false;\n return timingSafeEqual(got, want);\n}\n","/**\n * Sign a consent-sync token for a signed-in visitor (https://cookiemunch.net/docs/consent-sync).\n *\n * Hosted consent sync stores a person's consent decision so it follows them to their other\n * browsers. Cookie Munch never trusts an id the page names — anyone can send any id — so your\n * server, which knows who is signed in, signs a short-lived token with the site's consent-sync\n * secret (`client.consentSyncSecret.rotate(cbid)`, shown once) and puts it on the page:\n *\n * @example\n * import { mintConsentSyncToken } from '@cookiemunch/sdk';\n * const token = mintConsentSyncToken({\n * secret: process.env.CM_CONSENT_SYNC_SECRET!, // cmss_…\n * cbid: 'acme-com-9f3k',\n * subject: user.id, // an opaque id, never an email\n * });\n * // in the page, before the embed: <script>window.CookieMunchConfig = { syncToken: \"…\" }</script>\n *\n * Format: `cms1.<base64url(JSON {c, s, iat, exp})>.<base64url(HMAC-SHA256(secret, \"cms1.\" + payload))>`.\n * Server-side only: uses `node:crypto`. Never put the secret in a page.\n */\nimport { createHmac } from 'node:crypto';\n\nexport interface MintConsentSyncTokenInput {\n /** The site's consent-sync secret (`cmss_…`). */\n secret: string;\n /** The site the token is for. A token for one site is refused by every other. */\n cbid: string;\n /** Who is signed in: 1–128 of `A-Z a-z 0-9 . _ : @ + / = ~ -`. Use an opaque id. */\n subject: string;\n /** Lifetime in seconds. Default 3600; at most 86400 (Cookie Munch refuses longer). */\n ttlSeconds?: number;\n /** Current time in ms (injectable for tests). */\n now?: number;\n}\n\nconst SUBJECT_RE = /^[A-Za-z0-9._:@+/=~-]{1,128}$/;\n\nexport function mintConsentSyncToken(input: MintConsentSyncTokenInput): string {\n if (!input.secret) throw new Error('mintConsentSyncToken: secret is required');\n if (!input.cbid) throw new Error('mintConsentSyncToken: cbid is required');\n if (!SUBJECT_RE.test(input.subject)) throw new Error('mintConsentSyncToken: subject must be 1–128 characters of A-Z a-z 0-9 . _ : @ + / = ~ -');\n const ttl = Math.floor(input.ttlSeconds ?? 3600);\n if (!(ttl >= 1 && ttl <= 86_400)) throw new Error('mintConsentSyncToken: ttlSeconds must be between 1 and 86400');\n const iat = Math.floor((input.now ?? Date.now()) / 1000);\n const payload = Buffer.from(JSON.stringify({ c: input.cbid, s: input.subject, iat, exp: iat + ttl })).toString('base64url');\n const sig = createHmac('sha256', input.secret).update(`cms1.${payload}`).digest('base64url');\n return `cms1.${payload}.${sig}`;\n}\n"],"mappings":"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","names":["import_node_crypto","opts"]}