Skip to content

serverTracker.trackEvent()

Sends one GA4 event via the Measurement Protocol. The required clientId is GA4’s per-browser identifier (read from the _ga cookie) — without it, GA4 has no way to merge the server event into the browser session.

serverTracker.trackEvent(input: ServerEventInput): Promise<ServerEventResult>;
type ServerEventInput = {
name: string;
clientId: string;
params?: Record<string, unknown>;
userData?: UserData;
};
FieldRequiredNotes
nameyesThe GA4 event name.
clientIdyesThe GA4 client ID — the suffix of the _ga cookie value (GA1.1.<clientId>). Forward it from the browser to your server with the request that triggers the event.
paramsnoEvent parameters, forwarded to GA4 unchanged.
userDatanoIdentity fields. The SDK normalizes and hashes the fields Google expects hashed.
type ServerEventResult = {
ga4: SendResult;
};
type SendResult =
| { ok: true }
| { ok: false; error: Error };

Resolves after the HTTP call completes. Runtime API failures (network errors, non-2xx responses) do not throw — they’re captured on result.ga4.error. The structured shape leaves room for future destinations (Meta CAPI, TikTok Events API, etc.) without breaking the contract.

POSTs to:

https://www.google-analytics.com/mp/collect?measurement_id=<id>&api_secret=<secret>

with body:

{
"client_id": "<clientId>",
"events": [{ "name": "<name>", "params": { ... } }],
"user_data": { ... }
}

The user_data block is omitted entirely if no userData was passed.

Runtime API failures land on result.ga4.error regardless of the debug flag. The debug: true flag only controls whether the same failure is also logged via console.warn:

  • Non-2xx response: [trackbridge] GA4 MP returned <status> <statusText>
  • Network error: [trackbridge] GA4 MP request failed: <error>

This call never throws on HTTP failures — inspect the result to surface failures to your own observability pipeline.

GA4’s _ga cookie has the format GA1.1.<clientId>.<timestamp>. The fragment Trackbridge needs is <clientId> — the third dot-separated piece.

function readGa4ClientId(): string | null {
const match = document.cookie.match(/(?:^|;\s*)_ga=([^;]+)/);
if (!match) return null;
const parts = match[1].split('.');
return parts.length >= 4 ? parts.slice(2, 4).join('.') : null;
}

Send it to your server alongside the request:

await fetch('/api/some-server-event', {
method: 'POST',
body: JSON.stringify({ ...payload, ga4ClientId: readGa4ClientId() }),
});
import { serverTracker } from '@/lib/tracker.server';
const result = await serverTracker.trackEvent({
name: 'subscription_renewed',
clientId: payload.ga4ClientId,
params: { value: 9.99, currency: 'USD' },
});
if (!result.ga4.ok) {
// Forward to your monitoring; the call did not reach GA4 successfully.
reportError(result.ga4.error);
}