vendor/openai-conversions-api · vendor documented
OpenAI CAPI timestamps are milliseconds
The server hop is /v1/events, not /v1/sdk/events. timestamp_ms is thirteen digits. A ten-digit Unix second is invalid. action_source=web also needs source_url. data.type must match the event. contents is not available on customer_action.
timestamp_ms is 13 digits
Math.floor(Date.now() / 1000) is the Meta clock. OpenAI wants milliseconds. Sending seconds makes timestamp_ms too short and the event does not attribute.
Rule: vendor.openai-conversions-api.body.timestamp_ms.invalid
data.type has to match the event
order_created needs data.type contents. app_installed and app_opened need customer_action and action_source=mobile_app. contents on a customer_action event is rejected.
Rule: vendor.openai-conversions-api.body.contents_requires_contents_data
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 |
|---|---|---|---|---|
pid |
required | It is the Pixel ID, and it travels on the query string. Fix: Send `pid` as a query parameter on `/v1/events`. | vendor.openai-conversions-api.param.pid.missingvendor.openai-conversions-api.param.pid.empty |
docs |
events |
required | The Conversions API posts events under `events`, and OpenAI documents that array as required. Fix: Send at least one event in `events`. | vendor.openai-conversions-api.body.events.missing |
docs |
integration_source |
optional | It names the integration sending the batch. OpenAI documents 1-64 ASCII characters, starting with a letter or digit. Fix: Use a stable id such as `acme_measurement`. Start with a letter or digit; letters, digits, `.`, `_`, and `-` only. | vendor.openai-conversions-api.body.integration_source.emptyvendor.openai-conversions-api.body.integration_source.invalid |
docs |
validate_only |
optional | It validates events without saving them when `true`. OpenAI documents a boolean. Fix: Send `true` or `false`, or omit `validate_only`. | vendor.openai-conversions-api.body.validate_only.emptyvendor.openai-conversions-api.body.validate_only.invalid |
docs |
id |
required | It identifies the event for deduplication. OpenAI documents it as required on every event. Fix: Set `id` to the same value the image tag sent as `event_id`. | vendor.openai-conversions-api.body.id.missingvendor.openai-conversions-api.body.id.empty |
docs |
type |
required | It is the event type. OpenAI documents it as required. `app_installed` and `app_opened` are CAPI-only with `action_source=mobile_app`. Fix: Set `type` to a documented event name. | vendor.openai-conversions-api.body.type.missingvendor.openai-conversions-api.body.type.emptyvendor.openai-conversions-api.body.type.invalid |
docs |
timestamp_ms |
required | It is the event time in milliseconds. OpenAI documents `timestamp_ms`, so a 10-digit seconds value is the wrong unit. Fix: Send milliseconds, a 13-digit Unix timestamp. | vendor.openai-conversions-api.body.timestamp_ms.missingvendor.openai-conversions-api.body.timestamp_ms.emptyvendor.openai-conversions-api.body.timestamp_ms.invalid |
docs |
custom_event_name |
optional | It names a custom event. OpenAI documents 1-64 letters, digits, underscores, or hyphens, starting and ending with a letter or digit, and forbids standard event names. Fix: Send `custom_event_name` when `type` is `custom`. Do not reuse a standard event name. | vendor.openai-conversions-api.body.custom_event_name.emptyvendor.openai-conversions-api.body.custom_event_name.invalid |
docs |
action_source |
optional | It names where the event happened. OpenAI documents `web`, `mobile_app`, `offline`, `physical_store`, `phone_call`, `email`, or `other`. `source_url` is required when this is `web`. Fix: Set `action_source` to a documented source. Use `mobile_app` for `app_installed` and `app_opened`. | vendor.openai-conversions-api.body.action_source.emptyvendor.openai-conversions-api.body.action_source.invalid |
docs |
source_url |
optional | It is the page URL. OpenAI documents a URL with a scheme and host, required when `action_source` is `web`. Fix: Send `source_url` on web events, such as `https://shop.example.com/checkout`. | vendor.openai-conversions-api.body.source_url.emptyvendor.openai-conversions-api.body.source_url.invalid |
docs |
oppref |
optional | It is an opaque OpenAI-provided attribution identifier. Pass the original string without modification. Fix: Send `oppref` unchanged when you have it, or omit the field. | vendor.openai-conversions-api.body.oppref.empty |
docs |
opt_out |
optional | It opts the event out of future user-level personalization when `true`. OpenAI documents a boolean. Fix: Send `true` or `false`, or omit `opt_out`. | vendor.openai-conversions-api.body.opt_out.emptyvendor.openai-conversions-api.body.opt_out.invalid |
docs |
data |
required | It describes the conversion. OpenAI documents `data` as required, with a `type` that matches the event. Fix: Send a `data` object whose `type` is the shape documented for this event, such as `contents`. | vendor.openai-conversions-api.body.data.missing |
docs |
data.type |
required | It names the data shape. OpenAI documents `contents`, `customer_action`, `plan_enrollment`, or `custom`, matching the event. Fix: Set `data.type` to the shape documented for this event. | vendor.openai-conversions-api.body.data.type.missingvendor.openai-conversions-api.body.data.type.emptyvendor.openai-conversions-api.body.data.type.invalid |
docs |
data.amount |
optional | It is the event-level monetary value in the currency's minor unit. OpenAI documents an integer, such as `4200` for $42.00. Fix: Send the amount as an integer in minor units, and send `data.currency`. | vendor.openai-conversions-api.body.data.amount.emptyvendor.openai-conversions-api.body.data.amount.invalid |
docs |
data.currency |
optional | It is the ISO 4217 currency code. OpenAI documents it as required when `amount` is present. Fix: Send a three-letter code such as `USD`. | vendor.openai-conversions-api.body.data.currency.emptyvendor.openai-conversions-api.body.data.currency.invalid |
docs |
data.plan_id |
optional | It identifies a subscription or trial plan. OpenAI documents it on `plan_enrollment` and `custom` data. Fix: Set `data.plan_id` to the plan id, or omit the field. | vendor.openai-conversions-api.body.data.plan_id.empty |
docs |
data.contents |
optional | It is the item list. OpenAI documents it on `contents`, `plan_enrollment`, and `custom` data, not on `customer_action`. Fix: Send a `contents` array of item objects, or omit the field. | vendor.openai-conversions-api.body.data.contents.empty |
docs |
data.contents[].id |
optional | It is your internal item identifier. Fix: Set `contents[].id` to the product or content id, or omit the field. | vendor.openai-conversions-api.body.data.contents[].id.empty |
docs |
data.contents[].group_id |
optional | It identifies the product group or parent item. OpenAI documents it as Conversions API only. Fix: Set `contents[].group_id` to the group id, or omit the field. | vendor.openai-conversions-api.body.data.contents[].group_id.empty |
docs |
data.contents[].name |
optional | It is the item's display name. Fix: Set `contents[].name` to the display name, or omit the field. | vendor.openai-conversions-api.body.data.contents[].name.empty |
docs |
data.contents[].content_type |
optional | It describes the item category, such as `product`, `plan`, or `page`. Fix: Set `contents[].content_type` to a non-empty category, or omit the field. | vendor.openai-conversions-api.body.data.contents[].content_type.empty |
docs |
data.contents[].quantity |
optional | It is the item quantity. OpenAI documents an integer, not a string. Fix: Send `contents[].quantity` as an integer. | vendor.openai-conversions-api.body.data.contents[].quantity.emptyvendor.openai-conversions-api.body.data.contents[].quantity.invalid |
docs |
data.contents[].amount |
optional | It is the item-level monetary value in the currency's minor unit. OpenAI documents an integer. Fix: Send `contents[].amount` as an integer in minor units. | vendor.openai-conversions-api.body.data.contents[].amount.emptyvendor.openai-conversions-api.body.data.contents[].amount.invalid |
docs |
data.contents[].currency |
optional | It is the ISO 4217 currency code for the item. Fix: Send a three-letter code such as `USD`, or rely on event-level `data.currency`. | vendor.openai-conversions-api.body.data.contents[].currency.emptyvendor.openai-conversions-api.body.data.contents[].currency.invalid |
docs |
data.contents[].variant_dict |
optional | It is an object of string keys and values, such as size and color. OpenAI documents it as Conversions API only. Fix: Send `contents[].variant_dict` as an object of strings, such as `{"size": "medium", "color": "blue"}`. | vendor.openai-conversions-api.body.data.contents[].variant_dict.empty |
docs |
user |
optional | It is the optional conversion-matching object. OpenAI documents it as event-scoped. Include only fields you have. Fix: Send a `user` object with match keys, or omit it. | vendor.openai-conversions-api.body.user.empty |
docs |
user.emails_sha256 |
optional | It is a SHA-256 digest of a normalized email. OpenAI documents lowercase 64-character hex. Fix: Trim, lowercase, SHA-256, and send 64 lowercase hex characters. | vendor.openai-conversions-api.body.user.emails_sha256.emptyvendor.openai-conversions-api.body.user.emails_sha256.invalid |
docs |
user.emails_sha256[] |
optional | Every email in the list must be a SHA-256 digest. Fix: Trim, lowercase, SHA-256, and send 64 lowercase hex characters. | vendor.openai-conversions-api.body.user.emails_sha256[].emptyvendor.openai-conversions-api.body.user.emails_sha256[].invalid |
docs |
user.phone_numbers_sha256 |
optional | It is a SHA-256 digest of a normalized phone number. OpenAI documents lowercase 64-character hex. Fix: Normalize to 8-15 digits, SHA-256, and send 64 lowercase hex characters. | vendor.openai-conversions-api.body.user.phone_numbers_sha256.emptyvendor.openai-conversions-api.body.user.phone_numbers_sha256.invalid |
docs |
user.phone_numbers_sha256[] |
optional | Every phone number in the list must be a SHA-256 digest. Fix: Normalize to 8-15 digits, SHA-256, and send 64 lowercase hex characters. | vendor.openai-conversions-api.body.user.phone_numbers_sha256[].emptyvendor.openai-conversions-api.body.user.phone_numbers_sha256[].invalid |
docs |
user.external_ids_sha256 |
optional | It is a SHA-256 digest of a stable customer id. OpenAI documents lowercase 64-character hex. Fix: Trim, SHA-256, and send 64 lowercase hex characters. | vendor.openai-conversions-api.body.user.external_ids_sha256.emptyvendor.openai-conversions-api.body.user.external_ids_sha256.invalid |
docs |
user.external_ids_sha256[] |
optional | Every external id in the list must be a SHA-256 digest. Fix: Trim, SHA-256, and send 64 lowercase hex characters. | vendor.openai-conversions-api.body.user.external_ids_sha256[].emptyvendor.openai-conversions-api.body.user.external_ids_sha256[].invalid |
docs |
user.first_names_sha256 |
optional | It is a SHA-256 digest of a normalized first name. OpenAI documents lowercase 64-character hex. Fix: Lowercase, strip whitespace and ASCII punctuation, SHA-256, and send 64 lowercase hex characters. | vendor.openai-conversions-api.body.user.first_names_sha256.emptyvendor.openai-conversions-api.body.user.first_names_sha256.invalid |
docs |
user.first_names_sha256[] |
optional | Every first name in the list must be a SHA-256 digest. Fix: Lowercase, strip whitespace and ASCII punctuation, SHA-256, and send 64 lowercase hex characters. | vendor.openai-conversions-api.body.user.first_names_sha256[].emptyvendor.openai-conversions-api.body.user.first_names_sha256[].invalid |
docs |
user.last_names_sha256 |
optional | It is a SHA-256 digest of a normalized last name. OpenAI documents lowercase 64-character hex. Fix: Lowercase, strip whitespace and ASCII punctuation, SHA-256, and send 64 lowercase hex characters. | vendor.openai-conversions-api.body.user.last_names_sha256.emptyvendor.openai-conversions-api.body.user.last_names_sha256.invalid |
docs |
user.last_names_sha256[] |
optional | Every last name in the list must be a SHA-256 digest. Fix: Lowercase, strip whitespace and ASCII punctuation, SHA-256, and send 64 lowercase hex characters. | vendor.openai-conversions-api.body.user.last_names_sha256[].emptyvendor.openai-conversions-api.body.user.last_names_sha256[].invalid |
docs |
user.countries |
optional | It is a two-letter country code, such as `US`. Fix: Send ISO 3166-1 alpha-2 codes, such as `US`. | vendor.openai-conversions-api.body.user.countries.emptyvendor.openai-conversions-api.body.user.countries.invalid |
docs |
user.countries[] |
optional | Every country in the list must be a two-letter code, such as `US`. Fix: Send ISO 3166-1 alpha-2 codes, such as `US`. | vendor.openai-conversions-api.body.user.countries[].emptyvendor.openai-conversions-api.body.user.countries[].invalid |
docs |
user.cities |
optional | It is a raw city name. OpenAI documents geographic values as unhashed strings, limited to 128 characters after normalize. Fix: Send the city as a raw string of at most 128 characters, or omit the field. | vendor.openai-conversions-api.body.user.cities.emptyvendor.openai-conversions-api.body.user.cities.invalid |
docs |
user.cities[] |
optional | Every city in the list must be a raw, unhashed name of at most 128 characters. Fix: Send each city as a raw string of at most 128 characters. | vendor.openai-conversions-api.body.user.cities[].emptyvendor.openai-conversions-api.body.user.cities[].invalid |
docs |
user.regions |
optional | It is a raw region value. OpenAI documents geographic values as unhashed strings, limited to 128 characters after normalize. Fix: Send the region as a raw string of at most 128 characters, or omit the field. | vendor.openai-conversions-api.body.user.regions.emptyvendor.openai-conversions-api.body.user.regions.invalid |
docs |
user.regions[] |
optional | Every region in the list must be a raw, unhashed value of at most 128 characters. Fix: Send each region as a raw string of at most 128 characters. | vendor.openai-conversions-api.body.user.regions[].emptyvendor.openai-conversions-api.body.user.regions[].invalid |
docs |
user.postal_codes |
optional | It is a raw postal or ZIP code. OpenAI documents letters, numbers, spaces, or hyphens, up to 32 characters. Fix: Send the postal code as a raw string of letters, numbers, spaces, or hyphens. | vendor.openai-conversions-api.body.user.postal_codes.emptyvendor.openai-conversions-api.body.user.postal_codes.invalid |
docs |
user.postal_codes[] |
optional | It is a raw postal or ZIP code. OpenAI documents letters, numbers, spaces, or hyphens, up to 32 characters. Fix: Send the postal code as a raw string of letters, numbers, spaces, or hyphens. | vendor.openai-conversions-api.body.user.postal_codes[].emptyvendor.openai-conversions-api.body.user.postal_codes[].invalid |
docs |
user.android_advertising_id |
optional | It is a raw Android GAID in UUID format. OpenAI documents it as Conversions API only; IDFA is not supported. Fix: Send the GAID as a UUID. Do not send IDFA. | vendor.openai-conversions-api.body.user.android_advertising_id.emptyvendor.openai-conversions-api.body.user.android_advertising_id.invalid |
docs |
user.obref |
optional | It is the opaque browser reference from the Pixel `__obref` cookie. Pass it without hashing. Fix: Send the `__obref` cookie value unchanged, or omit the field. | vendor.openai-conversions-api.body.user.obref.empty |
docs |
user.ip_address |
optional | It is a valid IPv4 or IPv6 address. OpenAI documents it as unhashed. Fix: Send the client IP as IPv4 or IPv6, not a hash. | vendor.openai-conversions-api.body.user.ip_address.emptyvendor.openai-conversions-api.body.user.ip_address.invalid |
docs |
user.user_agent |
optional | It is the raw user agent from the client that generated the event. OpenAI documents it as unhashed. Fix: Send the client user agent string, not a hash. | vendor.openai-conversions-api.body.user.user_agent.empty |
docs |
body.web_requires_source_url |
required | The event is marked `action_source: web` but carries no `source_url`. OpenAI documents the page URL as required for web events. Fix: Set `source_url` to the page URL. | vendor.openai-conversions-api.body.web_requires_source_url |
docs |
body.custom_requires_name |
required | `type` is `custom` but `custom_event_name` is missing. OpenAI documents that name as required for custom events. Fix: Set `custom_event_name` to 1-64 letters, digits, underscores, or hyphens. | vendor.openai-conversions-api.body.custom_requires_name |
docs |
body.app_requires_mobile_source |
required | The event is `app_installed` or `app_opened` but carries no `action_source`. OpenAI documents `action_source` as `mobile_app` for those events. Fix: Set `action_source` to `mobile_app`. | vendor.openai-conversions-api.body.app_requires_mobile_source |
docs |
body.app_source_must_be_mobile |
required | The event is `app_installed` or `app_opened` but `action_source` is not `mobile_app`. OpenAI documents that source for app lifecycle events. Fix: Set `action_source` to `mobile_app`. | vendor.openai-conversions-api.body.app_source_must_be_mobile |
docs |
body.contents_requires_contents_data |
required | This event requires `data.type` `contents`. OpenAI documents that shape for page, content, cart, and order events. Fix: Set `data.type` to `contents`. | vendor.openai-conversions-api.body.contents_requires_contents_data |
docs |
body.customer_action_requires_customer_action_data |
required | This event requires `data.type` `customer_action`. OpenAI documents that shape for app, appointment, lead, and registration events. Fix: Set `data.type` to `customer_action`. | vendor.openai-conversions-api.body.customer_action_requires_customer_action_data |
docs |
body.plan_enrollment_requires_plan_enrollment_data |
required | This event requires `data.type` `plan_enrollment`. OpenAI documents that shape for subscription and trial events. Fix: Set `data.type` to `plan_enrollment`. | vendor.openai-conversions-api.body.plan_enrollment_requires_plan_enrollment_data |
docs |
body.custom_requires_custom_data |
required | A `custom` event requires `data.type` `custom`. OpenAI documents that shape for custom events. Fix: Set `data.type` to `custom`. | vendor.openai-conversions-api.body.custom_requires_custom_data |
docs |
body.standard_forbids_custom_event_name |
required | A standard event carries `custom_event_name`. OpenAI documents that field as required for `custom` and omitted otherwise. Fix: Drop `custom_event_name`, or set `type` to `custom`. | vendor.openai-conversions-api.body.standard_forbids_custom_event_name |
docs |
body.customer_action_forbids_contents |
required | `data.type` is `customer_action` but the event carries `data.contents`. OpenAI documents contents as unavailable on that shape. Fix: Remove `data.contents`. | vendor.openai-conversions-api.body.customer_action_forbids_contents |
docs |
body.contents_forbids_plan_id |
required | This data shape carries `data.plan_id`. OpenAI documents `plan_id` on `plan_enrollment` and `custom` only. Fix: Remove `data.plan_id`, or use `plan_enrollment` or `custom` data. | vendor.openai-conversions-api.body.contents_forbids_plan_id |
docs |
body.amount_requires_currency |
required | The event carries `data.amount` with no `data.currency`. OpenAI documents currency as required whenever amount is set. Fix: Add `data.currency` as an ISO 4217 code alongside the amount. | vendor.openai-conversions-api.body.amount_requires_currency |
docs |
body.reserved_custom_event_name |
required | `custom_event_name` matches a standard event name. OpenAI documents that a custom name cannot reuse a built-in event. Fix: Pick a name that is not a standard event, such as `quote_requested`. | vendor.openai-conversions-api.body.reserved_custom_event_name |
docs |
body.unhashed_email |
required | A field carries what looks like a raw email address. OpenAI requires emails to be normalized and SHA-256 hashed before they are sent. Fix: Trim the address, lowercase it, hash it with SHA-256, and send the hex digest in `user.emails_sha256`. | vendor.openai-conversions-api.body.unhashed_email |
docs |
body.hashed_plaintext_field |
required | This field looks like a SHA-256 digest, but OpenAI documents it as an unhashed string. Fix: Send the raw value. Hashing `ip_address`, `user_agent`, geo fields, or `obref` makes the event unmatchable. | vendor.openai-conversions-api.body.hashed_plaintext_field |
docs |
body.zero_advertising_id |
recommended | `android_advertising_id` is the all-zero UUID. OpenAI documents that the API ignores it without rejecting the event. Fix: Send a real GAID, or omit `android_advertising_id`. | vendor.openai-conversions-api.body.zero_advertising_id |
docs |
Validate a payload
pixellint validate json @payload.json --rulepack vendor/openai-conversions-api
Try this failing payload in the playground. OpenAI CAPI event missing id.
{"events":[{"type":"order_created","timestamp_ms":1770000000}]}
cargo install pixellint
·
npm install pixellint