How the mapping works
Every purpose you create in the dashboard has a category field. When a user consents to a purpose, the widget translates that category into the eight Google Consent Mode v2 signals using a fixed mapping. security_storage is always granted (Google requires this for fraud prevention and it's exempt from consent under DPDP, GDPR, and most other regimes); everything else is denied by default and granted only when a matching purpose is consented.
| Purpose category | Signals granted on consent |
|---|---|
| MARKETING | ad_storage, ad_user_data, ad_personalization |
| ANALYTICS | analytics_storage |
| PERSONALIZATION | personalization_storage |
| FUNCTIONAL | functionality_storage |
| SOCIAL | personalization_storage |
| NECESSARY | (none — security_storage is always granted) |
| OTHER | (no signals) |
The category is what matters — not the purpose name
A purpose named "Advertising" with categoryOTHER will not grant any ad signals. The widget reads the enum field, not the human name. If conversions are being blocked even after Accept All, the category is almost always the cause.Enabling the emitter
Switch on Google Consent Mode v2 in Project → Settings → Banner. Once enabled, the widget emits default during bootstrap (before any Google tag is allowed to evaluate consent) and update after the user makes a choice. Both calls land on window.dataLayer in the canonical ["consent", command, signals] shape — picked up by direct gtag.js and Google Tag Manager's built-in Consent Mode template alike.
Google Analytics 4
GA4 respects analytics_storage. When denied, GA4 still pings but skips the _ga cookies — you get cookieless modeled traffic instead of cross-session attribution. When granted, full analytics resumes.
1. Create the purpose
- Name: Analytics
- Category:
ANALYTICS - Required: off (consent is opt-in)
- Pre-ticked: off (DPDP §6 requires affirmative action)
2. Add the gtag.js snippet
Drop this directly into <head>. The widget loads asynchronously but emits the GCM default state synchronously during bootstrap, so GA4 sees the denied state on its first evaluation.
<script async src="https://www.googletagmanager.com/gtag/js?id=G-XXXX"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){ dataLayer.push(arguments); }
gtag('js', new Date());
gtag('config', 'G-XXXX');
</script>Using Google Tag Manager?
If you load GA4 through GTM, install only the GTM container snippet on the page — don't also add the gtag.js block above. GTM's built-in Consent Mode template reads the same dataLayer entries the widget emits, so no extra wiring is needed.Google Ads
Google Ads conversions, remarketing, and Customer Match all need three signals: ad_storage (write cookies / device IDs), ad_user_data (send user data to Google), and ad_personalization (use the data for personalised ads). All three are granted in one shot by the MARKETING category.
1. Create the purpose
- Name: Advertising (or Marketing)
- Category:
MARKETING - Required: off
- Pre-ticked: off
2. Add the conversion tag
<script async src="https://www.googletagmanager.com/gtag/js?id=AW-XXXXXXXXX"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){ dataLayer.push(arguments); }
gtag('js', new Date());
gtag('config', 'AW-XXXXXXXXX');
</script>
<!-- Fire on the conversion page (e.g. order confirmation) -->
<script>
gtag('event', 'conversion', {
send_to: 'AW-XXXXXXXXX/AbC-D_efG-h12_34-567',
value: 1.0,
currency: 'INR'
});
</script>Conversions still blocked after Accept All?
The most common cause is a marketing purpose with categoryOTHER (the default for purposes created before the category feature shipped). Open the purpose in the dashboard, change the category to MARKETING, save, and reload the test page. The dashboard also surfaces a warning if a purpose name suggests marketing but its category isn't set.Microsoft Clarity
Clarity is Microsoft's session recording and heatmap tool. It does not read Google Consent Mode signals — those are a Google API. For Clarity, gate the script tag itself with the DPDP Comply script blocker so it never loads until the user consents.
1. Create the purpose
Reuse the same Analytics purpose you set up for GA4 — Clarity is analytics-class data. No need to create a separate one.
- Name: Analytics
- Category:
ANALYTICS
2. Block the Clarity tag until consent
Two changes vs the standard Clarity snippet: set type="text/plain" (so the browser doesn't execute it) and add data-consent-category="Analytics" with the purpose name (case-insensitive). After the user consents, the widget rewrites the type to text/javascript and Clarity loads.
<script type="text/plain" data-consent-category="Analytics">
(function(c,l,a,r,i,t,y){
c[a]=c[a]||function(){(c[a].q=c[a].q||[]).push(arguments)};
t=l.createElement(r);t.async=1;t.src="https://www.clarity.ms/tag/"+i;
y=l.getElementsByTagName(r)[0];y.parentNode.insertBefore(t,y);
})(window, document, "clarity", "script", "YOUR_CLARITY_ID");
</script>Why purpose name and not category?
The script blocker matches on the human-readable purpose name so the same attribute works for any tool, even ones without a clean Google category mapping (TikTok Pixel, Hotjar, etc.). The GCM emitter uses the category enum because Google's signals are fixed. They're two independent gates — both fire on the same consent event.Verify it's working
Open DevTools on a page where the widget is loaded, then run these checks in the console.
Inspect dataLayer
// Every Consent Mode command the widget has emitted on this page
window.dataLayer.filter(e => Array.isArray(e) && e[0] === 'consent')On a fresh load you should see one entry shaped like ["consent", "default", {ad_storage: "denied", ...}]. After clicking Accept All, a second entry appears with "update" and every signal flipped to granted.
Tag Assistant (Google Ads / GA4)
Install Google Tag Assistant and record a session that covers banner load → Accept All → conversion event. Each Google tag shows its consent state at fire time. ad_storage: granted on the conversion event means everything is wired correctly.
Cookies (Clarity)
After consenting to Analytics, look in DevTools Application → Cookies for _clck and _clsk. They appear only when Clarity has actually loaded.
Common pitfalls
- Conversions stay blocked after Accept All. The marketing purpose has category
OTHER. Set it toMARKETING. - GA4 fires under denied state when you expected granted. The user clicked Customize and didn't tick the analytics purpose, OR the purpose category is
OTHER. The widget only grantsanalytics_storagewhen anANALYTICS-category purpose is consented. - Clarity records sessions even after Reject All. The script tag is missing
type="text/plain"ordata-consent-category. Without both, the widget can't gate it. The browser executes the script normally and Clarity ignores the consent state. - First page load shows analytics traffic before consent. This is expected with Google Consent Mode — under default-denied, GA4 still pings to send modeled events, but no cookies are set and no user identifiers are sent. To suppress entirely, also script-block the gtag.js loader using the same
data-consent-categorypattern as Clarity. - Region rule says "Force GCM" but it's still off in EU traffic. Region rules override the base setting only when a country match hits. If
X-Countryisn't being injected by your CDN / reverse proxy, every visitor falls through to the base config. Verify by hitting/api/v1/widget-configwith a test country header.
Need a tool that isn't here?
The same two gates — GCM signal mapping by category, script blocking by purpose name — cover every tool we've tested. For anything obscure, drop a note to support and we'll add a recipe.