Integration · Meta Conversions API
Purchase signals Meta can match, dedupe, and learn from
RoasProof delivers enriched server events to the Conversions API. Each event is deduplicated against your existing pixel, carries every identifier Meta accepts for matching, and stays queued until Meta confirms receipt.
The Meta Conversions API (CAPI) is Meta's server-to-server channel for conversion events: instead of the browser pixel firing, your server sends the event directly to Meta. RoasProof builds each event with hashed customer identifiers, recovered fbc/fbp click identifiers and a deterministic event_id, so Meta can match it to a real account and deduplicate it against your pixel.
What a RoasProof server event looks like
A single Purchase event, exactly as it leaves our servers for the Graph API. Every field below exists to make the conversion easier for Meta to match and impossible to double count.
POST https://graph.facebook.com/v24.0/{pixel_id}/events
{
"data": [{
"event_name": "Purchase",
"event_time": 1781430082,
"event_id": "ord_84213",
"action_source": "website",
"event_source_url": "https://store.example/checkout/thank-you",
"user_data": {
"em": ["9f3d0c52ab…"],
"ph": ["1b6e21c84f…"],
"external_id": ["7ac8044190…"],
"fbp": "fb.1.1780828882512.83179243",
"fbc": "fb.1.1780828882512.IwZXh0bgNhZW0…",
"client_ip_address": "203.0.113.24",
"client_user_agent": "Mozilla/5.0 (iPhone; …)"
},
"custom_data": {
"currency": "EUR",
"value": 184.50,
"order_id": "84213",
"content_type": "product",
"content_ids": ["SKU-2481", "SKU-1177"]
}
}],
"test_event_code": "TEST61045"
}event_id matches your pixel
The browser event and this server event both carry ord_84213 (derived from the order ID), so Meta counts the purchase exactly once no matter which copy arrives first.
fbc recovered, not hoped for
If the _fbc cookie is missing or was purged, we synthesize the parameter as fb.1.{timestamp}.{fbclid} from the fbclid stored on landing, so the click still gets credit.
Hashed identifiers, never raw PII
em and ph are normalized (trimmed, lowercased, phone in E.164) and SHA-256 hashed on our servers before the request is sent. Meta only ever sees the hashes.
Verified before you go live
During setup, events flow through Meta's Test Events tool with a test_event_code, so you can watch them arrive, match, and dedupe before switching to production.
One purchase, one count: pixel and server together
You keep your pixel. We coordinate with it. Meta pairs the two copies of each event by event_name + event_id and keeps a single one.
Deterministic IDs from your order IDs
The event ID is a stable function of the order, not a random string. Replays, retries, and the browser copy all resolve to the same ID. Dedup never depends on timing.
The script hands the ID to your pixel
At fire time, our on-page script passes the same eventID to the browser pixel that the server event will carry. Both copies reach Meta within the dedup window as a matched pair.
The redundant copy is discarded, the coverage stays
Where the pixel still works, Meta gets two copies and keeps one. Where it was blocked (iOS, ad blockers, closed tabs), the server event is the only copy, and the conversion is no longer lost.
Same event_name + event_id within the dedup window: Meta drops the duplicate and keeps the richer event.
Every matching key Meta accepts, on every event
EMQ scores how well Meta can connect your events to real accounts. More matched conversions mean better attribution and a smarter optimization loop, so we populate user_data as completely as your data allows.
Illustrative zones, not measured data. Your actual EMQ depends on which identifiers your events carry and what consent allows. Meta scores each event type in Events Manager.
| parameter | what it is | where we get it |
|---|---|---|
| em | SHA-256 of the normalized email address | Order or signup email, trimmed and lowercased before hashing |
| ph | SHA-256 of the phone number in E.164 format | Checkout or lead-form phone, normalized to international format |
| external_id | SHA-256 of a stable first-party visitor ID | Set on the first visit and reused across sessions and devices where identity resolves |
| fbp | Meta browser ID cookie | Read on-site and stored with the session, so it survives to conversion time |
| fbc | Meta click ID cookie | Read from _fbc when present; otherwise synthesized from the fbclid we stored on landing |
| client_ip / client_ua | IP address and user agent of the buyer | Recorded with the session that produced the conversion, not our server's |
The dashboard shows the share of events carrying each key per event type, so when EMQ dips you see exactly which identifier went missing instead of reverse-engineering it from Events Manager. New to the metric? Start with the Event Match Quality glossary entry, then the step-by-step EMQ guide on the blog.
Store-and-forward, with receipts
Conversions API requests fail for boring reasons: expired tokens, rate limits, brief outages. None of them should cost you a conversion.
action_source, set honestly
Meta requires every event to declare where it happened. Website conversions go out as website, offline and CRM-sourced events are marked accordingly. We don't apply blanket defaults that would misrepresent your data.
Queued, retried, never dropped
Events are persisted before the first delivery attempt and retried with exponential backoff on errors and rate limits. Events that exhaust retries are parked for review and one-click replay.
Per-event delivery status
Every event shows the exact payload sent and Meta's response, including trace IDs and error codes, so a failed Purchase is a diagnosable fact rather than a mystery in next week's numbers.
Ready to raise your Event Match Quality?
Connect a pixel ID and an access token, send a test event, and watch the dedup work in Meta's Test Events tool before anything goes to production.