Server-side CAPI is a great thing, but when deduplication with the pixel breaks, you have 40–80% more conversions in Ads Manager than actually flowed to the account. Five of the most common bugs we see in a Care audit.
1. event_id isn't passed from the web to the server
A classic bug: the GTM web container generates event_id = uuid() in the dataLayer, but the server-side request doesn't include it (it didn't get into the payload). Meta only deduplicates if both events have the same event_id — a missing event_id = every conversion counted 2×.
Fix: in GTM web → Meta Pixel tag → Advanced → Event ID → insert {{Event - Event ID}}. In GTM server → Meta CAPI tag → Event ID → the same variable from the incoming request.
2. event_time differs by more than 5 minutes
Meta only deduplicates if the client-side and server-side event_time are within a 5-minute window. If server-side runs in a cron job (a Stripe webhook with a delay), a delay of up to 30 minutes can occur.
Fix: the server-side event_time = the original client-side timestamp from the dataLayer, not time.time() in the server-side handler.
3. A Test Event Code in production
In the Meta Test Events tab you create a test code (TEST12345), forget to remove it from the server-side config, and deploy to production. All production conversions end up in Test Events instead of Live — they never reach Ads attribution.
Fix: the Test Event Code should be an env variable, not hardcoded. Leave it empty in production. The DataNostro dashboard has a separate "Test Event Code" field that is separate from the production setting.
4. Hashing email/phone incorrectly
Meta CAPI wants em = sha256(lowercase(trim(email))). Common mistakes:
- Hashing with preserved punctuation or whitespace (e.g. "[email protected]" → a different hash than "[email protected]")
- Phone without the E.164 format (it must be
+420..., not123456789) - An external ID in plain text (Meta hashes it itself, but the server-side sends it hashed — double hashing breaks the match)
Fix: normalize before anything is sent. DataNostro does this automatically per the Meta CAPI specification.
5. The Pixel sends, the server-side doesn't (and vice versa)
The server-side request fails (timeout, network error), the pixel goes through — Meta gets only 1 hit instead of 2. Meta can't recognize this as a problem, it just has less data.
Fix: in the DataNostro debug console you see the status of every server-side hit. If you have a higher than 2% error rate on Meta CAPI, something's off (typically an expired Access Token or an IP blocked by Meta).
How to debug it
Meta Events Manager → Test Events → send the pixel + CAPI. If you see a green "Deduplicated" badge, it's OK. If you see two separate events of the same type, the event_id doesn't match.
DataNostro Debug Events shows the payload of both paths with a diff highlight.