All resources
GuideUpdated September 8, 2026

Event deduplication: why the same purchase can count twice

Server-side tracking is usually deployed alongside the browser pixel you already have, not instead of it. Run both at once and the same purchase can reach Meta, Google or TikTok twice: once from the browser, once from your server. Without deduplication, that inflates conversions, skews ROAS in a direction that looks good until the numbers stop reconciling with actual revenue, and quietly erodes trust in every report built on top of it.

Why the double-count happens

The browser pixel fires the moment a shopper's checkout page loads, driven entirely by client-side JavaScript. A server-side event for the same purchase is sent independently, from your backend or the order webhook, with no direct link back to what the browser already reported. From the ad platform's point of view, two unrelated events arrived describing what looks like the same conversion, and by default it counts both.

The fix is a shared, deterministic event ID

The standard solution, and the one Meta, Google and TikTok all support, is to attach the same event ID to both the browser event and the server event for a single real-world action. The platform matches the two by that ID and counts the pair as one conversion, keeping whichever event carries richer data (usually the server-side one, since it can include the hashed identifiers a browser doesn't have access to).

The event ID has to be deterministic, generated the same way on both sides from information both the browser and the server independently have, such as the order ID. A random ID generated separately by each side defeats the purpose: there would be nothing to match.

Where it can still go wrong

  • Retries. Payment webhooks are typically delivered at-least-once, so the same order can arrive at your server two or three times if a delivery is retried. If each retry is treated as a new order, the server side alone can double-count before deduplication with the browser even enters the picture.
  • Race conditions. Two events for the same order processed concurrently can both check "have I seen this order?", both get "no", and both proceed. The check and the write need to happen in one atomic step, not two, or concurrency reopens the gap.
  • Mismatched IDs. If the browser and server pick the event ID independently, based on different data (a session id on one side, an order id on the other), they will not match and both events get counted.

What correct deduplication actually requires

  1. One deterministic event ID per real-world event, derivable identically by the browser and the server.
  2. Idempotent order handling on the server: an order already recorded is a no-op, checked and written within the same atomic operation, not two.
  3. The same ID passed through to every destination that receives both a browser and a server event for that action.
Deduplication is not a reporting nicety. Get it wrong and every downstream number, ROAS, LTV, cohort revenue, is built on inflated conversions. Get it right once, at the event layer, and every report built on top of it is correct by construction.

Frequently asked questions

Why does the same purchase get counted twice?

The browser pixel and a server-side event for the same purchase are sent independently, with no link back to each other. An ad platform receiving both, with no way to recognize them as the same conversion, counts them as two.

How does a shared event ID fix deduplication?

When the browser event and the server event for one purchase carry the same, deterministically generated event ID, the ad platform matches them and counts the pair once, keeping the richer of the two. The ID has to be derived the same way on both sides, typically from the order ID, or there is nothing to match.

Can deduplication fail even with a shared event ID?

Yes, if the server side itself double-counts first: a payment webhook retried by the sender, or two concurrent requests for the same order, can each be recorded as a new purchase before deduplication with the browser ever applies. That requires idempotent, atomic order handling on the server, not just a shared ID.

Own your customer data, end to end.

SetRoasFlow unifies every visitor into one first-party profile and feeds it to every channel you run. Server-side, on your own domain.

Sign up now