Section 2. The Measurement Stack · Last verified: MAY 2026

The JavaScript Measurement Pixel

Sacha Lefebvre, founder of Paid Ai Search
Sacha Lefebvre Founder, Paid Ai Search · LinkedIn

Chapter 08 - The JavaScript Measurement Pixel

The JavaScript Pixel is the browser-side SDK that fires website conversion events after a user clicks an ad in ChatGPT.

Install snippet

The pixel must be added to the <head> of every page that should report conversions, ideally near the top so early conversions are not lost while other content loads.

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

  oaiq("init", {
    pixelId: "<YOUR-PIXEL-ID>",
  });
</script>

pixelId is required. Provision one from the Conversions tab of Ads Manager. debug: true is optional and writes SDK activity to the browser console during testing.

CSP requirements

Sites with a Content-Security-Policy header that blocks 'unsafe-inline' will refuse the inline <script> install above. Two adjustments are then required.

Hosts to allowlist:

DirectiveHostWhy
script-srchttps://bzrcdn.openai.comThe SDK loads from this CDN.
connect-srchttps://bzr.openai.comThe SDK posts events here.
connect-srchttps://bzrcdn.openai.comSome SDK paths fetch resources from the CDN at runtime.

External-file install pattern (when 'unsafe-inline' is blocked):

Move the IIFE into a same-origin static file (for example static/oaiq-init.js) and reference it from the page with a data-pixel-id attribute:

// /oaiq-init.js
(function () {
    var self = document.currentScript;
    if (!self) return;
    var pixelId = self.dataset.pixelId;
    if (!pixelId || window.oaiq) return;

    var queue = function () { queue.q.push(arguments); };
    queue.q = [];
    window.oaiq = queue;

    var sdk = document.createElement('script');
    sdk.async = true;
    sdk.src = 'https://bzrcdn.openai.com/sdk/oaiq.min.js';
    var first = document.getElementsByTagName('script')[0];
    first.parentNode.insertBefore(sdk, first);

    window.oaiq('init', { pixelId: pixelId, debug: false });
})();

Page reference:

<script src="/oaiq-init.js" data-pixel-id="<YOUR-PIXEL-ID>"></script>

The script tag is external and same-origin (passes script-src 'self'), the pixel ID rides on a data attribute (not JS code, so CSP-irrelevant), and the SDK injection happens through the JavaScript DOM API rather than inline markup. This pattern works under both relaxed and strict CSP. Both the framework-level CSP (e.g. svelte.config.js, next.config.js) and the deploy-platform header (e.g. vercel.json) must include the host allowlist above; the browser intersects them and the most restrictive wins.

Sending events

oaiq("measure", eventName, eventProps, eventOptions);
ArgumentRequiredDescription
eventNameYesA standard event name (Chapter 10) or custom.
eventPropsNoEvent data. Always send type and any optional fields explicitly.
eventOptionsNoDelivery options, including event_id for deduplication and custom_event_name for custom events.

custom_event_name and event_id go in eventOptions, never in eventProps. This trips up almost everyone the first time.

What the SDK handles automatically

  • Captures oppref from the landing-page URL. OpenAI’s privacy-preserving identifier.
  • Stores oppref in a first-party __oppref cookie so later page views can reuse it.
  • Adds the current page origin as source_url on every event.
  • Timestamps each event.
  • Batches closely grouped measure calls.

You do not configure any of this manually. The SDK does it.

Operational rules

  • Use integers for amount and quantity. No strings, no floats.
  • Use only documented fields inside contents[] (Chapter 11).
  • Always run the pixel from the browser. Never call the server-side Conversions API directly from page code, it will leak your API key.

Example: order conversion (Shopify)

oaiq("measure", "order_created", {
  type: "contents",
  amount: 2599,
  currency: "USD",
  contents: [
    {
      id: "sku_123",
      name: "Starter bundle",
      content_type: "product",
      quantity: 1
    }
  ]
}, {
  event_id: "order_12345"
});

amount: 2599 means $25.99 in a USD account, integers in the lowest currency denomination, always.

Pixel-only is fine, dual-shipping is better

The pixel by itself produces usable attribution. OpenAI’s own recommendation is to dual-ship pixel + Conversions API for resilience: pixel events are vulnerable to ad-blockers, network drops, and JavaScript errors; server-side events are not. Chapter 9 covers the server side, Chapter 12 covers how to deduplicate when running both.