Server-side conversion tracking for Meta and Google, step by step
The Conversions API and Enhanced Conversions, without the vendor documentation. What each one fixes, what it does not, and the event_id detail that decides whether you double count.
same event_id on the pixel and the API → one conversion, better matching
Server-side conversion APIs recover signal the pixel lost, which makes the platform's optimisation better. They do not make the platform's attribution auditable — the platform still applies its own model to whatever you send.
The single most common implementation bug: no shared event_id, so the pixel event and the API event both count.
Meta’s Conversions API and Google’s Enhanced Conversions do a specific, useful job: they give the platform data the browser could not deliver, so its bidding algorithm optimises against a fuller picture.
They are worth implementing. They are also routinely mis-sold as “server-side attribution,” which they are not.
What they fix and what they do not
Fixed: signal loss. Events blocked by ad blockers, lost to ATT, or dropped when the tab closed now reach the platform from your backend.
Not fixed:
- View-through credit. Still on, still inflating.
- Cross-platform double claiming. Meta and Google still both claim the same sale.
- Auditability. You still cannot inspect why a specific conversion is credited where it is.
- The 7-day click ceiling. Meta’s window is unchanged.
So: better optimisation, same attribution. Both facts matter.
Meta Conversions API
1. Get a token. Events Manager → your dataset → Settings → Conversions API → generate an access token.
2. Send the event from your backend, mirroring what the pixel sends:
POST https://graph.facebook.com/v21.0/{PIXEL_ID}/events
{
"data": [{
"event_name": "Purchase",
"event_time": 1754239281,
"event_id": "order_10482", # ← THE de-duplication key
"action_source": "website",
"event_source_url": "https://yourstore.com/checkout/success",
"user_data": {
"em": ["<sha256 of lowercased, trimmed email>"],
"ph": ["<sha256 of E.164 phone>"],
"client_ip_address": "203.0.113.4",
"client_user_agent": "Mozilla/5.0…",
"fbc": "fb.1.1754239281.IwAR…", # from the fbclid on landing
"fbp": "fb.1.1754239281.1098115757"
},
"custom_data": { "currency": "USD", "value": 129.00 }
}],
"access_token": "…"
}
3. Send the same event_id from the pixel:
fbq("track", "Purchase", { value: 129.00, currency: "USD" }, { eventID: "order_10482" })
If those two strings differ by so much as a prefix, Meta counts two purchases. This is the single most common CAPI implementation bug, and it presents as a sudden, delightful jump in reported conversions.
4. Capture fbc properly. It is derived from the fbclid query parameter on the landing URL. Capture it server-side, on the first request — client-side capture happens after consent banners and redirects have had a chance to strip it.
5. Verify in Events Manager. The Event Deduplication panel will tell you whether your pairs are actually merging. Check it rather than assuming.
Google Enhanced Conversions
Same shape, different plumbing. Send hashed first-party data alongside the conversion so Google can match users it could not observe.
- Via gtag — set
user_datawith hashed email and address fields on the conversion event. - Via the API — upload conversions with
user_identifiers, using thegclidwhere you have it.
The gclid has the same fragility as fbc: it arrives as a query parameter and gets stripped by redirect chains, link shorteners and consent flows. Capture it on the first server-side request or you will lose a meaningful share of it.
Hashing, done correctly
email → lowercase → trim → SHA-256 hex
phone → E.164 (+15551234567) → SHA-256 hex
Get any step wrong and the match rate quietly collapses. Most SDKs handle it; verify with a known test value rather than trusting the docs.
What to expect afterwards
Reported conversions usually go up. Part of that is real recovery of blocked events; part of it is better matching within the platform’s own model.
Do not treat the increase as new revenue. Check the new number against your payment processor. If reported conversions jumped 40% and your bank did not move, you are either double counting (check event_id) or looking at improved modelling rather than improved reality.
The ghost-conversions checker does this comparison in two minutes.
The part that is still yours to build
After all of this, the platforms have better data and your reporting is still theirs. If you want a number you can reconcile and audit, that is a separate system: a durable first-party id, conversions confirmed by your own server, and a journey you can inspect.
Do both. Feed the platforms well so they optimise well, and keep your own ledger for the decisions.
Frequently asked questions
Will the Conversions API increase my reported conversions?
Usually yes, and some of that is genuine recovery of blocked events while some is just better matching within the platform's own model. Do not treat the increase as new revenue — check it against your payment processor.
What is event_id for?
De-duplication. The pixel and the server both report the same purchase; the shared event_id tells Meta they are one event. Without it you double count.
Do I need to hash the customer data?
Yes. Email and phone must be SHA-256 hashed, lowercased and trimmed first. Most SDKs do this for you — verify rather than assume, because an unhashed field is both a match failure and a data-handling problem.
Does this replace my own attribution?
No. It improves what the platform sees and optimises on. Your own first-party ledger is a separate thing, and the one you should reconcile revenue against.
What about Google's Enhanced Conversions?
Same shape: hashed first-party data sent back to improve matching. Same benefit, same limitation — it improves Google's model rather than giving you an auditable one.
Check whether you are double counting
Add up what every platform claims against what you actually banked. A sudden jump after a CAPI launch is usually a missing event_id.
Stop guessing which ad made the sale.
Flowsk Signals stitches the anonymous click to the email to the purchase — first-party, server-side, de-duplicated. One snippet, $29/mo, and every conversion comes with a receipt you can inspect.