📢 Marketing
Meta Conversions API and Pixel Deduplication Spec: event_id Matching, user_data Hashing, and Test Events Checks for an Ecommerce Store
Write the implementation spec a developer needs to send the same purchase and lead events from the Meta Pixel and the Conversions API without double counting: event_name and event_id pairing, which user_data fields to hash and which to send raw, timing limits, consent gating, and a Test Events checklist.
0Reviews
Prompt
Act as a marketing measurement engineer who implements Meta Pixel plus Conversions API setups for ecommerce brands and writes the spec the developer builds from. Double counted purchases and broken deduplication are the most common failure you fix, so the spec is about matching, not about adding more events. Inputs: - Platform and how the browser Pixel is installed today (native integration, Google Tag Manager, hard coded): [PixelSetup] - Server side path planned (platform integration, Conversions API Gateway, server GTM, custom backend): [ServerPath] - Events to send and where each one fires (page, button, order confirmation, webhook): [EventList] - Customer data available at each event (email, phone, name, address, customer id, IP and user agent): [CustomerData] - Consent setup (cookie banner tool, regions where consent is required, what happens on decline): [ConsentSetup] - Known symptoms in Events Manager (duplicates, low match quality, events received late): [Symptoms] - Output format: [Format] - Language: [Lang] Generate: 1. Event matrix from EventList: event_name exactly as sent by both sources, the trigger on each side, and the event_id source. Explain that Meta deduplicates a browser and server event when event_name matches and the Pixel eventID equals the server event_id, so both must come from the same value (order id, lead id, or a generated id passed to the server). 2. Browser side code for PixelSetup: the fbq track call with the eventID option in the fourth argument, and how the same id reaches the server. 3. Server payload for ServerPath: event_name, event_time in Unix seconds (not older than seven days), event_id, action_source website, event_source_url, user_data, and custom_data with value, currency, and content_ids. Mark which user_data keys are SHA-256 hashed after normalization (em, ph, fn, ln, ct, st, zp, country, external_id) and which are sent unhashed (client_ip_address, client_user_agent, fbp, fbc). 4. Normalization rules before hashing for each CustomerData field (lowercase and trim email, phone with country code and digits only, two letter lowercase country). 5. Consent gating from ConsentSetup: which events are sent, held, or dropped on decline, on both sides, so the server does not send what the browser was refused. 6. Test plan with the Test Events tool and a test_event_code, plus a diagnosis of Symptoms with the likely cause for each. Constraints: - Do not promise match quality scores or attribution lift. - Never send raw email or phone; never hash IP, user agent, fbp, or fbc. - Mark field names to confirm against the current Meta developer docs. - No em dashes.
Instructions
Replace every [bracket] with your details before running. Works on ChatGPT, Claude, and Gemini.
Generated Output
This image was generated using the prompt above.

Examples
Example Input
PixelSetup: WooCommerce store, Pixel hard coded in the theme header plus a Purchase event on the thank you page template ServerPath: custom PHP hook on woocommerce_payment_complete calling the Conversions API EventList: ViewContent on product pages, AddToCart button, InitiateCheckout, Purchase on thank you page and on payment complete webhook CustomerData: email and phone at checkout, billing name and address, WooCommerce customer id for logged in buyers, IP and user agent on the server request ConsentSetup: Complianz banner, consent required for EU visitors, marketing cookies declined means no Pixel Symptoms: Purchases in Events Manager are about double the orders in WooCommerce Format: matrix, code, payload, then tests Lang: English
Example Output
1. Event matrix
| event_name | Browser trigger | Server trigger | event_id |
| ViewContent | product page load | none for now | n/a |
| AddToCart | add to cart button | none for now | n/a |
| InitiateCheckout | checkout page load | none for now | n/a |
| Purchase | thank you page | woocommerce_payment_complete | order_{order_id} |
Purchase is the only dual source event, so it is the only one that needs deduplication. Both sides use order_{order_id}; the thank you page reads the same order id WooCommerce already has.
2. Browser
fbq('track', 'Purchase', {value: 84.00, currency: 'EUR', content_ids: ['SKU-114'], content_type: 'product'}, {eventID: 'order_5521'});
The template prints the order id into eventID. Guard it so a refresh of the thank you page does not fire twice (store a flag on the order once it fired).
3. Server payload (PHP, on payment complete)
{"data": [{"event_name": "Purchase", "event_time": 1791220000, "event_id": "order_5521", "action_source": "website",
"event_source_url": "https://shop.example.com/checkout/order-received/5521/",
"user_data": {"em": ["<sha256>"], "ph": ["<sha256>"], "fn": ["<sha256>"], "ln": ["<sha256>"], "ct": ["<sha256>"], "zp": ["<sha256>"], "country": ["<sha256>"],
"external_id": ["<sha256 of customer id>"], "client_ip_address": "<raw>", "client_user_agent": "<raw>", "fbp": "<_fbp cookie>", "fbc": "<_fbc cookie if present>"},
"custom_data": {"value": 84.00, "currency": "EUR", "content_ids": ["SKU-114"], "content_type": "product"}}]}
IP and user agent: the payment webhook may come from the payment provider, not the shopper. Save the shopper's IP, user agent, _fbp, and _fbc on the order at checkout and send those, not the webhook request's values.
4. Normalization
- Email: trim, lowercase, then hash.
- Phone: digits only with country code (a German number 0151 2345678 becomes 491512345678), then hash.
- Names and city: lowercase, trim, remove punctuation, then hash.
- Zip: lowercase, no spaces; country: two letter lowercase (de), then hash.
5. Consent
- EU visitor declines marketing cookies: no Pixel, and the PHP hook checks the consent value saved on the order and skips the Conversions API call too.
- Accepted or non EU: both fire, deduplicated by order_{order_id}.
- Save the consent state on the order at checkout so the server knows what the browser was told.
6. Tests and Symptoms
- Copy the test_event_code from Test Events and add it to the server payload temporarily. Place a test order, then confirm one Purchase shows both Browser and Server with Deduplicated status.
- Symptom, Purchases about double orders: most likely the current Pixel Purchase has no eventID, or the server will send a different id. Also check whether a plugin already sends server events alongside your hook.
- Remove test_event_code before going live. Confirm field names against the current Meta developer docs.