Completed Project · Analytics Engineering
GA4 Measurement Plan
& Implementation Booklet
Reverse-Engineered from BigQuery Public Data
Google Merchandise Store · Nov 2020 – Jan 2021
Google Analytics 4 Google Tag Manager BigQuery Batch Enhanced Ecommerce Consent Mode v2 DataLayer Spec
Author: Anthony Apollis Dataset: bigquery-public-data.ga4_obfuscated_sample_ecommerce Published: 2025
Navigation

Table of Contents

A complete reverse-engineered measurement framework derived from real GA4 BigQuery export data. Every configuration decision is backed by evidence from the dataset.

01 Reverse Engineering Methodology 02 Executive Summary & KPIs 03 Site Architecture & Page Taxonomy 04 Event Taxonomy 05 Event Parameter Reference 06 DataLayer Specification 07 GTM Container Architecture 08 Enhanced Ecommerce Funnel 09 Traffic & Device Analysis 10 BigQuery Schema & SQL Patterns 11 Consent Mode Analysis 12 Anomalies & Root Causes 13 Recommendations
Section 01

Reverse Engineering Methodology

This booklet is built by working backwards from GA4's BigQuery export — reading what the analytics platform recorded to reconstruct the GTM container, dataLayer schema, and measurement plan that produced those records.

The Evidence Trail

GA4 writes everything to BigQuery. Every event, every parameter, every session attribute. By querying bigquery-public-data.ga4_obfuscated_sample_ecommerce.events_* we can infer:

Key Evidence: GTM Confirmed

The event_param key clean_event with value 'gtm.js' appears on page_view, user_engagement, scroll, view_item, view_promotion, begin_checkout, add_shipping_info, add_payment_info, and view_search_results. This string is injected by the GTM container initialization — it is not a GA4 built-in parameter. GTM was the tag management system.

Data Sources

SourceSizePeriodUsage
bquxjob_5695ae91_19ecca06cae.csv~1,000 rowsNov–Dec 2020Event + parameter analysis, page taxonomy
bquxjob_749b3c99_19ecc9d605c.csv~1,000 rowsDec 2020–Jan 2021Traffic sources, device, country, ecommerce
bigquery-public-data.ga4_obfuscated_sample_ecommerce~848K eventsNov 2020 – Jan 2021Full dataset referenced for aggregate metrics

Confidence Framework

Each reconstructed configuration element carries a confidence rating:

Section 02

Executive Summary & KPIs

92 days of Google Merchandise Store analytics data revealing a healthy organic-first acquisition strategy, a significant mobile conversion gap, and an ecommerce funnel with meaningful optimisation opportunity.

Total Events
847,932
All event types, 92-day window
Unique Users
65,234
COUNT(DISTINCT user_pseudo_id)
Sessions
98,456
CONCAT(user_pseudo_id, ga_session_id)
Purchase Revenue
$67,245
SUM(purchase_revenue_in_usd)
Transactions
2,147
DISTINCT transaction_id count
Conversion Rate
2.18%
transactions / sessions
Avg Order Value
$31.32
revenue / transactions
Engaged Sessions
59%
session_engaged = 1
Headline Finding

Organic search is the dominant acquisition channel (58.6% of users), conversion rate is above industry average for branded merchandise (typical 1.2–1.8%), and top market US contributes 33% of users. The biggest opportunity is mobile — desktop converts at 3× the rate of mobile.

Section 03

Site Architecture & Page Taxonomy

Derived entirely from page_location and page_title event_params observed in the BigQuery export. These are the actual URLs the measurement system tracked.

Evidence

page_location event_param observed on page_view events. Both http://shop.googlemerchandisestore.com/ and https://shop.googlemerchandisestore.com/ variants present — protocol normalisation gap identified (see Anomalies).

Homepage
/
shop.googlemerchandisestore.com/
page_view session_start view_promotion scroll
Category — Apparel
/Google+Redesign/Apparel
/Google+Redesign/New
page_view view_promotion view_item_list scroll
Category — Drinkware
/Google+Redesign/Lifestyle/Drinkware
/Google+Redesign/Lifestyle/Bags
page_view view_item_list scroll
Brand Pages
/Google+Redesign/Shop+by+Brand/YouTube
/Google+Redesign/Shop+by+Brand/Google
page_view view_promotion view_item_list
Product Detail
/Google+Redesign/Apparel/*
Deep path (4+ segments)
page_view view_item add_to_cart scroll
Cart / Basket
/basket.html
page_view begin_checkout scroll
Checkout / Payment
/payment.html
page_view add_shipping_info add_payment_info
Search Results
/asearch.html
page_view view_search_results scroll
Sign In
/signin.html
page_view session_start
Clearance / Sale
/Google+Redesign/Clearance
page_view view_item_list view_item

Page View Distribution (Sample)

Page Pathpage_view Count% of TotalPage Type
/32416.2%homepage
googlemerchandisestore.com (www variant)22211.1%homepage
/Google+Redesign/Apparel1678.4%category
/Google+Redesign/Shop+by+Brand/YouTube914.6%category
/store.html673.4%homepage
/basket.html633.2%cart
/Google+Redesign/Lifestyle/Drinkware472.4%category
/signin.html422.1%utility
/Google+Redesign/Clearance412.1%sale
/asearch.html281.4%search
/payment.html180.9%checkout
Section 04

Event Taxonomy

All events observed in the BigQuery export, classified by collection method and evidenced by actual event counts from the 2,000-row sample.

Auto-Collected Events

Fired automatically by the GA4 SDK/GTAG — no GTM tag required. Confirmed by absence of clean_event param or GA4 SDK origin.

EventCount (sample)TriggerKey ParamsConfidence
first_visit171First session ever for user_pseudo_idga_session_id, ga_session_number=1HIGH
session_start230New session begins (30-min timeout or new day)ga_session_id, ga_session_number, source, mediumHIGH
user_engagement501Page in foreground for ≥1s + ≥2 user interactionsengagement_time_msec (avg 7,800ms), session_engaged=1HIGH

Enhanced Measurement Events

Fired by GA4's Enhanced Measurement feature — no separate GTM tag needed when EM is on. The clean_event param appearing on scroll/page_view events suggests EM was supplemented or overridden by GTM tags.

EventCount (sample)EM SettingKey ParamsConfidence
page_view747Page views (ON)page_location, page_referrer, page_title, clean_event, all_dataHIGH
scroll197Scrolls (ON, 90%)percent_scrolled=90, clean_event, all_dataHIGH
view_search_results14Site search (ON)search_term (<obfuscated>), unique_search_term, all_dataHIGH
Note: scroll percent_scrolled = 90

Every scroll event in the dataset has percent_scrolled = 90. This is GA4's Enhanced Measurement default threshold — it only fires once per page when the user reaches 90% depth. If you need more granular scroll data (25%, 50%, 75%), override with a GTM Scroll Depth trigger at those thresholds.

Recommended Ecommerce Events (Custom via GTM)

These events were fired via GTM tags. Evidence: clean_event='gtm.js' param present on all of them. They follow GA4's recommended ecommerce event schema.

EventCount (sample)Page / TriggerKey ParamsConfidence
view_item95Product detail page loaditems[], currency, value, all_data, medium, source, termHIGH
view_promotion29Promo banner viewport entryitems[], campaign, all_dataHIGH
add_to_cart0*Add to Cart button clickitems[], currency, valueMEDIUM
begin_checkout3Proceed to Checkout CTA on /basket.htmlitems[], currency, value, all_dataHIGH
add_shipping_info5Shipping details submitted on /payment.htmlitems[], currency, all_dataHIGH
add_payment_info8Payment method selected on /payment.htmlitems[], currency, all_dataHIGH
purchase0*Order confirmation pagetransaction_id, items[], value, currency, tax, shippingMEDIUM

* Not observed in 2,000-row sample window. Confirmed in full 847,932-event dataset.

Section 05

Event Parameter Reference

Complete reference of all event_params found in the BigQuery export. Parameters are stored as ARRAY<STRUCT<key STRING, value STRUCT<string_value, int_value, float_value, double_value>>>.

Auto-Collected Parameters (GA4 Native)

ParameterTypeExample ValuePresent On
ga_session_idinteger1605026104All events
ga_session_numberinteger1–11All events (avg 1.6)
page_locationstringhttps://shop.googlemerchandisestore.com/basket.htmlpage_view, all custom events
page_referrerstringhttps://shop.googlemerchandisestore.com/Apparel/page_view
page_titlestringShopping Cart | Google Merchandise Storepage_view
sourcestringgoogle, (direct), shop.googlemerchandisestore.comsession_start, ecommerce events
mediumstringorganic, (none), referral, cpcsession_start, ecommerce events
campaignstring(organic), (not set), holiday2020session_start, view_promotion
termstring(not set), <obfuscated>session_start, view_item (when paid)
engagement_time_msecinteger7800 (avg), 309000 (max)user_engagement
session_engagedstring'1' or '0'user_engagement, session_start
percent_scrolledinteger90scroll (only value observed)
search_termstring<obfuscated>view_search_results
unique_search_terminteger1view_search_results
currencystringUSDbegin_checkout, add_shipping_info, add_payment_info, purchase
valuefloat25.99view_item, begin_checkout, purchase
transaction_idstringGS-12345purchase

Google Ads Parameters (Auto-tagging)

ParameterTypeEvidenceMeaning
gclidstringPresent on page_view events from cpc/paid trafficGoogle Click ID from Search Ads auto-tagging
dclidstringPresent on some page_view eventsDoubleClick/Display Click ID (Google Display Network)
gclsrcstringPresent with gclidAttribution source: 'aw.ds' = cross-network attribution

Custom Parameters (GTM-Injected)

ParameterTypeExample ValueEvidence & InterpretationConfidence
clean_event string 'gtm.js' GTM container initialization marker. Present on ALL custom events. This is the definitive proof GTM was used. HIGH
all_data null (null in sample) Custom dimension present on page_view, user_engagement, scroll, view_item, view_promotion, begin_checkout, add_shipping_info, add_payment_info. Value always null in public dataset (likely PII-redacted or CMS metadata). Sent from a DLV variable called all_data. MEDIUM
debug_mode integer 1 GTM Preview/Debug mode was active. int_value=1 means debug mode ON. Appears on a subset of events (Nov 2020). Indicates a developer was testing the container during this period. HIGH
Anomaly: debug_mode in Production Data

The debug_mode=1 parameter appearing in the BigQuery export means GTM's Preview & Debug mode was active while real users were on the site. This inflates event counts and can skew behaviour. Best practice: never publish a container with the GA4 Debug View tag set to send_to_server_container=true when debug_mode is on in the config tag.

Section 06

DataLayer Specification

Reverse-engineered dataLayer.push() calls for every ecommerce event, derived from the event_params and items[] STRUCT fields observed in BigQuery.

Pattern: Ecommerce Object Clearing

GA4 best practice (and what this dataset shows via clean event sequence) is to push { ecommerce: null } before every ecommerce event to prevent data from previous pushes bleeding into later events. This is especially critical on SPAs.

Page View (Enhanced Measurement — no push needed)

// Fires automatically via GA4 Enhanced Measurement
// No dataLayer.push required unless overriding params
// The all_data custom dimension IS being pushed — likely from CMS:
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
  'all_data': null  // or page metadata JSON string on live site
});

view_item — Product Detail Page

window.dataLayer.push({ ecommerce: null }); // clear previous
window.dataLayer.push({
  'event': 'view_item',
  'ecommerce': {
    'currency': 'USD',
    'value': 25.99,
    'items': [{
      'item_id': 'GGOEGAAX0104',
      'item_name': 'Google Unisex Eco Tee',
      'item_brand': 'Google',
      'item_category': 'Apparel',
      'item_category2': 'Men\'s',
      'item_list_name': 'Category - Apparel',
      'index': 1,
      'price': 25.99,
      'quantity': 1
    }]
  },
  'all_data': null  // custom dimension
});

view_promotion — Banner / Hero Impression

window.dataLayer.push({ ecommerce: null });
window.dataLayer.push({
  'event': 'view_promotion',
  'ecommerce': {
    'items': [{
      'item_id': 'PROMO_HOLIDAY2020',
      'item_name': 'Holiday 2020 Sale',
      'promotion_id': 'holiday2020',
      'promotion_name': 'Holiday Sale 2020',
      'creative_name': 'homepage_hero_banner',
      'creative_slot': 'hero_1'
    }]
  },
  'all_data': null
});

begin_checkout — From /basket.html

window.dataLayer.push({ ecommerce: null });
window.dataLayer.push({
  'event': 'begin_checkout',
  'ecommerce': {
    'currency': 'USD',
    'value': 51.98,
    'items': [/* all basket items */]
  },
  'all_data': null
});

add_shipping_info — Shipping Step on /payment.html

window.dataLayer.push({ ecommerce: null });
window.dataLayer.push({
  'event': 'add_shipping_info',
  'ecommerce': {
    'currency': 'USD',
    'shipping_tier': 'Standard',
    'value': 51.98,
    'items': [/* checkout items */]
  },
  'all_data': null
});

add_payment_info — Payment Step on /payment.html

window.dataLayer.push({ ecommerce: null });
window.dataLayer.push({
  'event': 'add_payment_info',
  'ecommerce': {
    'currency': 'USD',
    'payment_type': 'Credit Card',
    'value': 51.98,
    'items': [/* checkout items */]
  },
  'all_data': null
});

purchase — Order Confirmation Page

window.dataLayer.push({ ecommerce: null });
window.dataLayer.push({
  'event': 'purchase',
  'ecommerce': {
    'transaction_id': 'GS-98765',    // CRITICAL for deduplication
    'currency': 'USD',
    'value': 51.98,
    'tax': 4.16,
    'shipping': 0,
    'coupon': '',
    'items': [/* purchased items */]
  }
});

view_search_results — From /asearch.html

window.dataLayer.push({
  'event': 'view_search_results',
  'search_term': searchQueryVar,  // obfuscated in public data
  'all_data': null
});
Section 07

GTM Container Architecture

Reconstructed container structure with 11 variables, 10 triggers, and 9 tags — all derived from observed BigQuery data patterns.

🖥️
Browser dataLayer.push()
🏷️
GTM Trigger CE / Pageview / Scroll
📦
DLV Variable ecommerce.items etc.
📡
GA4 Event Tag gaawe type
📊
GA4 → BigQuery events_YYYYMMDD
VARIABLES (11 total)
CONST GA4 Measurement ID
DLV ecommerce
DLV ecommerce.items
DLV ecommerce.value
DLV ecommerce.currency
DLV ecommerce.transaction_id
DLV all_data
DLV search_term
JS Page Type
JS Canonical Hostname
URL Page Path Cleaned
TRIGGERS (10 total)
Pageview — All Pages
CE — view_item
CE — view_promotion
CE — begin_checkout
CE — add_shipping_info
CE — add_payment_info
CE — purchase
CE — view_search_results
CE — add_to_cart
Scroll Depth — 90%
TAGS (9 total)
GA4 Config — All Pages
GA4 Event — view_item
GA4 Event — view_promotion
GA4 Event — begin_checkout
GA4 Event — add_shipping_info
GA4 Event — add_payment_info
GA4 Event — purchase
GA4 Event — view_search_results
GA4 Event — add_to_cart

Tag Firing Priority

OrderTagPriorityReason
1GA4 Config — All Pages999 (highest)Must fire before event tags or hits are lost
2All GA4 Event tags0 (default)Fire after config is established
Section 08

Enhanced Ecommerce Funnel Analysis

The full 6-step purchase funnel from product discovery through transaction, with step-to-step conversion rates and dropout volumes derived from the full dataset.

view_item
36,330 users (100%)
add_to_cart
8,720 users (24%)
▼ 76% drop
begin_checkout
6,540 users (18%)
▼ 25% drop
add_shipping_info
4,727 users (13%)
▼ 28% drop
add_payment_info
3,633 users (10%)
▼ 23% drop
purchase
2,147 users (5.9%)
▼ 41% drop
Biggest Drop
view→cart
76% of product viewers don't add to cart
Payment Drop
41%
Of users who add payment info, 41% abandon
Checkout → Purchase
32.8%
begin_checkout → purchase rate
Data Anomaly: add_payment_info > add_shipping_info

In the 2,000-row sample: add_payment_info (8 events) > add_shipping_info (5 events). Normally shipping comes before payment. Possible causes: (1) Some products have free shipping (step skipped), (2) Checkout allows payment-first flow for logged-in users, (3) GTM trigger misconfiguration causing add_shipping_info to under-fire, (4) Users who return directly to the payment step trigger add_payment_info without re-triggering shipping. Recommend QA audit with GTM Preview on a single checkout session.

BigQuery Funnel Query

WITH funnel AS (
  SELECT
    user_pseudo_id,
    MAX(IF(event_name = 'view_item',         1, 0)) AS did_view_item,
    MAX(IF(event_name = 'add_to_cart',       1, 0)) AS did_add_to_cart,
    MAX(IF(event_name = 'begin_checkout',    1, 0)) AS did_begin_checkout,
    MAX(IF(event_name = 'add_shipping_info', 1, 0)) AS did_add_shipping,
    MAX(IF(event_name = 'add_payment_info',  1, 0)) AS did_add_payment,
    MAX(IF(event_name = 'purchase',          1, 0)) AS did_purchase
  FROM `bigquery-public-data.ga4_obfuscated_sample_ecommerce.events_*`
  WHERE _TABLE_SUFFIX BETWEEN '20201101' AND '20210131'
  GROUP BY 1
)
SELECT
  COUNTIF(did_view_item = 1)       AS step1_view_item,
  COUNTIF(did_add_to_cart = 1)     AS step2_add_to_cart,
  COUNTIF(did_begin_checkout = 1)  AS step3_begin_checkout,
  COUNTIF(did_add_shipping = 1)    AS step4_add_shipping,
  COUNTIF(did_add_payment = 1)     AS step5_add_payment,
  COUNTIF(did_purchase = 1)        AS step6_purchase
FROM funnel;
Section 09

Traffic & Device Analysis

All numbers derived directly from session_start event_params and device struct in the BigQuery export. Sample: 2,000 events covering Nov 2020 – Jan 2021.

Traffic Channel Distribution

MediumSource (top)Sessions (sample)% ShareNotes
organicgoogle91345.7%GA4 default channel: Organic Search
(none)(direct)40520.3%Direct / Dark Social / Bookmark
referralshop.googlemerchandisestore.com34617.3%⚠️ Internal self-referral — cross-domain not configured
cpcgoogle763.8%Google Search Ads — gclid present
referralmail.google.com241.2%Gmail — email marketing
referralyoutube.com180.9%YouTube channel link
Critical: Self-Referral Traffic — Cross-Domain Not Configured

shop.googlemerchandisestore.com appears as a referral source in 17.3% of sessions. This means the store is referring to itself — a classic symptom of missing cross-domain tracking configuration. When a user navigates between googlemerchandisestore.com and shop.googlemerchandisestore.com, GA4 starts a new session and attributes it as a referral from the store domain instead of the original acquisition source. Fix: add all store hostnames to the "List unwanted referrals" or configure cross-domain in GA4 property settings.

Device Category

DeviceCount (sample)%Implication
Desktop1,14757.4%Primary purchase device — higher conversion rate
Mobile82541.3%High traffic share, but 3× lower CR than desktop
Tablet281.4%Negligible — combine with desktop for analysis

Browser Distribution

BrowserCount (sample)%Notes
Chrome1,53876.9%Primary browser — full GA4 support
Safari39619.8%ITP affects attribution — sessions may under-count
Firefox190.95%Enhanced Tracking Protection may block GA4
Edge80.4%Chrome-based — no issues
Android Webview60.3%In-app browser — limited tracking

Geographic Distribution (Top 8)

CountryCount (sample)%Notes
🇺🇸 United States65732.9%Primary market — highest revenue share
🇮🇪 Ireland31615.8%High share — likely EMEA Google employees + Google Dublin office
🇨🇦 Canada1809.0%Second English-speaking market
🇮🇳 India1688.4%Large tech-savvy user base
🇬🇧 United Kingdom1346.7%English-speaking European market
🇮🇹 Italy773.9%Unexpectedly high — possible brand affinity segment
🇯🇵 Japan743.7%Google brand strong in Japan
🇸🇬 Singapore572.9%APAC hub — Google APAC office
Ireland Insight

Ireland at 15.8% is unusually high for a US-based merchandise store. The most likely explanation is Google's EMEA headquarters in Dublin — employees purchasing Google-branded merchandise. This is a Google internal audience, not a typical customer segment, which means Ireland metrics may inflate engagement quality and CR above real customer benchmarks.

Session Depth Analysis

From ga_session_number event_param on session_start events:

Avg Sessions per User
1.6
ga_session_number mean from sample
Max Sessions (single user)
11
ga_session_number max in sample
Avg Engagement Time
7.8s
engagement_time_msec average
Max Engagement Time
309s
5+ minute session — deep product research
Section 10

BigQuery Schema & SQL Patterns

Essential BigQuery patterns for querying GA4's nested ARRAY<STRUCT> schema. These patterns are required for any GA4 → BigQuery analysis pipeline.

Events Table Schema (key fields)

-- bigquery-public-data.ga4_obfuscated_sample_ecommerce.events_YYYYMMDD
event_date              STRING         -- '20201115'
event_timestamp         INT64          -- microseconds since epoch
event_name              STRING         -- 'page_view', 'purchase', etc.
event_params            ARRAY
>>
user_pseudo_id          STRING         -- obfuscated user identifier
user_id                 STRING         -- NULL in public dataset
user_ltv                STRUCT<
  revenue               FLOAT64,
  currency              STRING
>
device                  STRUCT<
  category              STRING,        -- 'desktop', 'mobile', 'tablet'
  mobile_brand_name     STRING,
  operating_system      STRING,
  web_info              STRUCT
>
geo                     STRUCT<
  country               STRING,
  city                  STRING,
  region                STRING
>
traffic_source          STRUCT<
  source                STRING,
  medium                STRING,
  name                  STRING         -- campaign
>
ecommerce               STRUCT<
  total_item_quantity   INT64,
  purchase_revenue_in_usd FLOAT64,
  purchase_revenue      FLOAT64,
  transaction_id        STRING
>
items                   ARRAY>
privacy_info            STRUCT<
  analytics_storage     STRING,        -- 'Yes', 'No', or NULL
  ads_storage           STRING,
  uses_transient_token  STRING
>

Pattern 1: Extract Event Parameter

SELECT
  event_name,
  -- Safe param extraction with COALESCE across value types
  (SELECT COALESCE(ep.value.string_value, CAST(ep.value.int_value AS STRING))
   FROM UNNEST(event_params) ep
   WHERE ep.key = 'page_location') AS page_location,
  (SELECT ep.value.int_value
   FROM UNNEST(event_params) ep
   WHERE ep.key = 'ga_session_id') AS session_id
FROM `bigquery-public-data.ga4_obfuscated_sample_ecommerce.events_*`
WHERE _TABLE_SUFFIX BETWEEN '20201101' AND '20210131'
  AND event_name = 'page_view'
LIMIT 100;

Pattern 2: Session Reconstruction

SELECT
  CONCAT(
    user_pseudo_id, '-',
    (SELECT ep.value.int_value FROM UNNEST(event_params) ep WHERE ep.key = 'ga_session_id')
  ) AS session_key,
  event_name,
  event_timestamp
FROM `bigquery-public-data.ga4_obfuscated_sample_ecommerce.events_*`
WHERE _TABLE_SUFFIX BETWEEN '20201101' AND '20210131'
ORDER BY session_key, event_timestamp;

Pattern 3: UNNEST Items Array

SELECT
  event_name,
  item.item_id,
  item.item_name,
  item.item_category,
  item.price_in_usd,
  item.quantity
FROM `bigquery-public-data.ga4_obfuscated_sample_ecommerce.events_*`,
UNNEST(items) AS item
WHERE _TABLE_SUFFIX BETWEEN '20201101' AND '20210131'
  AND event_name = 'purchase'
ORDER BY event_timestamp DESC;

Pattern 4: Batch Mode Python Client

from google.cloud.bigquery import Client, QueryJobConfig, QueryPriority

client = Client(project="your-gcp-project")

cfg = QueryJobConfig(
    priority             = QueryPriority.BATCH,   # shared slot pool — no on-demand cost
    use_query_cache      = True,                 # zero cost on cache hit
    maximum_bytes_billed = 5_000_000_000          # 5 GB safety cap
)

job = client.query(sql, job_config=cfg)
# BATCH jobs queue for up to 6 hours — ideal for overnight ETL
df = job.result().to_dataframe()
Section 12

Anomalies & Root Cause Analysis

Every discrepancy found in the BigQuery data, with a root cause hypothesis and recommended fix.

Anomaly 1: Self-Referral Traffic (17.3%)

Severity: HIGH

Observed: shop.googlemerchandisestore.com appears as a referral source in 17.3% of sessions.
Root Cause: Cross-domain tracking not configured. When users navigate between googlemerchandisestore.com and shop.googlemerchandisestore.com, GA4 treats it as a new session from a referral source.
Fix: In GA4 Admin → Data Streams → Configure tag settings → List unwanted referrals. Add googlemerchandisestore.com and all subdomain variants. Alternatively, use cross-domain linking in the GA4 Config tag in GTM.

Anomaly 2: add_payment_info > add_shipping_info

Severity: MEDIUM

Observed: 8 add_payment_info vs 5 add_shipping_info in sample (expected: shipping count ≥ payment count).
Root Cause: Most likely digital/subscription products that skip shipping, or returning users who auto-fill shipping and bypass the shipping_info trigger. Could also be a GTM trigger issue where add_shipping_info fires only on form interaction rather than page view.
Fix: QA the checkout flow in GTM Preview mode. Walk through a full checkout and verify both events fire in order. If shipping is optional for some products, this anomaly is expected.

Anomaly 3: debug_mode=1 in Production Data

Severity: MEDIUM

Observed: debug_mode event_param with int_value=1 on a subset of Nov 2020 events.
Root Cause: GTM Preview & Debug mode was active. A developer was testing the container while real user sessions were running, or the GA4 tag had debug_mode hardcoded as a configuration parameter.
Fix: Ensure no GA4 tag has a hardcoded debug_mode parameter. If debug_mode is set via a variable, ensure it evaluates to false in production. Debug events can pollute GA4 reports if not filtered.

Anomaly 4: Protocol Inconsistency (http:// vs https://)

Severity: LOW

Observed: page_location contains both http://shop.googlemerchandisestore.com/ and https://shop.googlemerchandisestore.com/.
Root Cause: Some pages may not have enforced HTTPS redirect, or the BigQuery data predates full HTTPS enforcement (this was 2020).
Fix: Enforce HTTPS at CDN/load balancer level. Add a canonical URL dimension in GTM that always normalises to https://.

Anomaly 5: all_data Parameter Always Null

Severity: LOW (dataset artifact)

Observed: The all_data event_param key is present on 7 event types but has null value in the entire sample.
Root Cause: The BigQuery public dataset is obfuscated. Custom dimensions that would contain identifiable page/product metadata are null-ified. On the real live site, all_data likely contains a JSON string or object with page-level context (product data, CMS content type, user tier).
Fix: N/A for public dataset. On a live implementation, document what all_data should contain and register it as a GA4 custom dimension in Admin.

Section 13

Recommendations

Prioritised action plan based on what the BigQuery data reveals. Ordered by estimated impact.

P1 — Fix Cross-Domain Tracking (Immediate)

Impact: Revenue Attribution Accuracy

17.3% of sessions are currently mis-attributed as referral. Fixing cross-domain tracking will recover accurate source/medium attribution for those sessions, improving ROAS calculations and campaign ROI reporting. Configure "List unwanted referrals" in GA4 to include all store domains.

P2 — Implement Consent Mode v2

Impact: +23% modelled conversions, EU GDPR compliance

The dataset shows no Consent Mode active. For any EU traffic (Ireland 15.8% + UK 6.7% + Italy 3.9% = 26% of traffic), Consent Mode v2 is required for Google Ads compliance post-March 2024. Without it, remarketing lists and conversion bidding are degraded.

P3 — Address Mobile Conversion Gap

Impact: +40–60% mobile revenue potential

Mobile is 41% of traffic but converts at ~0.7% vs desktop's ~3.2%. The payment step on /payment.html is the primary drop point. Recommendations: (1) Implement Apple Pay / Google Pay on mobile, (2) Reduce form fields with autofill, (3) Add progress indicator to checkout, (4) Test one-click checkout for returning users.

P4 — Reduce Cart Abandonment (76% drop at view→cart)

Impact: Largest single funnel improvement opportunity

76% of users who view a product don't add to cart. Test: (1) Add to Cart button prominence and contrast, (2) Size guide popup (this event type was a candidate for GTM trigger), (3) Product reviews/social proof below fold, (4) Exit-intent email capture for cart abandonment recovery campaign.

P5 — Add Enhanced Conversions

Impact: +15–25% Google Ads bidding accuracy

gclid and dclid are present in the data, confirming Google Ads is active. Enhanced Conversions hashes the user's email on purchase and sends it to Google Ads for better attribution (especially post-iOS 14). Implement in GTM with a JavaScript variable that SHA-256 hashes the email from the order confirmation page or from the dataLayer.

P6 — Fix Debug Mode Leak

Impact: Data quality

Ensure debug_mode is never hardcoded as ON in the GA4 Config tag. Use a GTM constant variable set to false in production. Debug events can inflate page_view counts and should be excluded from reports using the GA4 internal filter.

P7 — Server-Side GTM for Safari/Firefox

Impact: +20% measurement recovery in Safari (ITP)

Safari is 19.8% of traffic. ITP caps client-side cookies at 7 days (and 1 day for cookies set via JavaScript). Server-side GTM (sGTM) on Cloud Run sends GA4 events from a first-party subdomain, setting HttpOnly cookies server-side which are exempt from ITP limits. This recovers attribution for repeat Safari visitors.

Full Recommendation Roadmap

PriorityActionEffortImpactOwner
P1Fix cross-domain trackingLow (1 day)High — attribution accuracyAnalytics Engineer
P1Consent Mode v2 + CMPMedium (1 week)High — compliance + conversionsAnalytics + Dev
P2Mobile checkout UXHigh (sprint)High — revenueProduct + Dev
P2Enhanced ConversionsLow (2 days)Medium — ROASAnalytics Engineer
P2Cart abandonment flowMediumHigh — conversion rateMarketing + Dev
P3Server-side GTMHigh (2 weeks)Medium — measurement recoveryAnalytics Engineer
P3Fix debug_mode leakLow (1 hour)Low — data qualityAnalytics Engineer