Every offer order traced to its offer, option and version.

Each order arrives on a signed webhook, or is fetched by the daily sync or an import, and an order Offer Suite's cart sold is attributed to the offer, option and version in its cart. Browser events build the funnel, but only a confirmed order counts as a sale.

Measure: know what each option earnedIn production

Every order kept, offer orders attributed

In production

Shopify sends each order on the orders/create and orders/updated webhooks. Offer Suite checks every delivery's HMAC signature before it reads a field, and stores the order once per Shopify order, however many times it arrives.

The order is then attributed to the exact offer, option and version that sold it. The ingest tries three sources, in this order:

SourceWritten byUsed whenCounted as
The _os cart attributeOffer Suite's carts: every cart the API builds or hands you as a cart input, the Verified offer theme block's included. It names the offer, option and version.First, whenever it names an offer in your workspace.attribute
The order tags os_offer, os_option, os_versionYour own automation, for example a Shopify Flow workflow. Offer Suite never writes order tags.When _os is missing, cannot be read, or names an offer outside your workspace, and the os_offer code names one of your offers.tag
NeitherNothing usable: no _os or tags, or both point outside your workspace.The order is kept with no offer, so nothing is lost.none
How each order is attributed. GET /orders/counts (get_order_counts) reports how many of your orders each row attributed.
  • Offer Suite never writes order tags. It has read-only access to orders. The os_offer, os_option and os_version tags are a fallback for orders your own automation tags, for example a Shopify Flow workflow.
  • A redelivery only fills in missing attribution. A stored order's lines are never rewritten, and an order attributed once keeps its offer.
  • Gross revenue is frozen at the sale as placed: from the orders/create body, or read from the Shopify Admin when another delivery arrives first. Paid edits made after the sale are tracked separately.
  • Unattributed orders are kept with no offer, so nothing a store sold is lost.

Buyer contact details (names, email, phone, street addresses, IP address, payment details and order notes) are stripped from every order before it is stored, as the security page describes.

The event ladder

In production

Five steps are stored per offer, option and the version the visitor was shown. Each step has one sender, so no step is counted twice.

Each step and its one sender
View, select, add to cartYour page. The Verified offer theme block sends them for you; a buy box your developers build posts them to POST /events with the store's public key.
Checkout startedOffer Suite's Web Pixel, installed in the store automatically. It runs only when the buyer allows analytics, sends no buyer identity, and counts one step per checkout, so a reloaded checkout counts once.
PurchaseThe store's signed order webhook. The Web Pixel also sends a browser copy of each purchase, for comparison only: a purchase that only the browser reported counts as unverified, never as a sale.
  • A retried event with the same eventId counts once.
  • Nothing is forwarded to GA4, Meta or TikTok.

Funnel

In production

GET /offers/{id}/funnel and the MCP tool get_offer_funnel give, for one offer over the last 1 to 90 days (7 by default), the distinct events and distinct visitors that reached each step. Only purchases the order ingest recorded count as bought; purchases only a browser reported come back apart, as unverifiedPurchases.

Offer funnel

Distinct visitors per step, last 30 days · specimen data

18,420 viewers over 30 days; 7.0% bought
Viewed the offer18,420
Chose an option9,87153.6%
Added to cart4,21242.7%
Started checkout2,39156.8%
Bought1,28453.7%

Started checkout comes from the Web Pixel, which follows the store's cookie consent, so it can undercount; purchases never do.

One offer over 30 days, drawn with the dashboard's own funnel chart. The funnel is API and MCP only: there is no dashboard or App Home view of it yet. Specimen data.

The first four steps are tracking from browsers, not confirmed sales: read them for where visitors drop off, and read purchases as sales.

Browser vs server purchases

In production

GET /offers/{id}/purchase-comparison (compare_offer_purchases) sets the purchases the Web Pixel reported beside the orders the ingest recorded, matched by the event id both sides share. The window is a rolling 1 to 90 days, or a fixed since up to 90 days back. It returns counts only, never a list of orders.

The response's counts, last 30 days
FieldCountWhat it counts
browser1,216Purchases the Web Pixel reported from the thank-you page.
server1,284Purchases the order ingest recorded from the store's signed order webhooks.
matched1,207Orders both sides reported, matched by the event id they share.
browserOnly9Unverified: the browser reported them and no order webhook confirms them. No other report counts them.
serverOnly77Orders the browser never reported: no pixel, a buyer who declined analytics, a blocked request or a cart built without the pointer. It does not say which.
variance−68browser − server.
The same 30 days: server equals the funnel's purchases. Specimen data.

Use a fixed since to compare two reads over the same purchases, before and after a launch or a campaign; a rolling window drops the oldest purchases as time passes.

Renewals

In production

Orders created by Shopify subscription billing, whose source is subscription_contract or anything under subscription_contract_, are stored as renewals. They are costed as renewals, so costs that apply only to a first order are skipped, and each option's economics report first orders and renewals apart.

Shopify copies the cart attribute and the tags from the order that started the subscription, so a renewal counts toward the option that won the subscriber, even after the subscriber swaps or adds products.

Missed orders and history

  • The daily sync checks every connected store once a day. It re-creates order webhooks Shopify deleted and fetches orders missed or changed in the last 60 days, through the same ingest as a live delivery. The store page shows how many could not be stored. In production
  • Past-order import goes a year back by default, or from any earlier date you ask for (start_order_import). Until the store has granted read_all_orders, only the last 60 days can be imported. Imported orders count toward item profit, never toward an offer, because past orders carry no offer link. Set the workspace's defaultCostProfile first (update_tenant); the import refuses without it. In production

An import's progress can be read at any time (get_order_import): orders found, imported, already stored and failed, with the first 50 reasons. Running it again skips orders already stored unchanged, fills the gaps and brings changed orders up to date.

What it does not do yet

  • The funnel is not split by option or version, and has no dashboard or App Home view.
  • Checkout started can undercount where buyers decline analytics; purchases never do.
  • serverOnly does not say why the browser missed a purchase.
  • Renewals are verified only with Shopify-native subscription contracts; third-party subscription apps such as Recharge and Skio are unconfirmed.
  • A renewal is not yet linked back to the first order of its subscription.

Questions

Does Offer Suite tag my orders?

No. Offer Suite has read-only access to orders and never writes tags. Its carts carry the _os cart attribute, which Shopify copies onto the order. The os_offer, os_option and os_version tags are a fallback that only your own automation, such as a Shopify Flow workflow, can set.

What counts as a sale?

Only an order the store's signed webhook delivered, or one the daily sync or an import read from Shopify. A purchase that only a browser reported is shown as unverified and never counts as a sale, because anyone holding the store's public key could post one.

Does the Web Pixel need the buyer's consent?

Yes. It runs only when the buyer allows analytics, and it sends no buyer identity. Where buyers decline, Checkout started can undercount; purchases come from the order webhook and do not.

Do orders from before I installed count?

Past-order import brings in a year of history by default, or the last 60 days until the store grants read_all_orders. Those orders count toward item profit, not toward any offer, because they were not sold through one.