Context
We are launching Google Ads campaigns on kestra.io and need Google Consent Mode v2 properly implemented. Currently, GTM does not load at all until the user accepts cookies in Europe, which means Google cannot use conversion modeling to estimate conversions from users who decline cookies — causing ~10-30% underreporting of conversions in European markets.
Internal spec / business source of truth: Notion — Google Consent Mode v2 rollout.
Current Implementation (source: src/scripts/cookieconsent.ts)
Library: vanilla-cookieconsent (open-source)
Detection: Europe detection via Intl.DateTimeFormat().resolvedOptions().timeZone
Current behavior:
- Non-Europe users: GTM + PostHog + marketing load immediately (no consent required) ✅
- Europe users: GTM is not loaded at all until user clicks "Accept". The
enabledAnalytics() function injects the GTM script tag only after consent is granted via the onConsent callback.
Two consent categories exist:
analytics → triggers enabledAnalytics() (loads GTM + PostHog)
marketing → triggers enabledMarketing() (pushes enable_marketing event to dataLayer)
The problem: Google Consent Mode v2 requires GTM to be loaded before consent, with all consent parameters set to denied. This allows Google to send anonymous pings for conversion modeling. Currently, GTM is not present on the page at all before consent, so Google receives zero signals.
Tag IDs
- GTM container:
GTM-T4F85WRF
- GA4 property:
G-EYVNS03HHR
- Google Ads tag:
AW-18075963910
Expected Behavior
The implementation should satisfy the following behavior. Implementation details are left to the developer — please refer to Google's Consent Mode v2 documentation for the canonical patterns.
| Aspect |
Current |
Required |
| GTM loading (Europe) |
Only after consent |
Always loaded, with denied defaults |
| Consent signals to Google |
None |
denied by default → granted on accept |
| Conversion modeling |
Not possible |
Enabled (Google receives anonymous pings) |
Specifically:
- Europe users, before any interaction with the banner: GTM is loaded, and the four Consent Mode v2 signals (
ad_storage, ad_user_data, ad_personalization, analytics_storage) are set to denied before GTM loads, with wait_for_update configured.
- Europe users, after accepting a category: the relevant signals transition to
granted via gtag('consent', 'update', ...).
- Europe users, after declining or only partially accepting: Google tags fire in cookieless ping mode for the still-
denied signals (no client-side cookies, anonymous pings only).
- Europe users who later revoke consent via the Settings modal: the relevant signals must transition back to
denied. This is the edge case the developer should explicitly handle and test.
- Non-Europe users: all four signals default to
granted, behavior otherwise unchanged.
Out of scope — do not regress
- PostHog initialization must continue to fire only after
analytics consent (it is not a Google product and is not driven by Consent Mode v2 signals; just keep the existing trigger).
enable_marketing dataLayer event must continue to fire only after marketing consent.
- No regression on existing analytics or marketing tracking for non-Europe users.
Acceptance Criteria
GTM Configuration Required (Marketing/SEO team — blocking)
The code change alone does not enable cookieless pinging. The following GTM configuration is required for the acceptance criteria to be met, and must be coordinated with this PR:
- Each Google Ads tag's "Consent Settings" must require
ad_storage and ad_user_data.
- The GA4 tag's "Consent Settings" must require
analytics_storage.
Priority
High — Prerequisite for accurate Google Ads conversion reporting and full GDPR compliance. Blocking conversion modeling for European paid campaigns.
References
Context
We are launching Google Ads campaigns on kestra.io and need Google Consent Mode v2 properly implemented. Currently, GTM does not load at all until the user accepts cookies in Europe, which means Google cannot use conversion modeling to estimate conversions from users who decline cookies — causing ~10-30% underreporting of conversions in European markets.
Internal spec / business source of truth: Notion — Google Consent Mode v2 rollout.
Current Implementation (source:
src/scripts/cookieconsent.ts)Library:
vanilla-cookieconsent(open-source)Detection: Europe detection via
Intl.DateTimeFormat().resolvedOptions().timeZoneCurrent behavior:
enabledAnalytics()function injects the GTM script tag only after consent is granted via theonConsentcallback.Two consent categories exist:
analytics→ triggersenabledAnalytics()(loads GTM + PostHog)marketing→ triggersenabledMarketing()(pushesenable_marketingevent to dataLayer)The problem: Google Consent Mode v2 requires GTM to be loaded before consent, with all consent parameters set to
denied. This allows Google to send anonymous pings for conversion modeling. Currently, GTM is not present on the page at all before consent, so Google receives zero signals.Tag IDs
GTM-T4F85WRFG-EYVNS03HHRAW-18075963910Expected Behavior
The implementation should satisfy the following behavior. Implementation details are left to the developer — please refer to Google's Consent Mode v2 documentation for the canonical patterns.
denieddefaultsdeniedby default →grantedon acceptSpecifically:
ad_storage,ad_user_data,ad_personalization,analytics_storage) are set todeniedbefore GTM loads, withwait_for_updateconfigured.grantedviagtag('consent', 'update', ...).deniedsignals (no client-side cookies, anonymous pings only).denied. This is the edge case the developer should explicitly handle and test.granted, behavior otherwise unchanged.Out of scope — do not regress
analyticsconsent (it is not a Google product and is not driven by Consent Mode v2 signals; just keep the existing trigger).enable_marketingdataLayer event must continue to fire only aftermarketingconsent.Acceptance Criteria
ad_storage,analytics_storage,ad_user_data,ad_personalizationdenied, and GTM is loaded on the pagegrantedbased on accepted categoriesdenied, Google tags fire in cookieless modedeniedgrantedenable_marketingdataLayer event still fires only after marketing consent (unchanged)GTM Configuration Required (Marketing/SEO team — blocking)
The code change alone does not enable cookieless pinging. The following GTM configuration is required for the acceptance criteria to be met, and must be coordinated with this PR:
ad_storageandad_user_data.analytics_storage.Priority
High — Prerequisite for accurate Google Ads conversion reporting and full GDPR compliance. Blocking conversion modeling for European paid campaigns.
References