Why this happens
Meta's Conversions API deliberately mixes hashed and unhashed fields in the same user_data object, which is the single most common source of degraded or rejected matching. Hashing an IP address, which should stay in plaintext, or sending a raw unhashed email, which must be hashed, both pass basic JSON validation but fail Meta's matching logic - sometimes with no hard error at all.
event_time goes wrong in three specific, recurring ways: sending milliseconds instead of seconds (off by a factor of 1000, which usually resolves to a date decades away and gets rejected outright), sending the time your backend processed the request instead of the time the user action happened (which silently degrades matching even though the request itself succeeds), or a queued pipeline that delays sending long enough to fall outside the accepted window.
Fix it
- Capture
event_timeat the moment the action happens, not when a backend job later processes it - for a purchase, that is checkout completion time, not whenever an async worker picks the order up from a queue. - Convert to Unix seconds explicitly (
Math.floor(Date.now() / 1000)in JavaScript,int(time.time())in Python) - never pass a millisecond timestamp or an ISO string directly. - For identity fields (em, ph, external_id, fn, ln, ge, db, ct, st, zp, country): lowercase and trim whitespace first, then SHA-256 hash each field individually - Meta will not hash plaintext for you, and a plaintext or malformed hash simply fails to match.
- For
client_ip_addressandclient_user_agent: send the exact raw values captured server-side from the request that corresponds to the user's action - do not hash, truncate, or normalize either field. - Behind a load balancer or reverse proxy, read the real client IP from
X-Forwarded-For(or your provider's equivalent header) rather than the proxy's own address - sending your server's IP asclient_ip_addressquietly degrades match quality without producing an error. - Include
fbpandfbccookie values when available, captured from the browser at the same time as the corresponding Pixel event - they meaningfully help matching and cost nothing to add correctly.
How to verify it worked
- Send one test event through Meta's Conversions API Test Events tool (using a test_event_code) and check the Server Events tab - a correctly formatted
event_timerenders as a normal recent date; a milliseconds-instead-of-seconds bug typically shows as a wildly wrong date or an outright rejection. - In the Pixel's Events Manager diagnostics, check the Parameters breakdown for the event - Meta explicitly flags fields it could not use for matching (for example "Email - Invalid format" or "IP Address - Not provided"), the fastest way to catch a hashing/plaintext mix-up.
- Compare the Event Match Quality score before and after a formatting fix on a controlled test batch of otherwise identical traffic - an improved score confirms the fix changed what Meta could actually use, not just what your own validation accepted.