GA4 Custom Events
Expert guidance for creating business-specific custom events beyond Google's recommended events.
Overview
Custom events enable measurement of unique business goals, industry-specific actions, and contextual behaviours not covered by Google's recommended events. This reference covers event naming conventions, parameter design strategies, and implementation across all platforms.
When to Use Custom Events
Create custom events when:
- Business action not covered by recommended events
- Industry-specific tracking requirements
- Unique user interactions to measure
- Business-specific conversion points
- Detailed behavioural analysis needs
Event Naming Conventions
Format and Constraints
| Constraint |
Value |
| Case |
snake_case (lowercase with underscores) |
| Maximum length |
40 characters |
| Starting character |
Letter |
| Allowed characters |
Letters, numbers, underscores |
Naming Framework
[action]_[object]_[context]
Examples:
- product_comparison_viewed
- pricing_calculator_used
- whitepaper_downloaded
- trial_signup_completed
- support_ticket_created
Good Event Names
| Event Name |
Description |
| demo_requested |
User requested product demo |
| feature_trial_started |
User started feature trial |
| integration_configured |
User set up integration |
| report_exported |
User exported report |
| team_member_invited |
User invited team member |
Anti-Patterns (Avoid)
| Bad Name |
Problem |
Better Name |
| click |
Too generic |
button_click |
| event1 |
Not descriptive |
form_submit |
| MyEvent |
Wrong case |
my_event |
| trackAction |
Camel case |
track_action |
| data |
Meaningless |
data_exported |
Event Parameter Design
Parameter Strategy
For each custom event, identify:
- What is the action? The user behaviour being measured
- What context is needed? Information to analyse the action
- What parameters provide context? Data points to collect
- What are the constraints? Length limits, data types
Parameter Limits
| Limit |
Value |
| Parameters per event |
25 |
| Parameter name length |
40 characters |
| Parameter value length |
100 characters |
| Data types |
String, Integer, Float |
Parameter Examples
demo_requested Event:
gtag('event', 'demo_requested', {
'demo_type': 'product_walkthrough',
'industry': 'technology',
'company_size': 'enterprise',
'lead_source': 'organic_search',
'product_interest': 'analytics_suite'
});
course_enrollment Event:
gtag('event', 'course_enrollment', {
'course_id': 'COURSE_101',
'course_name': 'Advanced GA4',
'instructor': 'Jane Smith',
'price': 99.99,
'currency': 'USD',
'level': 'advanced',
'duration_hours': 8
});
support_ticket_created Event:
gtag('event', 'support_ticket_created', {
'ticket_type': 'bug_report',
'product': 'mobile_app',
'severity': 'high',
'department': 'engineering',
'resolution_time_expected': 24
});
Industry-Specific Patterns
SaaS Events
| Event |
Parameters |
| trial_started |
plan_type, trial_duration, feature_count |
| trial_ended |
conversion_status, days_active, features_used |
| upgrade_initiated |
from_plan, to_plan, estimated_value |
| plan_downgraded |
from_plan, to_plan, reason_category |
| feature_activated |
feature_name, activation_method |
| integration_connected |
integration_name, connection_type |
| api_key_generated |
key_purpose, environment |
| workspace_created |
workspace_type, team_size |
Education Events
| Event |
Parameters |
| lesson_completed |
course_id, lesson_id, completion_time |
| quiz_submitted |
quiz_id, score_percentage, attempts |
| certificate_earned |
course_id, grade, completion_date |
| course_enrolled |
course_id, enrollment_type, price |
| instructor_contacted |
inquiry_type, response_time |
| resource_downloaded |
resource_type, file_format |
| discussion_posted |
topic_category, word_count |
| peer_review_submitted |
assignment_id, rating_given |
Media Events
| Event |
Parameters |
| article_shared |
article_id, share_platform, category |
| video_watched |
video_id, duration, engagement_percent |
| podcast_completed |
episode_id, listen_duration, completion |
| newsletter_subscribed |
newsletter_type, source |
| content_bookmarked |
content_id, content_type |
| author_followed |
author_id, follow_source |
| comment_posted |
content_id, word_count |
| paywall_reached |
content_type, view_count_prior |
E-commerce (Beyond Recommended)
| Event |
Parameters |
| product_compared |
products_compared, category |
| review_submitted |
product_id, star_rating, has_photo |
| wishlist_shared |
item_count, share_method |
| size_guide_viewed |
product_category, size_selected |
| store_locator_used |
postcode, results_count |
| gift_wrap_selected |
wrap_type, price |
| loyalty_redeemed |
points_redeemed, reward_type |
| back_in_stock_signup |
product_id, notification_preference |
Implementation Examples
gtag.js Implementation
// Simple event
gtag('event', 'demo_requested', {
'demo_type': 'product_walkthrough',
'industry': 'technology'
});
// Event with multiple parameters
gtag('event', 'subscription_changed', {
'change_type': 'upgrade',
'from_plan': 'basic',
'to_plan': 'professional',
'price_difference': 50,
'currency': 'USD',
'billing_cycle': 'monthly'
});
GTM Data Layer Implementation
// Push event to data layer
dataLayer.push({
'event': 'demo_requested',
'demo_type': 'product_walkthrough',
'industry': 'technology',
'company_size': 'enterprise'
});
// In GTM:
// 1. Create Data Layer Variables for each parameter
// 2. Create Custom Event trigger for 'demo_requested'
// 3. Create GA4 Event tag with mapped parameters
Measurement Protocol (Server-Side)
import requests
import json
MEASUREMENT_ID = "G-XXXXXXXXXX"
API_SECRET = "your_api_secret"
def send_custom_event(client_id, event_name, params):
url = f"https://www.google-analytics.com/mp/collect?measurement_id={MEASUREMENT_ID}&api_secret={API_SECRET}"
payload = {
"client_id": client_id,
"events": [{
"name": event_name,
"params": params
}]
}
response = requests.post(
url,
headers={"Content-Type": "application/json"},
data=json.dumps(payload)
)
return response.status_code == 204
# Example usage
send_custom_event(
client_id="123.456",
event_name="subscription_renewed",
params={
"plan_type": "professional",
"renewal_value": 299.00,
"currency": "USD",
"renewal_count": 3
}
)
Registering Custom Dimensions
Custom parameters won't appear in GA4 reports until registered as custom dimensions.
Registration Process
- Send parameter in event (any platform)
- Verify in DebugView (Admin -> DebugView)
- Register dimension:
- Admin -> Data Display -> Custom Definitions
- Create Custom Dimension
- Dimension Name: Human-friendly (e.g., "Demo Type")
- Scope: Event, User, or Item
- Event Parameter: Exact name from code (e.g., "demo_type")
- Wait 24-48 hours for data to populate
- Use in reports (Explorations, Standard Reports)
Scope Selection
| Scope |
Use For |
Example |
| Event |
Single event data |
demo_type, button_name |
| User |
User attributes |
subscription_tier, customer_segment |
| Item |
Product data |
item_color, item_size |
Testing Custom Events
DebugView Workflow
Enable debug mode:
- Chrome extension: Google Analytics Debugger
- URL parameter:
?debug_mode=true
- gtag config:
debug_mode: true
Trigger custom event
Check DebugView:
- Event appears with correct name
- All parameters present
- Parameter values correct
- No errors or warnings
Validation Checklist
Common Issues
Event Not Appearing
Causes:
- Debug mode not enabled
- Wrong Measurement ID
- JavaScript error before gtag call
- Ad blocker blocking
Solutions:
- Enable Google Analytics Debugger extension
- Verify Measurement ID
- Check browser console for errors
- Test in incognito mode
Parameters Missing
Causes:
- Parameter name typo
- Value is undefined/null
- Data layer variable not populated
Solutions:
- Check exact parameter name
- Validate value before sending
- Debug data layer in GTM Preview
Wrong Data Type
Causes:
- Sending string instead of number
- Array where string expected
Solutions:
- Validate data type before sending
- Convert values appropriately
- Test with DebugView
Best Practices
Design Guidelines
- Be consistent - Same event name everywhere
- Be descriptive - Clear, action-oriented names
- Be specific - Avoid generic catch-all events
- Plan parameters - Document required vs optional
- Validate values - Check before sending
Documentation
Document each custom event:
## Event: demo_requested
**Description:** User requested a product demonstration
**Parameters:**
| Name | Type | Required | Description |
|------|------|----------|-------------|
| demo_type | string | Yes | Type of demo requested |
| industry | string | No | User's industry |
| company_size | string | No | Size category |
**Example:**
gtag('event', 'demo_requested', {
'demo_type': 'product_walkthrough',
'industry': 'technology'
});
Version Control
Track event changes:
- Document when events added/modified
- Include in code review process
- Update documentation on changes
1---2name: ga4-custom-events3description: Expert guidance for creating business-specific custom events beyond Google's recommended events.4---5# GA4 Custom Events67Expert guidance for creating business-specific custom events beyond Google's recommended events.89## Overview1011Custom events enable measurement of unique business goals, industry-specific actions, and contextual behaviours not covered by Google's recommended events. This reference covers event naming conventions, parameter design strategies, and implementation across all platforms.1213## When to Use Custom Events1415Create custom events when:1617- Business action not covered by recommended events18- Industry-specific tracking requirements19- Unique user interactions to measure20- Business-specific conversion points21- Detailed behavioural analysis needs2223## Event Naming Conventions2425### Format and Constraints2627| Constraint | Value |28|------------|-------|29| Case | snake_case (lowercase with underscores) |30| Maximum length | 40 characters |31| Starting character | Letter |32| Allowed characters | Letters, numbers, underscores |3334### Naming Framework3536```37[action]_[object]_[context]38```3940**Examples:**41- product_comparison_viewed42- pricing_calculator_used43- whitepaper_downloaded44- trial_signup_completed45- support_ticket_created4647### Good Event Names4849| Event Name | Description |50|------------|-------------|51| demo_requested | User requested product demo |52| feature_trial_started | User started feature trial |53| integration_configured | User set up integration |54| report_exported | User exported report |55| team_member_invited | User invited team member |5657### Anti-Patterns (Avoid)5859| Bad Name | Problem | Better Name |60|----------|---------|-------------|61| click | Too generic | button_click |62| event1 | Not descriptive | form_submit |63| MyEvent | Wrong case | my_event |64| trackAction | Camel case | track_action |65| data | Meaningless | data_exported |6667## Event Parameter Design6869### Parameter Strategy7071For each custom event, identify:72731. **What is the action?** The user behaviour being measured742. **What context is needed?** Information to analyse the action753. **What parameters provide context?** Data points to collect764. **What are the constraints?** Length limits, data types7778### Parameter Limits7980| Limit | Value |81|-------|-------|82| Parameters per event | 25 |83| Parameter name length | 40 characters |84| Parameter value length | 100 characters |85| Data types | String, Integer, Float |8687### Parameter Examples8889**demo_requested Event:**90```javascript91gtag('event', 'demo_requested', {92 'demo_type': 'product_walkthrough',93 'industry': 'technology',94 'company_size': 'enterprise',95 'lead_source': 'organic_search',96 'product_interest': 'analytics_suite'97});98```99100**course_enrollment Event:**101```javascript102gtag('event', 'course_enrollment', {103 'course_id': 'COURSE_101',104 'course_name': 'Advanced GA4',105 'instructor': 'Jane Smith',106 'price': 99.99,107 'currency': 'USD',108 'level': 'advanced',109 'duration_hours': 8110});111```112113**support_ticket_created Event:**114```javascript115gtag('event', 'support_ticket_created', {116 'ticket_type': 'bug_report',117 'product': 'mobile_app',118 'severity': 'high',119 'department': 'engineering',120 'resolution_time_expected': 24121});122```123124## Industry-Specific Patterns125126### SaaS Events127128| Event | Parameters |129|-------|------------|130| trial_started | plan_type, trial_duration, feature_count |131| trial_ended | conversion_status, days_active, features_used |132| upgrade_initiated | from_plan, to_plan, estimated_value |133| plan_downgraded | from_plan, to_plan, reason_category |134| feature_activated | feature_name, activation_method |135| integration_connected | integration_name, connection_type |136| api_key_generated | key_purpose, environment |137| workspace_created | workspace_type, team_size |138139### Education Events140141| Event | Parameters |142|-------|------------|143| lesson_completed | course_id, lesson_id, completion_time |144| quiz_submitted | quiz_id, score_percentage, attempts |145| certificate_earned | course_id, grade, completion_date |146| course_enrolled | course_id, enrollment_type, price |147| instructor_contacted | inquiry_type, response_time |148| resource_downloaded | resource_type, file_format |149| discussion_posted | topic_category, word_count |150| peer_review_submitted | assignment_id, rating_given |151152### Media Events153154| Event | Parameters |155|-------|------------|156| article_shared | article_id, share_platform, category |157| video_watched | video_id, duration, engagement_percent |158| podcast_completed | episode_id, listen_duration, completion |159| newsletter_subscribed | newsletter_type, source |160| content_bookmarked | content_id, content_type |161| author_followed | author_id, follow_source |162| comment_posted | content_id, word_count |163| paywall_reached | content_type, view_count_prior |164165### E-commerce (Beyond Recommended)166167| Event | Parameters |168|-------|------------|169| product_compared | products_compared, category |170| review_submitted | product_id, star_rating, has_photo |171| wishlist_shared | item_count, share_method |172| size_guide_viewed | product_category, size_selected |173| store_locator_used | postcode, results_count |174| gift_wrap_selected | wrap_type, price |175| loyalty_redeemed | points_redeemed, reward_type |176| back_in_stock_signup | product_id, notification_preference |177178## Implementation Examples179180### gtag.js Implementation181182```javascript183// Simple event184gtag('event', 'demo_requested', {185 'demo_type': 'product_walkthrough',186 'industry': 'technology'187});188189// Event with multiple parameters190gtag('event', 'subscription_changed', {191 'change_type': 'upgrade',192 'from_plan': 'basic',193 'to_plan': 'professional',194 'price_difference': 50,195 'currency': 'USD',196 'billing_cycle': 'monthly'197});198```199200### GTM Data Layer Implementation201202```javascript203// Push event to data layer204dataLayer.push({205 'event': 'demo_requested',206 'demo_type': 'product_walkthrough',207 'industry': 'technology',208 'company_size': 'enterprise'209});210211// In GTM:212// 1. Create Data Layer Variables for each parameter213// 2. Create Custom Event trigger for 'demo_requested'214// 3. Create GA4 Event tag with mapped parameters215```216217### Measurement Protocol (Server-Side)218219```python220import requests221import json222223MEASUREMENT_ID = "G-XXXXXXXXXX"224API_SECRET = "your_api_secret"225226def send_custom_event(client_id, event_name, params):227 url = f"https://www.google-analytics.com/mp/collect?measurement_id={MEASUREMENT_ID}&api_secret={API_SECRET}"228229 payload = {230 "client_id": client_id,231 "events": [{232 "name": event_name,233 "params": params234 }]235 }236237 response = requests.post(238 url,239 headers={"Content-Type": "application/json"},240 data=json.dumps(payload)241 )242243 return response.status_code == 204244245# Example usage246send_custom_event(247 client_id="123.456",248 event_name="subscription_renewed",249 params={250 "plan_type": "professional",251 "renewal_value": 299.00,252 "currency": "USD",253 "renewal_count": 3254 }255)256```257258## Registering Custom Dimensions259260Custom parameters won't appear in GA4 reports until registered as custom dimensions.261262### Registration Process2632641. **Send parameter in event** (any platform)2652. **Verify in DebugView** (Admin -> DebugView)2663. **Register dimension:**267 - Admin -> Data Display -> Custom Definitions268 - Create Custom Dimension269 - Dimension Name: Human-friendly (e.g., "Demo Type")270 - Scope: Event, User, or Item271 - Event Parameter: Exact name from code (e.g., "demo_type")2724. **Wait 24-48 hours** for data to populate2735. **Use in reports** (Explorations, Standard Reports)274275### Scope Selection276277| Scope | Use For | Example |278|-------|---------|---------|279| Event | Single event data | demo_type, button_name |280| User | User attributes | subscription_tier, customer_segment |281| Item | Product data | item_color, item_size |282283## Testing Custom Events284285### DebugView Workflow2862871. Enable debug mode:288 - Chrome extension: Google Analytics Debugger289 - URL parameter: `?debug_mode=true`290 - gtag config: `debug_mode: true`2912922. Trigger custom event2932943. Check DebugView:295 - Event appears with correct name296 - All parameters present297 - Parameter values correct298 - No errors or warnings299300### Validation Checklist301302- [ ] Event name follows snake_case303- [ ] Event name under 40 characters304- [ ] All parameters present305- [ ] Parameter names under 40 characters306- [ ] Parameter values under 100 characters307- [ ] Correct data types (string vs number)308- [ ] No PII in parameters309310## Common Issues311312### Event Not Appearing313314**Causes:**315- Debug mode not enabled316- Wrong Measurement ID317- JavaScript error before gtag call318- Ad blocker blocking319320**Solutions:**3211. Enable Google Analytics Debugger extension3222. Verify Measurement ID3233. Check browser console for errors3244. Test in incognito mode325326### Parameters Missing327328**Causes:**329- Parameter name typo330- Value is undefined/null331- Data layer variable not populated332333**Solutions:**3341. Check exact parameter name3352. Validate value before sending3363. Debug data layer in GTM Preview337338### Wrong Data Type339340**Causes:**341- Sending string instead of number342- Array where string expected343344**Solutions:**3451. Validate data type before sending3462. Convert values appropriately3473. Test with DebugView348349## Best Practices350351### Design Guidelines3523531. **Be consistent** - Same event name everywhere3542. **Be descriptive** - Clear, action-oriented names3553. **Be specific** - Avoid generic catch-all events3564. **Plan parameters** - Document required vs optional3575. **Validate values** - Check before sending358359### Documentation360361Document each custom event:362363```markdown364## Event: demo_requested365366**Description:** User requested a product demonstration367368**Parameters:**369| Name | Type | Required | Description |370|------|------|----------|-------------|371| demo_type | string | Yes | Type of demo requested |372| industry | string | No | User's industry |373| company_size | string | No | Size category |374375**Example:**376gtag('event', 'demo_requested', {377 'demo_type': 'product_walkthrough',378 'industry': 'technology'379});380```381382### Version Control383384Track event changes:385- Document when events added/modified386- Include in code review process387- Update documentation on changes