# ChatGPT 会展示广告。以下是完整的归因流程

- 来源：Hacker News 热门（buzzing.cc 中文翻译）
- 作者：lmbbuchodi
- 发布时间：2026-04-29 09:37
- AIHOT 分数：54
- AIHOT 链接：https://aihot.virxact.com/items/cmoje0r2603ihslzpe2peldmw
- 原文链接：https://www.buchodi.com/how-chatgpt-serves-ads-heres-the-full-attribution-loop

## AI 摘要

ChatGPT 已开始展示广告，并公开了完整的广告归因流程。该流程详细描述了广告从展示、用户互动到转化跟踪的全链条机制，确保广告效果可准确测量和优化。文章源自 buchodi.com，在 Hacker News 上获得 114 点关注，探讨了这一商业化功能的技术实现与行业影响。

## 正文

OpenAI's ad platform has two halves. On the ChatGPT side, the backend injects structured single_advertiser_ad_unit objects into the conversation SSE stream while the model is responding. On the merchant side, a tracking SDK called OAIQ runs in the visitor's browser and reports product views back to OpenAI. The two are tied together by Fernet-encrypted click tokens, four of them per ad.

single_advertiser_ad_unit

I captured both halves on a consented mobile-traffic research fleet. Everything below comes from observed traffic.

How an ad gets into a conversation

When you send a message to ChatGPT, the backend opens an SSE response at chatgpt.com/backend-api/f/conversation. Most events in that stream are model-output. Some are ad units. They look like this:

chatgpt.com/backend-api/f/conversation

event: delta data: { "type": "single_advertiser_ad_unit", "ads_request_id": "069e89b3-c038-7764-8000-6e5a193e5f69", "ads_spam_integrity_payload": "gAAAAABp6Js_", "preamble": "", "advertiser_brand": { "name": "Grubhub", "url": "www.grubhub.com", "favicon_url": "https://bzrcdn.openai.com/cabfae7ead26b03d.png", "id": "adacct_6984ed0ba55481a29894bb192f7773b4" }, "carousel_cards": [{ "title": "Get Chinese Food Delivered", "body": "Satisfy Your Cravings with Grubhub Delivery.", "image_url": "https://bzrcdn.openai.com/cabfae7ead26b03d.png", "target": { "type": "url", "value": "https://www.grubhub.com/?utm_source=chatgptpilot&utm_medium=paid&utm_campaign=diner_gh_search_chatgpt_kw_traffic_nb_x_nat_x&utm_content=nbchinese&oppref=gAAAA&olref=gAAAA", "open_externally": false }, "ad_data_token": "eyJwYXlsb2" }] }

event: delta data: { "type": "single_advertiser_ad_unit", "ads_request_id": "069e89b3-c038-7764-8000-6e5a193e5f69", "ads_spam_integrity_payload": "gAAAAABp6Js_", "preamble": "", "advertiser_brand": { "name": "Grubhub", "url": "www.grubhub.com", "favicon_url": "https://bzrcdn.openai.com/cabfae7ead26b03d.png", "id": "adacct_6984ed0ba55481a29894bb192f7773b4" }, "carousel_cards": [{ "title": "Get Chinese Food Delivered", "body": "Satisfy Your Cravings with Grubhub Delivery.", "image_url": "https://bzrcdn.openai.com/cabfae7ead26b03d.png", "target": { "type": "url", "value": "https://www.grubhub.com/?utm_source=chatgptpilot&utm_medium=paid&utm_campaign=diner_gh_search_chatgpt_kw_traffic_nb_x_nat_x&utm_content=nbchinese&oppref=gAAAA&olref=gAAAA", "open_externally": false }, "ad_data_token": "eyJwYXlsb2" }] }

Notes:

single_advertiser_ad_unit is a typed schema. The naming implies siblings (multi-advertiser, etc.).

single_advertiser_ad_unit

advertiser_brand.id is adacct_ — a stable per-merchant account identifier.

advertiser_brand.id

adacct_

Brand favicon and ad image both load from bzrcdn.openai.com. OpenAI hosts the advertiser's creative, not the merchant.

bzrcdn.openai.com

target.open_externally: false opens the link in ChatGPT's in-app webview, so OpenAI observes the post-click navigation on top of any pixel signal.

target.open_externally: false

Four Fernet tokens per ad: ads_spam_integrity_payload, oppref, olref, and a base64-wrapped ad_data_token. Each is AES-128-CBC under a server-only key with HMAC-SHA256 integrity.

ads_spam_integrity_payload

oppref

olref

ad_data_token

How ads get selected

A single account in the panel received six different ads across six conversations on six different topics. The targeting is contextual to the chat:

Conversation topic Advertiser delivered Beijing trip planning (Great Wall, Forbidden City) Grubhub — "Get Chinese Food Delivered" Beijing tour bookings GetYourGuide — Great Wall tour, ad_id=beijing003 Beijing flights Axel — utm_term=vflight_beijing_03 NBA playoffs Gametime — utm_campaign=nba&utm_content=playoffs Spring fashion/trends Aritzia — utm_campaign=chatgptpilot_trav3 Productivity / slides Canva — utm_campaign=…link-clicks_products

ad_id=beijing003

utm_term=vflight_beijing_03

utm_campaign=nba&utm_content=playoffs

utm_campaign=chatgptpilot_trav3

utm_campaign=…link-clicks_products

Same account, different topic, different brand. I didn't find evidence one way or the other on whether targeting also incorporates prior conversation history.

The four-token attribution chain

Every ad ships with four distinct Fernet-encrypted blobs. Their roles, based on where they appear:

ads_spam_integrity_payload sent inside the SSE data, never on the click URL. Server-side integrity check against forged ad clicks.

ads_spam_integrity_payload

oppref present on the click URL and copied verbatim by the OAIQ pixel into the cookie __oppref (TTL 720 hours / 30 days). The forward attribution token. Travels with every subsequent merchant pixel event.

oppref

__oppref

olref paired with oppref on the click URL but not stored by the SDK we observed. Likely impression-side / outbound-link-reference logging on OpenAI's servers.

olref

oppref

ad_data_token base64-wrapped JSON containing yet another Fernet token. Carried in the SSE payload, presumably reconciled server-side at click time.

ad_data_token

Fernet's first nine bytes are public: version byte 0x80 plus an 8-byte big-endian Unix timestamp. So the mint time of any of these tokens is recoverable without OpenAI's key:

0x80

import base64, struct, datetime b = base64.urlsafe_b64decode("gAAAAABp7fdA" + "==") print(datetime.datetime.utcfromtimestamp(struct.unpack(">Q", b[1:9])[0])) # → 2026-04-26 11:30:08 UTC

import base64, struct, datetime b = base64.urlsafe_b64decode("gAAAAABp7fdA" + "==") print(datetime.datetime.utcfromtimestamp(struct.unpack(">Q", b[1:9])[0])) # → 2026-04-26 11:30:08 UTC

The Home Depot click URL I captured was minted at 11:30:08; the browser fetched the merchant page at 11:31:43. Click latency: 95 seconds.

How the loop closes on the merchant side

User taps the card. Browser opens:

https://www.grubhub.com/?utm_source=chatgptpilot&... &oppref=gAAAA &olref=gAAAA

https://www.grubhub.com/?utm_source=chatgptpilot&... &oppref=gAAAA &olref=gAAAA

The merchant page loads the OAIQ SDK:

oaiq.min.js is at version 0.1.3. On init it reads ?oppref= from window.location, writes it into the first-party cookie __oppref with a 720-hour TTL, and sets a probe cookie __oaiq_domain_probe. Every subsequent measure call POSTs JSON to:

oaiq.min.js

init

?oppref=

window.location

__oppref

__oaiq_domain_probe

measure

POST https://bzr.openai.com/v1/sdk/events?pid=&st=oaiq-web&sv=0.1.3

POST https://bzr.openai.com/v1/sdk/events?pid=&st=oaiq-web&sv=0.1.3

Two domains to add to your filter list if you want to block ChatGPT ad events: bzrcdn.openai.com, bzr.openai.com. Two cookie names to inspect after any ChatGPT-recommended click: __oppref, __oaiq_domain_probe.

bzrcdn.openai.com

bzr.openai.com

__oppref

__oaiq_domain_probe
