The JavaScript Measurement Pixel
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:
| Directive | Host | Why |
|---|---|---|
script-src | https://bzrcdn.openai.com | The SDK loads from this CDN. |
connect-src | https://bzr.openai.com | The SDK posts events here. |
connect-src | https://bzrcdn.openai.com | Some 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); | Argument | Required | Description |
|---|---|---|
eventName | Yes | A standard event name (Chapter 10) or custom. |
eventProps | No | Event data. Always send type and any optional fields explicitly. |
eventOptions | No | Delivery 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
oppreffrom the landing-page URL. OpenAI’s privacy-preserving identifier. - Stores
opprefin a first-party__opprefcookie so later page views can reuse it. - Adds the current page origin as
source_urlon every event. - Timestamps each event.
- Batches closely grouped
measurecalls.
You do not configure any of this manually. The SDK does it.
Operational rules
- Use integers for
amountandquantity. 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.