vendor/meta-conversions-api · vendor documented
Meta Conversions API wants seconds, not Date.now()
A Purchase posts to the Graph API events edge and returns 200. Events Manager stays empty. The usual suspect is the access token. The usual bug is the clock.
event_time is a Unix timestamp in seconds, ten digits, and at most seven days old when it arrives. Date.now() is milliseconds. The event lands far in the future, outside the window, and never attributes.
event_time is seconds, not milliseconds
Send Math.floor(Date.now() / 1000). The same Date.now() is the correct unit on LinkedIn and Amplitude. Copying a payload between vendors without converting the clock is a silent miss on one of them.
Hash the email. Do not hash the IP.
em, ph, name, city, and the rest of the customer information parameters are SHA-256 hex digests. client_ip_address and client_user_agent are sent in the clear. Hashing those two makes the event unmatchable.
Rule: vendor.meta-conversions-api.body.hashed_plaintext_field
Purchase needs value and currency
A payload that only names the product is a legal JSON object and an incomplete conversion. Currency is an ISO 4217 code (USD), not a dollar sign.
Rule: vendor.meta-conversions-api.body.purchase_requires_value_and_currency
What this pack matches
Rules
Codes are stable. A finding in CI, MCP, or the playground lands on the same id.
| Field | Required | What it checks | Rule ids | Source |
|---|---|---|---|---|
access_token |
required | The events edge authenticates with an access token on the request. Fix: Send a system user access token, and keep it server-side. | vendor.meta-conversions-api.param.access_token.missingvendor.meta-conversions-api.param.access_token.empty |
docs |
test_event_code |
optional | It routes events to the Events Manager test tool instead of production reporting, so it should not appear on live traffic. Fix: Drop `test_event_code` outside of testing. | vendor.meta-conversions-api.param.test_event_code.empty |
docs |
pii.unhashed_email |
required | A query parameter carries what looks like a raw email address. Conversions API customer information must be normalized and SHA-256 hashed. Fix: Hash the value with SHA-256 and send it in the event payload rather than on the URL. | vendor.meta-conversions-api.pii.unhashed_email |
docs |
testing.test_event_code_present |
recommended | The request carries `test_event_code`, which routes events to the Events Manager test tool instead of production reporting. Fix: Drop `test_event_code` before this runs against live traffic. | vendor.meta-conversions-api.testing.test_event_code_present |
docs |
event_name |
required | It is the standard or custom event name, and it has to match the browser event for deduplication to work. Fix: Set `event_name` to the standard event, such as `Purchase`, or to your custom event name. | vendor.meta-conversions-api.body.event_name.missingvendor.meta-conversions-api.body.event_name.empty |
docs |
event_time |
required | Meta documents it as a Unix timestamp in seconds, and it can be up to 7 days before the event is sent. A 13-digit value is milliseconds and lands far in the future. Fix: Send seconds, not milliseconds: divide a JavaScript `Date.now()` by 1000 and floor it. | vendor.meta-conversions-api.body.event_time.missingvendor.meta-conversions-api.body.event_time.emptyvendor.meta-conversions-api.body.event_time.invalid |
docs |
action_source |
required | It tells Meta where the conversion happened, and Meta accepts a fixed set of values. Fix: Use one of the documented values; website conversions are `website`. | vendor.meta-conversions-api.body.action_source.missingvendor.meta-conversions-api.body.action_source.emptyvendor.meta-conversions-api.body.action_source.invalid |
docs |
user_data |
required | Customer information is required on every server event, and it is what Meta matches the conversion to a person with. Fix: Add a `user_data` object with at least one normalized, hashed identifier. | vendor.meta-conversions-api.body.user_data.missing |
docs |
event_id |
recommended | Meta recommends it for deduplication: the browser pixel and the server event have to carry the same `event_id` and `event_name` to be counted once. Fix: Generate one id per conversion and send the same value from the pixel and the Conversions API. | vendor.meta-conversions-api.body.event_id.missingvendor.meta-conversions-api.body.event_id.empty |
docs |
event_source_url |
optional | It is the page URL where the conversion happened, and Meta requires it for website events. Fix: Send the full URL of the page, including the scheme. | vendor.meta-conversions-api.body.event_source_url.emptyvendor.meta-conversions-api.body.event_source_url.invalid |
docs |
opt_out |
optional | When true, the event is used for attribution only and not for ads delivery optimization. | docs | |
data_processing_options[] |
optional | Meta accepts `LDU` for Limited Data Use, or an empty array. Fix: Send `["LDU"]` to enable Limited Data Use, or `[]` to leave it off. | vendor.meta-conversions-api.body.data_processing_options[].emptyvendor.meta-conversions-api.body.data_processing_options[].invalid |
docs |
data_processing_options_country |
optional | It is 1 for the United States, or 0 to let Meta geolocate. | vendor.meta-conversions-api.body.data_processing_options_country.emptyvendor.meta-conversions-api.body.data_processing_options_country.invalid |
docs |
data_processing_options_state |
optional | It is 1000 for California, or 0 to let Meta geolocate. | vendor.meta-conversions-api.body.data_processing_options_state.emptyvendor.meta-conversions-api.body.data_processing_options_state.invalid |
docs |
custom_data.value |
optional | Meta requires it for purchase events and for any event that uses value optimization. Fix: Send the monetary value as a number, without a currency symbol. | docs | |
custom_data.currency |
optional | Meta requires a valid ISO 4217 three-digit currency code. Fix: Use the three-letter code, such as `USD`. | vendor.meta-conversions-api.body.custom_data.currency.emptyvendor.meta-conversions-api.body.custom_data.currency.invalid |
docs |
user_data.em |
optional | Email must be trimmed, lowercased, and SHA-256 hashed before it is sent. Fix: Normalize the address, hash it with SHA-256, and send the hex digest. | vendor.meta-conversions-api.body.user_data.em.emptyvendor.meta-conversions-api.body.user_data.em.invalid |
docs |
user_data.em[] |
optional | Every email in the list must be normalized and SHA-256 hashed. Fix: Normalize each address, hash it with SHA-256, and send the hex digests. | vendor.meta-conversions-api.body.user_data.em[].emptyvendor.meta-conversions-api.body.user_data.em[].invalid |
docs |
user_data.ph |
optional | Phone numbers must have symbols and letters removed, carry the country code, and be SHA-256 hashed. Fix: Normalize the number to digits with the country code, hash it with SHA-256, and send the hex digest. | vendor.meta-conversions-api.body.user_data.ph.emptyvendor.meta-conversions-api.body.user_data.ph.invalid |
docs |
user_data.ph[] |
optional | Every phone number in the list must be normalized and SHA-256 hashed. Fix: Normalize each number to digits with the country code and hash it with SHA-256. | vendor.meta-conversions-api.body.user_data.ph[].emptyvendor.meta-conversions-api.body.user_data.ph[].invalid |
docs |
user_data.fn |
optional | First name must be lowercased and SHA-256 hashed. | vendor.meta-conversions-api.body.user_data.fn.emptyvendor.meta-conversions-api.body.user_data.fn.invalid |
docs |
user_data.ln |
optional | Last name must be lowercased and SHA-256 hashed. | vendor.meta-conversions-api.body.user_data.ln.emptyvendor.meta-conversions-api.body.user_data.ln.invalid |
docs |
user_data.ge |
optional | Gender is sent as a lowercase initial and SHA-256 hashed. | vendor.meta-conversions-api.body.user_data.ge.emptyvendor.meta-conversions-api.body.user_data.ge.invalid |
docs |
user_data.db |
optional | Date of birth is normalized to YYYYMMDD and SHA-256 hashed. | vendor.meta-conversions-api.body.user_data.db.emptyvendor.meta-conversions-api.body.user_data.db.invalid |
docs |
user_data.ct |
optional | City is lowercased with punctuation and spaces removed, then SHA-256 hashed. | vendor.meta-conversions-api.body.user_data.ct.emptyvendor.meta-conversions-api.body.user_data.ct.invalid |
docs |
user_data.st |
optional | State is the lowercase two-character code, SHA-256 hashed. | vendor.meta-conversions-api.body.user_data.st.emptyvendor.meta-conversions-api.body.user_data.st.invalid |
docs |
user_data.zp |
optional | Zip is lowercased without spaces or dashes, then SHA-256 hashed. | vendor.meta-conversions-api.body.user_data.zp.emptyvendor.meta-conversions-api.body.user_data.zp.invalid |
docs |
user_data.country |
optional | Country is the lowercase ISO 3166-1 alpha-2 code, SHA-256 hashed. Fix: Hash the two-letter code rather than sending it in the clear. | vendor.meta-conversions-api.body.user_data.country.emptyvendor.meta-conversions-api.body.user_data.country.invalid |
docs |
user_data.external_id |
optional | Meta recommends hashing the advertiser's own identifier with SHA-256. | vendor.meta-conversions-api.body.user_data.external_id.emptyvendor.meta-conversions-api.body.user_data.external_id.invalid |
docs |
user_data.client_ip_address |
optional | It is sent unhashed, and it must be the real IPv4 or IPv6 address of the browser. Fix: Send the client address as-is; do not hash it and do not send your server's address. | vendor.meta-conversions-api.body.user_data.client_ip_address.empty |
docs |
user_data.client_user_agent |
optional | It is sent unhashed, and it must be the browser's user agent rather than your HTTP client's. Fix: Forward the browser `User-Agent` header from the original request. | vendor.meta-conversions-api.body.user_data.client_user_agent.empty |
docs |
user_data.fbc |
optional | Meta documents the click id cookie as `fb.${subdomain_index}.${creation_time}.${fbclid}`. Fix: Send the `_fbc` cookie verbatim, or rebuild it in the documented format. | vendor.meta-conversions-api.body.user_data.fbc.emptyvendor.meta-conversions-api.body.user_data.fbc.invalid |
docs |
user_data.fbp |
optional | Meta documents the browser id cookie as `fb.${subdomain_index}.${creation_time}.${random_number}`. Fix: Send the `_fbp` cookie verbatim. | vendor.meta-conversions-api.body.user_data.fbp.emptyvendor.meta-conversions-api.body.user_data.fbp.invalid |
docs |
user_data.lead_id |
optional | It is the numeric id Meta generated for a Lead Ads lead. | vendor.meta-conversions-api.body.user_data.lead_id.emptyvendor.meta-conversions-api.body.user_data.lead_id.invalid |
docs |
body.purchase_requires_value_and_currency |
required | A `Purchase` event is missing `custom_data.value` or `custom_data.currency`. Meta documents both as required for purchase events. Fix: Add `custom_data.value` as a number and `custom_data.currency` as an ISO 4217 code. | vendor.meta-conversions-api.body.purchase_requires_value_and_currency |
docs |
body.website_requires_source_url |
required | The event is marked `action_source: website` but carries no `event_source_url`, which Meta requires for website events. Fix: Send the page URL the conversion happened on. | vendor.meta-conversions-api.body.website_requires_source_url |
docs |
body.ldu_requires_country |
required | Limited Data Use is enabled but `data_processing_options_country` is missing. Fix: Send `data_processing_options_country` as 1 for the United States, or 0 to let Meta geolocate. | vendor.meta-conversions-api.body.ldu_requires_country |
docs |
body.unhashed_email |
required | A field carries what looks like a raw email address. Meta requires customer information to be normalized and SHA-256 hashed before it is sent. Fix: Trim the address, lowercase it, hash it with SHA-256, and send the hex digest. | vendor.meta-conversions-api.body.unhashed_email |
docs |
body.hashed_plaintext_field |
required | This field looks like a SHA-256 digest, but Meta documents it as one of the parameters that is sent unhashed. Fix: Send the raw value. Hashing it makes the event unmatchable. | vendor.meta-conversions-api.body.hashed_plaintext_field |
docs |
Validate a payload
pixellint validate json @payload.json --rulepack vendor/meta-conversions-api
Or paste it into the playground. Same engine, in the browser, nothing sent anywhere.
cargo install pixellint
·
npm install pixellint