---
title: "OpenAI Conversions API deduplication: match the pixel event_id"
description: "Your server copy of a ChatGPT Ads event has no event ID or a different one, so OpenAI may count the conversion twice. How to pass one ID from browser to server."
updated: "2026-10-23"
url: "https://ispixelworking.com/guides/openai.server_copy_without_event_id/generic"
---

# OpenAI Conversions API deduplication: match the pixel event_id

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

For the findings “OpenAI server events have no event ID” (`openai.server_copy_without_event_id`) and “OpenAI server events carry a different event ID” (`openai.server_event_id_mismatch`).

## What this means

Your site sends its OpenAI events twice, on purpose: once from the browser through the OpenAI Pixel, and once more to your own server, which forwards them to OpenAI through the Conversions API. OpenAI suggests using both, to combine browser activity with conversions your backend confirms, as long as both copies can be recognized as one.

The server copy we saw does not carry the browser event's ID:

- **No event ID:** the request to your server has the event name but no ID at all.
- **A different event ID:** the request carries an ID, but not the one the browser event had.

OpenAI treats two events as the same conversion only when the Pixel ID, the event name and the event ID all match: `event_id` in the pixel, `id` in the Conversions API. Without that, each conversion may be counted twice.

We report this as **possible**: we can see the request from the browser to your server, but never the request from your server to OpenAI, so check the server side yourself.

Why it costs you money:

- Purchases and leads count twice in ChatGPT Ads, so reported results and return on ad spend look better than they are.
- Conversion-optimized campaigns learn from inflated numbers and optimize toward the wrong results.
- When someone later "fixes" the double counting by switching one side off, you lose the reason you added the server copy.

## How to confirm it yourself

1. Open your page in a **private (incognito) window**, open developer tools (F12) and go to the **Network** tab. Accept cookies and trigger the conversion (for example, submit a test lead).
2. Filter by `bzr.openai.com`, open the pixel request and note the event's `type` and `id` in the payload.
3. Clear the filter and find the request your site sends to its own server at the same moment: a form submission, an `/api/...` call, or a request to your server-side tag manager address. Open its payload.
4. Compare: does it contain the same ID as the pixel event? If there is no ID, or a different one, the two copies cannot be matched.
5. Check your server code (or your server-side tag) for what it sends to `bzr.openai.com/v1/events` as each event's `id`.

A frequent surprise: the browser `id` is a random UUID that nobody chose. That means your code calls `oaiq("measure", ...)` without an `event_id`, and the pixel filled in a value of its own (current SDK behaviour; OpenAI's docs only describe the `event_id` you set). The server cannot know it, so the copies will never match until the browser sends an ID of yours (see "OpenAI events have no event ID").

## How to fix

The rule: **one action, one ID, created in one place and used by both copies.**

1. **Pick where the ID is created.**
   - For purchases, the order number is ideal: the confirmation page can use it in the browser, and your backend already has it.
   - For leads, page views and clicks, create a random ID in the browser when the action happens (for example `crypto.randomUUID()`), or create it on your server when it renders the page or processes the form, and hand it to the browser.
2. **Pass it to the pixel** in the fourth argument:

   ```js
   const eventId = crypto.randomUUID();
   oaiq("measure", "lead_created", { type: "customer_action" }, { event_id: eventId });
   ```

3. **Send the same value to your server** with the request it already receives: a hidden form field, a property in your `/api/...` call, or an event parameter in your server-side tag manager feed.
4. **Use it as `id` in the Conversions API event**, with the same event `type` and the same Pixel ID (`pid`) as the browser event:

   ```json
   {
     "events": [{
       "id": "4b1f0c6e-2f7d-4f6b-9a51-2d3c8e7f9a10",
       "type": "lead_created",
       "timestamp_ms": 1792771200000,
       "action_source": "web",
       "source_url": "https://example.com/contact",
       "data": { "type": "customer_action" }
     }]
   }
   ```

   For custom events, both copies also need the same `custom_event_name`.
5. **Do not create a second ID on the server.** A server that makes its own random ID, prefixes it (`srv-…`), or uses a different value (for example the database row ID while the browser used the order number) produces a mismatch even though both copies "have an ID".
6. **With server-side Google Tag Manager:** generate the ID once with a variable such as Stape's **Unique Event ID**, put it into the web tag's server-side tracking settings, and map the same value to **Event ID** in the server tag (Stape's **OpenAI Ads Events API** tag). Check both containers in Preview mode: the web tag and the server tag must show the same value for the same event.
7. **Forward the click and browser references too.** While you are in the server code: OpenAI asks you to pass the ad click ID as `oppref` and the `__obref` cookie value as `user.obref`, unchanged, when available and allowed by the visitor's consent. They improve attribution but do not replace the event ID.
8. **Publish** and run a test conversion.

## Common causes

- The browser calls `measure` without `event_id`, so the pixel uses a random ID the server never sees.
- The server-side code was added later and never received the ID from the browser.
- Browser and server each generate their own random ID.
- The server sends a different identifier (database ID, a prefixed value, the order number while the browser used a random value).
- The server event uses another event name (for example `purchase` or a custom name) than the browser's `order_created`.
- In server-side GTM, the web tag and the server tag read different variables.

## How to verify the fix

1. For one test conversion, the pixel event's `id`, the ID in the request to your server and the `id` your server sends to OpenAI are identical, and so are the event names.
2. With access to the Advertiser API, OpenAI's recent-events endpoint (`GET /v1/conversions/events?pid=<PIXEL-ID>`) shows a sample of the last roughly 15 minutes, with `pixel_sdk` for browser events and `server_to_server` for server events.
3. Re-run the check on IsPixelWorking. The finding should be gone.

## Sources

- OpenAI Developers, Conversions API (event `id`; deduplication on Pixel ID, event name and `id`; first event wins; `oppref` and `user.obref`): https://developers.openai.com/ads/conversions-api
- OpenAI Developers, Measurement Pixel (`event_id` option, deduplicating browser and server events): https://developers.openai.com/ads/measurement-pixel
- OpenAI Developers, Conversion Tracking (use the same Pixel ID, event name and event ID through both integrations; Monitoring Events): https://developers.openai.com/ads/conversion-tracking
- Stape, OpenAI Ads Events API tag (Unique Event ID variable, Event ID mapping between web and server containers): https://stape.io/helpdesk/documentation/openai-ads-events-api-tag
