How to Track Add-to-Cart, Checkout, and Purchase Events for OpenAI Ads
Add-to-cart, checkout, and purchase events map directly onto three OpenAI Ads standard events:
items_added,checkout_started, andorder_created. Each one fires from anoaiq("measure", ...)call placed at the moment that action happens on your site, carrying the product and monetary data OpenAI Ads needs to optimize toward — and eventually report — real purchases, not just clicks.
This guide covers which events map to which funnel step, the parameters that matter most (especially amount, currency, and the contents array), example measure calls you can adapt, and the formatting mistake that breaks more implementations than anything else. If you haven't installed the base pixel yet, start with how to install the OpenAI Ads measurement pixel — everything below assumes the pixel is already loading on every page. And if you want the full picture of how pixel events, the Conversions API, and attribution fit together, the complete guide to ChatGPT Ads tracking is the pillar reference.
Supported purchase-flow events
OpenAI Ads defines a fixed set of standard events for the pixel. The ones relevant to an ecommerce purchase funnel are:
| Event name | Funnel step | What it represents |
|---|---|---|
page_viewed | Any page | Automatic on pixel load — you rarely need to fire this manually |
contents_viewed | Product page | A visitor viewed one or more specific products |
items_added | Add to cart | A visitor added one or more items to their cart |
checkout_started | Checkout begins | A visitor started the checkout process |
order_created | Purchase complete | An order was successfully placed (your primary conversion event) |
Two related events worth knowing about even though they're outside a typical web purchase funnel: lead_created and registration_completed cover lead-gen and signup flows, and subscription_created/trial_started cover subscription businesses. If your funnel doesn't fit any standard event, the pixel also supports custom events via a custom data shape and a custom_event_name passed in the options argument.
One thing the pixel does not cover: app_installed and app_opened are not supported through the pixel at all. Those are Conversions API-only events, sent server-side with action_source: mobile_app. If you're tracking a mobile app funnel, that's a CAPI implementation, not a pixel one — see the Conversions API setup guide.
Required and recommended parameters
Every oaiq("measure", ...) call takes the same basic shape:
oaiq("measure", "event_name", eventData, options);
eventName— one of the standard event names above (or your custom name, handled viaoptions).eventData— an object whose shape must match what that event expects. For purchase-funnel events, this is almost always thecontentsdata shape: an object built around acontentsarray plus top-level fields likeamountandcurrency.options— required for custom events (to passcustom_event_name); optional otherwise. This is also where you'd pass anevent_idif you're deduplicating against a matching Conversions API call — more on that below.
The fields that matter most for items_added, checkout_started, and order_created:
amount— the monetary value of the action, required whenever an amount is present. It must be an integer in ISO 4217 minor units — cents for USD, pence for GBP, and so on.129.99is wrong;12999is correct.currency— required wheneveramountis present. Use the ISO 4217 currency code ("USD","EUR","GBP").contents— an array of line-item objects. Only use the fields documented for thecontentsdata shape — don't invent extra keys hoping they'll be picked up. Quantities insidecontentsare integers, same as amounts.
Stick to the documented fields inside contents[]. Adding undocumented properties doesn't extend the schema — it just adds noise the SDK ignores or, worse, data that silently fails validation. Full field-level detail lives in the measurement pixel reference.
Example event calls
These use clearly generic example values — swap in your actual product IDs, amounts, and currency before shipping.
contents_viewed — product page:
oaiq("measure", "contents_viewed", {
currency: "USD",
contents: [
{ id: "SKU-1001", quantity: 1 }
]
});
items_added — add to cart:
oaiq("measure", "items_added", {
currency: "USD",
amount: 4999,
contents: [
{ id: "SKU-1001", quantity: 1 }
]
});
checkout_started — checkout begins:
oaiq("measure", "checkout_started", {
currency: "USD",
amount: 8998,
contents: [
{ id: "SKU-1001", quantity: 1 },
{ id: "SKU-2002", quantity: 1 }
]
});
order_created — purchase complete:
oaiq("measure", "order_created", {
currency: "USD",
amount: 8998,
contents: [
{ id: "SKU-1001", quantity: 1 },
{ id: "SKU-2002", quantity: 1 }
]
});
Custom event example, if your funnel has a step none of the standard events fit (say, a "size guide viewed" interaction you want to track separately):
oaiq("measure", "size_guide_viewed", {
type: "custom"
}, {
custom_event_name: "size_guide_viewed"
});
Notice the eventData.type matches the event's expected data shape in every case — contents for the purchase-flow events, custom for the custom one. Mismatching the shape to the event is one of the quieter ways an implementation looks like it's working (no console errors) while actually sending malformed data.
Amount and currency formatting — the most common error
If there's one mistake worth memorizing before you write any of these calls, it's this: amounts are integers in minor units, not decimal dollars.
// Wrong — decimal amount
oaiq("measure", "order_created", { amount: 129.99, currency: "USD", contents: [...] });
// Correct — 129.99 USD as an integer in cents
oaiq("measure", "order_created", { amount: 12999, currency: "USD", contents: [...] });
12999 means $129.99. This trips people up because it's the opposite of what most payment UIs display, and because a decimal amount doesn't throw an obvious error — it just sends a value that's off by a factor of 100, which quietly wrecks any revenue reporting built on top of it. The same rule applies to quantities inside contents[]: whole integers, not fractional or decimal values.
Before you consider an integration done, pull up a real order in your test environment and manually check that the amount you're sending equals the displayed price times 100 (for two-decimal currencies like USD or EUR) — not the raw decimal price.
Firing at the right funnel step
Where you place each measure call matters as much as what's inside it:
contents_viewedfires on the product detail page, ideally on page load or when the primary product image/data has rendered — not on every scroll or interaction.items_addedfires at the moment the add-to-cart action succeeds — after the server or cart state confirms the item was added, not on button click alone (a failed add-to-cart shouldn't register as a conversion signal).checkout_startedfires when the visitor lands on or begins the checkout page/flow — once, per checkout attempt, not on every field interaction within it.order_createdfires on the confirmation/thank-you page after the order is actually placed — this is your primary conversion event, so it needs to be reliable above all else. If your thank-you page can be reloaded or revisited (browser back button, bookmarked link), guard against firing this event more than once per order.
A pixel event fired at the wrong moment isn't just imprecise — it changes what the data means. An items_added fired on button click regardless of success overstates cart activity; an order_created that fires on every page refresh inflates your reported purchases against what actually shipped.
Testing checklist before go-live
Don't flip these events live without checking each one individually:
- Enable
debug: truein youroaiq("init", ...)call (see the pixel install guide if you haven't set this up) and watch the console as you walk through the funnel manually — view a product, add to cart, start checkout, complete a test order. - Check the Network tab for each step — you should see a request to
bzr.openai.comreturn 202 each time an event fires, not just on page load. - Use the Pixel Helper extension to confirm which events are firing on a given page without digging through raw network requests.
- Confirm amounts in the payload match what you'd expect in minor units — this is the moment to catch the
129.99vs12999mistake before it reaches Ads Manager. - Watch the Ads Manager event stream and confirm each event type arrives a few minutes after you trigger it, and that your primary conversion event (typically
order_created) is linked to the campaign you're measuring. - Test the duplicate-order scenario — reload or revisit the thank-you page and confirm
order_createddoesn't fire again.
Full verification detail, including what a correctly firing pixel looks like end-to-end, is in Measure results with OpenAI Ads.
Where the Conversions API comes in
The pixel only sees what happens in the browser. Ad blockers, browser privacy settings, server-side redirects, and events that happen after the browser session ends (a delayed order confirmation, a payment that settles later) are all cases where the pixel alone won't catch the conversion. That's what the Conversions API is for — sending the same event types server-side, with an event_id that matches the pixel's, so OpenAI Ads can deduplicate the two signals against the same Pixel ID instead of double-counting.
If you're running on Shopify, the pixel-plus-CAPI setup has some platform-specific shortcuts worth knowing — see pixel and CAPI setup for Shopify. Otherwise, once your pixel events are firing cleanly through the checklist above, the Conversions API setup guide is the next step to close the gaps the browser can't cover on its own.
Want this done for you?
Fixed-scope setup, tested end-to-end and documented.
Explore the serviceTake the free audit