---
title: "How to set up the OpenAI Conversions API for ChatGPT Ads"
description: "OpenAI Conversions API setup: Pixel ID and API key, the event payload, oppref and obref, deduplication with the pixel, testing, and fixing doubled events."
updated: "2026-10-23"
url: "https://ispixelworking.com/guides/openai.conversions_api_setup/generic"
---

# How to set up the OpenAI Conversions API for ChatGPT Ads

Fix guide · Any website · advanced · about 90 min · updated 2026-10-23


## What the OpenAI Conversions API is and what it fixes

The OpenAI Pixel (the ChatGPT Ads Measurement Pixel) sends events from the visitor's browser. The **Conversions API** (OpenAI also calls it CAPI) sends them from your server: for example, when your backend confirms a purchase, it posts an `order_created` event to OpenAI. OpenAI describes it as a complement to the pixel and suggests using both, to combine browser activity with conversions your backend confirms.

What the server copy helps with, according to OpenAI:

- **Conversions the browser misses.** You measure conversions without depending only on the browser delivering the event: a purchase your server records still reaches OpenAI if the visitor closed the tab or blocked scripts.
- **Better matching.** Server events can carry customer details you already have, such as the hashed email from an order, so more conversions can be matched to ChatGPT ad clicks.
- **Control.** Your server decides when to send, and retries failed deliveries.
- **Events the pixel cannot send.** App installs and app opens (`app_installed`, `app_opened`) go through the Conversions API only, and so do actions that happen off your website, such as `offline`, `physical_store` or `phone_call` sources.

OpenAI also recommends sending all conversion events from your server, not only those that came with an ad click ID, so that view-through conversions can be included in your campaign reporting.

## What it does not fix

- **Consent.** Sending from a server does not change which consent you need. OpenAI asks you to follow your site's measurement consent requirements before collecting or forwarding the pixel's browser reference, and to stop sending it if the visitor withdraws consent. We report technical behaviour only and cannot tell you what the law requires.
- **A broken pixel setup.** In most setups the server event takes the ad click ID and the browser reference from the pixel's cookies (`__oppref` and `__obref`). If the pixel fires before consent, never fires after consent, runs twice or loses the click ID, the server copy inherits the problem. Fix those first: [fires before consent](https://ispixelworking.com/guides/openai.fires_before_consent/generic), [does not fire after consent](https://ispixelworking.com/guides/openai.not_firing_after_consent/generic), [duplicate events](https://ispixelworking.com/guides/openai.duplicate_pixel/generic), [click ID not stored](https://ispixelworking.com/guides/openai.click_id_not_set/generic).

## What you need before you start

- **A Pixel ID.** OpenAI's Conversions API uses the Pixel ID to identify the data source, even for a server-only setup. If you also run the browser pixel on the same website, reuse its data source: deduplication only works within one Pixel ID. You create Pixel IDs in the conversions tab of ChatGPT Ads Manager.
- **A Conversions API key.** OpenAI says you can provision it from the conversions tab in Ads Manager, or create it with your Advertiser API key (`POST /v1/conversions/api_keys`). It is a different key from the Advertiser API key used for campaign management. Keep both on your server, never in browser code: OpenAI is explicit that the Conversions API must not be called from page code.
- **An events plan.** List the actions you will send and their standard event names: `order_created` (purchase), `lead_created`, `registration_completed`, `appointment_scheduled`, `checkout_started`, `items_added`, `contents_viewed`, `page_viewed`, `subscription_created`, `trial_started`. Use a custom event only when none fits; its name goes in `custom_event_name` and must be identical on browser and server. Decide where each event's ID comes from (order number, form submission ID, or a random value shared with the browser).
- **Conversion event settings.** Sending events and counting them as conversions are separate steps: a conversion event setting selects which event from which source counts, and you attach it to campaigns.

## Pick a setup route

| Route | Fits | Cost | Developer needed |
|---|---|---|---|
| An app or plugin for your platform | Shopify apps, WordPress and WooCommerce plugins | Depends on the app | No |
| Server-side Google Tag Manager with Stape's OpenAI templates | Any site, best if you already use Tag Manager | Hosting, from a free tier | Usually |
| Direct integration from your backend | Any site, also app and offline events | Developer time | Yes |

OpenAI's documentation does not mention a fee for the Conversions API itself; the cost is the tool or the development time.

**Platform apps.** For Shopify, several apps in the Shopify App Store install the OpenAI pixel and send server events through the Conversions API; they typically ask for your Pixel ID and a Conversions API key. On WordPress, the tracking plugins PixelYourSite and Pixel Manager for WooCommerce support the OpenAI pixel; PixelYourSite says its browser pixel and Conversions API share one event ID, and Pixel Manager adds the Conversions API in its Pro version. None of these apps and plugins is made by OpenAI, so check how each one sets event IDs (browser and server copies must share them) and how it handles cookie consent, and remove any pixel you pasted into the theme by hand, or events are counted twice.

**Server-side Google Tag Manager.** Stape publishes two templates: **OpenAI Ads Pixel** for the web container and **OpenAI Ads Events API** for the server container. The server tag takes your Pixel ID and API key, can store the click ID in the `__oppref` cookie, and maps user and event data. Deduplication works through Stape's **Unique Event ID** variable: the web tag and the server tag must read the same value. You need a server container (hosted by Stape or on your own cloud account) and a way to send data from the web container to it, for example server-side GA4 or Stape's Data Tag.

**Direct integration.** Your backend sends events itself, for example an `order_created` when payment is confirmed. It gives you the most control and is the route for app lifecycle and offline events.

## Send an event from your server

Send a POST request to `https://bzr.openai.com/v1/events?pid=<PIXEL-ID>` with your Conversions API key as a bearer token:

```bash
curl -X POST "https://bzr.openai.com/v1/events?pid=<PIXEL-ID>" \
  -H "Authorization: Bearer <CONVERSIONS-API-KEY>" \
  -H "Content-Type: application/json" \
  --data '{
    "events": [{
      "id": "order_12345",
      "type": "order_created",
      "timestamp_ms": 1792771200000,
      "action_source": "web",
      "source_url": "https://shop.example.com/checkout/confirmation",
      "oppref": "<value from the ad click>",
      "user": {
        "obref": "<value of the __obref cookie>",
        "emails_sha256": ["<SHA-256 hash of the normalized email>"],
        "ip_address": "203.0.113.1",
        "user_agent": "Mozilla/5.0 ..."
      },
      "data": {
        "type": "contents",
        "amount": 2599,
        "currency": "USD"
      }
    }]
  }'
```

What each part means:

- `id`: the event ID. Required. One per action; reuse it when you retry the same event and when the browser pixel sends the same conversion.
- `type`: the event name, from the list above, or `custom` together with `custom_event_name`.
- `timestamp_ms`: when the action happened, as a Unix timestamp in **milliseconds**. It must be within the last 7 days and no more than 10 minutes in the future.
- `action_source`: `web` for website events (then `source_url` is required, with scheme and host); other values are `mobile_app`, `offline`, `physical_store`, `phone_call`, `email` and `other`.
- `data.type`: the data shape the event needs: `contents` for `order_created`, `checkout_started`, `items_added`, `contents_viewed` and `page_viewed`; `customer_action` for leads, registrations and appointments; `plan_enrollment` for subscriptions and trials.
- `amount`: an **integer in the currency's minor unit**: `2599` is USD 25.99. If you send `amount`, `currency` is required.

One request can carry up to 1,000 events, but if one event fails, the whole batch fails. To check a request without saving anything, add `"validate_only": true` at the top level. If you send events on behalf of advertisers (an agency or a tool), add a stable `integration_source` value to every request.

## Prevent duplicate events between the pixel and the Conversions API

When the browser pixel and your server report the same action, OpenAI counts them as one conversion if all three match:

1. the **Pixel ID** (the pixel's `pixelId` and the server's `pid`);
2. the **event name** (`order_created` on both sides; for custom events, the same `custom_event_name`);
3. the **event ID**: `event_id` in the pixel call, `id` in the server event.

OpenAI keeps the first event it receives for that combination and ignores later duplicates. For the purchase above, the confirmation page sends:

```js
oaiq(
  "measure",
  "order_created",
  { type: "contents", amount: 2599, currency: "USD" },
  { event_id: "order_12345" }
);
```

Two things break this most often:

- **The browser sends no `event_id`.** The pixel then fills in a random ID of its own (current SDK behaviour), which your server never sees, so the copies never match. See [OpenAI events have no event ID](https://ispixelworking.com/guides/openai.missing_event_id/generic).
- **The ID repeats.** Because OpenAI keeps only the first event per ID, a fixed or cached ID makes OpenAI drop real conversions from other visitors. See [OpenAI event IDs repeat across visits](https://ispixelworking.com/guides/openai.event_id_reused/generic).

If your site sends its events to your own server first and the server forwards them, the forwarded copy must carry the browser's ID: [OpenAI Conversions API deduplication](https://ispixelworking.com/guides/openai.server_copy_without_event_id/generic).

## Improve matching

With each website event, send what you have:

- **`oppref`**, the ChatGPT ad click ID, as an event-level field. OpenAI stresses that, unlike the pixel, the API does not capture it for you: read it from the landing page URL or the pixel's `__oppref` cookie, keep it with the visit or order, and pass the original string unchanged.
- **`user.obref`**, the pixel's browser reference from the `__obref` cookie, unchanged and not hashed. Collect and forward it only in line with the visitor's consent choice.
- **`user.ip_address`** and **`user.user_agent`** of the visitor's device, not hashed.
- **Hashed identifiers:** `emails_sha256`, `phone_numbers_sha256`, `external_ids_sha256`, `first_names_sha256`, `last_names_sha256`. Normalize first (email trimmed and lower case; phone as 8 to 15 digits with the country code, no `+` or leading zeros; names lower case without spaces and ASCII punctuation, accents kept), then hash with SHA-256 and send lower-case hex. Never send raw values.
- **Location as plain text:** `countries` (two-letter codes), `regions`, `cities`, `postal_codes`.

The list fields above (hashed identifiers and locations) hold at most three values each; more returns an HTTP 400 error instead of being cut off.

## How to test and verify

1. **Validate the payload.** Send your first requests with `"validate_only": true` and fix any errors. Validation-only requests do not show up in monitoring.
2. **Send a real test event** and check recent events. OpenAI's monitoring endpoint (`GET https://api.ads.openai.com/v1/conversions/events?pid=<PIXEL-ID>`, with the Advertiser API key) returns a sample from roughly the last 15 minutes. Check the event type, the source and the channel: `pixel_sdk` for browser events, `server_to_server` for yours.
3. **Check one conversion end to end.** For a test purchase, the browser event and the server event share the event name and ID.
4. **Attach the event to campaigns.** Create a conversion event setting for it and attach it to the campaigns you want to measure; attributed conversions then appear in Ads Manager reporting and the Insights API.
5. **Re-run the check on IsPixelWorking.** Server-to-server traffic is invisible from a browser, so a purely server-side setup can still show "not detected". If your site forwards browser events to its own server, the report shows whether those copies carry the browser's event ID.

## If events are missing or duplicated

**Server events are missing:**

- Read the API response in your server log. One invalid event rejects the whole batch.
- Check that `timestamp_ms` is in milliseconds (13 digits), no more than 7 days old and no more than 10 minutes ahead.
- Check that `amount` is an integer in minor units with a `currency`, that `data.type` matches the event, and that `source_url` is present for web events.
- Check the key and host: the Conversions API key goes to `bzr.openai.com`; the Advertiser API key goes to `api.ads.openai.com`. Mixing them up fails authentication.
- Check that the Pixel ID is the data source your campaigns use.

**Events are counted twice:**

- The browser call has no `event_id`, or the server makes up its own ID.
- The names differ (for example a custom `purchase` on the server and `order_created` in the browser).
- The two copies go to different Pixel IDs.
- The same browser event appears twice: a second pixel installation ([duplicate events](https://ispixelworking.com/guides/openai.duplicate_pixel/generic)). Also check for a second server-side sender, such as an app and a tag manager both sending the same purchase.

**Far fewer conversions after adding IDs:** the ID is probably reused across actions. Each action needs its own value.

## Questions people ask

### Do I still need the pixel if I use the Conversions API?

Not strictly: a server-only integration needs a Pixel ID but no browser installation. Without the pixel, though, nothing captures `oppref` from the landing page for you, so your site has to read it from the URL and keep it until the conversion. OpenAI suggests using both, with shared event IDs.

### Can I send Conversions API events from the browser?

No. The API key would be visible to every visitor, and OpenAI's documentation says to always use the pixel in the browser and never call the Conversions API from page code.

### Does the Conversions API work for app installs?

Yes, for `app_installed` and `app_opened` with `action_source` set to `mobile_app`, using the Pixel ID of an existing web data source. OpenAI says native mobile SDKs and mobile data sources are not supported yet.

## Sources

- OpenAI Developers, Conversions API (endpoint, `validate_only`, `integration_source`, batches up to 1,000 events, event fields, timestamp limits, user data and hashing, list limits, `oppref` not captured by the API, `user.obref`, deduplication, app lifecycle events, send all conversion events): https://developers.openai.com/ads/conversions-api
- OpenAI Developers, Conversion Tracking (Pixel ID and data source, Conversions API key, Pixel and Conversions API together, conversion event settings, Monitoring Events, `pixel_sdk` and `server_to_server`): https://developers.openai.com/ads/conversion-tracking
- OpenAI Developers, Measurement Pixel (`event_id`, first event wins, `__oppref` and `__obref` cookies, never call the Conversions API from page code): https://developers.openai.com/ads/measurement-pixel
- OpenAI Developers, Supported Events (event names, data shapes, minor units): https://developers.openai.com/ads/supported-events
- OpenAI Developers, Conversion Setup API reference (pixels and Conversions API keys): https://developers.openai.com/ads/api-reference/conversion-setup
- PixelYourSite, OpenAI pixel (browser pixel and Conversions API share one event ID): https://www.pixelyoursite.com/openai-pixel
- WordPress.org, Pixel Manager for WooCommerce (OpenAI pixel free, Conversions API in Pro): https://wordpress.org/plugins/woocommerce-google-adwords-conversion-tracking-tag/
- Stape, OpenAI Ads Pixel tag for web GTM: https://stape.io/helpdesk/documentation/openai-ads-pixel-tag
- Stape, OpenAI Ads Events API tag for server GTM (Pixel ID, API key, `__oppref` cookie, Unique Event ID deduplication): https://stape.io/helpdesk/documentation/openai-ads-events-api-tag
- Google Tag Manager, Server-side tagging: https://developers.google.com/tag-platform/tag-manager/server-side
