Guide

Secure OpenAI Ads CAPI Implementation: API Keys, Server Boundaries, and Common Leaks

The most common mistake in OpenAI Ads Conversions API (CAPI) implementations is calling the API from browser-side JavaScript, which exposes the secret API key to anyone who opens developer tools. CAPI is a server-to-server integration — the key belongs in your backend, in an environment variable or secrets manager, never in code that ships to a browser. If you're not certain your own implementation avoids this, you can check it yourself in about two minutes using the steps below.

Why this happens: client-side habits copied into a server integration

The OpenAI Ads measurement pixel is designed to run in the browser. You add a script tag, initialize it with a public Pixel ID, and it fires events as visitors interact with your site. That pattern — a snippet that lives in your frontend and just works — is how most teams first encounter OpenAI Ads tracking.

The Conversions API looks superficially similar: same Pixel ID, same general idea of "send an event." But it is a fundamentally different kind of integration. CAPI requires an Authorization: Bearer <API-KEY> header carrying a secret credential with write access to your event stream — not a public identifier, a key. When a developer who's used to pasting pixel snippets into frontend code gets handed a CAPI integration task, the instinct to "just add this API call where the other tracking calls are" is exactly how the key ends up in browser-side JavaScript.

It's an easy mistake to make and a genuinely risky one. Anyone who opens their browser's developer tools on your site can read every request your page sends, including the headers. A leaked CAPI key lets someone send fabricated conversion events under your Pixel ID, or simply use the credential elsewhere without your knowledge.

How to check your own site in two minutes

You don't need backend access to verify this — it's checkable from the same browser tab a visitor would use.

  1. Open your site in a browser and open developer tools (F12, or right-click → Inspect).
  2. Go to the Network tab and reload the page. Complete whatever action would normally trigger a conversion event (add to cart, submit a form, complete a test purchase in a sandbox environment).
  3. In the network request list, search or filter for bzr.openai.com. The public measurement pixel calls this domain too, so seeing requests here isn't automatically a problem — you're looking specifically for requests going to bzr.openai.com/v1/events (the CAPI endpoint) that originate from the page itself, not from your server.
  4. Click into any matching request and check its Request Headers. If you see an Authorization: Bearer ... header on a request the browser itself is sending, your API key is exposed to anyone who runs this exact same check.
  5. Separately, use your browser's View Page Source or search the rendered JavaScript bundles (Sources tab) for the string Bearer or for a key-shaped string near any reference to bzr.openai.com. Keys sometimes end up in client-side code without an active request ever firing — for example, hardcoded in a script that's currently unused but still shipped to the browser.

If either check turns up a key, treat it as already leaked — see the rotation steps near the end of this guide — and fix the implementation using the pattern below.

The correct pattern: browser, backend, CAPI

There is exactly one safe shape for a CAPI integration, and it's worth internalizing as a simple rule: the browser never talks to the CAPI endpoint directly.

Browser (visitor action)
   |
   |  event happens: purchase, lead form submit, signup
   v
Your backend (server you control)
   |
   |  POST https://bzr.openai.com/v1/events?pid=<PIXEL-ID>
   |  Authorization: Bearer <API-KEY>
   v
OpenAI Ads (event recorded)

Concretely: a purchase completes, and your order confirmation logic — running on your server, in code that never ships to a browser — is what calls CAPI. A lead form is submitted, and your form-handling backend calls CAPI after saving the lead. The visitor's browser is never the thing making that request.

Contrast that with the browser-side pixel, which follows a completely different and entirely appropriate pattern: the browser does talk directly to bzr.openai.com, but using a public Pixel ID with no secret involved — that's what the pixel SDK is built for. The distinction that matters is not "does my browser talk to OpenAI's domain" (the pixel is supposed to) but "does my browser hold or transmit the secret API key" (it never should).

If you're setting up CAPI for the first time rather than fixing an existing implementation, our complete CAPI setup guide walks through the payload schema and a working example from scratch, built around this same server-only boundary from the start.

Key storage best practices

Once the key is confirmed server-side only, how you store it on the server still matters:

  • Use environment variables or a secrets manager, not a config file that lives in your codebase. An environment variable injected at deploy time, or a proper secrets manager (your cloud provider's, or a dedicated tool), keeps the key out of anywhere it could be committed by accident.
  • Never commit it to source control — not in a .env file that gets added by mistake, not in a comment "for reference," not in a commit message. If it's ever been committed, even in a private repository, treat it as exposed and rotate it; git history is not a safe place to bury a secret you've since removed from the current file.
  • Scope access to who actually needs it. If your CAPI-calling code runs in one backend service, only that service's environment needs the key — not every developer's local .env, not a shared team wiki, not a general-purpose config file that half your engineering org can read.
  • Rotate periodically, and immediately after any suspected exposure. A key that's been in place for a long time with broad access is a bigger blast radius if something goes wrong. Rotation is a normal part of key hygiene, not just an incident response step.
  • Keep the key separate from the Pixel ID in how you think about risk. The Pixel ID is meant to be public — it's in your browser pixel's initialization call, visible to anyone who views your page source. The API key is not, and conflating the two ("well, the Pixel ID is already public, so...") is how teams talk themselves into being casual with the key.

Logging and access-boundary hygiene

Key exposure isn't the only way sensitive data leaks out of a CAPI integration. Two habits worth building in from the start:

  • Don't log full request payloads that include user data. It's tempting to log the entire body of every CAPI call for debugging, but if your data object includes anything tied to a real person — order details, contact information passed via the optional user field — that data now lives in your logging system too, often with looser access controls and longer retention than your primary database. Log enough to debug (status codes, event id, event type, timestamps) without logging the full payload by default.
  • Apply least privilege to whoever and whatever can trigger a CAPI call. If your CAPI integration lives inside a shared backend service, make sure the code path that calls it doesn't also have broader permissions than it needs, and that access to view or export logs isn't wider than access to the systems those logs describe.

Neither of these is specific to OpenAI Ads — they're standard practice for any server-side integration handling conversion or customer data — but they're easy to skip when a CAPI integration gets built quickly to hit a launch date.

Catching unsafe implementations automatically

Manually checking your own network tab, as described above, works fine for a one-time audit of your own site. If you're managing tracking across multiple client sites, or want an ongoing check rather than a manual one, our free OpenAI Ads Pixel Helper Chrome extension flags unsafe browser-side CAPI calls automatically as you browse — the same pattern described in the two-minute check above, run continuously instead of by hand. It's a lightweight way to catch a regression (a future code change that accidentally reintroduces a browser-side CAPI call) before it turns into an actual leak.

If a key leaked: rotate first, audit second

If you've confirmed — through the check above, a code review, or any other means — that your API key has been exposed in browser-side code at any point, treat it as compromised even if you've since removed the exposing code. The sequence that matters:

  1. Rotate the key immediately in the conversions tab of OpenAI Ads Manager. Generate a new key before you do anything else; the exposed one should be treated as unusable from this point forward.
  2. Update every place the old key was stored — environment variables, secrets manager entries, any deployed service still referencing it — with the new key, and confirm the old one is no longer active anywhere in your infrastructure.
  3. Audit for how it got there. Was it a one-off mistake in a script that's since been fixed, or a pattern that could recur — for example, a shared code template that other integrations were copied from? Fix the root cause, not just the immediate instance.
  4. Re-run the two-minute browser check described earlier, on the live site, after deploying the fix, to confirm the new implementation is actually server-side only and not just moved to a different client-side location.

Rotating the key is not optional caution — it's the only way to actually close the exposure, since simply removing the code that leaked it doesn't invalidate a key that's already been seen.


Getting the CAPI boundary right the first time is simpler than fixing it after a launch, mostly because a security review is much easier to build into a setup than to bolt on afterward. If you want that boundary verified as part of the build rather than checked after the fact, that's what a security-conscious CAPI setup should include from day one.

Want this done for you?

Fixed-scope setup, tested end-to-end and documented.

Explore the serviceTake the free audit