RFC-001: GA4 Event Contract & Reporting Alignment

Prev Next

RFC-001: GA4 Event Contract & Reporting Alignment

Related Documents: ADR-018 (Typed Analytics Architecture)

Abstract

This document specifies the Google Analytics 4 (GA4) event contract and reporting alignment protocol for the OneCMS platform. It defines event naming conventions, payload structure, versioning strategy, parameter normalization rules, and cross-functional ownership boundaries between Engineering and Analytics teams.

The specification is designed to prevent analytics schema drift, ensure dashboard stability, enforce compile-time type safety, and establish a formal telemetry contract that evolves safely over time. Implementations emitting analytics events from OneCMS MUST comply with this specification.

1. Introduction

1.1 Background

OneCMS has implemented a typed Google Analytics (GA4) event architecture (ADR-018) to enforce compile-time validation, schema consistency, and production-safe event emission. With the involvement of the Analytics team, telemetry becomes a cross-functional contract between Engineering and Analytics.

Analytics events are sent to a dedicated GA4 property separate from consumer-facing analytics to enable operational insights, product telemetry, and behavioral analysis without interfering with end-user tracking.

1.2 Motivation

Without a clearly documented event schema, naming convention, and ownership boundary, the following risks arise:

  • Silent event schema drift leading to invalid dashboard queries
  • Dashboard instability due to inconsistent payload shapes
  • Ambiguity around event ownership and configuration authority
  • Confusion regarding impression tracking and conversion event designation
  • Inability to safely evolve analytics schema over time

1.3 Goals

This RFC formalizes:

  1. The canonical event naming pattern
  2. The typed event envelope structure
  3. Parameter naming and validation rules
  4. Schema versioning and backward compatibility strategy
  5. Ownership boundaries between Engineering and Analytics
  6. Production emission safeguards

1.4 Scope

This specification supersedes previous untyped analytics implementations and governs all GA4 event emission from OneCMS frontend applications.

2. Terminology

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119.

2.1 Definitions

  • Event: An analytics occurrence representing a user action or system state change
  • Category: A domain grouping for related events (e.g., authentication, content)
  • Event Name: The GA4-emitted event identifier in the format category_event
  • Envelope: The canonical typed container for all event data
  • Payload: Event-specific parameters beyond the common envelope fields
  • Schema Version: A centrally managed version identifier for the analytics contract
  • Normalization: The process of transforming event data to meet GA4 constraints
  • Production Gating: The mechanism preventing non-production events from reaching GA4

3. Event Naming Convention

3.1 Format Specification

All events emitted from OneCMS MUST follow the format:

<category>_<event>

Where:

  • <category> is an approved domain category (Section 13.1)
  • <event> is a lowercase action identifier using snake_case
  • The complete event name MUST be lowercase

3.2 Event Name Construction

Event names are constructed programmatically:

const eventName = `${category}_${event}`.toLowerCase();

3.3 Examples

Valid Event Names:

  • authentication_login
  • content_publish
  • upload_complete
  • customer_support_session_start

Invalid Event Names (DO NOT USE):

  • AuthenticationLogin (not lowercase)
  • content-publish (hyphen instead of underscore)
  • login_success (outcome encoded in name - use outcome parameter instead)

3.4 Event Name Stability

Once an event name is published to production, it MUST NOT be changed. Renaming requires schema version increment and deprecation period.

4. Event Contract Structure

4.1 Envelope Structure

All events MUST conform to the AnalyticsEventEnvelope type:

type AnalyticsEventEnvelope<C extends AnalyticsCategory, E extends CategoryEventMap[C]> = {
  category: C;
  event: E;
  outcome?: EventOutcome;
} & BaseEventContext &
  EventPayloadRegistry[E];

4.2 Common Parameters

The following parameters MUST be present on all events:

Parameter Type Required Description
category string Yes Event domain
event string Yes Action identifier
schema_version string Yes Analytics contract version (injected automatically)
site string Yes Site identifier
siteId string Yes Numeric site ID
env string Yes Environment: development | testing | production
outcome string No Result: success | fail | cancel
userId string No Opaque user identifier (hashed)

4.3 Event-Specific Parameters

Each event MAY define additional typed parameters beyond the common envelope.

Example: authentication_login

type LoginPayload = {
  method: 'credentials' | 'google' | 'microsoft';
  error_code?: string; // Only present when outcome = 'fail'
};

Example: upload_init

type UploadInitPayload = {
  file_type: 'video' | 'audio' | 'image' | 'document';
  file_size_mb: number;
  upload_strategy: 'chunked' | 'direct';
};

Example: content_publish

type ContentPublishPayload = {
  content_type: 'movie' | 'series' | 'clip' | 'article';
  has_schedule: boolean;
  duration_sec?: number;
  error_code?: string;
};

5. Parameter Specifications

5.1 Parameter Naming Convention

All custom parameters MUST follow these rules:

  1. Case: snake_case (lowercase with underscores)
  2. Boolean prefixes: is_ or has_ (e.g., is_scheduled, has_errors)
  3. Error parameters: Prefixed with error_ (e.g., error_code, error_message)
  4. Duration parameters: Suffixed with unit (e.g., duration_sec, timeout_ms, timestamp_unix)
  5. Count parameters: Suffixed with subject (e.g., item_count, retry_count)

5.2 Parameter Value Constraints

Implementations MUST enforce the following GA4 limits:

  • Parameter name length: Maximum 40 characters
  • Parameter value length: Maximum 100 characters (strings auto-truncated)
  • Total custom parameters per event: SHOULD NOT exceed 25 to ensure compatibility with GA4 reporting limits (built-in parameters do not count toward this limit)
  • Array values: Serialized as comma-separated strings

5.3 Parameter Types

Type Serialization Example
string As-is (truncated to 100 chars) "credentials"
number Numeric or string (implementation choice; GA4 accepts both) 7200 or "7200"
boolean "true" or "false" "true"
Array<string> Comma-joined "tag1,tag2,tag3"
undefined Removed from payload N/A
null Removed from payload N/A

5.4 Reserved Parameter Names

The following parameter names are RESERVED by GA4 and MUST NOT be used for custom parameters:

  • page_location, page_title, page_referrer
  • language, screen_resolution
  • client_id, session_id
  • Any parameter prefixed with ga_ or firebase_

6. Versioning and Evolution

6.1 Schema Version Format

The schema_version follows semantic versioning: MAJOR.MINOR.PATCH

Current version: 1.0.0

6.2 Version Increment Rules

MAJOR increment (e.g., 1.0.02.0.0) is REQUIRED when:

  • Removing a common parameter
  • Renaming any parameter
  • Changing parameter semantics
  • Restructuring envelope shape
  • Changes that would break interpretation of historical data

MINOR increment (e.g., 1.0.01.1.0) is OPTIONAL for:

  • Adding new optional parameters
  • Adding new event types
  • Expanding enum values

PATCH increment (e.g., 1.0.01.0.1) for:

  • Bug fixes in normalization logic
  • Documentation clarifications
  • Internal implementation improvements that do not affect payload structure

6.3 Backward Compatibility

Schema changes MUST maintain dashboard compatibility:

  • Historical queries MUST remain valid
  • Dashboards MUST filter by schema_version if behavior differs
  • Deprecated parameters MUST be documented with sunset date

6.4 Deprecation Process

  1. Document deprecation with replacement guidance
  2. Dual-emit old and new versions for 30 days minimum
  3. Deprecated parameters MUST remain readable by dashboards until sunset
  4. Remove deprecated version only after Analytics team confirmation

7. Payload Normalization

7.1 Normalization Adapter

All events MUST pass through the normalizeAnalyticsPayload adapter before emission:

function normalizeAnalyticsPayload(payload: Record<string, unknown>): Record<string, unknown> {
  const normalizedEntries = Object.entries(payload)
    .filter(([, value]) => value !== undefined)
    .map(([key, value]) => [key, normalizeValue(value)]);

  const limitedEntries = normalizedEntries.slice(0, param_limit);
  return Object.fromEntries(limitedEntries);
}

7.2 Normalization Rules

The normalization process:

  1. Removes: undefined and null values
  2. Truncates: Strings exceeding 100 characters
  3. Serializes: Arrays to comma-separated strings
  4. Limits: Total parameters to 25 per event
  5. Preserves: Parameter order (first 25 if overflow)

7.3 Value Normalization

function normalizeValue(value: unknown): unknown {
  if (value === undefined || value === null) return undefined;
  if (typeof value === 'string') {
    return value.length > 100 ? value.slice(0, 100) : value;
  }
  if (Array.isArray(value)) {
    return value.join(',');
  }
  return value;
}

8. Environment and Emission Control

8.1 Production Gating

Events MUST only be emitted to GA4 when:

envelope.env === 'production';

8.2 Non-Production Behavior

In non-production environments (development, testing):

  • Events MUST be logged to console with [GA - Skipped in non-prod] prefix
  • Events MUST NOT be sent to GA4
  • Full payload MUST be visible for debugging
if (envelope.env !== 'production') {
  console.debug('[GA - Skipped in non-prod]', eventName, finalPayload);
  return;
}

8.3 Implementation

static trackAnalytics(envelope: AllAnalyticsEnvelopes) {
  const eventName = `${envelope.category}_${envelope.event}`.toLowerCase();
  const finalPayload = normalizeAnalyticsPayload({
    ...envelope,
    schema_version: Config.GA.schema_version
  });

  if (envelope.env !== 'production') {
    console.debug('[GA - Skipped in non-prod]', eventName, finalPayload);
    return;
  }

  this.track(eventName, finalPayload);
}

9. Ownership and Responsibilities

9.1 Engineering Team (OneCMS)

Owns and Maintains:

  • Event emission logic and transport layer
  • Typed event contracts and TypeScript definitions
  • Category → event mapping enforcement
  • Schema versioning (incrementing schema_version)
  • Payload normalization adapter
  • Production gating mechanism
  • Compile-time type safety
  • Event payload registry
  • Frontend analytics instrumentation
  • Backward compatibility within TypeScript contracts

Does NOT Own:

  • GA4 property configuration
  • Dashboard design or queries
  • Custom dimension registration in GA4
  • Conversion event designation

9.2 Analytics Team

Owns and Maintains:

  • GA4 property configuration and settings
  • Custom dimension and metric registration
  • Conversion event designation and goals
  • Impression tracking design and implementation
  • Report structuring, collections, and visualizations
  • Event grouping strategy within GA4
  • Dashboard queries and data exploration
  • BigQuery export configuration (if applicable)

Does NOT Own:

  • Frontend event payload structure
  • Event naming conventions
  • TypeScript type definitions
  • Event emission timing or logic

9.3 Collaboration Points

Both teams MUST collaborate on:

  1. Custom Dimension Mapping: Which payload parameters should be registered as GA4 custom dimensions
  2. Conversion Events: Which events represent meaningful conversions
  3. High-Frequency Events: Sampling or throttling strategies for noisy events
  4. Breaking Changes: Schema version increments and migration planning
  5. New Categories: Adding new event categories requires joint approval

10. Security Considerations

10.1 Personally Identifiable Information (PII)

Event payloads MUST NOT contain:

  • Real names, email addresses, phone numbers
  • IP addresses (anonymized at GA4 property level)
  • Plaintext passwords or credentials
  • Payment information
  • Unmasked sensitive identifiers

10.2 User Identification

  • userId MUST be an opaque, hashed identifier
  • MUST NOT be reversible to original user identity
  • MUST comply with GDPR and privacy regulations

10.3 Error Messages

error_code and error_message parameters MUST NOT contain:

  • Stack traces with file paths
  • Database query details
  • API keys or tokens
  • Internal system architecture details

10.4 Data Minimization

Events SHOULD include only parameters necessary for analytics insights. Avoid over-instrumentation.

Raw user-generated content (e.g., titles, comments, descriptions) MUST NOT be emitted as analytics parameters to prevent PII exposure and excessive data collection.

10.5 Consent Management

GA4 tracking respects user consent settings:

static DefaultPermissionMap: Record<GtagCategoryType, GAPermissionType> = {
  analytics: 'denied',  // Requires explicit user consent
  necessary: 'granted',  // Essential cookies
};

11. Implementation Requirements

11.1 Type Safety

All events MUST be strongly typed using TypeScript:

type AnalyticsEventEnvelope<C extends AnalyticsCategory, E extends CategoryEventMap[C]> = {
  category: C;
  event: E;
  outcome?: EventOutcome;
} & BaseEventContext &
  EventPayloadRegistry[E];

11.2 Event Emission

Events MUST be emitted via the centralized GA.trackAnalytics() method:

GA.trackAnalytics({
  category: 'authentication',
  event: 'login',
  outcome: 'success',
  method: 'google',
  site: currentSite,
  siteId: currentSiteId,
  env: process.env.NODE_ENV,
});

11.3 Testing Strategy

Development Testing

  • Non-production events logged to console
  • Payload shape validated via TypeScript compiler
  • Console logs inspected for correct structure

Staging Testing

  • Separate GA4 property for staging environment
  • Events emitted to staging property for validation
  • Analytics team reviews staging data before production

Production Testing

  • Smoke tests for critical event flows
  • Dashboard monitoring for event volume anomalies
  • Schema version tracking in GA4 reports

11.4 Error Handling

Event emission failures MUST:

  • NOT throw exceptions that block user workflow
  • Log errors to console in development mode
  • Fail silently in production (fire-and-forget)
  • NOT retry automatically (prevents event duplication)
private static track(event: string, payload: Record<string, unknown>) {
  if (typeof window === 'undefined' || !('gtag' in window)) return;
  try {
    window.gtag('event', event, payload);
  } catch (error) {
    console.debug('[GA Error]', error);
  }
}

11.5 Configuration

Analytics configuration MUST be centralized in /src/config/config.ts:

GA: {
  measurementId: process.env.NEXT_PUBLIC_GA_MEASUREMENT_ID,
  schema_version: '1.0.0',
  string_char_limit: 100,
  param_limit: 25,
}

12. References

12.1 Normative References

  • [RFC-2119]: Key words for use in RFCs to Indicate Requirement Levels
  • [ADR-018]: Typed Analytics (GA) Architecture
  • [GA4-LIMITS]: Google Analytics 4 Event Parameter Limits https://support.google.com/analytics/answer/9267744

12.2 Informative References

  • [GDPR]: General Data Protection Regulation
  • [GA4-DOCS]: Google Analytics 4 Developer Documentation
  • [TYPESCRIPT]: TypeScript Handbook

12.3 Internal References

  • /src/lib/external/GA.ts: Analytics transport layer
  • /src/adapters/analytics/normalizeAnalyticsPayload.adapter.ts: Normalization logic
  • /src/types/analytics/index.ts: Event type definitions
  • /src/config/config.ts: Analytics configuration

13. Appendices

13.1 Approved Event Categories

The following categories are currently approved for use:

Category Domain Example Events
authentication User authentication lifecycle login, logout, mfa, reset_password
content Content management operations publish, update, archive, preview
upload Media upload workflows init, progress, complete, fail
monetization Revenue and pricing operations plan_change, subscription_update
customer_support Support interactions session_start, ticket_create
navigation User navigation patterns menu_item_click, tab_change
appcms App configuration management build_submit, settings_save

13.2 High-Frequency Event Guidance

Events that fire more than 10 times per user session SHOULD be evaluated for:

  • Throttling: Limit to 1 emission per N seconds
  • Sampling: Emit only X% of occurrences
  • Exclusion: Don't track at all if low analytical value

This threshold is guidance, not a strict rule. Actual thresholds depend on event value and GA4 quota considerations.

Examples:

  • autosave_success: Throttle to max 1/minute
  • content_search_keystroke: Don't track (use search_complete instead)
  • upload_progress: Sample at 25% intervals only

13.3 Conversion Event Recommendations

The following recommendations are informative only and subject to Analytics team approval.

Primary Conversions (high-value actions):

  • authentication_login (outcome: success)
  • content_publish (outcome: success)
  • build_submitted
  • monetization_subscription_upgrade

Secondary Conversions (engagement indicators):

  • content_preview
  • customer_support_session_start
  • settings_save

Final conversion designation determined by Analytics team.

13.4 Custom Dimension Mapping

Recommended Custom Dimensions:

Parameter GA4 Dimension Name Scope Rationale
schema_version analytics_version Event Track contract evolution
site tenant_site User Multi-tenant analysis
outcome event_outcome Event Success/fail segmentation
method (auth) auth_method Event SSO vs credentials tracking
content_type cms_content_type Event Content type analysis

Analytics team configures actual dimension registration in GA4.

13.5 Sample Event Payloads

Example 1: Successful Login

{
  "category": "authentication",
  "event": "login",
  "outcome": "success",
  "method": "google",
  "schema_version": "1.0.0",
  "site": "acme-ott",
  "siteId": "12345",
  "env": "production",
  "userId": "a1b2c3d4e5f6"
}

Example 2: Failed Content Publish

{
  "category": "content",
  "event": "publish",
  "outcome": "fail",
  "content_type": "movie",
  "error_code": "validation_failed",
  "duration_sec": 7200,
  "has_schedule": false,
  "schema_version": "1.0.0",
  "site": "acme-ott",
  "siteId": "12345",
  "env": "production"
}

Example 3: Upload Complete

{
  "category": "content",
  "event": "upload_complete",
  "outcome": "success",
  "file_type": "video",
  "file_size_mb": 1250,
  "upload_strategy": "chunked",
  "duration_sec": 3840,
  "schema_version": "1.0.0",
  "site": "acme-ott",
  "siteId": "12345",
  "env": "production"
}

13.6 Migration Path

From: Untyped analytics implementation (pre-ADR-018)

To: Typed analytics contract (this RFC)

Timeline:

  1. Phase 1 (Current): New events use typed contract
  2. Phase 2 (Q2 2026): Legacy events dual-emit (old + new format)
  3. Phase 3 (Q3 2026): Legacy events deprecated, warnings logged
  4. Phase 4 (Q4 2026): Legacy events removed

13.7 Change Management Process

For new events (no RFC required):

  1. Add type definition to /src/types/analytics/<category>/
  2. Add to EventPayloadRegistry
  3. Update CategoryEventMap if needed
  4. Create PR with type-checked implementation
  5. Notify Analytics team via Slack (include sample payload)

For breaking changes (RFC required):

  1. Draft RFC amendment
  2. Increment schema_version
  3. Engineering + Analytics team review
  4. Plan deprecation timeline
  5. Dual-emit old + new versions
  6. Monitor dashboard impact
  7. Complete migration

14. Open Questions

The following items require alignment with the Analytics team before finalization:

  1. Conversion Event Designation:

    • Which CMS actions should be marked as GA4 conversion events?
    • Should publish workflows be primary or secondary conversions?
  2. High-Frequency Event Handling:

    • Specific throttling rates for autosave, search, progress events?
    • Which events warrant complete exclusion from tracking?
  3. Custom Dimension Priorities:

    • Which 50 parameters (GA4 limit) should be promoted to custom dimensions?
    • What dimension scopes (event vs user vs session)?
  4. Multi-Site Handling:

    • How to differentiate events when users switch sites in one session?
    • Should site be a dimension or filter?
  5. Event Lifecycle Patterns:

    • Prefer paired events (upload_start + upload_complete) or atomic events?
    • Should failed events have separate event names or rely on outcome?

15. Decision Outcome

Status: APPROVED

Approved On: 2026-03-11

This RFC is the governing analytics contract blueprint for dependent implementation RFCs. It is treated as a standards baseline and remains in APPROVED state as the authoritative specification.

16. Acknowledgments

This specification builds upon ADR-018 (Typed Analytics Architecture) and incorporates feedback from the OneCMS Engineering and Analytics teams. Special thanks to contributors who reviewed early drafts and provided implementation insights.

17. Document History

Date Version Change Summary Author
2026-02-25 1.0 Initial draft of GA4 global event contract RFC Aakash Jha
2026-03-05 1.1 Revised to RFC standard format and expanded technical specification details Aakash Jha
2026-03-11 1.2 Status promoted to APPROVED as canonical analytics contract blueprint Aakash Jha
2026-03-14 1.3 Decision outcome clarified and document history normalized to standard table format Aakash Jha