pixellint

Blog · ChatGPT ads

A ChatGPT ads phone lead in a tracking dashboard is not the same conversion OpenAI optimizes on.

ChatGPT ads can drive calls, texts, and form fills the way paid search always has for local services. The new part is a Conversions API on bzr.openai.com with its own event enum and a click reference the API will not capture for you. A lead platform that closes the loop in its UI and sends qualified conversions back to OpenAI is a partner path, not a substitute for reading the worker your checkout team owns.

Press around SMB lead tracking describes integrations that attribute calls, texts, and form submissions to ChatGPT ad campaigns and ad groups, then share qualified conversion data with OpenAI to improve targeting and bidding. Setup copy mentions an API key and a conversion pixel ID in integration settings, with no developer required. That is fast for a media buyer. It is also a different hop from the JSON your server would POST if you owned the mapping.

Industry surveys cited in the same announcements put a large share of consumer searches starting on AI tools rather than traditional search engines. Call-heavy verticals were already buying on click-to-call economics. ChatGPT ads add a conversational surface where the click id is oppref on the landing URL, not gclid. If the only proof of life is a ringing phone matched by dynamic number insertion, you still need to know which event type and which click reference OpenAI stored when the bidder moved.

What the dashboard ROI actually measures

A lead platform dashboard measures its own attribution graph: which tracking number fired, which form listener fired, which UTM or campaign label was stamped at routing time. That graph is built for the marketer who lives inside the lead product. It compares ChatGPT rows against Google, Meta, and Microsoft in one reporting environment because that is where the SMB already reconciles spend.

OpenAI Ads Manager and the Conversions API measure a different population: events that arrived on bzr.openai.com with a valid key, a pixel id on the query string, and fields that pass schema checks. validate_only can tell you the body is legal without saving it. A legal body is still not a join. Click-through optimization expects oppref on the event when the conversion should tie to the ad click, and the docs state plainly that unlike the pixel, the API does not capture oppref for you.

Partner-forwarded conversions sit between those two views. The lead vendor may send qualified rows upstream after its own filters. You see ROI in the tool you trust. The exchange and the advertiser still need to know whether the forwarded payload used the same id, clock, and type your independent worker would have sent. When those differ, the campaign can look healthy in the lead dashboard while the API dataset the bidder trains on is thin or mis-typed.

This is not an argument against phone tracking. Paid search never required the call center to POST Graph Purchase either. It is an argument against treating a green row in a third-party report as proof that bzr.openai.com received an order_created or lead_created with the oppref that was on the landing URL when the consumer tapped the ad.

Where the click id dies between the ad and the call

The ad click lands on a web URL with oppref appended. The JavaScript pixel can stash that value in a first-party cookie for later page events. A phone call often bypasses that cookie entirely: the consumer dials a tracking number on the page, or a click-to-call link that never round-trips through your thank-you host. Dynamic number insertion swaps the visible number per session. The lead platform knows the session. The OpenAI API only knows what you POST.

OpenAI publishes native types that fit lead funnels, including lead_created and appointment_scheduled, not only order_created. A call that becomes a booked job in a CRM is still a conversion story, but the type string has to match the published enum. A partner integration that maps call completed internally to a generic custom label may be fine inside the lead product while failing batch validation or under-training the objective you think you bought.

Amount and currency on web orders use minor units in the documented shape. A phone lead may have no cart total at ring time. Sending zero or omitting value is a different optimization signal from a checkout_started followed by order_created with integer minor units. Mixing those stories in one integration brief is how media teams optimize on qualified calls while finance asks why revenue columns in Ads Manager stay flat.

A solutions engineer at an ad exchange sees the impression and click identifiers that crossed the programmatic hop, and they can replay a sandbox sample from a measurement partner. They do not own the advertiser CRM or the call recording. The exchange can pass a click with oppref on the redirect URL and still have no visibility into whether the downstream lead platform forwarded that same reference when it pushed a qualified conversion upstream. Production leaves empty what the integration screenshot filled.

The partner sample versus the advertiser worker

Lint the production body you control or redact from the advertiser queue. A partner PDF is not that body.

partner sample: type lead_created, timestamp_ms <ms>, oppref oppref_from_test_landing
production call path: type custom or missing, timestamp_ms <call_time>, oppref absent unless copied from landing

The sample in a partnership deck is complete on purpose. It uses oppref copied from a test click, timestamp_ms in milliseconds inside the seven-day window, and a type string from the supported list. Pixellint can call that sample clean against the OpenAI pack. Clean means the demo is a legal event. It does not mean the SMB activation sent it.

Self-serve setup with an API key and conversion pixel id wires credentials into the lead product. Credentials are not a schema. They tell OpenAI which pixel id to attribute against. They do not prove every qualified call was emitted as lead_created with the obref or oppref fields the click-through model expects. The pixel id on the query as pid is the same parameter your own worker must use; duplicating keys in two systems without duplicating event ids creates double counting or silent drops depending on which pipe arrives first.

Batch semantics matter when a partner batches calls with form fills. OpenAI documents batch limits and fail-whole behavior when one event is invalid. A lead platform batching mixed shapes can fail entirely while the dashboard still shows individual calls as attributed inside the vendor UI. The exchange-side review should ask for one redacted production payload per event source, not only the integration quick-start screen.

Dual pipes when MMP, lead tracking, and CAPI all run

Mobile and web stacks already learned this lesson on Meta: MMP postbacks, pixel eventID, and Graph CAPI are three lenses on one purchase. ChatGPT adds a fourth when organic AI referral reporting sits beside paid oppref clicks. Lead tracking adds a fifth when calls are the primary KPI. Each lens can show a conversion without the others receiving the same join keys.

Press on MMP ChatGPT integrations describes partner posting on behalf of advertisers. Lead-tracking integrations describe another partner path with qualified conversion feedback. An advertiser-owned worker posting from the order or CRM webhook is still the artifact you can lint without trusting a vendor slide. Compare ids: reuse the same event id when retrying or when pixel and server both fire, per OpenAI dedup guidance, instead of minting a fresh uuid per upload attempt.

For call-heavy accounts, appointment_scheduled may be closer to the business outcome than order_created. Trafficking should name the objective in Ads Manager and the type string on the wire in the same meeting. Otherwise ad ops optimizes on calls in the lead report while the API trains on page_viewed rows from a pixel that never saw the call.

Consent and PII follow the collector you actually control. Hashed email lists belong in user.emails_sha256 after normalization rules. IP stays plaintext in the documented user block. A lead platform hashing phone numbers for its own graph does not automatically map those hashes into the OpenAI user object your worker would build. Treat partner hashing as vendor-specific unless you have the exact field map.

What validate_only proves and what it cannot

validate_only on the OpenAI path checks shape without persisting events. Use it on the body your server will emit after call disposition, not on a translated export from a lead CSV. A 200 there is still not a row in Ads Manager with revenue attached. It is permission to turn the same JSON live.

View-through and click-through columns in Ads Manager remain product-specific. Conversions as click-through totals expect the click reference story to hold. Phone conversions that never carried oppref into the POST behave like view-through or unattributed depending on account settings, even when the call tracker shows campaign-level ROI.

The seven-day window and ten-minute future skew on timestamp_ms apply to every type string. Do not refresh timestamps to now when a CRM upload runs nightly. Late call dispositions should carry the original ring time converted to milliseconds, or drop outside the window explicitly. Refreshing to upload time mis-attributes demand to the sync job.

Sandbox versus production on the exchange side

Pixellint is not affiliated with OpenAI or with lead platform vendors. A pass on the OpenAI pack means the pasted JSON matches the published envelope. It does not mean the call happened, and it does not mean the exchange click id survived to the collector.

ChatGPT ads for SMBs will keep arriving as self-serve integrations because agencies need parity with Google and Meta reporting. The engineering work stays the same: capture oppref at the landing hop, choose the correct type for the business outcome, and prove the production worker matches the sample before media scales spend on dashboard ROAS alone.

Sources

Contract pages

The dated argument is above. These pages are the field lists.

Read OpenAI CAPI is not Meta Docs