Integration Guide

Wire Google Consent Mode v2 — and other ad-tech — to DPDP purposes.

Categorise each purpose correctly in the dashboard and the widget translates user choices into the right Google signals automatically. This guide shows the exact category to pick for Google Analytics 4, Google Ads, and Microsoft Clarity — plus how to verify it's working end-to-end.

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 categorySignals granted on consent
MARKETINGad_storage, ad_user_data, ad_personalization
ANALYTICSanalytics_storage
PERSONALIZATIONpersonalization_storage
FUNCTIONALfunctionality_storage
SOCIALpersonalization_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 category OTHER 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.

html
<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.

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.

html
<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

js
// 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 to MARKETING.
  • 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 grants analytics_storage when an ANALYTICS-category purpose is consented.
  • Clarity records sessions even after Reject All. The script tag is missing type="text/plain" or data-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-category pattern 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-Country isn't being injected by your CDN / reverse proxy, every visitor falls through to the base config. Verify by hitting /api/v1/widget-config with 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.