Why this happens
Deduplication is a strict string comparison Meta runs at intake, not a fuzzy match on "roughly the same purchase." Two implementation mistakes look identical in your own logs but break dedup differently: generating a fresh event_id server-side for what is logically the same action instead of reusing the ID the browser Pixel already created, and sending a different event_name string for the client versus the server call for the same action - a capitalization difference (Purchase versus purchase) is enough to make Meta treat them as two unrelated events.
Both mistakes pass basic validation on both sides. The server call still succeeds, the Pixel call still succeeds, and neither produces an error - the only symptom is a doubled conversion count that looks like real growth until someone reconciles it against actual orders.
Fix it
- Generate exactly one
event_idper real-world event, as early as possible - typically client-side, at the moment the action happens (for examplecrypto.randomUUID()in the browser on the purchase-confirmation page load). - Pass that same ID to the server: a hidden form field submitted with the order, a dataLayer push read by server-side GTM, or a value your backend already has that is unique per event - never regenerate a new ID server-side for the same action.
- Send identical
event_namestrings on both sides, matching Meta's standard event taxonomy exactly (Purchase, notpurchase) unless you are intentionally using a custom event name, in which case use the same custom name on both sides. - If using server-side GTM, confirm the Meta CAPI tag template reads
event_idandevent_namefrom the same variables the client-side Pixel tag reads - a common bug is two separate variables populated by two separate pieces of logic that quietly drift apart over time. - Check timing: send both events without an unnecessary delay. Meta's matching window is generous but not infinite, and a batched or queued server send that goes out hours later can miss it.
How to verify it worked
- In Meta Events Manager's Test Events tool, trigger one real action and confirm exactly one event row appears carrying both a Browser and a Server source tag on that same row - two separate rows for one action means dedup failed.
- Check the Overview tab's deduplication column over a day of real traffic - a healthy setup shows the large majority of matched pairs collapsing into one counted event; a persistent pattern of duplicates confirms the mismatch is systemic, not a one-off test artifact.
- Log the raw payload your server sends before the POST and compare the
event_idandevent_namestrings character-by-character against what the browser Pixel actually sent, visible in the Network tab request tofacebook.com/tr, for the same action.