Docs

Integrations

AdminUpdated Sep 15, 2026

Integrations

Cookie Munch's ad/analytics integrations live in @cookiemunch/integrations (packages/integrations/src/index.ts) — a framework-agnostic, dependency-free layer of pure functions and "sinks" that translate a Cookie Munch consent decision into each platform's own consent signal. Site-embed integrations (GTM, WordPress, page builders) instead just install the same consent.js tag through a different channel. Nothing here imports a real ad SDK; every platform global (gtag, uetq, fbq, ttq, googletag, pbjs, …) is passed in as a parameter, so every adapter is pure and unit-testable without the vendor's script loaded.

Google Consent Mode v2

consentModeV2(state) is the single source of truth for the 7 Consent Mode v2 signals — the same mapping backs the web gtag sink, native Firebase mappers, and any custom code, so no platform's signals can drift from another's:

import { consentModeV2 } from '@cookiemunch/integrations';

consentModeV2({ preferences: true, statistics: true, marketing: false });
// → { ad_storage: 'denied', analytics_storage: 'granted', ad_user_data: 'denied',
//     ad_personalization: 'denied', functionality_storage: 'granted',
//     personalization_storage: 'granted', security_storage: 'granted' }

The mapping: marketingad_storage + ad_user_data; statisticsanalytics_storage; preferencesfunctionality_storage + personalization_storage; security_storage is always granted (strictly necessary). ad_personalization follows IAB TCF purpose 4 when granular state.tcf selections are present, otherwise it follows the coarse marketing gate.

Web: createGoogleConsentSink

import { createGoogleConsentSink, attachSink } from '@cookiemunch/integrations';

const sink = createGoogleConsentSink({ waitForUpdate: 500 }); // pushes onto window.dataLayer by default, or pass { gtag }
attachSink(window.CookieMunch, sink);

attachSink fires emitDefault() once (a consent('default', …) call with everything denied except security_storage, plus ads_data_redaction and url_passthrough), then pushes the current consent state, then subscribes to every future change via onConsentChange.

Native / Firebase: firebaseConsent

firebaseConsent(state) derives the Firebase Analytics setConsent map (ANALYTICS_STORAGE, AD_STORAGE, AD_USER_DATA, AD_PERSONALIZATION) from consentModeV2, so native values are always identical to the corresponding web ones:

// iOS (Swift)
import FirebaseAnalytics
Analytics.setConsent([
  .analyticsStorage:  map["ANALYTICS_STORAGE"]  == "granted" ? .granted : .denied,
  .adStorage:         map["AD_STORAGE"]         == "granted" ? .granted : .denied,
  .adUserData:        map["AD_USER_DATA"]       == "granted" ? .granted : .denied,
  .adPersonalization: map["AD_PERSONALIZATION"] == "granted" ? .granted : .denied,
])
// Android (Kotlin)
firebaseAnalytics.setConsent(mapOf(
  ConsentType.ANALYTICS_STORAGE  to status(map["ANALYTICS_STORAGE"]!!),
  ConsentType.AD_STORAGE         to status(map["AD_STORAGE"]!!),
  ConsentType.AD_USER_DATA       to status(map["AD_USER_DATA"]!!),
  ConsentType.AD_PERSONALIZATION to status(map["AD_PERSONALIZATION"]!!),
))

Call it once on app start with the persisted decision, and again from your consent client's onChange callback whenever the user updates consent.

Microsoft UET

createMicrosoftUetSink() pushes a single ad_storage signal onto window.uetq — UET only exposes the one signal, gated on marketing:

import { createMicrosoftUetSink, attachSink } from '@cookiemunch/integrations';
attachSink(window.CookieMunch, createMicrosoftUetSink());
// on consent: uetq.push(['consent', 'update', { ad_storage: 'granted' | 'denied' }])

Meta Pixel

createMetaSink() calls fbq('consent', 'grant' | 'revoke') based on the marketing purpose:

import { createMetaSink, attachSink } from '@cookiemunch/integrations';
attachSink(window.CookieMunch, createMetaSink());

Pass { fbq: myFbq } to override the global lookup (e.g. in tests).

TikTok Pixel

createTiktokSink() calls ttq.grantConsent() / ttq.revokeConsent(), again gated on marketing:

import { createTiktokSink, attachSink } from '@cookiemunch/integrations';
attachSink(window.CookieMunch, createTiktokSink());

Universal ad-network toolkit

For ad stacks with no first-class sink, a smaller set of primitives shares the same convention: personalized/behavioral ads require marketing granted; contextual ads may keep running regardless.

  • adPersonalizationAllowed(consent) — the single source of truth (marketing === true).

  • onAdConsent(api, cb) — subscribe; fires immediately with { personalized, consent } and again on every change.

  • applyGoogleAdManager(googletag, personalized) — queues onto googletag.cmd, prefers setPrivacySettings({ nonPersonalizedAds }), falls back to legacy setRequestNonPersonalizedAds.

  • applyAdSense(personalized) — sets adsbygoogle.requestNonPersonalizedAds (must run before AdSense units load; for EEA/UK use the Consent Mode sink instead — NPA alone isn't the compliant path there).

  • refreshPrebidOnConsent(pbjs, cb?) — re-runs the Prebid auction on consent change for non-TCF setups (Prebid's own consentManagement module reads window.__tcfapi automatically when TCF is enabled, so no manual wiring is needed in that case).

  • createGenericAdSink(onChange) — wrap any in-house/custom ad server as a ConsentSink.

import { onAdConsent, applyGoogleAdManager } from '@cookiemunch/integrations';

onAdConsent(window.CookieMunch, ({ personalized }) => {
  applyGoogleAdManager(window.googletag, personalized);
});

Site-embed integrations

Every channel below installs the same canonical <script id="CookieMunch"> tag — pick the one matching your stack; you only need one.

<script id="CookieMunch"
        src="https://<host>/consent.js"
        data-cbid="<SITE_ID>"
        data-blockingmode="auto"></script>

Place it as early in <head> as possible so automatic prior-blocking intercepts trackers before they run. Get the exact tag pre-filled via GET /v1/sites/{cbid}/snippet or client.sites.snippet(cbid) — see Client reference or the get_install_snippet MCP tool.

WordPress

Location: plugins/wordpress/forgeconsent/.

  1. Upload to wp-content/plugins/ (or zip and use Plugins → Add New → Upload Plugin), then activate.

  2. Configure under Settings → Cookie Munch: API host, site id (cbid), blocking mode, culture, enable toggle.

  3. The plugin injects the embed at wp_head priority 1 — as early as possible, even ahead of aggressively-enqueued analytics scripts.

  4. Render the cookie table anywhere with the [cookiemunch_cookie_declaration] shortcode.

Google Tag Manager

Location: integrations/gtm/. Two install paths:

Option 1 — Custom Template (recommended): GTM → TemplatesNew → overflow menu → Importintegrations/gtm/template.tpl. Then Tags → New → choose the Cookie Munch template, fill in API host/cbid/blocking mode/culture, set the trigger to Consent Initialization - All Pages (fall back to Initialization - All Pages), then Submit / Publish.

Option 2 — Custom HTML (fallback): paste the canonical embed into a Custom HTML tag with "Support document.write" unchecked, same trigger, Submit / Publish.

The Custom Template passes cbid/blockingmode/culture via the consent.js query string plus a window.CookieMunchConfig global (GTM's sandboxed injectScript can't set data-* attributes); the Custom HTML fallback uses the literal data-* attributes and is byte-for-byte identical to a direct install.

Page builders (Shopify, Wix, Webflow, generic HTML)

Location: integrations/snippets/. Same canonical embed, different paste-point per platform:

Platform

Where

Shopify

theme.liquid, immediately after <head>, before any analytics/pixel/app scripts. Full checkout customization requires Shopify Plus.

Wix

Settings → Custom Code, added to <head> of all pages.

Webflow

Site Settings → Custom Code → Head Code (site-wide).

Generic HTML

Any hand-built or templated site — same tag, same placement rule.

Cookie declaration table

A second, optional embed for a Cookie Policy page, listing scanned cookies by category:

<script src="https://<host>/cookie-declaration.js" data-cbid="<SITE_ID>"></script>

Verify the install

After installing on any channel, open the site in a fresh session, confirm a request to /consent.js fires and the banner appears, then run POST /v1/sites/{cbid}/verify (dns/meta/file challenge) — domain verification unlocks consent CSV export and signed receipts for that site.

Was this page helpful?