Skip to main content

ChatGPT's Ad Pixel and the __obi Cookie

Intermediate
What you'll learn
  • Separate the three cookies in OpenAI's ad stack — two on the advertiser's domain, one on .openai.com — and understand why only the third one links you
  • Follow the sync flow: a 60-second RS256 JWT minted by ChatGPT and POSTed cross-site to a collector that sets the join key
  • Learn why a cookie banner's "reject marketing" can leave this running, and why the advertiser's own consent call cannot switch it off
  • See what Automatic Advanced Matching scrapes from a page nobody configured it to read — and which fields OpenAI's own docs say travel unhashed
  • Know exactly where the mechanism does not work (and why an iPhone is a different threat model from an Android)
  • Check your own browser in five minutes, and harden your own pixel in two settings

Most people's mental model of ChatGPT ads is a box: ads appear inside ChatGPT, personalized by what you said inside ChatGPT, and the box has a wall around it. Advertisers buy placements; they never see your conversations. That part is true, and OpenAI says it plainly.

The part outside the box is the interesting part. OpenAI also ships a measurement pixel that advertisers install on their own websites, the way they install Meta's or Google's. Measurement pixels need a join key: some identifier that says the browser that saw the ad is the browser that reached checkout. In September 2026 an independent researcher traced how that join key gets created, and it turns out to be minted by ChatGPT itself, handed to an OpenAI ad collector, and then carried by your browser to every advertiser site running the pixel.

The flow is not a leak or a bug. It's a designed ad-measurement pipeline — the same shape Meta and Google have run for a decade. What makes it worth a page is one specific design choice about consent classification, plus a default that flipped on in August 2026 and quietly turned a lot of advertiser websites into identity scrapers.

Three cookies, two domains​

Read any of the coverage and you will see __obi, __obref and __oppref used almost interchangeably. They are three different things with three different privacy profiles, and conflating them is how people end up either overreacting or reassuring themselves with the wrong control.

CookieSet onSet byLifetimeWhat it is
__opprefthe advertiser's own domainthe pixel SDK30 days from each writeAttribution ID captured from the landing-page URL. Ordinary click-tracking.
__obrefthe advertiser's own domainthe pixel SDK365 daysA random browser reference, scoped to that one advertiser.
__obi.openai.combzr.openai.com365 days (Max-Age=31536000)The cross-site join key. HttpOnly, Secure, SameSite=None.

The first two are documented by OpenAI in its public advertiser docs. They are first-party to the advertiser and, on their own, do not link you across sites — the researcher's capture bears this out: 2,828 of 2,860 observed __obref values appeared under exactly one advertiser, which is what a per-site cookie should look like.

__obi is the one that isn't per-site. It lives on .openai.com with SameSite=None, which is precisely the configuration that lets a browser attach it to requests made from someone else's page. Of 30 distinct __obi values the researcher observed, 12 appeared under more than one advertiser — one of them under ten. That is the cross-site graph, measured.

★ __obi is HttpOnly. No JavaScript on any page can read it, including the pixel's. That sounds protective and mostly isn't: the browser attaches it automatically to requests aimed at bzr.openai.com, so the pixel never needs to read it. HttpOnly stops page scripts from stealing the identifier; it does nothing to stop the identifier from being used.

How the join gets made​

Guided walkthrough1 of 4
  1. The ChatGPT web client generates 16 random bytes and POSTs to /backend-api/bazaar/obi/sync-token on chatgpt.com — or /backend-anon/… when you are logged out. The response is an RS256 JWT with a 60-second expiry.

Of 932 sync tokens the researcher decoded, 736 carried subject_type: account_user and 196 were anonymous. So in roughly four out of five observed syncs, the identifier being planted was minted in the context of a logged-in ChatGPT account.

The author is careful about what that does and does not prove, and you should be too: the join from __obi back to an account was inferred from the token design, not directly observed. What is observed is that ChatGPT mints an account-scoped token, a collector converts it into a long-lived cross-site cookie, and that cookie then shows up under multiple unrelated advertisers.

The consent classification is the whole story​

Here is the part that makes this different from "Meta has a pixel too."

OpenAI's cookie policy lists __obi under analytics. The campaign-measurement cookies of Google, Meta, LinkedIn, TikTok, Reddit and Microsoft sit under marketing in the same interface. The JWT that triggers the sync carries consent_decision: "analytics_allowed" — not a marketing consent.

Follow the consequence through a normal European cookie banner:

  • You click "Reject marketing, allow analytics" — a choice a large fraction of privacy-conscious users make, and one many consent platforms present as the sensible middle.
  • Analytics consent is granted, so the sync token issues.
  • __obi gets planted on .openai.com for a year.
  • Every advertiser pixel you subsequently hit receives it.

You declined the category the behaviour belongs to, and the behaviour happened anyway, because it was filed under a different category. The researcher asked OpenAI about exactly this — why __obi is classified as analytics, and whether users refusing marketing consent still receive it — by mail to press@openai.com and privacy@openai.com on 2026-09-14. OpenAI Support acknowledged the inquiry and answered neither question.

★ The advertiser's consent switch cannot help you. OpenAI's docs give advertisers oaiq("consent", false), which "removes both cookies" — meaning __obref and __oppref, the two on the advertiser's own domain. It has no reach into .openai.com. A site that gates the pixel perfectly behind its CMP is gating the two cookies that were never the linkability problem.

There is a separate, real control in ChatGPT itself: Settings → Data Controls → Marketing Privacy, reported as on by default for free accounts and off for paid ones. Treat its relationship to __obi as unresolved: the investigation did not test whether flipping it stops the sync, OpenAI did not answer when asked, and a setting labelled marketing governing a cookie filed as analytics is exactly the mismatch at issue. Turn it off — it is the right switch to reach for — but do not assume it is the one that stops the wire behaviour until someone captures traffic with it off.

Automatic Advanced Matching: the pixel reads the page​

A conversion pixel's problem is that click identifiers go missing — blocked, stripped, or lost across a redirect. The industry answer is advanced matching: send hashed identity alongside the event so the ad platform can join on that instead. Meta has shipped it for years. OpenAI's version has one twist worth knowing about.

Automatic Advanced Matching (AAM) does not wait for the advertiser to pass anything. OpenAI's docs say the pixel "automatically detects supported customer information from your website." The researcher's capture shows four labelled collection sources:

LabelSourceHow it gets the value
inAdvertiser-suppliedThe site deliberately passes it
fmForm fieldsScraped from recognizable inputs on the page
htPage textParsed out of rendered content
jsTag managerRead from window.dataLayer, the Adobe data layer, or renamed GTM layers

In the observed events, scraped identity outnumbered advertiser-supplied identity 685 to 255. The pixel was, most of the time, collecting things nobody asked it to collect.

Hashed vs clear — straight from OpenAI's own docs. Email, phone, external ID, first name and last name are normalized and SHA-256'd in the browser. Country (ISO code), region, city and postal code are listed as clear fields. OpenAI's line "raw customer information is not sent to OpenAI through automatic advanced matching" is scoped to the identity set; geography is not in that set. The capture agrees: postal code was the most-harvested form field, 100 events across 28 sites, unhashed.

There is a denylist — OpenAI excludes passwords, one-time codes, card numbers, SSNs, dates of birth, medical history, diagnosis and court-related fields. That is a real mitigation and worth crediting. It is also a denylist, which means it protects the categories someone thought of.

Identity is not the only payload. The events also carry the URL path of the page that fired them — product pages, article URLs, checkout steps — with query-string search terms reported as stripped. For a retailer that means the specific items you looked at; for a publisher it means the specific articles you read.

★ The default flipped on 2026-08-17. AAM was already the default for newly created Web pixels; on that date OpenAI turned it on for existing pixels too, opt-out rather than opt-in. The researcher found AAM enabled on 638 of 881 pixels whose settings were visible. If you installed an OpenAI pixel in early 2026 and never touched it since, your site started scraping form fields in August without a deploy on your side.

Where it does not work​

This is not a universal tracker, and the exceptions matter more than they usually do:

  • Chrome for Android — the platform the researcher reproduced it on. Affected.
  • Safari, and Chrome on iOS — not affected. Safari's Intelligent Tracking Prevention blocks the third-party cookie, and every iOS browser runs on WebKit, so "Chrome on iPhone" inherits the same block. An iPhone user and an Android user are genuinely in different threat models here.
  • Desktop Chrome — untested in the published work. Unknown, not safe.
  • Coverage is partial anyway. Only about 20% of observed ChatGPT sessions produced a sync token at all; the mobile web client serves ads without syncing. Whatever graph exists is sparse, not total.

That last number cuts both ways. It means the identifier is far from universal — and it means "I checked once and saw nothing" is not evidence of absence.

Check your own browser​

Five minutes, no tooling beyond DevTools. Do this on Android Chrome if you want to see the mechanism at all.

Look for the sync, then the cookie

# 1. Open ChatGPT with DevTools → Network, filter: obi
#    Look for a POST to:
#      /backend-api/bazaar/obi/sync-token      (logged in)
#      /backend-anon/bazaar/obi/sync-token     (logged out)
#    then a cross-site POST to:
#      https://bzr.openai.com/v1/obi/sync

# 2. Paste the JWT from the sync-token response into a local decoder.
#    You are looking for these claims:
#      iss: "chatgpt-wadi"    aud: "bzr.openai.com"
#      purpose: "obi_sync"    consent_decision: "analytics_allowed"
#      subject_type: "account_user" | "anonymous"

# 3. Application → Cookies → https://chatgpt.com
#    Look for __obi on domain .openai.com, Max-Age 31536000,
#    HttpOnly, Secure, SameSite=None.

# 4. Now visit a retailer that runs the pixel and filter for:
#      bzrcdn.openai.com/sdk/oaiq.min.js
#      bzr.openai.com/v1/sdk/events
#    Check the Cookies tab of that events request: __obi riding along
#    is the cross-site join, observed on your own machine.

Decode the JWT locally. Pasting a token that contains an account-scoped sub into a web-based decoder hands that identifier to a third party — a slightly absurd way to end a privacy investigation.

If you run the pixel​

You are a data controller here in a way the pixel's install snippet did not make obvious. Two settings and one habit:

Guided walkthrough1 of 4
  1. Ads Manager → Tools → Conversions → Data Source → Edit pixel, then turn Automatic Advanced Matching off (or confirm you want it on). The setting is per-pixel — if you run several, you change several. Since 2026-08-17 the default is on, so inaction is a decision.

★ Hashed is not anonymous. SHA-256 of an email address is a deterministic identifier: anyone holding the same email computes the same digest. That is the entire point — it is how the join works. Treat hashed identity as pseudonymous personal data in your compliance analysis, not as de-identified.

If you just use ChatGPT​

  • Turn off Settings → Data Controls → Marketing Privacy. Right switch to reach for; effect on __obi unconfirmed, as above.
  • Use the cookie manager, not only the account setting — for logged-out browsing, the "Manage Cookies" / "Your Privacy Choices" footer link is the only surface you have.
  • Send Global Privacy Control. OpenAI states it honours GPC as a legally valid opt-out signal. Firefox and Brave send it natively; there are extensions for Chrome. It is a machine-readable signal with regulatory weight behind it in California and several other jurisdictions.
  • Browser choice is a real control here. Safari blocks the mechanism today. That is not an argument to switch browsers over one cookie — it is a reminder that third-party cookie policy is doing more work in your privacy posture than any toggle you will find in a settings screen.

What is actually established, and what is not​

Worth keeping straight, because the coverage blurs it:

Established by capture: the endpoints, the cookie attributes, the JWT claims including consent_decision, the four collection sources, the field-level hashed/clear split (which OpenAI's own docs confirm), the counts — 936 pixels across 1,029 hostnames, 12 of 30 __obi values under multiple advertisers, 638 of 881 pixels with AAM on.

Inferred, and labelled as such by the author: that __obi is joined back to a ChatGPT account server-side. The token design points that way; the join was not observed.

Unanswered by OpenAI: why __obi is classified as analytics, and whether declining marketing consent still results in the sync.

Untested: desktop Chrome, and whether the Marketing Privacy toggle stops the wire behaviour.

The ad stack, in eight terms
Press Enter or Space to flip the card. Use the left and right arrow keys to move between cards.Term shown.
1 / 8

Check yourself

0/5
  1. A European user clicks "Reject marketing, allow analytics" in a cookie banner. What happens to the __obi sync?
  2. An advertiser calls oaiq("consent", false) on every page load before consent. What does that actually remove?
  3. Which fields does OpenAI's own pixel documentation say are transmitted WITHOUT hashing?
  4. Why is an iPhone user in a different position from an Android user here?
  5. You installed an OpenAI ads pixel in March 2026 and haven't touched the settings since. What changed on 2026-08-17?

Sources & further reading​

Next​