# ChatGPT organic vs ChatGPT Ads: measuring two channels in one

> ChatGPT sends shoppers via organic product results or paid ads. How to split the two with oppref, UTMs and referrers, and compute ROAS honestly.

Canonical: https://convrail.com/blog/chatgpt-organic-vs-ads-attribution/

A shopper reaches your store from ChatGPT in one of two ways: through an organic product result drawn from your product feed, or through a paid ad whose click lands with an `oppref` identifier. Only the second costs money, so the two must be reported as separate channels, with ROAS computed on the ads half alone. The split is a fixed rule on each order's landing URL and referrer.

## Two doors into the same store

From the shopper's side the experience is continuous: a question, an answer, a product, a click. From the merchant's side there are two distinct mechanisms with different economics, and the whole point of attribution is to keep them apart.

The organic door is the product feed. The [OpenAI product feed specification](https://developers.openai.com/commerce/product-feeds/spec) describes a snapshot of your catalog, with nine required fields per item (`item_id`, `title`, `description`, `url`, `brand`, `seller_name`, `image_url`, `availability`, `price`), delivered by SFTP at least daily. Items are eligible for organic results by default: the `is_eligible_search` field, when omitted, defaults to true. Nothing is billed when a shopper clicks one of these results. The same feed also carries an `is_ads_eligible` flag, which the specification lists as required for Ads processing, so one file feeds both doors.

The paid door is ChatGPT Ads. The [Campaigns reference](https://developers.openai.com/ads/api-reference/campaigns) documents campaigns with a `bidding_type` of `"impressions"`, `"clicks"` or `"conversions"` and a lifetime budget. Whatever the bidding type, an ad click carries the shopper to your `url` with something an organic click never has: the `oppref` parameter. The [Measurement Pixel reference](https://developers.openai.com/ads/measurement-pixel) states that the pixel "captures `oppref` from the landing page URL" and "stores `oppref` in a first-party `__oppref` cookie so later page views can reuse it", and the [Conversions API reference](https://developers.openai.com/ads/conversions-api) describes the field as "An opaque, OpenAI-provided attribution identifier."

| | ChatGPT organic | ChatGPT Ads |
| --- | --- | --- |
| How the product appears | Product result drawn from your feed | Paid placement |
| Cost per click | None | Billed per valid click (or per impression, by campaign type) |
| Identifier on the landing URL | None issued by OpenAI; your own UTMs if you added them to the feed `url` | `oppref` |
| Where it is measured by OpenAI | Not reported as conversions in the Ads Manager | Ads Manager and Insights (click-through conversions) |
| What ROAS needs | Nothing; there is no spend | Attributed revenue and imported spend |

## Why the two channels must never be summed

Merging them produces two numbers that are each wrong in a different direction.

If organic orders are counted as ads revenue, ROAS is inflated by revenue that the campaign did not buy. A campaign that looks profitable may be riding on results the feed would have produced anyway. Decisions follow: budget increases, bids raised, a second campaign, all justified by a ratio whose numerator was padded.

If ads orders are counted as organic, the campaign looks like it produced nothing, and it is cut. The feed then gets credit for revenue it did not earn, which also misleads any effort you put into [feed optimization](/feed-optimization/).

The channels also behave differently over time. Organic exposure depends on the completeness and freshness of your feed. Paid exposure depends on budget and bid, and stops the moment either runs out. Reading them as one line hides the only comparison that matters commercially: what the paid channel adds on top of what the free channel already delivers.

## The methodology, in full

The split rests on three signals recorded on each order, applied in a fixed order. Convrail uses exactly the following table and stores which row matched, with the matching value, on every order.

| Signal on the order | Verdict | Medium |
| --- | --- | --- |
| Landing URL contains `oppref=<id>` | ChatGPT | ads |
| `utm_source` is chatgpt, openai or chatgpt.com, and `utm_medium` is cpc, ppc, paid or ads | ChatGPT | ads |
| `utm_source` is chatgpt, openai or chatgpt.com, any other medium | ChatGPT | organic |
| Referrer is chatgpt.com, chat.openai.com or openai.com (or a subdomain) | ChatGPT | organic |
| None of the above | Other channel | not attributed |

### Why `oppref` means ads

Because OpenAI issues it only on ad clicks, and because it sits on the URL rather than in a header. A URL parameter is recorded by Shopify's `landingPage` ("URL of the first page the customer landed on for the session.", per the [CustomerVisit object](https://shopify.dev/docs/api/admin-graphql/latest/objects/CustomerVisit)) and by WooCommerce's order attribution data regardless of whether the browser sent a referrer. It is the one signal that survives a stripped referrer, so it is checked first, and a paid order can never fall through to the organic rows.

### Why a paid `utm_medium` means ads

Because if you built a tracking link for a ChatGPT Ads destination with `utm_medium=cpc` or similar, you declared it paid. Convrail honors the declaration. This row exists for merchants who template their ad URLs; it is not needed for `oppref` to work.

### Why a referrer, or a non-paid UTM, means organic

Because a click from chatgpt.com that carries no `oppref` and no paid medium is what an organic product result looks like. Shopify defines `referrerUrl` as "Webpage where the customer clicked a link that sent them to the online store." If you followed the [feed best practices](https://developers.openai.com/commerce/guides/best-practices) and appended a feed-specific parameter such as `utm_medium=feed` to your feed `url`, the UTM row catches it as organic even when the referrer is gone. We have not found an official OpenAI page that documents a fixed set of UTM parameters appended to organic ChatGPT links, so the referrer rule is the safety net and your own feed UTMs are the reliable version.

### What "first match wins" buys you

Determinism. Two people looking at the same order, with the same raw fields, reach the same verdict. The stored evidence (the `oppref` value, the UTM pair or the referrer host) makes every row re-derivable from a CSV export, which is the only honest way to let a merchant or an agency check the work. How that evidence shows up in Shopify specifically is covered in [Shopify orders with ChatGPT as the source](/blog/shopify-order-source-chatgpt/).

## The ROAS definition, stated once

ROAS in Convrail is attributed ads revenue divided by imported ads spend, over the period you select.

- Attributed ads revenue is the sum of order totals for orders whose medium is ads. Organic orders are not in the numerator.
- Spend is imported once a day from the OpenAI Advertiser API together with impressions, clicks, CTR, CPC, CPM and conversions, stored per campaign and per day. The [Insights reference](https://developers.openai.com/ads/api-reference/insights) lists `impressions, clicks, spend, ctr, cpc, cpm` as delivery metrics and `conversions`, `click_through_conversions` and `view_through_conversions` as conversion metrics.
- Imports are idempotent: re-running a day never double-counts spend.
- When no spend has been imported for the period, ROAS is shown empty rather than as a misleading figure.

Organic attribution needs none of this. It works from your order webhooks alone. The Advertiser API key only adds the spend side, which is why it is optional and why a store can run organic attribution on day one.

## The limits, stated plainly

Every attribution above is last-touch, first-party evidence. That description carries three consequences.

Last touch means the order is credited to the session that placed it. A shopper who asked ChatGPT for advice on Monday and bought on Thursday after a Google search is a Google order under this rule, and the ChatGPT influence is invisible. We do not model it, because a modelled number without evidence cannot be audited.

First-party means the rules only see what your own store recorded: the landing URL and the referrer on the order. If a redirect chain, a consent tool or an app rewrote the URL before your platform saw it, the evidence is whatever survived. The `__oppref` cookie the pixel keeps for 7 days helps the conversion events sent to OpenAI, so the Ads Manager and your dashboard describe the same click, but it cannot recover a parameter the platform never stored.

Evidence-only means unattributed is a real category. An order with a plain referrer and no parameters is "not attributed", not "organic by default". Rounding it up would inflate the organic line in exactly the way this article warns against for ads.

A further asymmetry: the Ads Manager and your dashboard will not agree to the order, and should not be expected to. The Insights reference states that "`conversions` is always equal to `click_through_conversions`" and that "Click-through attribution follows the applicable configured click window." The Ads Manager counts events it received inside a window; the dashboard counts orders whose landing URL carried the identifier, whether or not an event was sent (a consent refusal, for instance, withholds the event but leaves the URL). Compare over a week, not a day, and expect the dashboard's ads count to be equal to or slightly above the Ads Manager's.

## A worked reading of the dashboard

The numbers below are invented for illustration. They are not measurements from any store, and they are not typical of anything; they exist only to show how the columns relate.

Suppose a store records 200 orders in a hypothetical 7-day period, and Convrail attributes them as follows:

| Row | Orders | Revenue (hypothetical, EUR) | Evidence type |
| --- | --- | --- | --- |
| `oppref` on landing URL | 12 | 1,140 | ads |
| Paid UTM (`utm_medium=cpc`) | 2 | 210 | ads |
| Non-paid UTM (`utm_medium=feed`) | 9 | 630 | organic |
| chatgpt.com referrer only | 6 | 390 | organic |
| Not attributed | 171 | 14,200 | other channels |

Suppose further that the Advertiser API import for the same seven days shows 600 EUR of spend across two campaigns.

Reading it:

- Attributed ads revenue is 1,140 + 210 = 1,350 EUR from 14 orders.
- Attributed organic revenue is 630 + 390 = 1,020 EUR from 15 orders.
- ROAS is 1,350 / 600 = 2.25. The organic 1,020 EUR is not in the numerator.
- The ChatGPT channel as a whole produced 29 of 200 orders, but only 14 of them cost anything.

Now the comparison the merged view would have hidden. Had the 15 organic orders been counted as ads, ROAS would have read (1,350 + 1,020) / 600 = 3.95, and the campaign would have looked nearly twice as efficient as it was. Had the 14 ads orders been folded into organic, the campaign would have shown 600 EUR of spend against zero revenue.

Two further checks the dashboard supports. The top products list, built from the line items of attributed orders, tells you whether the products the ads are winning are the same ones organic already wins; if they are, the paid channel may be buying clicks the feed would have earned. And the daily trend, which plots revenue, orders and spend per day, tells you whether organic revenue held steady while spend paused, which is the closest a last-touch view gets to incrementality without pretending to measure it.

Then, still in this hypothetical, suppose the Ads Manager shows 11 conversions for the same week against Convrail's 14 ads orders. Per the reconciliation above that is not an error: two of the 14 might have been placed after the click window, and one might have been withheld for consent. The order to investigate would be one with `oppref` on the landing URL, inside the window, with an accepted `order_created` event, and no conversion on the OpenAI side.

## Common mistakes

- Reporting one "ChatGPT revenue" figure and dividing it by ads spend. Half the numerator was free.
- Treating a chatgpt.com referrer as a paid click. Without `oppref` or a paid medium it is organic.
- Defaulting unattributed orders to organic because "they probably came from somewhere". Unattributed is a category, not a rounding error.
- Skipping UTMs on the feed `url`. The referrer is fragile; a feed-specific medium is the durable organic signal you control.
- Expecting the Ads Manager and the dashboard to match to the order. They count different things over different windows.
- Reading a single day's ROAS. Spend and orders do not land on the same day, and idempotent daily imports mean yesterday's figure may still be settling.

## What to do next

If you want ChatGPT organic and ChatGPT Ads on separate lines, with the rule and the evidence stored on every order and ROAS computed only on what you paid for, start with [Convrail attribution](/attribution/).