Fix guide · Framer · advanced · about 60 min
OpenAI Pixel event_id missing on Framer: fix deduplication
For the finding “OpenAI events have no event ID”. Also for: Any website, Shopify, WordPress, Webflow.
What this means
Each event the OpenAI Pixel sends can carry an event ID that your site sets: the event_id option in the fourth argument of oaiq("measure", ...), or the Event ID field of a Google Tag Manager (GTM) tag. Your Framer site sends its events without one.
The requests still contain an ID: when your site passes none, the pixel puts a random value in the event’s id field by itself (OpenAI does not document this, but you can see it in the request payload). That value exists only in the visitor’s browser. Your server never learns it, so it cannot send the same ID with its copy of the event.
Whether this costs you money depends on one question: does your site also send these events to OpenAI from a server, through the OpenAI Conversions API?
- Yes, or you are not sure: OpenAI recognizes a browser event and a server event as one conversion when they share the Pixel ID, the event name and the event ID. With a random browser ID they never match, so the same lead can be counted twice.
- No, you only use the browser pixel: there is nothing to deduplicate yet. It matters the day you add the Conversions API.
We cannot see server-to-server traffic from a browser, so we cannot tell whether your Conversions API works. We only see whether your site passes its own event IDs to the pixel.
How to confirm it yourself
Because the pixel fills in an ID on its own, the Network tab cannot tell you whether your site sets one. Look at your setup instead.
- Search Site Settings > Code, each page’s own custom code, your code overrides and your GTM tags for
oaiq("measure". For each call, look for a fourth argument withevent_id, as in the general guide. - In GTM, open each OpenAI tag. In OpenAI’s template (OpenAI Ads Measurement Pixel) the field is Event ID; in Stape’s OpenAI Ads Pixel template it is in the Server-side tracking settings section. An empty field means the pixel uses its own random value.
- Submit a test form on the published site with the Network tab open (filter
bzr.openai.com) and compare theidin thelead_createdevent with the ID your server sends for the same submission. If they differ, deduplication fails.
How to fix
OpenAI keeps the first event it receives for the same Pixel ID, event name (for custom events, custom_event_name) and event ID, and ignores later duplicates. So: give the browser event your own ID, send exactly the same ID from the server, and never reuse a fixed value, or real conversions are dropped as duplicates.
On a Framer site the key conversion is usually a form submission (lead_created). Pick the route that matches how your server events are sent.
Route 1: Tag Manager with a server container. This fits when your OpenAI tags already run in GTM behind Framer’s Cookie Banner.
-
Add a variable that produces a unique ID per event, for example Stape’s Unique Event ID variable (third-party, from the Community Template Gallery).
-
Put it in the web tag’s event ID field: Event ID in OpenAI’s template, Server-side tracking settings in Stape’s.
-
Send the event to your server container (for example through server-side GA4 or Stape’s Data Tag) and map the same value to the event ID of the server tag. In Stape’s OpenAI Ads Events API tag this is Event ID under Server Event Data Parameters.
-
To fire the
lead_createdtag when a Framer form is submitted, give the form a tracking ID in its settings. Framer then dispatches aframer:formsubmitbrowser event on each submission. Pass it to the data layer with a custom code entry in Site Settings > Code, set to Run: Once (Framer recommends Once for event listeners):<script> document.addEventListener("framer:formsubmit", function (event) { window.dataLayer = window.dataLayer || []; window.dataLayer.push({ event: "framer_form_submit", form_tracking_id: event.detail.trackingId }); }); </script>In GTM, use a Custom Event trigger for
framer_form_submit, and keep the samead_storageconsent requirement as your other OpenAI tags.
Route 2: a Framer form webhook and your own server. This fits when the form goes to a webhook (your backend or an automation tool) that sends lead_created to the Conversions API.
-
Add a hidden field to the form: select the Input, turn on Hidden and give it the name
event_id. Do not hide it with the Visible property: Framer leaves invisible inputs out of the submission. -
Fill it with a fresh ID for each submission. Framer does not fill hidden fields by itself; its help points to a code override for this (its example is UTM parameters). Have the override write a new random value, for example from
crypto.randomUUID(), whenever the form is shown. -
Send the same value with the browser event. Give the form a tracking ID and read the field when Framer reports the submission (custom code entry, Run: Once). Replace
contact-formwith your form’s tracking ID:<script> var lastForm = null; // The browser's own submit event says which form was sent document.addEventListener("submit", function (event) { lastForm = event.target; }, true); document.addEventListener("framer:formsubmit", function (event) { if (event.detail.trackingId !== "contact-form" || !lastForm) return; var field = lastForm.querySelector('input[name="event_id"]'); if (!field || !field.value || !window.oaiq) return; oaiq("measure", "lead_created", { type: "customer_action" }, { event_id: field.value }); }); </script>framer:formsubmitonly tells you the form’s tracking ID, so the code reads the field inside the form the browser just submitted. A plaindocument.querySelectorwould take the field of the first form on the page, which is wrong on pages with several forms. Framer firesframer:formsubmitwhen someone submits the form, not when the submission is confirmed, so a failed submission can still send a browser event; step 2 of “How to verify the fix” compares the IDs on both sides.The
window.oaiqcheck skips visitors whose pixel has not loaded because they have not consented. If the pixel runs in GTM, pushfield.valueto the data layer instead and use it in the tag’s Event ID field through a Data Layer Variable. -
Reuse it on the server. Framer’s webhook sends the submission as JSON with the input names as keys, so the ID arrives as
event_id. Send it asidon the Conversions API event, with the typelead_createdand the same Pixel ID. Do not use theFramer-Webhook-Submission-Idheader for this: it is unique per submission, but the browser never sees it, so the pixel cannot send it.
Route 3: a third-party tool sends your server events. Check its documentation for how it sets event IDs. It can only match browser events it sends itself, so remove any manually installed pixel; a second copy keeps sending random IDs and usually also shows up as “Duplicate OpenAI events”.
Publish the Framer site, and the GTM container if you changed it.
Common causes
- The snippet and the
measurecalls were pasted into Framer’s custom code without the optional fourth argument. - The Event ID field of the GTM web tag is empty, or the server tag reads a different variable.
- Both sides generate their own ID: for example the server uses Framer’s webhook submission ID while the pixel makes up its own.
- The hidden field was hidden with Visible instead of Hidden, so the ID never reaches the webhook.
- The Conversions API was added later, and the browser calls were never updated.
How to verify the fix
- Every key OpenAI event (tag or
measurecall) passes an event ID, and the value changes from one submission to the next. - For one test submission, the
idin the browser request equals theevent_idin the webhook payload and theidyour server sends. - With access to the Advertiser API, OpenAI’s recent-events endpoint (
GET /v1/conversions/events?pid=<PIXEL-ID>) lists a sample of events from roughly the last 15 minutes with their channel:pixel_sdkfor the browser andserver_to_serverfor your server. - Re-run the check on IsPixelWorking. The issue should be gone.
Sources
- OpenAI Developers, Measurement Pixel (
measurearguments,event_idoption, deduplicating browser and server events, first event wins): https://developers.openai.com/ads/measurement-pixel - OpenAI Developers, Conversions API (event
id, deduplication on Pixel ID, event name andid): https://developers.openai.com/ads/conversions-api - OpenAI Developers, Conversion Tracking (same Pixel ID, event name and event ID through both integrations; Monitoring Events): https://developers.openai.com/ads/conversion-tracking
- OpenAI on GitHub, Ads Measurement Pixel GTM template (Event ID field): https://github.com/openai/ads-measurement-pixel-gtm-template
- Stape, OpenAI Ads Pixel tag (third-party; Server-side tracking settings): https://stape.io/helpdesk/documentation/openai-ads-pixel-tag
- Stape, OpenAI Ads Events API tag (third-party; Unique Event ID variable, Event ID in Server Event Data Parameters): https://stape.io/helpdesk/documentation/openai-ads-events-api-tag
- Framer Help, How to track links and forms in Framer (form tracking ID,
framer:formsubmitevent withtrackingId): https://www.framer.com/help/articles/how-to-track-links-and-forms-in-framer/ - Framer Help, How to add hidden fields to forms for tracking (Hidden property, invisible inputs excluded, code override): https://www.framer.com/help/articles/hidden-fields-form-tracking/
- Framer Help, Connect Framer forms to a webhook (JSON with input names as keys,
Framer-Webhook-Submission-Idheader): https://www.framer.com/help/articles/framer-form-webhook-setup/ - Framer Help, How to add custom code (“Once” for event listeners): https://www.framer.com/help/articles/how-to-add-custom-code/
More OpenAI guides
- OpenAI Pixel fires before consent on Framer: how to fix
- OpenAI Pixel not firing after consent on Framer: how to fix
- Repeated OpenAI event IDs drop ChatGPT Ads conversions
- Duplicate OpenAI Pixel events on Framer: how to fix
- ChatGPT Ads click ID (oppref) lost on Framer: how to fix
- OpenAI Pixel tracks a button on desktop but not on mobile
- Two OpenAI Pixel IDs on one site: when it's fine, how to fix
- OpenAI Conversions API deduplication: match the pixel event_id
- OpenAI Ads Pixel setup: install the ChatGPT Measurement Pixel
- How to set up the OpenAI Conversions API for ChatGPT Ads