Somewhere between "I sent the test payload" and "Events Manager shows nothing," CAPI turns into a guessing game. The error messages don't help: Invalid parameter, error code 100, or worse, a clean 200 response and still nothing showing up. I've debugged this on more Shopify and WooCommerce builds than I can count. The causes repeat. This is the decoder.
- Your server sends — POST to graph.facebook.com/DATASET_ID/events
- Auth — Code 190: token revoked, or wrong dataset ID
- Payload — user_data, action_source, event_time required
- Match — 200 OK still matches nobody if hashes differ
- Counted once — Same event_name and event_id dedupe vs Pixel
Why is Meta rejecting my Conversions API events?
Because one of four things is wrong: your credentials, your payload structure, your event fields, or your delivery volume. That's the whole map. Authentication errors bounce the request before Meta reads it. Structural errors (missing user_data, missing action_source, bad event_time) get rejected with error code 100. Event-level problems are sneakier: Meta accepts the event, but it double-counts against your Pixel or matches nobody. Delivery problems (rate limits, oversized batches) fail intermittently.
One thing that trips everyone up: a 200 response with events_received: 1 means Meta received the event. Not that it matched a user or deduplicated correctly. Plenty of "working" CAPI setups ship events that match nothing.
How do I fix access token and dataset ID errors?
Regenerate the token in Events Manager and check the dataset ID in your endpoint URL. The two errors you'll see:
- Error code 190 (invalid OAuth access token). The token was pasted with whitespace, truncated, or revoked. Tokens generated in Events Manager (Settings, Conversions API section) don't expire on a timer, but they die when the system user behind them is removed, someone regenerates the token, or the pixel changes Business Manager ownership. Agency handoffs kill more tokens than anything else.
- "Unsupported post request" or "object with ID does not exist." Wrong dataset ID, or a token with no permission on that dataset. The dataset ID is the long number in your Events Manager URL and must match the ID in your request path:
graph.facebook.com/v21.0/DATASET_ID/events.
Quick sanity check: send one bare-bones event through Meta's Graph API Explorer with the same token and dataset ID. If that works, the problem is your payload.
What does "user_data is required" or "invalid parameter" actually mean?
Your payload is structurally broken, and there are three usual suspects.
1. Empty or missing user_data. Meta requires at least one customer information parameter on every event. No user_data object, or one holding only empty strings, means rejection. For website events, client_user_agent plus client_ip_address is the floor; send hashed email (em), fbp, and fbc whenever you have them.
2. Missing or wrong action_source. action_source is required on every event. For web events it must be website, and web events also need event_source_url. Send action_source: "web" (not a valid value) and you get an invalid parameter error that names neither field.
3. event_time outside the window. Meta accepts event_time up to 7 days in the past. Older: dropped. In the future: rejected.
| SHA-256, after normalizing | Never hashed |
|---|---|
| em: trimmed and lowercased | _fbp browser ID |
| ph: digits with country code only | _fbc from the ad click |
| fn / ln: lowercase, no punctuation | client_ip_address |
| ct / st / zp: lowercase, no spaces | client_user_agent |
| country: two-letter ISO, lowercase |
Why is my hashing failing or matching nobody?
You hashed the raw value instead of the normalized one. Meta requires SHA-256 hashing of customer identifiers, but a hash only matches if both sides hash the same string. SHA-256("John@Gmail.com ") and SHA-256("john@gmail.com") are completely different values. Meta won't reject the event; it just matches nothing and your Event Match Quality tanks.
Normalize first, then hash:
| Field | Normalize before SHA-256 |
|---|---|
em (email) | Trim whitespace, lowercase the whole address |
ph (phone) | Digits only, with country code, no leading zeros, +, spaces, or dashes: 14155551234 |
fn / ln (name) | Lowercase, no punctuation |
ct (city) | Lowercase, no spaces or punctuation: newyork |
st (state) | Two-letter code, lowercase: ny |
zp (zip) | Lowercase, no spaces; first 5 digits for US |
country | Two-letter ISO code, lowercase: us |
The flip side: fbp, fbc, client_ip_address, and client_user_agent go out raw, never hashed. Hash your _fbc cookie value and you've destroyed the strongest match key Meta has for ad-click traffic.
Why are my purchases double-counting against the Pixel?
Deduplication is failing, and it fails for exactly two reasons: the event_name doesn't match, or the event_id is missing on one side. Meta pairs a browser event with a server event only when both carry the same event_name and the same event_id, character for character. Purchase from the Pixel and purchase from your server are two different events, and both count. No event_id on either side means nothing to pair, so a store running Pixel plus CAPI without shared IDs reports roughly double its real purchases.
While you're in there, check value fields: Purchase requires currency (a three-letter ISO code like USD) and value as a plain number. "49.99 USD" in the value field is an invalid parameter error waiting to happen.
- Read the actual API response — Log the error code, message and fbtrace_id instead of firing server events blind
- Retest in Graph API Explorer — Same token and dataset ID, one bare-bones event. If that works, your payload is the problem
- Add a test_event_code — Watch the event land field by field in the Test Events tab, in real time
- Check Diagnostics weekly — It flags missing dedup parameters, invalid hashing and upload delays
- Normalize and hash in one place — A server container generates event_id and hashes once, so the same bug can't come back
How do I debug CAPI errors in Events Manager?
Three tools, in this order: the API response, Test Events, then Diagnostics.
- Read the actual API response. A rejected request returns an error code, a message, and an
fbtrace_id. Log it. Half the teams I audit are firing server events blind. - Use Test Events with a test_event_code. In Events Manager, open the Test Events tab and copy the code (looks like
TEST12345). Add it to your payload astest_event_codeand watch the event appear in real time. It's the only place you see exactly what arrived, field by field. - Check Diagnostics weekly. The Diagnostics tab flags recurring problems: events missing dedup parameters, invalid hashing, upload delays.
For structure questions, Meta's Payload Helper lets you build a sample event and validate the JSON shape before you write server code.
Which errors are specific to server-side GTM and Stape setups?
Mostly field mapping and double hashing. The official Meta CAPI tag for server GTM hashes identifiers for you. Feed it an already-hashed email and Meta receives SHA-256 of a SHA-256 string. No error, no warning, zero matches. Hash in one place only; with the sGTM template, that means sending raw values and letting the tag normalize and hash.
The other classics I find in audits:
- Wrong key mapping. A checkout pushing
customer_emailwhile the tag readsemailsilently ships events with no identifiers. Check the outgoing request in server container preview mode, not just the incoming one. - Empty strings instead of absent fields. A variable resolving to
""can trigger invalid parameter errors or drag match quality. Omit undefined fields; don't send them blank. - event_id not passed through. The web container generates an ID for the Pixel but never sends it with the server-bound event, so dedup breaks. Generate one ID, send it both ways.
- Batching and rate limits. The API accepts up to 1,000 events per request. Bigger batches, or aggressive retry loops, produce intermittent throttling errors.
Do I need first-party server-side tracking to stop these errors for good?
If you want them fixed once instead of repeatedly, yes. Plain-language version for anyone new to the term: right now the Meta Pixel runs in your visitor's browser and reports to Meta directly. Server-side tracking adds a middle layer you own, a Google Tag Manager server container on your own subdomain (like track.yourstore.com), which receives each event first and forwards it to Meta over the Conversions API.
That architecture is exactly why the errors in this article stop recurring. Every event passes through one server you control, so normalization, hashing, event_time, and event_id generation live in one place instead of scattered across theme edits, apps, and checkout scripts. Because events fire from your subdomain, you keep capturing _fbp and _fbc as first-party data with longer cookie lifetime, and you recover conversions that ad blockers and iOS privacy features strip from browser-only setups. More identifiers on more events means higher Event Match Quality, so Meta optimizes on real purchase data instead of a sample of it. That's what actually moves CPAs. Across the audits we run, browser-only setups consistently miss a real slice of conversions.
Frequently asked questions
Why does Meta say events were received but I see no conversions?
A 200 response only confirms delivery, not matching. Usually the identifiers were hashed without normalization or are too thin to match anyone; check Event Match Quality in Events Manager.
How do I fix "Invalid OAuth access token" on the Conversions API?
Generate a fresh token in Events Manager under Settings, Conversions API, and replace it everywhere it's used. Regeneration, system-user deletion, and Business Manager moves all kill old tokens.
How old can event_time be for Meta CAPI?
Up to 7 days in the past, in Unix seconds. Older events are dropped, and future timestamps (usually a milliseconds bug) are rejected.
Do I hash fbp and fbc before sending them?
No. Send fbp, fbc, client_ip_address, and client_user_agent raw. Only customer identifiers like email, phone, and name get SHA-256 hashed, after normalization.
Why aren't my Pixel and CAPI events deduplicating?
The event_name and event_id must match exactly on both events. A missing event_id on either side, or Purchase vs purchase, means both count and your numbers double.
Does the Conversions API replace the Meta Pixel?
No. Meta recommends running both with shared event IDs for deduplication: the Pixel supplies browser signals like fbp and fbc while the server side fills in what browsers block.
- capi
- meta ads
- errors
- debugging

