Claude-code-marketing-skills ga4-events
GA4 Event Implementation Reference — complete event taxonomy, parameter lists, implementation patterns (gtag.js, GTM, Measurement Protocol), and validation techniques
git clone https://github.com/cognyai/claude-code-marketing-skills
T=$(mktemp -d) && git clone --depth=1 https://github.com/cognyai/claude-code-marketing-skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/ga4-events" ~/.claude/skills/cognyai-claude-code-marketing-skills-ga4-events && rm -rf "$T"
skills/ga4-events/SKILL.mdGA4 Event Implementation Reference
Complete reference for implementing Google Analytics 4 events: automatically collected, enhanced measurement, recommended, and custom events with exact parameter lists, code examples, and validation techniques.
Full docs: https://cogny.com/docs/ga4-event-implementation
Usage
/ga4-events # Show event model overview /ga4-events purchase # Show purchase event parameters and code /ga4-events ecommerce # Full e-commerce event funnel reference /ga4-events enhanced measurement # Enhanced measurement events list /ga4-events custom events # Custom event naming rules and limits /ga4-events validation # DebugView, Realtime, and BigQuery checks /ga4-events gtm # GTM dataLayer implementation patterns /ga4-events measurement protocol # Server-side Measurement Protocol examples
Instructions
You are a GA4 event implementation expert. Use this reference to help users implement, audit, and validate GA4 events correctly. Provide precise, copy-paste-ready code examples.
When the user asks a question, find the relevant section below and provide actionable answers with ready-to-use code. If GA4 MCP tools are available, use them to inspect the user's actual property configuration (custom dimensions, data streams, conversion events) for context-aware advice.
If the user provides a specific event name or topic as an argument, focus on that area. Otherwise, provide an overview of the event model.
Event Model Overview
GA4 uses an event-based data model where everything is an event. Unlike Universal Analytics hit types (pageview, event, transaction), GA4 has a single concept: events with parameters.
| Concept | Universal Analytics | GA4 |
|---|---|---|
| Data model | Hit-based (pageview, event, transaction) | Event-based (everything is an event) |
| Event structure | Category / Action / Label / Value | Event name + parameters (key-value pairs) |
| Sessions | Server-defined, 30-min timeout | Derived from event |
| Pageviews | Dedicated hit type | event with parameter |
| E-commerce | Enhanced Ecommerce plugin | Built-in recommended events with items array |
| Custom data | Custom dimensions/metrics (index-based) | Event parameters + custom dimensions/metrics (name-based) |
Every GA4 event has:
- Event name: String identifier (e.g.,
,page_view
)purchase - Event parameters: Key-value pairs (e.g.,
,page_location
)transaction_id - User properties: Persistent user attributes (e.g.,
)membership_tier
Automatically Collected Events
Collected automatically with no configuration. Cannot be disabled.
Web
| Event | Trigger | Key Parameters |
|---|---|---|
| First time user visits (new cookie) | None |
| New session begins (30-min timeout) | , |
| Every page load (or in SPAs) | , , |
| Page in focus for 1+ second with interaction | |
Mobile (Firebase SDK)
| Event | Trigger | Key Parameters |
|---|---|---|
| First app open after install | , |
| New session begins | , |
| Screen transition | , , |
| App in foreground 1+ second | |
| App updated and launched | |
| App removed (Android only) | None |
| OS updated | |
Auto-attached Parameters (every event)
language, page_location, page_referrer, page_title, screen_resolution, ga_session_id, ga_session_number, engagement_time_msec
Enhanced Measurement Events
Collected automatically when enabled in Admin > Data Streams > Enhanced Measurement. Each can be toggled individually.
| Event | Trigger | Key Parameters |
|---|---|---|
| User scrolls past 90% of page | (always 90) |
| Outbound link click | , , , , |
| URL contains search query parameter | |
| YouTube embed starts playing | , , , , |
| YouTube video reaches 10/25/50/75% | Same as plus |
| YouTube video reaches end | Same as |
| Click on file link (pdf, xls, doc, zip, etc.) | , , , , |
| First interaction with a form | , , |
| Form submitted | , , , |
Note: Video tracking only works with YouTube embeds using
enablejsapi=1.
Recommended Events — All Properties
| Event | Parameters |
|---|---|
| (STRING) |
| (STRING) |
| (STRING), (STRING), (STRING) |
| (STRING) |
| (STRING), (STRING) |
gtag('event', 'login', { method: 'Google' }); gtag('event', 'sign_up', { method: 'Email' }); gtag('event', 'search', { search_term: 'running shoes' });
Recommended Events — E-commerce
Implement in order for full funnel reporting.
view_item_list
gtag('event', 'view_item_list', { item_list_id: 'category_123', item_list_name: 'Running Shoes', items: [{ item_id: 'SKU_123', item_name: 'Trail Runner Pro', item_brand: 'RunCo', item_category: 'Shoes', item_category2: 'Running', item_variant: 'Blue', price: 129.99, currency: 'USD', index: 0, item_list_id: 'category_123', item_list_name: 'Running Shoes' }] });
select_item
gtag('event', 'select_item', { item_list_id: 'category_123', item_list_name: 'Running Shoes', items: [{ item_id: 'SKU_123', item_name: 'Trail Runner Pro', price: 129.99, currency: 'USD', index: 0 }] });
view_item
gtag('event', 'view_item', { currency: 'USD', value: 129.99, items: [{ item_id: 'SKU_123', item_name: 'Trail Runner Pro', item_brand: 'RunCo', item_category: 'Shoes', item_variant: 'Blue', price: 129.99, currency: 'USD', quantity: 1 }] });
add_to_cart
gtag('event', 'add_to_cart', { currency: 'USD', value: 129.99, items: [{ item_id: 'SKU_123', item_name: 'Trail Runner Pro', price: 129.99, currency: 'USD', quantity: 1 }] });
remove_from_cart
gtag('event', 'remove_from_cart', { currency: 'USD', value: 129.99, items: [{ item_id: 'SKU_123', item_name: 'Trail Runner Pro', price: 129.99, currency: 'USD', quantity: 1 }] });
view_cart
gtag('event', 'view_cart', { currency: 'USD', value: 259.98, items: [ { item_id: 'SKU_123', item_name: 'Trail Runner Pro', price: 129.99, currency: 'USD', quantity: 1 }, { item_id: 'SKU_456', item_name: 'Road Runner Elite', price: 149.99, currency: 'USD', quantity: 1 } ] });
begin_checkout
gtag('event', 'begin_checkout', { currency: 'USD', value: 259.98, coupon: 'SUMMER20', items: [ { item_id: 'SKU_123', item_name: 'Trail Runner Pro', price: 129.99, currency: 'USD', quantity: 1 }, { item_id: 'SKU_456', item_name: 'Road Runner Elite', price: 149.99, currency: 'USD', quantity: 1 } ] });
add_shipping_info
gtag('event', 'add_shipping_info', { currency: 'USD', value: 259.98, coupon: 'SUMMER20', shipping_tier: 'Express', items: [{ item_id: 'SKU_123', item_name: 'Trail Runner Pro', price: 129.99, currency: 'USD', quantity: 1 }] });
add_payment_info
gtag('event', 'add_payment_info', { currency: 'USD', value: 259.98, coupon: 'SUMMER20', payment_type: 'Credit Card', items: [{ item_id: 'SKU_123', item_name: 'Trail Runner Pro', price: 129.99, currency: 'USD', quantity: 1 }] });
purchase
gtag('event', 'purchase', { transaction_id: 'T12345', // REQUIRED — unique transaction ID value: 259.98, // REQUIRED — total value currency: 'USD', // REQUIRED — ISO 4217 tax: 20.80, shipping: 9.99, coupon: 'SUMMER20', items: [ { item_id: 'SKU_123', item_name: 'Trail Runner Pro', affiliation: 'Online Store', coupon: 'ITEM10OFF', discount: 13.00, item_brand: 'RunCo', item_category: 'Shoes', item_category2: 'Running', item_variant: 'Blue', price: 129.99, currency: 'USD', quantity: 1 }, { item_id: 'SKU_456', item_name: 'Road Runner Elite', affiliation: 'Online Store', item_brand: 'RunCo', item_category: 'Shoes', price: 149.99, currency: 'USD', quantity: 1 } ] });
Critical:
purchase requires transaction_id, value, and currency. GA4 deduplicates by transaction_id within 72 hours.
refund
// Full refund gtag('event', 'refund', { transaction_id: 'T12345', value: 259.98, currency: 'USD' }); // Partial refund gtag('event', 'refund', { transaction_id: 'T12345', value: 129.99, currency: 'USD', items: [{ item_id: 'SKU_123', price: 129.99, currency: 'USD', quantity: 1 }] });
Recommended Events — Lead Generation
generate_lead
gtag('event', 'generate_lead', { currency: 'USD', value: 50.00 });
Recommended Events — Gaming
| Event | Parameters |
|---|---|
| (STRING), (NUMBER) |
| (STRING), (NUMBER), (STRING) |
| (NUMBER), (STRING) |
| (NUMBER, required), (NUMBER), (STRING) |
| None |
| None |
| (STRING, required) |
gtag('event', 'earn_virtual_currency', { virtual_currency_name: 'Coins', value: 100 }); gtag('event', 'spend_virtual_currency', { virtual_currency_name: 'Coins', value: 50, item_name: 'Power Boost' }); gtag('event', 'level_up', { level: 5, character: 'Warrior' }); gtag('event', 'post_score', { score: 15000, level: 5, character: 'Warrior' }); gtag('event', 'tutorial_begin'); gtag('event', 'tutorial_complete'); gtag('event', 'unlock_achievement', { achievement_id: 'first_blood' });
Item Parameter Reference
The
items array supports these parameters per item:
| Parameter | Type | Description |
|---|---|---|
| STRING | SKU/ID (recommended) |
| STRING | Display name (recommended) |
| STRING | Store or affiliation |
| STRING | Item-level coupon |
| NUMBER | Discount amount |
| NUMBER | Position in list |
| STRING | Brand name |
| STRING | Primary category |
- | STRING | Category levels 2-5 |
| STRING | List ID |
| STRING | List name |
| STRING | Variant (color, size) |
| STRING | Physical location |
| NUMBER | Item price |
| STRING | ISO 4217 code |
| NUMBER | Quantity |
| STRING | Promotion ID |
| STRING | Promotion name |
| STRING | Promotion creative |
| STRING | Creative slot |
At least one of
item_id or item_name is required. Max 200 items per event.
Custom Events
Use when no automatically collected, enhanced measurement, or recommended event fits.
Naming Rules
- Max 40 characters
- Must start with alphabetic character
- Only
[a-zA-Z][a-zA-Z0-9_]* - Case sensitive —
andAdd_To_Cart
are different eventsadd_to_cart - Cannot use reserved prefixes:
,firebase_
,google_ga_ - Cannot use reserved event names (e.g.,
,first_visit
,session_start
,app_install
, etc.)in_app_purchase
Limits
| Limit | Value |
|---|---|
| Unique event names per property | 500 |
| Parameters per event | 25 |
| Parameter name length | 40 characters |
| Parameter value (string) | 100 characters |
| User property name length | 24 characters |
| User property value (string) | 36 characters |
| User properties per project | 25 |
Examples
gtag('event', 'newsletter_signup', { newsletter_type: 'weekly_digest', signup_location: 'footer' }); gtag('event', 'feature_used', { feature_name: 'export_csv', feature_category: 'data_tools', plan_tier: 'pro' }); gtag('event', 'article_read', { article_id: 'post_12345', article_category: 'technology', read_time_seconds: 245 });
Custom Dimensions and Metrics
Register event parameters or user properties for reporting in Admin > Custom definitions.
Scoping
| Scope | Source | Use Case |
|---|---|---|
| Event-scoped dimension | Event parameter | Page/action attributes |
| User-scoped dimension | User property | Persistent user attributes |
| Custom metric | Event parameter (numeric) | Numeric aggregation |
Quotas
| Resource | Standard | Analytics 360 |
|---|---|---|
| Event-scoped custom dimensions | 50 | 125 |
| User-scoped custom dimensions | 25 | 100 |
| Custom metrics | 50 | 125 |
Processing time: 24-48 hours for standard reports (immediate in Realtime/DebugView).
// Set user properties gtag('set', 'user_properties', { membership_tier: 'gold', signup_date: '2025-01-15' });
GTM dataLayer Implementation
// Custom event window.dataLayer = window.dataLayer || []; window.dataLayer.push({ event: 'newsletter_signup', newsletter_type: 'weekly_digest', signup_location: 'footer' }); // E-commerce — ALWAYS clear ecommerce first window.dataLayer.push({ ecommerce: null }); window.dataLayer.push({ event: 'purchase', ecommerce: { transaction_id: 'T12345', value: 259.98, currency: 'USD', tax: 20.80, shipping: 9.99, coupon: 'SUMMER20', items: [{ item_id: 'SKU_123', item_name: 'Trail Runner Pro', item_brand: 'RunCo', item_category: 'Shoes', price: 129.99, quantity: 1 }] } });
Measurement Protocol (Server-Side)
POST https://www.google-analytics.com/mp/collect?measurement_id=G-XXXXXXXXXX&api_secret=YOUR_API_SECRET { "client_id": "abc123.def456", "events": [{ "name": "offline_purchase", "params": { "transaction_id": "OFFLINE_T789", "value": 250.00, "currency": "USD" } }] }
import requests, json url = 'https://www.google-analytics.com/mp/collect?measurement_id=G-XXXXXXXXXX&api_secret=YOUR_API_SECRET' payload = { 'client_id': 'abc123.def456', 'events': [{'name': 'offline_purchase', 'params': { 'transaction_id': 'OFFLINE_T789', 'value': 250.00, 'currency': 'USD' }}] } response = requests.post(url, data=json.dumps(payload))
Limitations: No response validation (use debug endpoint),
client_id must match existing GA4 cookie, events not in Realtime (30-min delay), cannot trigger first_visit/session_start.
Validation Server
POST https://www.google-analytics.com/debug/mp/collect?measurement_id=G-XXXXXXXXXX&api_secret=YOUR_API_SECRET # Returns validation errors instead of silently accepting
Validation and Debugging
DebugView
Enable debug mode to see events in near real-time in Admin > DebugView:
gtag('config', 'G-XXXXXXXXXX', { debug_mode: true }); // Or per-event: gtag('event', 'purchase', { debug_mode: true, transaction_id: 'T12345', value: 99.99, currency: 'USD' });
BigQuery Validation Queries
Event volume audit:
SELECT event_name, COUNT(*) as event_count, COUNT(DISTINCT user_pseudo_id) as unique_users FROM `project.analytics_123456789.events_*` WHERE _TABLE_SUFFIX = FORMAT_DATE('%Y%m%d', CURRENT_DATE() - 1) GROUP BY event_name ORDER BY event_count DESC
Missing purchase parameters:
SELECT 'missing_transaction_id' as issue, COUNT(*) as count FROM `project.analytics_123456789.events_*` WHERE _TABLE_SUFFIX = FORMAT_DATE('%Y%m%d', CURRENT_DATE() - 1) AND event_name = 'purchase' AND (SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'transaction_id') IS NULL UNION ALL SELECT 'missing_currency', COUNT(*) FROM `project.analytics_123456789.events_*` WHERE _TABLE_SUFFIX = FORMAT_DATE('%Y%m%d', CURRENT_DATE() - 1) AND event_name = 'purchase' AND (SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'currency') IS NULL
Duplicate transactions:
SELECT transaction_id, COUNT(*) as dupes FROM ( SELECT (SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'transaction_id') as transaction_id FROM `project.analytics_123456789.events_*` WHERE _TABLE_SUFFIX BETWEEN FORMAT_DATE('%Y%m%d', DATE_SUB(CURRENT_DATE(), INTERVAL 7 DAY)) AND FORMAT_DATE('%Y%m%d', CURRENT_DATE() - 1) AND event_name = 'purchase' ) WHERE transaction_id IS NOT NULL GROUP BY transaction_id HAVING COUNT(*) > 1 ORDER BY dupes DESC
Naming convention audit:
SELECT event_name, COUNT(*) as event_count, CASE WHEN LENGTH(event_name) > 40 THEN 'exceeds_40_chars' WHEN REGEXP_CONTAINS(event_name, r'^(firebase_|google_|ga_)') THEN 'reserved_prefix' WHEN NOT REGEXP_CONTAINS(event_name, r'^[a-zA-Z][a-zA-Z0-9_]*$') THEN 'invalid_characters' WHEN event_name != LOWER(event_name) THEN 'mixed_case_warning' ELSE 'valid' END as naming_issue FROM `project.analytics_123456789.events_*` WHERE _TABLE_SUFFIX = FORMAT_DATE('%Y%m%d', CURRENT_DATE() - 1) GROUP BY event_name HAVING naming_issue != 'valid'
Parameter truncation check:
SELECT event_name, ep.key, MAX(LENGTH(ep.value.string_value)) as max_len, COUNTIF(LENGTH(ep.value.string_value) >= 100) as at_limit FROM `project.analytics_123456789.events_*`, UNNEST(event_params) as ep WHERE _TABLE_SUFFIX = FORMAT_DATE('%Y%m%d', CURRENT_DATE() - 1) AND ep.value.string_value IS NOT NULL GROUP BY 1, 2 HAVING max_len >= 100
Common Pitfalls
- Case sensitivity —
!=Purchase
. Always usepurchase
.snake_case - String truncation at 100 chars — URLs and long values get silently cut.
- Custom dimension quotas — 50 event-scoped, 25 user-scoped (standard). Plan carefully.
- Duplicate purchases — Always include
for dedup (72-hour window).transaction_id - Missing currency —
withoutvalue
is ignored in monetization reports.currency - Stale dataLayer — Always push
before ecommerce events in GTM.{ ecommerce: null } - PII in parameters — No emails, phone numbers, or names. Violates GA4 ToS.
- Dynamic event names — Burns through the 500-name limit. Use parameters instead.
Resources
- GA4 Event Reference: https://developers.google.com/analytics/devguides/collection/ga4/reference/events
- Recommended Events: https://support.google.com/analytics/answer/9267735
- Measurement Protocol: https://developers.google.com/analytics/devguides/collection/protocol/ga4
- Enhanced Measurement: https://support.google.com/analytics/answer/9216061
- Event Limits & Quotas: https://support.google.com/analytics/answer/9267744
- Full Cogny Docs: https://cogny.com/docs/ga4-event-implementation