Skip to content

trackbridge-conversions

The conversion-wiring skill. Implements the dual-send pattern: same call shape on browser and server, same transactionId, click identifiers forwarded across the boundary. Refuses anti-patterns (per-side UUID generation, pre-hashing, single-side-only fires) when it sees them.

name: trackbridge-conversions
description: Use when firing a Trackbridge conversion event (purchase, signup, lead) — placing trackConversion calls in client checkout-success handlers and the corresponding server webhooks or order-confirmation handlers, including click-identifier forwarding and dedup via shared transactionId.
  • “Wire up purchase conversions”
  • “Add a trackConversion call to my Stripe webhook”
  • “Fire a conversion when this signup form submits”
  • “Forward gclid to my server”
  • Any mention of trackConversion, dual-send, dedup, or the click-identifier forwarding pattern
  • Both trackers already created via trackbridge-setuplib/tracker.client.ts and lib/tracker.server.ts exist.
  • Server-side credentials filled in via trackbridge-ads-oauthGA4_API_SECRET, GOOGLE_* env vars are populated and the conversionActions map has at least one entry.
  • An order/transaction object with a stable ID available on both client and server.
  • For click-identifier forwarding: a way for the client to POST to the server before the conversion fires (typically the same request that creates the order).

No new files created. Edits:

  • The browser-side checkout-success handler (success page, post-payment effect, redirect handler — wherever the user lands after payment).
  • The server-side conversion handler (webhook handler, order-confirmation API route, server action).
  • Your order schema, only if you confirm you want click-ID forwarding and the columns aren’t there yet (gclid, gbraid, wbraid as nullable text columns).
  • Where to place the browser-side call (path or component).
  • Where to place the server-side call (webhook handler, API route, server action — full file path).
  • Whether you need click-identifier forwarding (defaults: yes, if you don’t already have it).
  • The conversion label (must exist in the conversionActions map; the skill checks before generating the call).
  • Set up Google Ads API secrets. See trackbridge-ads-oauth.
  • Wire a CMP. See trackbridge-consent.
  • Author webhook authentication or payload parsing. The skill assumes verifyStripeSignature (or your equivalent) already exists.
  • Generate a fresh transactionId per call. It refuses; the dual-send pattern requires a stable ID you control.
  1. Different transactionIds per side. If you ask for Date.now() or crypto.randomUUID() on either side, the skill stops and explains: that defeats dedup. The skill insists on order.id (or equivalent stable string).
  2. Pre-hashing identity fields. If you try to pass already-hashed email or phone, the skill explains the SDK does this — and your hash will diverge from Google’s expectations.
  3. Single-side-only fires. The skill always wires both sides. If you only have one (e.g., no server endpoint for the conversion), it stops and recommends adding one before continuing — single-side defeats the SDK’s purpose.
  4. tracker.server imported from a client component. The skill watches for the server tracker leaking into client code and refuses to add the import there; instead it routes through an API endpoint.

Browser side (in your success-page component or post-payment effect):

import { tracker } from '@/lib/tracker.client';
await tracker.trackConversion({
label: 'purchase',
value: order.total,
currency: 'USD',
transactionId: order.id,
userData: {
email: order.customer.email,
phone: order.customer.phone,
firstName: order.customer.firstName,
lastName: order.customer.lastName,
address: {
street: order.billingAddress.street,
city: order.billingAddress.city,
region: order.billingAddress.region,
postalCode: order.billingAddress.postalCode,
country: order.billingAddress.country,
},
},
});

Server side (in your webhook handler):

import 'server-only';
import { serverTracker } from '@/lib/tracker.server';
await serverTracker.trackConversion({
label: 'purchase',
value: order.total,
currency: 'USD',
transactionId: order.id,
gclid: order.gclid,
gbraid: order.gbraid,
wbraid: order.wbraid,
userData: {
/* same shape as browser */
},
});

Plus, if click-ID forwarding wasn’t already in place: edits to your checkout API to capture and store the click identifiers from the client.

You: Wire up purchase conversions for my Stripe checkout.
Claude (router → conversions): I see you have lib/tracker.client.ts
and lib/tracker.server.ts already (from trackbridge-setup), and
GOOGLE_ADS_REFRESH_TOKEN is filled in (from trackbridge-ads-oauth).
Two questions:
1. Where's the browser-side success page? (e.g., app/checkout/
success/page.tsx)
2. Where's the Stripe webhook handler? (e.g., app/api/webhooks/
stripe/route.ts)
You: app/(checkout)/success/page.tsx and app/api/webhooks/stripe/
route.ts.
Claude: Got it. Do you currently store gclid/gbraid/wbraid on the
order? If not, I'll add click-ID forwarding too — three nullable text
columns plus capture in your checkout API.
You: Don't have it.
Claude: Adding the migration and the capture path:
- lib/db/schema.ts: + gclid, gbraid, wbraid columns on orders
- app/api/checkout/route.ts: capture clickIds from request body
- app/checkout/page.tsx: client reads tracker.getClickIdentifiers()
and forwards on submit
Then the conversions:
- app/(checkout)/success/page.tsx: tracker.trackConversion(...) in
a useEffect
- app/api/webhooks/stripe/route.ts: serverTracker.trackConversion
(...) on checkout.session.completed, gated on conversionFiredAt
for idempotency
The transactionId on both sides is order.id. Same value, dedup works.