serverTracker.trackPurchase()
The server-side counterpart to tracker.trackPurchase(). Fires GA4 via the Measurement Protocol, plus an Ads conversion when conversionLabels.purchase is configured. Returns a structured ServerHelperResult with per-destination outcome.
clientId is required — the server has no _ga cookie of its own. Forward it from the browser via tracker.getClientId() or tracker.exportContext().
Signature
Section titled “Signature”serverTracker.trackPurchase(input: ServerPurchaseInput): Promise<ServerHelperResult>;ServerPurchaseInput
Section titled “ServerPurchaseInput”type ServerPurchaseInput = { transactionId: string; value: number; currency: string; items: TrackbridgeItem[]; clientId: string; userId?: string; gclid?: string; gbraid?: string; wbraid?: string; affiliation?: string; coupon?: string; shipping?: number; tax?: number; userData?: UserData; consent?: ServerConsent;};| Field | Required | Notes |
|---|---|---|
transactionId | yes | Must match the browser-side trackPurchase call. No auto-generation. |
value | yes | Order total in currency. |
currency | yes | ISO 4217. |
items | yes | TrackbridgeItem[]. Empty arrays are sent as []. |
clientId | yes | GA4 client ID forwarded from the browser. |
userId | no | Optional GA4 user_id. |
gclid / gbraid / wbraid | no | Click identifiers from the browser session. Required by Ads if you want this conversion attributed to a click. |
affiliation, coupon, shipping, tax | no | GA4 params. |
userData | no | Hashed per enhanced conversions. Dropped if consent.ad_user_data === 'denied'. |
consent | no | Per-call consent signals. Omit to send the request in full. |
Returns
Section titled “Returns”type ServerHelperResult = { ads: HelperSendResult; ga4: HelperSendResult;};
type HelperSendResult = | { ok: true } | { ok: false; error: Error } | { skipped: true; reason: 'no_label_configured' | 'refund_ads_unsupported' };ads carries the Ads-side outcome:
{ ok: true }if the Ads API accepted the upload.{ ok: false, error }if the Ads API rejected, OAuth refresh failed, or the network failed.{ skipped: true, reason: 'no_label_configured' }ifconversionLabels.purchaseis unset.
ga4 carries the GA4 MP outcome with the same { ok: true } | { ok: false, error } shape. GA4 does not have a “skipped” path for trackPurchase — it always fires when ga4MeasurementId is configured.
Errors
Section titled “Errors”Throws synchronously if transactionId is missing or empty. Throws if ads is configured but conversionLabels.purchase references an unknown conversionActions key.
Example
Section titled “Example”import { serverTracker } from '@/lib/tracker.server';
export async function POST(req: Request) { const event = await verifyStripeSignature(req); if (event.type !== 'checkout.session.completed') return new Response();
const order = await db.orders.findByStripeId(event.data.object.id);
const result = await serverTracker.trackPurchase({ transactionId: order.id, value: order.total, currency: order.currency, items: order.items, clientId: order.gaClientId, // captured from tracker.getClientId() at checkout gclid: order.gclid, userData: { email: order.customer.email }, });
if (!result.ads.ok && !('skipped' in result.ads)) reportError(result.ads.error); if (!result.ga4.ok && !('skipped' in result.ga4)) reportError(result.ga4.error);
return new Response();}See also
Section titled “See also”tracker.trackPurchase()— the matching browser-side call.serverTracker.fromContext()— when the envelope already carriesclientId/gclid/userData.- Semantic helpers — overview.