Performance Marketing

ChatGPT Ads Conversion Tracking: The Setup That Finally Worked for Me

Anirban Saha · 6 July 2026 · 6 minutes read

ChatGPT Ads Conversion Tracking

OpenAI Ads is new, thin, and a little fiddly. Conversion tracking is where most people get stuck. Here is the exact process that cleared the “no recent conversion events” warning for me, step by step.

Why ChatGPT Ads conversion tracking trips people up

Performance marketers have been waiting for ChatGPT Ads, or OpenAI Ads, for some time now. It sat in talks and speculation for a long while, and it finally arrived in May. These are still early days. It still looks pretty thin and nascent to anyone who has been running Google Ads, LinkedIn Ads, or Meta Ads for a few years.

Setting up campaigns, ad groups, and ads is fairly straightforward, apart from the context hints (that’s a topic for another day). Where a lot of people get stuck is conversion tracking. OpenAI does provide a step-by-step process for it, and even with that, I had my own share of struggles.

I tried a few things. I tried the events stream. There is a “start polling” option that did nothing for me. The warning stayed put, telling me no events were set up, and at one point flagging two events with parameters that didn’t match. I know several people who have hit this exact wall, so let me put down the process that finally worked. There may be other ways that work for you, but this is definitely one of them.

Two steps, nothing exotic

Setting up conversions has two parts: setting up the data source, and then setting up the conversion event. There’s nothing new here conceptually. It is the same shape as any CPC platform you’ve used before. Honestly, the data source part is quite uneventful. The conversion event is where the friction lives.

Step 1: Set up the data source (the base pixel)

In Ads Manager, go to Tools → Conversions. There’s a Create button. Click it and create a data source. OpenAI gives you a snippet that needs to sit in the <head> section of every page (or every landing page) of your site. You can do this a few different ways. I used Google Tag Manager.

Here’s how I set it up in GTM:

  • Create a new tag. I named mine openai_ads_setup_code.
  • Tag type: Custom HTML.
  • Paste the base code and add your Pixel ID.
  • Set the trigger to Initialization – All Pages.
  • Run a Preview. At page load, the tag should show as fired.
  • Submit and save.

The base pixel looks like this (swap in your own Pixel ID):

<script>
!function(w,d,s,u){if(w.oaiq)return;var q=function(){q.q.push(arguments)};q.q=[];w.oaiq=q;var j=d.createElement(s);j.async=1;j.src=u;var f=d.getElementsByTagName(s)[0];f.parentNode.insertBefore(j,f)}(window,document,"script","https://bzrcdn.openai.com/sdk/oaiq.min.js");
oaiq("init",{pixelId:"YOUR-PIXEL-ID"});
</script>

If the tag isn’t firing, cross-check three things: the Pixel ID, the code itself, and (most commonly the culprit) whether you’ve set the trigger to Initialization – All Pages rather than a normal page-view trigger.

Step 2: Set up the conversion event

There are multiple event types you can set up: registration completed, lead created, app installed, app opened, and plenty more. I went with lead created.

In my case, the flow is simple. The user sees the ad in ChatGPT, clicks it, and lands on a landing page. On that page there’s a short form: they drop in their email and submit. In GTM, I already had an event set up for a successful form submission, so I had the trigger side covered.

OpenAI provides a script you can add as a Custom HTML tag in GTM, and in theory you set the right trigger for a successful form submit and it should just work. For me, it didn’t. That’s where the newer Codex route came in.

The Codex route that unblocked me

There’s an option that recently showed up in Ads Manager where you can create the conversion event using ChatGPT Codex. Here’s how that played out:

  • Click the “set up with Codex” option in Ads Manager.
  • If you don’t have Codex installed, install it. It adds a plugin called openai-ads-conversions.
  • Tell Codex your constraints. I told it plainly: I don’t have access to the website, the server side, or any code, but I can implement through Google Tag Manager, so give me steps accordingly.
  • Codex then handed me GTM-specific steps and a code snippet to add as a Custom HTML tag.
  • I also gave Codex the base code I’d already implemented, so it had the full picture.

The three things Codex caught that mattered

A few details from Codex made the difference between a tag that fires and a conversion that actually registers:

  • The debug: true flag. The script I’d copied from Ads Manager had debug: true in it. Codex confirmed that’s fine for testing and told me to keep it while validating in GTM Preview and the browser console, then remove it before pushing to production.
  • The two parameters that have to match. In the conversion event code there’s a parameter under oaiq with a main value and a type. Those two are critical and they have to match what you configured. This is exactly the “parameters that didn’t match” warning I kept seeing.
  • Load order. Codex made sure the base code loads before the conversion event code. My original line only worked if oaiq was already available at that exact moment. So it added a window.oaiq && guard to prevent an error if the lead event fired before the base pixel had initialised. It shouldn’t normally happen, but I added it anyway. Codex also suggested tag sequencing, which I didn’t end up implementing.

The lead event tag ended up looking like this:

<script>
window.oaiq && oaiq("measure", "lead_created", {
  type: "customer_action"
});
</script>

The trigger is the successful form submission event you already have in GTM. In my setup the tags line up like this:

  • openai_ads_setup_code – trigger: Initialization / All Pages.
  • OpenAI_ads_Leads (the lead_created event) – trigger: successful form submit only.

Validation: four layers of checks

This is the part I wouldn’t skip past too quickly if I were you, so slow down here. There are multiple layers of checks and each one matters.

  • GTM Preview. Both tags should fire: the base code at page load, and the conversion event on form submit.
  • Browser Network tab. Filter for openai, bzr, or oaiq. Confirm the SDK loads from bzrcdn.openai.com.
  • Live landing page test. Go to the actual published landing page and submit a test lead. In the Network tab, confirm the request fires again to bzr.openai.com/v1/sdk/events.
  • Inspect the payload. Click the request and open the payload. It should contain the exact lead type you selected. In my case that was "type": "lead_created".

A healthy request payload looks like this:

[
  {
    "type": "lead_created",
    "timestamp_ms": 1782541528292,
    "id": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
    "source_url": "https://your-landing-page.com/lp/example",
    "data": {
      "type": "customer_action"
    }
  }
]

The thing that actually fixed it

Here’s the honest bit. Initially the payload wasn’t coming through, and that’s why everything was silently failing. The tags fired, the SDK loaded, and still nothing registered. What broke the logjam was going back and removing debug: true, doing a hard refresh, filling out the form again, and re-checking. That’s when I finally saw the payload carrying "type": "lead_created".

Then I waited. The “no recent conversion events” warning references the last 24 complete UTC hours, so it doesn’t clear instantly. A couple of hours after my live test, the warning on the Ads Manager conversions tab was gone, and conversion events started showing up in the OpenAI Ads Manager Conversions tab.

If you’re stuck at the same warning, the sequence that worked for me was: valid base pixel on all pages, a matching conversion event on the real submit, remove debug: true, hard refresh, confirm the payload on a live submission, then give it time to process. That was it.

Frequently Asked Questions

Without conversion tracking, OpenAI Ads only sees clicks. It has no idea which of those clicks turned into a lead or a sale. ChatGPT Ads still doesn’t have a conversion objective — the best your campaign can optimise for right now is clicks. The conversions objective is listed as “Coming soon” in Ads Manager. Having conversion tracking in place now means you can see which ads are performing, and when the conversion objective eventually launches, you’ll already have data to seed it with.

No. Codex is one path, and it happens to be the one that unblocked me, mostly because it caught the small details around debug: true, matching parameters, and load order. You can also take the standard script OpenAI provides in Ads Manager and place it yourself, whether that’s directly in your site’s code or as a Custom HTML tag in Google Tag Manager. If your form submission trigger is solid and the pixel parameters match, the manual route works fine. Codex is a convenience for getting the exact snippet and catching mistakes, not a hard requirement.

Not for the browser pixel. The entire setup described here runs through Google Tag Manager without touching site or server code, which is exactly the constraint I was working under. Server-side tracking through a Conversions API is a separate, more advanced option that does need a secret key handled on your server, so it shouldn’t be placed in browser-side GTM.

A firing tag isn’t the same as a received, normalised event. The warning looks at whether OpenAI has processed conversion events in the last 24 complete UTC hours. Common reasons it lingers: the GTM container wasn’t actually published to the live site, an ad blocker or consent setting stopped the tag for real users, the event fired in Preview but not on the live container, or the payload didn’t carry the correct event type. Once a real live submission sends a valid payload, give it a few hours to clear.

In my experience it was a couple of hours after a valid live test, not instant. Because the warning is tied to complete UTC reporting windows, allow for processing time before assuming something is broken. If it’s still empty roughly a full day after a confirmed live event with a valid payload, that’s when it’s worth re-checking your publish status and triggers.

Pick the one that matches the actual action you’re paying to drive. If your goal is form fills on a landing page, lead_created is the natural fit. If you’re driving purchases, registrations, or app events, use the matching type instead. The important thing is that the event type in your code matches the event type configured in Ads Manager, because a mismatch there is one of the quiet reasons tracking appears to fail.