On Hydrogen, Web Pixels and the Conversions API often fire purchase events with different event IDs, so Meta counts both. On Cyclewear that inflated ROAS from a real 2.9x to a reported 4.2x. How to diagnose it and how we fixed it.
If you run a headless Shopify store on Hydrogen and use Meta's Conversions API, you almost certainly have a deduplication problem. You do not know it yet because your ROAS looks great. The cause is that the browser event and the server event arrive at Meta with different event IDs, so Meta counts both, and the fix takes a day once you know where to look.
This is an Operator Note from our work on Cyclewear, a headless Shopify Hydrogen storefront for a performance cycling brand. The store was reporting a 4.2x ROAS on Meta campaigns. When we dug into the raw data, real ROAS was closer to 2.9x. The gap, a 30 to 40% inflation, was entirely explained by a silent deduplication bug in how Web Pixels and the Conversions API were each firing purchase events.
What is the bug?
Meta's CAPI deduplication matches events by their event_id. When a browser event from the Meta Pixel and a server event from CAPI share the same event_id, Meta deduplicates them and counts one. When they do not match, Meta counts both, doubling reported conversion volume and inflating ROAS.
On a standard Shopify theme the Pixel fires from the browser with a client-side event_id, and CAPI fires from Shopify's backend with the same ID. Deduplication works. On a headless Hydrogen build the flow breaks. Shopify's Web Pixels extension runs in a sandboxed iframe, and the fbq cookie, which holds the browser's click ID (_fbc) and browser ID (_fbp), is often unreadable from inside that sandbox. When the CAPI server event fires it sends a different event_id or missing _fbc data. Meta treats it as a separate event. Every purchase now counts twice.
How do you diagnose it?
- Open Meta Events Manager and check your pixel's Event Matching Quality score for Purchase events
- Check the Deduplication tab; duplicate rates above 5% confirm the problem
- Compare browser event volume with server event volume; server events consistently exceeding browser events by more than 10% is a red flag
- In your Hydrogen app, log the event_id you generate client-side and confirm it matches the ID you send to CAPI server-side
- Check whether _fbc and _fbp cookies are readable from within the Web Pixels sandbox (in Hydrogen they often are not)
How did we fix it on Cyclewear?
The fix took three changes. First, we moved fbq cookie reading outside the Web Pixels sandbox: a lightweight first-party endpoint reads the _fbc and _fbp cookies server-side from the Remix loader context and passes them into the order metadata at checkout. Second, we standardised event_id generation as a deterministic hash of order ID plus timestamp, generated once on the server and passed to both the CAPI call and the Web Pixels event payload. Third, we added a deduplication validation step to our deployment checklist. After every Hydrogen update we run a test purchase and verify in Events Manager that only one Purchase event appears.
After the fix, reported ROAS dropped from 4.2x to 2.9x. That was not a comfortable conversation with the client, and it was a necessary one. The campaigns were performing fine. The problem was the budget allocation decisions being made on the inflated number: the store was under-investing in Google Shopping because Meta "looked so much better".
Why does this matter beyond ROAS?
Inflated ROAS misallocates your whole channel mix. We have seen stores cut Google Shopping spend by 40% because Meta looked better in the dashboard, when true performance was essentially equivalent. If you run headless Shopify and have not audited CAPI deduplication, make it the first item on your technical marketing checklist. The fix takes a day. The compounding cost of making budget decisions on bad data can run for years.
- Shopify Hydrogen
- Meta CAPI
- tracking
- headless