Third-Party Mode (Planned)

Prev Next

Third-Party Mode (Planned)

In third-party mode, ONECMS generates the same typed, normalized GA-compatible payload but does not own the delivery pipeline. An external library provides its own script tags and handles sending events to GA4.

When to Use This Mode

This mode applies when ONECMS integrates a third-party package that:

  • Ships its own GA4 / GTM script tags
  • Has its own gtag or dataLayer initialization
  • Expects a GA-compatible event payload from the host application

In this scenario, injecting a second gtag.js would cause double-counting, tag conflicts, or measurement ID collisions. The CMS should not load its own script — only generate the payload.

What Changes

Concern Owned Pipeline (Mode 1) Third-Party Mode (Mode 2)
Script injection GoogleAnalytics component loads gtag.js External lib provides its own tags
window.gtag Initialized by our script Initialized by external lib (or not used)
Payload generation GA.trackAnalytics() + normalizeAnalyticsPayload() Same
Event types AllAnalyticsEnvelopes Same
Delivery window.gtag('event', ...) External SDK's send method
Consent GA.updateGtagConsent() External lib's consent API

What Does Not Change

The entire event generation and normalization layer remains identical:

  • Typed envelopes (AllAnalyticsEnvelopes)
  • normalizeAnalyticsPayload() adapter
  • Schema versioning
  • Production-only gating
  • Domain helper functions

This is by design — the envelope is the contract, delivery is pluggable.

Expected Architecture

Image

Implementation Guidance

When a third-party analytics package is installed:

1. Disable the owned script

Remove or conditionally skip the GoogleAnalytics component in ClientWrapper. This can be driven by a config flag or feature flag:

// Example — config-driven
{!Config.GA.useThirdPartyMode && (
  <GoogleAnalytics measurementId={Config.GA.measurementId} />
)}

2. Create a delivery adapter

Add a new delivery adapter alongside the existing track() method in GA.ts:

private static trackViaThirdParty(event: string, payload: Record<string, unknown>) {
  // Call the external SDK's send/track method
  ExternalAnalyticsSDK.send(event, payload);
}

3. Route based on mode

trackAnalytics() dispatches to the correct delivery method:

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;
  }

  if (Config.GA.useThirdPartyMode) {
    this.trackViaThirdParty(eventName, finalPayload);
  } else {
    this.track(eventName, finalPayload);
  }
}

4. Adapt consent

If the external library has its own consent API, updateGtagConsent() should delegate to it instead of calling window.gtag('consent', ...).

Constraints

  • The external library must accept a flat key-value payload (GA4's event parameter model)
  • Event names must remain in category_event format to preserve dashboard compatibility
  • schema_version must still travel with payloads
  • Production gating must still be enforced — the external SDK should not receive non-production events
  • No domain code should need to change — the switch is entirely at the transport layer

Open Questions

These will be resolved when the specific third-party package is selected:

  • What is the SDK's send/track API signature?
  • Does the SDK initialize its own dataLayer, or does it use a different mechanism?
  • Does the SDK handle consent natively, or does it need consent state from the CMS?
  • Should both modes be active simultaneously (e.g., owned pipeline for some events, third-party for others)?