pixellint

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

Hosts
bzr.openai.com
Paths
/v1/events
Vendor docs
developers.openai.com/ads/conversions-api

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.missing
vendor.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.empty
vendor.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.empty
vendor.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.missing
vendor.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.missing
vendor.openai-conversions-api.body.type.empty
vendor.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.missing
vendor.openai-conversions-api.body.timestamp_ms.empty
vendor.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.empty
vendor.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.empty
vendor.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.empty
vendor.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.empty
vendor.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.missing
vendor.openai-conversions-api.body.data.type.empty
vendor.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.empty
vendor.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.empty
vendor.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.empty
vendor.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.empty
vendor.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.empty
vendor.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.empty
vendor.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[].empty
vendor.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.empty
vendor.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[].empty
vendor.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.empty
vendor.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[].empty
vendor.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.empty
vendor.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[].empty
vendor.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.empty
vendor.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[].empty
vendor.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.empty
vendor.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[].empty
vendor.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.empty
vendor.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[].empty
vendor.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.empty
vendor.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[].empty
vendor.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.empty
vendor.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[].empty
vendor.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.empty
vendor.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.empty
vendor.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