Banner library
- Written for
- + Written for
- Deprecated
- + Deprecated
- Applies to
- + Applies to
Banner library
The banner library is an account-level store of reusable banner designs — distinct from a single site's live config. A design is a json payload (in practice a v2 { v: 2, flow, categories, ... } banner config) that isn't tied to any one site: build it once, assign it to any number of cbids, and publish to push it live everywhere it's assigned. Assignment is one-design-per-site — assigning a cbid to a new design silently moves it off whatever design it was previously assigned to.
All routes live under /v1/banners, require an API key, and are scoped to the calling key's org (a key can never read or write another org's designs). Scoped keys need banners:read for GETs and banners:write for everything else; a legacy unscoped key has full access regardless. publish only ever overlays banner-presentation keys (v, theme, categories, customCss, flow) onto a site's existing config — it can never smuggle a site-level or plan-gated field (tcf, blocking, geoRules, …) past the same plan-gating a direct PUT /v1/sites/{cbid}/config enforces.
If this deployment has no banner-library store wired up (self-hosted only — never true against the hosted API), every route below returns
501 { "error": "banner library not configured" }.
List designs
GET /v1/bannersScope: banners:read
Returns lightweight summaries only — no json payload — so listing stays cheap regardless of library size.
curl -H "Authorization: Bearer $COOKIEMUNCH_API_KEY" \
https://api.cookiemunch.net/v1/banners[
{
"id": "bnr_8f2c1a",
"name": "EU cookie banner — dark",
"updatedAt": 1730332800000,
"assignedCbids": ["site_abc123", "site_def456"]
}
]const banners = await client.banners.list();Create a design
POST /v1/bannersScope: banners:write
Field | Type | Required | Description |
|---|---|---|---|
| string | yes | Display name for the design. |
| object | yes | A v2 banner config ( |
curl -X POST https://api.cookiemunch.net/v1/banners \
-H "Authorization: Bearer $COOKIEMUNCH_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "EU cookie banner — dark",
"json": { "v": 2, "flow": { "views": [] }, "categories": [] }
}'201 Created:
{
"id": "bnr_8f2c1a",
"orgId": "org_9f01",
"name": "EU cookie banner — dark",
"json": { "v": 2, "flow": { "views": [] }, "categories": [] },
"createdAt": 1730332800000,
"updatedAt": 1730332800000
}Errors: 400 { "error": "name required" } / { "error": "json required" } for missing fields; 400 { "error": "invalid_config", "issues": [...] } if json fails validation/lint.
const banner = await client.banners.create({ name, json });Get / update / delete a design
GET /v1/banners/{id}
PUT /v1/banners/{id}
DELETE /v1/banners/{id}Scope: banners:read for GET, banners:write for PUT/DELETE.
PUT accepts a partial patch ({ name?, json? }) and re-validates json when it's supplied.
curl -X PUT https://api.cookiemunch.net/v1/banners/bnr_8f2c1a \
-H "Authorization: Bearer $COOKIEMUNCH_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "name": "EU cookie banner — dark v2" }'DELETE refuses while the design is still assigned to any site:
{ "error": "Banner is assigned to 2 site(s): site_abc123, site_def456", "code": "banner_in_use", "cbids": ["site_abc123", "site_def456"] }Errors: 404 { "error": "banner not found" }. 409 (the banner_in_use body above) from DELETE while assigned — unassign every site first. 400 { "error": "invalid_config", "issues": [...] } from PUT if the patched json fails validation.
const banner = await client.banners.get('bnr_8f2c1a');
await client.banners.update('bnr_8f2c1a', { name: 'EU cookie banner — dark v2' });
await client.banners.delete('bnr_8f2c1a');Assignments
GET /v1/banners/{id}/assignments
PUT /v1/banners/{id}/assignmentsScope: banners:read for GET, banners:write for PUT.
GET returns the cbids currently pointed at this design. PUT replaces the entire assignment set in one call — any cbid you drop reverts to unassigned (its already-published config is untouched), and any cbid you add is moved off whichever other design previously owned it.
Field | Type | Required | Description |
|---|---|---|---|
| string[] | yes | The complete set of site ids this design should own. |
curl -X PUT https://api.cookiemunch.net/v1/banners/bnr_8f2c1a/assignments \
-H "Authorization: Bearer $COOKIEMUNCH_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "cbids": ["site_abc123", "site_def456"] }'{ "cbids": ["site_abc123", "site_def456"] }Errors: 400 { "error": "cbids must be an array of strings" }. 422 { "error": "cbid_not_in_org", "code": "cbid_not_in_org" } if any cbid doesn't belong to the calling key's org — the whole call is rejected, none of the assignments are applied. 404 if id doesn't exist.
const { cbids } = await client.banners.assignments('bnr_8f2c1a');
await client.banners.setAssignments('bnr_8f2c1a', ['site_abc123', 'site_def456']);Publish
POST /v1/banners/{id}/publishScope: banners:write
Compiles the design and overlays it onto the live SiteConfig of every currently-assigned site — this is the call that actually reaches visitors. Re-run it after every edit to a published design; PUT /v1/banners/{id} alone does not push changes live.
curl -X POST https://api.cookiemunch.net/v1/banners/bnr_8f2c1a/publish \
-H "Authorization: Bearer $COOKIEMUNCH_API_KEY"{ "publishedCbids": ["site_abc123", "site_def456"] }Publishing fires a banner.published webhook per site (see webhooks).
Errors: 400 { "error": "invalid_config", "issues": [...] } if the design has drifted into an invalid state since it was last saved. 404 if id doesn't exist.
const { publishedCbids } = await client.banners.publish('bnr_8f2c1a');Note: publishing one design across a fleet of sites — say, all EU properties sharing a brand — is the fastest way to keep them visually consistent. Build once, then call this endpoint (or wire it into a deploy pipeline) whenever the design changes, instead of editing every site's config individually.
Next: cookies, scan & verify to see what feeds a banner's cookie table, or API keys, members, webhooks & usage for the full webhook event catalogue.