ChatGPT Ads takes offline conversions through OpenAI’s Conversions API. Your server sends each CRM, phone or in-store outcome with an offline action source, the click reference you saved on the lead, and hashed contact details, within 7 days of the sale.
This guide is part of our offline conversions series. It assumes you already have a data source, a Pixel ID and a Conversions API key. If not, start with the ChatGPT Ads Conversions API guide, which covers the Pixel, the server setup and deduplication.
What OpenAI supports for offline conversions
ChatGPT Ads is a new platform, and OpenAI documents little that is specific to offline conversions. As of October 2026, this is what its docs confirm:
- Offline action sources. The Conversions API reference accepts
action_sourcevalues ofweb,mobile_app,offline,physical_store,phone_call,emailandother. Onlywebevents need asource_url. - Send everything you may share. OpenAI recommends sending all conversion events, not just those with an
oppref. Its measurement guide says hashed contact information can help when click information is incomplete. - Partners. OpenAI’s partner guide points to LiveRamp for offline conversions such as in-store purchases. It also lists Hightouch, which syncs event models to an OpenAI destination with your Pixel ID and API key, and Datahash, which helps implement the Conversions API.
What the docs don’t describe: a file or CSV upload for conversions in Ads Manager, a definition of each action source, or a way to correct a conversion after it’s sent. We cover those gaps further down.
Direct Conversions API upload vs LiveRamp’s program
There are two documented routes for offline outcomes. LiveRamp’s OpenAI Conversions API program passes a file through the same API. Click through to compare them.
1 / 6
LiveRamp’s setup starts with a sample file, then a destination account in LiveRamp Connect with your Pixel ID and Conversions API key. LiveRamp says the key is generated once, so store it when you create it. Each row needs at least one identifier (name and postal address, email or phone) plus the event name, transaction category, timestamp, amount and order ID.
What you need before you start
- A data source in the Conversions section of Ads Manager, its Pixel ID, and a Conversions API key for the same ad account. A server-only setup still needs a Pixel ID.
- The oppref click reference saved on each new lead, plus the email and phone the person gave you, and their consent choice.
- A CRM that keeps those fields on the contact and links each deal or order back to it.
- A short list of CRM stages to send, each mapped to an OpenAI event name.
- A conversion event setting in Ads Manager for each stage you want to report, attached to your campaigns.
- A server process that can send an event when a stage changes, not only in a weekly batch.
How to send CRM conversions to ChatGPT Ads
1. Save the click reference on the lead
OpenAI appends oppref to the landing page URL. The Pixel stores it in a first-party __oppref cookie for 30 days. Read that value when the form is submitted and save it on the lead, next to the email, phone and a timestamp. OpenAI says to pass it unchanged and never to invent one or reuse another visitor’s.
Our default is to capture click references from your own tracking domain: a server container on a subdomain such as sgtm.example.com. Google’s custom domain guide says a container on the provider’s default domain can only set JavaScript cookies, while your own subdomain gets full access to server-set cookies. The server-side tagging guide covers the setup.

2. Pick the CRM stages to send
Send the stages that tell OpenAI which leads turned into revenue. Use a standard event where one fits, from OpenAI’s supported events, and a custom event where none does.
| CRM stage | Event type | action_source | Notes |
|---|---|---|---|
| Form submitted on the site | lead_created | web | Usually sent by the Pixel at submit. If your server sends it too, reuse the same event ID. |
| Meeting booked by a rep | appointment_scheduled | phone_call or offline | OpenAI’s event for a booked meeting, demo or consultation. |
| Qualified lead | custom, with a name such as qualified_lead | offline | Custom names are 1–64 characters and can’t reuse a standard name. Not usable as an optimization goal. |
| Deal won on a call | order_created, with amount and currency | phone_call | OpenAI’s event for a completed purchase. |
| In-store purchase | order_created | physical_store | Send the order total as the event amount. |
| Paid subscription starts | subscription_created | offline | Uses the plan_enrollment data shape, with an optional plan_id. |
Give every stage its own event ID, such as deal_10427_won. Keep that ID if you retry, and use a new one for each different action.
3. Build and send the event
Offline events go to the same endpoint as web events. Leave out source_url, add the saved oppref, and add the hashed identifiers you have. This batch sends a qualified lead and a won deal:
POST https://bzr.openai.com/v1/events?pid=<PIXEL-ID>
Authorization: Bearer <CONVERSIONS-API-KEY>
Content-Type: application/json
{
"events": [
{
"id": "lead_8f2k91_qualified",
"type": "custom",
"custom_event_name": "qualified_lead",
"timestamp_ms": 1792990000000,
"action_source": "offline",
"oppref": "<oppref saved on the lead>",
"user": {
"emails_sha256": ["<SHA-256 of the normalized email>"],
"external_ids_sha256": ["<SHA-256 of your CRM contact ID>"]
},
"data": { "type": "custom" }
},
{
"id": "deal_10427_won",
"type": "order_created",
"timestamp_ms": 1793000000000,
"action_source": "phone_call",
"oppref": "<oppref saved on the lead>",
"user": {
"emails_sha256": ["<SHA-256 of the normalized email>"],
"phone_numbers_sha256": ["<SHA-256 of 14155552671>"]
},
"data": { "type": "contents", "amount": 1200000, "currency": "USD" }
}
]
}- Time.
timestamp_msis when the stage changed in your CRM, in Unix milliseconds, not when you send it. - Value. Amounts are integers in minor units, so 1200000 is $12,000.00. A currency is required with an amount.
- Hashing. Trim and lowercase emails. For phones, keep the country code, remove whitespace, parentheses, periods and hyphens, then a leading + and any leading zeros. Then SHA-256 each value as lowercase hex. Each list takes at most 3 values.
- IP and user agent. OpenAI asks for the real client’s values and says not to substitute your server’s. A CRM stage has no browser behind it, so leave them out.
- Batches. Up to 1,000 events per request. One invalid event fails the whole batch.
In our own integration, CRM stage events use offline as the action source unless the stage happened on a call or in a store.
4. Attach the events to your campaigns
Sending an event doesn’t make it a conversion. OpenAI’s conversion tracking guide connects events to campaigns through a conversion event setting: one event type, one data source and an attribution window. For a custom event, the setting’s custom_event_name must match exactly. Updating a campaign’s list of settings through the API replaces the list, so include the ones you want to keep.
A conversion-optimized campaign takes exactly one standard event, chosen when you create it. Custom events, such as a qualified lead, can be reported on but not used as that goal.
The two clocks: 7 days to send, 30 days to credit
Two separate time limits decide whether a CRM outcome counts. OpenAI’s troubleshooting guide says they are not the same thing.
- The send limit. Each event’s timestamp must be within the last 7 days and no more than 10 minutes in the future. You can’t backfill older CRM history, and OpenAI says not to change an old event’s time to make it pass. Its event quality check also flags server events that arrive more than 1 hour after the action, so send stages as they change.
- The attribution window. A conversion is credited only if it happens within the window after an eligible ad click. OpenAI’s API reference says to use 30 days for a conversion event setting. A deal that closes 45 days after the click falls outside it, so send an earlier stage, such as a qualified lead, as well.
Reporting has its own settings. OpenAI’s Measure Results guide lets you pick a 7, 14 or 30-day click window and a 0 or 1-day view window. These change reporting only, not optimization, bidding or billing. Per the reporting docs, results are dated by the ad interaction by default, so a deal won today shows up on the day of the click. Set the time basis to conversion time when you compare with CRM close dates.
On view-through: OpenAI’s developer docs describe a one-day view window for web events, and say app events stay click-through only. They don’t say how it applies to offline action sources. LiveRamp’s page says its program helps with view-through conversions. Its FAQ also links its 3-day cadence to a 21-day attribution window, which differs from the 30 days in OpenAI’s docs, so confirm which applies with your rep.
Field mapping: CRM to Conversions API
| CRM field | Conversions API field | Notes |
|---|---|---|
| Saved click reference | oppref | The original string, unchanged. An event-level field, not inside user. |
| Stage or deal ID | id | Unique per action. Reuse it on retries. |
| Stage | type, plus custom_event_name for custom | Must match the conversion event setting exactly. |
| Stage change time | timestamp_ms | Unix milliseconds, within the last 7 days. |
| Where it happened | action_source | offline, phone_call, physical_store and others |
user.emails_sha256 | Trimmed, lowercased, SHA-256. Up to 3 values. | |
| Phone | user.phone_numbers_sha256 | Country code and digits only, then SHA-256. |
| CRM contact ID | user.external_ids_sha256 | Trimmed, case kept, then SHA-256. Use a stable ID per person. |
| First and last name | user.first_names_sha256, user.last_names_sha256 | Lowercase, no spaces or ASCII punctuation, then SHA-256. |
| Postal code, city, country | user.postal_codes, user.cities, user.countries | Raw values, not hashed. Country is a two-letter code. |
| Deal value | data.amount and data.currency | Integer minor units with an ISO 4217 code. |
How to check that it works
- Send one batch with validate_only set to true. It checks the format without saving the events, and those events don’t appear in monitoring.
- Send one real stage change, then call the recent events endpoint (GET /v1/conversions/events with your pid) with the Advertiser API key. It returns a sample from roughly the last 15 minutes. Server events show the server_to_server channel.
- Check that the event type and custom event name match the conversion event setting on the campaign.
- After 24 to 48 hours, compare Ads Manager with your CRM for the same dates, with the time basis set to conversion time and the same click and view windows.
- Open event quality for the data source a few days later. It refreshes daily over 7 complete days, and warns about missing matching data or delayed server events.
Common errors and fixes
| Symptom | Likely cause | Fix |
|---|---|---|
| Old deals are rejected | The timestamp is more than 7 days old, or was sent in seconds | Send stages as they change, in milliseconds. Older history can’t be sent. |
| A whole batch is rejected | One invalid event fails the batch | Check the batch with validate_only, fix the bad event, and resend with the same IDs. |
| A web event is rejected | action_source is web but there is no source_url | Use an offline action source for CRM stages, or add the page URL. |
| The API returns an authentication error | The Advertiser API key was used instead of the Conversions API key | Use the Conversions API key for the same ad account as a Bearer token. |
| Events arrive but show zero conversions | No matching conversion event setting on the campaign, or the stage fell outside the click window | Attach a setting with the exact event name, and send earlier stages. |
| Few offline events are credited | No oppref on the lead, and little hashed contact data | Save oppref at form submit, and send email, phone and a hashed CRM ID. |
| Event quality flags delayed server events | A daily or weekly batch | Send each stage soon after it changes in the CRM. |
| HTTP 400 on customer data | A user list has more than 3 values | Send at most 3 hashed values per list. The API does not truncate. |
What isn’t supported or isn’t documented
- No file upload in Ads Manager. OpenAI’s conversion guides cover the Pixel, the Conversions API, an image tag and partner integrations. None describe a file or CSV upload for conversions.
- No corrections. OpenAI says not to resend a duplicate to change an event’s value or matching data, and doesn’t document a way to adjust or retract a conversion. Send the right value the first time.
- No backfill. The 7-day timestamp limit means CRM history older than a week can’t be sent.
- No action source definitions. OpenAI lists the values but doesn’t say how each one is used in attribution or reporting.
- No optimizing on custom stages. Conversion-optimized campaigns take one standard event, which can’t be changed after the campaign is created.
- No CRM write-back from HubSpot. OpenAI’s HubSpot article covers connecting accounts, creating campaigns and viewing performance in HubSpot. It doesn’t describe sending deal stages back as conversions.
How ChatGPT Ads fits with your other platforms
The groundwork is the same everywhere: save the click ID and contact details when the lead arrives, keep them on the CRM record, and send each stage inside the platform’s limits. Our guide to offline conversions compares match keys and upload windows across platforms.
- ChatGPT Ads website events use the Pixel and the Conversions API with a shared event ID. See the ChatGPT Ads Conversions API guide.
- Google Ads imports CRM conversions through Data Manager, up to 90 days after the click with a GCLID. See the Google Ads offline conversion import guide.
- GoHighLevel users can follow the GoHighLevel offline conversions guide for Google Ads and Meta.
Maven sends CRM stages to OpenAI Ads through its Conversion Events API, with the saved click reference and hashed contact details, alongside Google Ads and Meta. To check the website side first, a Free Tracking Scan shows which tags load on your lead pages today.
Get the next one before it hits.
Short emails when Meta, Google, GA4 or LinkedIn change something that moves your numbers.
Part of: Offline conversions
Also in By ad platform: Google Ads offline conversion import: how to upload CRM conversions through Data Manager, Microsoft Ads offline conversions: how to import CRM outcomes into Microsoft Advertising
Frequently asked questions
Can I upload offline conversions to ChatGPT Ads?
Yes, through the Conversions API. Your server sends each CRM, phone or in-store outcome as an event with an action_source such as offline, phone_call or physical_store. OpenAI’s docs don’t describe a file upload for conversions in Ads Manager. LiveRamp runs a file-based OpenAI program for offline conversions, which it says is in limited availability and by invitation only.
Which action_source should I use for CRM conversions?
The Conversions API accepts web, mobile_app, offline, physical_store, phone_call, email and other. OpenAI lists the values but doesn’t define when to use each one. We use phone_call for sales closed on a call, physical_store for in-store sales, and offline for other CRM stages. Events with action_source web must also include source_url.
How long after a sale can I send it to ChatGPT Ads?
The event timestamp must be within the last 7 days and no more than 10 minutes in the future, so send each outcome within 7 days of when it happened. To be credited to an ad, it also has to fall inside the attribution window. OpenAI’s docs use a 30-day click window for conversion event settings.
Do offline conversions need the oppref click reference?
No, but it helps. oppref is the click reference OpenAI adds to the landing page URL. Save it on the lead and send it unchanged with the later event. OpenAI also recommends sending every conversion you are permitted to share, not just those with an oppref, and adding hashed email, phone or customer ID for matching.
What is LiveRamp’s OpenAI Conversions API program?
It is a LiveRamp service that takes a file of offline conversions with customer details and passes it to OpenAI’s Conversions API. LiveRamp says it is in limited availability and by invitation only. You upload by SFTP or from a cloud bucket, within 7 days of the conversion, using your Pixel ID and a Conversions API key.
Can a campaign optimize toward a qualified lead from my CRM?
Only if you send it as a standard event. A qualified lead usually has no standard event name, so it goes in as a custom event. Custom events can be measured on any campaign, but OpenAI says they cannot be the goal of a conversion-optimized campaign.
Does the HubSpot integration send deal stages to ChatGPT Ads?
OpenAI’s HubSpot article covers connecting the accounts, creating campaigns from HubSpot and viewing performance there. It does not describe sending HubSpot lifecycle or deal stages back to ChatGPT Ads as conversions. For that, send the stages through the Conversions API or a partner.
Why don’t my CRM numbers match Ads Manager?
Your CRM counts every sale. Ads Manager counts sales it credits to an ad, within the selected click and view windows. Reports also date conversions by the ad interaction by default, not by the close date. Set the time basis to conversion time and use the same windows when you compare.
Sources and references
- OpenAI Help: Conversion Measurement
- OpenAI Help: Troubleshooting conversion tracking for ChatGPT Ads
- OpenAI Help: Set up Measurement Partner Integrations
- OpenAI Help: Measure Results
- OpenAI Help: Understanding and improving event quality
- OpenAI Help: Setting up ChatGPT Ads in HubSpot
- OpenAI Ads docs: Conversions API
- OpenAI Ads docs: Supported Events
- OpenAI Ads docs: Conversion Tracking
- OpenAI Ads API reference: Conversion Setup
- OpenAI Ads docs: Reporting
- OpenAI Ads docs: Measurement Pixel
- LiveRamp: The OpenAI Conversions API Program for Offline Conversions
- LiveRamp: Using a Universal File for Multiple Conversions API Programs
- Google Tag Manager: Map a custom domain to the server container
