Docs

Site config

AdminUpdated Sep 15, 2026

Site config

A site's SiteConfig controls everything about how the banner behaves for that property: theme, copy, categories, blocking mode, geo rules, IAB framework, Google Consent Mode wiring, and more. It's the same object the embed fetches at runtime from the public, unauthenticated GET /config/:cbid — these /v1 routes are the authenticated read/write path for managing it programmatically, e.g. from a CI pipeline or an internal admin tool.

Requires the sites scope: sites:read to read, sites:write to save. See sites for how a cbid is created, and authentication & scopes for scoped keys in general.

GET /v1/sites/:cbid/config

Returns the current config, or a fresh default config if the site has never been configured. This endpoint never 404s for a config that doesn't exist yet — only for an unknown or foreign cbid (the site lookup happens first).

curl https://api.cookiemunch.net/v1/sites/acme-com-9f3k/config \
  -H "Authorization: Bearer fck_…" \
  -H "User-Agent: Mozilla/5.0"
{
  "cbid": "acme-com-9f3k",
  "ver": 1,
  "banner": {
    "type": "multilevel",
    "layout": "bottom",
    "theme": { "background": "#0b0f1a", "text": "#f5f5f5", "highlight": "#5b8def" }
  },
  "categories": {},
  "blocking": { "mode": "auto" },
  "i18n": { "defaultCulture": "en", "autoDetect": true },
  "framework": "none",
  "consentMode": { "enabled": true, "mode": "advanced", "waitForUpdate": 500 },
  "defaultMode": "opt-in",
  "bots": { "suppress": true },
  "consent": { "version": 1 }
}
import { createCookieMunch } from '@cookiemunch/sdk';
const fc = createCookieMunch({ apiKey: process.env.FC_KEY, baseUrl: 'https://api.cookiemunch.net' });
const config = await fc.sites.getConfig('acme-com-9f3k');

PUT /v1/sites/:cbid/config

Saves the config. The request body is a SiteConfig object (v1 inline shape shown above, or a v2 { v: 2, flow, categories, ... } document from Banner Studio).

Important — this is not a deep merge against your saved config. The server normalizes whatever you send by merging it over a fresh default config (defaultConfig(cbid)), not over what's currently stored, then overwrites the record wholesale. Concretely: banner, categories, blocking, i18n, consentMode, consent, bots, tcf, gpp, usPrivacy, adPersonalization, controller, and withdrawal are each shallow-merged onto their default value (so a partial { "banner": { "type": "inline" } } keeps the default theme/layout, not whatever theme you'd previously saved); framework, defaultMode, geoRules, consentSync, and jurisdiction are replaced outright when present in the patch, and otherwise fall back to the default. Any top-level field you omit reverts to its default, it does not keep its previously-saved value. To change one field safely, GET the current config first, edit the object client-side, and PUT the whole thing back — that's what the dashboard and the SDK examples below do implicitly by round-tripping the full object.

curl -X PUT https://api.cookiemunch.net/v1/sites/acme-com-9f3k/config \
  -H "Authorization: Bearer fck_…" \
  -H "User-Agent: Mozilla/5.0" \
  -H "Content-Type: application/json" \
  -d '{
    "blocking": { "mode": "auto", "ignoreSelectors": [".fc-first-party"] },
    "defaultMode": "opt-in",
    "framework": "iab"
  }'
{
  "cbid": "acme-com-9f3k",
  "ver": 1,
  "banner": { "type": "multilevel", "layout": "bottom", "theme": { "background": "#ffffff", "text": "#111111", "highlight": "#2563eb" } },
  "categories": {},
  "blocking": { "mode": "auto", "ignoreSelectors": [".fc-first-party"] },
  "i18n": { "defaultCulture": "en", "autoDetect": true },
  "framework": "iab",
  "consentMode": { "enabled": true, "mode": "advanced", "waitForUpdate": 500 },
  "defaultMode": "opt-in",
  "bots": { "suppress": true },
  "consent": { "version": 1 }
}

Note banner.theme came back as the default palette, not any previously-saved custom theme, because this request's banner key was omitted — see the merge caveat above.

// Safe partial update: read-modify-write.
const current = await fc.sites.getConfig('acme-com-9f3k');
const updated = await fc.sites.putConfig('acme-com-9f3k', {
  ...current,
  framework: 'iab',
  blocking: { ...current.blocking, ignoreSelectors: ['.fc-first-party'] },
});

Key fields

Field

Type

Description

banner

object

type (multilevel|inline|accept-decline|accept-only|do-not-sell), layout, theme, content — or, on a v2 config, the whole banner is instead a flow document from Banner Studio ({ v: 2, flow, categories }).

categories

object

Per-category label/description overrides for the four standard categories (necessary/preferences/statistics/marketing).

blocking

object

{ mode: "auto" | "manual", ignoreSelectors?: string[] }.

i18n

object

{ defaultCulture, autoDetect } — default banner language and whether to auto-detect from the visitor's browser.

framework

"none" | "iab"

"iab" enables IAB TCF v2.2 (requires a plan with TCF access — see the gating note below).

consentMode

object

Google Consent Mode v2 wiring: { enabled, mode: "basic" | "advanced", waitForUpdate }.

geoRules

array

Ordered { match: { countries?, regions? }, mode } rules; first match wins, falling through to defaultMode.

defaultMode

"opt-in" | "opt-out" | "off"

Banner mode when no geoRule matches.

experiment

object

A/B test config: { enabled, splitB, variantB } — results at GET /v1/sites/:cbid/ab.

consent

object

{ version, expiryDays? } — bump version to force re-consent from everyone.

tcf / gpp / usPrivacy

object

IAB TCF publisher settings, Global Privacy Platform, and legacy US Privacy string config, respectively.

controller / withdrawal / jurisdiction

object / object / string

Data-controller identity and consent-withdrawal info surfaced on signed receipts.

adPersonalization

object

{ enabled, default?, label? } — the personalized-vs-basic-ads split; see POST /v1/sites/:cbid/elements/ad-personalization below.

Errors

404 if the cbid is unknown or belongs to another org. 400 with structured issues when a v2 flow config fails server-side validation or lint:

{
  "error": "invalid_config",
  "issues": [
    { "code": "unknown_start_view", "message": "flow.start references a view id that doesn't exist", "path": "flow.start" }
  ]
}

Note — plan gating. Plan limits are enforced here too, not just in the dashboard UI. If your org is on a plan without the relevant feature, the server silently resets the offending field before saving rather than erroring: framework: "iab" reverts to "none" (and any tcf block, autoFrameworkByRegion, and v2 tcfPanel/tcfVendorCount flow elements are stripped) without tcf access; banner.content.poweredBy is forced back to true without white-labeling; and experiment.enabled is forced to false without A/B testing. A scoped automation key can never bypass plan limits this way, but the response also won't flag that it happened — diff the response body against what you sent if a premium field doesn't seem to take effect.

POST /v1/sites/:cbid/elements/ad-personalization

A narrower, idempotent helper for one specific toggle: enables (or disables) the personalized-vs-basic ad split on whichever banner form the site uses (inline v1 or a v2 flow), injecting the in-banner toggle element into a v2 flow automatically when needed.

Field

Type

Required

Description

enabled

boolean

no

Defaults to true.

default

boolean

no

Default state of the toggle for new visitors.

label

string

no

Custom label for the injected element.

curl -X POST https://api.cookiemunch.net/v1/sites/acme-com-9f3k/elements/ad-personalization \
  -H "Authorization: Bearer fck_…" \
  -H "User-Agent: Mozilla/5.0" \
  -H "Content-Type: application/json" \
  -d '{ "enabled": true }'
{ "ok": true, "form": "flow", "enabled": true, "injectedElement": true }

form is "flow" for a v2 config or "inline" for v1 (inline banners render the toggle from the config flag directly, so no element injection is needed — note may explain why injection was skipped on a flow site, e.g. no matching view).

Related: programmatic flow editing

Sites on a v2 (Banner Studio) config also expose GET/PUT /v1/sites/:cbid/flow (read the flow plus lint issues, or wholesale-replace it) and POST /v1/sites/:cbid/flow/ops (apply structured edit operations) as an alternative to sending a raw flow object through this page's PUT .../config. Both paths persist through the same plan-gating and validation as PUT .../config; 501 if flow ops aren't configured on this deployment. See the @cookiemunch/sdk's fc.sites.getFlow() / fc.sites.editFlow() / fc.sites.setFlow() for the typed client methods.

Next: consent log, stats & export to read the decisions this config produces, or cookies, scan & verify to populate the cookie declaration a cookieTable element renders.

Was this page helpful?