OpenAI Ads (Conversions)
OpenAI Ads Conversions API is OpenAI's server-side conversion API for ChatGPT Ads. This destination sends conversion events directly from Chord to OpenAI, so measurement is unaffected by ad blockers, browser tracking restrictions and cookie expiry.
Pair it with the OpenAI Ads Pixel destination. The two are complementary: the pixel captures OpenAI's oppref click identifier in the browser, which the Conversions API needs but does not collect for itself.
Getting Started
You will need:
- Pixel ID — your OpenAI Ads pixel ID.
- API Key — your OpenAI Ads Conversions API key.
Create both in OpenAI Ads Manager under Tools > Conversions, which offers a Conversion Key and a Pixel ID.
The Conversion Key is per ad account, not per pixel — one key covers every pixel in the account.
Optionally:
- Custom Event Mapping — routes a Chord event that OpenAI has no standard equivalent for onto a custom event you have registered in Ads Manager.
- Validate Only — OpenAI validates each event and records nothing. Use it to confirm an integration end to end without affecting reporting, then turn it off.
Connecting to the OpenAI Ads Conversions API CDP Destination
- Log into the Chord data platform.
- Navigate to the CDP.
- Click the "Add" button next to Destinations.
- Select OpenAI Ads Conversions API from the destination catalog.
- Enter the destination name, your Pixel ID and your API Key.
- Optionally configure Custom Event Mapping and Validate Only.
- Click "Create" to connect.
Verifying an Integration
Enable Validate Only before sending live traffic. OpenAI will validate the full shape of every event and record nothing, so you can confirm the integration is correct against a production pixel without affecting reporting. Turn it off once you are satisfied.
Deduplication
Every event carries id, set to the Chord messageId.
OpenAI deduplicates on the combination of pixel ID, event name and event ID, keeping the first event it receives for a matching key and ignoring later duplicates. The OpenAI Ads Pixel destination sends the same messageId as its event_id, so a conversion reported by both the browser and your server is recorded once.
This also protects you if you are sending your own events to OpenAI alongside Chord: reuse the Chord messageId as your event ID and OpenAI will merge them rather than double-counting.
Event Mapping
OpenAI accepts a fixed set of event names. A Chord event with no equivalent is skipped and logged — it is not forwarded under a made-up name, because OpenAI silently discards an unregistered event name, which is indistinguishable from a successful send.
Chord Event | OpenAI Event | data.type |
|---|---|---|
page | page_viewed | contents |
Product Viewed | contents_viewed | contents |
Product Added | items_added | contents |
Checkout Started | checkout_started | contents |
Order Completed | order_created | contents |
Signed Up | registration_completed | customer_action |
Email Captured | lead_created | customer_action |
Subscription Created | subscription_created | plan_enrollment |
To forward anything else, first create a custom event in ChatGPT Ads Manager, then add a Custom Event Mapping entry pointing your Chord event name at it. Names are lowercased and spaces become dashes, so Wishlist Added becomes wishlist-added; the result must be 1–64 characters of lowercase letters, numbers, underscores or dashes.
Event Attributes
OpenAI Attribute | Chord Source (Priority Order) | Notes |
|---|---|---|
id | messageId | The deduplication key |
type | — | Set from the event mapping table |
custom_event_name | Custom Event Mapping | Custom events only |
timestamp_ms | timestamp → receivedAt | Milliseconds since epoch |
action_source | — | Always web |
source_url | context.page.url | |
oppref | properties.oppref → the captured click identifier | See Attribution below |
data | See below | |
Conversion Data
OpenAI Attribute | Chord Source (Priority Order) | Notes |
|---|---|---|
type | — | Set from the event mapping table. Required by OpenAI |
amount | properties.total → properties.revenue → properties.value | Minor currency units. See below |
currency | properties.currency | Defaults to USD. Required whenever amount is present |
contents | properties.products[] | Omitted for customer_action events, which do not accept it |
plan_id | properties.subscription_id → properties.plan_id | subscription_created only |
Each entry in contents is built from a product:
OpenAI Attribute | Chord Source (Priority Order) |
|---|---|
id | properties.products[].product_id → .sku → .id |
name | properties.products[].name |
content_type | properties.products[].category |
quantity | properties.products[].quantity (defaults to 1) |
amount | properties.products[].price, in minor units |
currency | The order currency |
Amounts are sent in minor units
OpenAI expects every monetary amount as an integer in the currency's minor unit, so $129.99 is sent as 12999. Chord converts this for you.
Zero-decimal currencies are handled correctly: 1000 JPY is sent as 1000, not 100000. This matters because OpenAI accepts either value without complaint — a mistake here would silently report every conversion 100 times too high.
Conversion Matching
Visitor identifiers are SHA-256 hashed before being sent. Plaintext PII is never transmitted.
OpenAI Attribute | Chord Source (Priority Order) | Normalization |
|---|---|---|
emails_sha256 | context.traits.email → properties.email | Trimmed and lowercased, then hashed |
phone_numbers_sha256 | context.traits.phone → properties.phone | Country code kept; whitespace, punctuation, a leading + and leading zeroes removed. Must be 8–15 digits |
external_ids_sha256 | userId | Trimmed, case preserved, then hashed |
first_names_sha256 | context.traits.first_name → properties.first_name | Lowercased, whitespace and ASCII punctuation removed, accents preserved |
last_names_sha256 | context.traits.last_name → properties.last_name | Same as first name |
cities | context.traits.address.city | Sent unhashed |
regions | context.traits.address.state | Sent unhashed |
postal_codes | context.traits.address.zipcode | Sent unhashed |
countries | context.traits.address.country | Sent unhashed |
obref | The __obref cookie set by the OpenAI Ads Pixel | Sent unhashed |
ip_address | requestIp → context.ip | Sent unhashed |
user_agent | context.userAgent | Sent unhashed |
A value that cannot be used is omitted rather than hashed — placeholders such as n/a or unknown, and phone numbers too short to be real, are dropped, since hashing them would send a digest that matches nobody. A value that is already a SHA-256 hash is passed through rather than hashed twice.
These normalization rules match the OpenAI Ads Pixel destination exactly, so the same person produces the same digest on both surfaces. Note that the Conversions API's field names are plural lists (emails_sha256) while the pixel's are singular (email_sha256) — both are correct for their own surface.
Chord sends one value per identifier. OpenAI accepts up to three per list and ignores the rest.
Android advertising IDs are forwarded as android_advertising_id on Android traffic only. OpenAI has no equivalent field for an iOS advertising ID, so one is omitted rather than sent in the wrong place.
Attribution
OpenAI appends an oppref query parameter to ad click-through URLs, and OpenAI's documentation is explicit that — unlike the pixel — the Conversions API does not capture it for you.
Chord handles this: the browser library captures oppref from the landing page URL, along with the __oppref and __obref cookies the OpenAI Ads Pixel sets, and carries them on every subsequent event including the purchase. Installing the OpenAI Ads Pixel destination alongside this one is therefore the recommended setup — without a source of oppref, server-side conversions cannot be attributed to the ad that produced them.
If your storefront resolves the click identifier itself, send it as properties.oppref and it will be used in preference.
Timestamps
OpenAI rejects an event timestamped more than 7 days in the past or more than 10 minutes in the future. If an event carries no parseable timestamp, Chord falls back to the time it was received and then to the current time — an approximate time is better than a rejected batch, which would lose the event entirely.
Retries
Rate limits (429) and OpenAI server errors (5xx) are retried. A rejected event — a validation failure such as a malformed field — is not retried, because the same payload would be rejected again; it is logged instead so it appears in the CDP function logs.
Property Mappings
OpenAI validates each event against a closed schema: the conversion data accepts only type, amount, currency, contents and plan_id, and there is no custom-data field. An unrecognized field causes OpenAI to reject the event, so Chord forwards no custom properties to this destination.
Mappings that change where a supported field is read from work normally — that is what the table below is for. If you need to segment on a property OpenAI has no field for, register a custom event in Ads Manager and route to it with Custom Event Mapping instead.
Use custom property mappings if the default field resolution does not match your data model:
Destination Path | Description |
|---|---|
properties.email | Email for conversion matching |
properties.phone | Phone for conversion matching |
properties.first_name | First name for conversion matching |
properties.last_name | Last name for conversion matching |
properties.total | Primary conversion value |
properties.revenue | Fallback conversion value |
properties.currency | Currency code (e.g. USD) |
properties.oppref | OpenAI click identifier, if your storefront resolves it |
properties.subscription_id | Plan ID for subscription events |
properties.products[].product_id | Product content IDs |
properties.products[].price | Per-item price |
properties.products[].quantity | Per-item quantity |
properties.products[].name | Product display name |
properties.products[].category | Product content type |
Troubleshooting
No events appear in Ads Manager
- Confirm the API Key is the Conversion Key from the Conversions section, not the API key from Settings. The Settings key belongs to OpenAI's management API and is rejected here.
- Confirm the Pixel ID and the key belong to the same ad account.
- Confirm Validate Only is switched off. While it is on, OpenAI validates events and records nothing.
- Check the CDP function logs for the response OpenAI returned.
Conversions are attributed to no campaign
- Confirm the OpenAI Ads Pixel destination is installed on the storefront, since it is what captures oppref.
- Confirm the __oppref cookie is being set and is not blocked by a consent tool.
A conversion is counted twice
- Confirm the OpenAI Ads Pixel destination uses the same Pixel ID.
- Confirm any events you send to OpenAI outside Chord reuse the Chord messageId as their event ID.
An event is missing entirely
- Check the CDP function logs for a "Skipping unsupported event" warning. OpenAI accepts only the fixed event set above; anything else needs a registered custom event and a Custom Event Mapping entry.
Updated 31 August 2026