Back to all articles
Developer GuidesShopify

Webhooks and Order Events: How Affiliate Attribution Flows From Shopify Checkout to Commission

A technical walk-through of the path from a creator's link click to a paid commission. Covers the Web Pixel and webhook chain, why attribution is decided server-side rather than in theme scripts, idempotent handling of Shopify redeliveries, code lifetime versus cookie window, and a debugging checklist for sales that never reach the dashboard.

Webhooks and Order Events: How Affiliate Attribution Flows From Shopify Checkout to Commission

Someone on the marketing team says a creator's sale is "in Shopify but not in the affiliate dashboard", and the ticket lands with you. To answer it properly you need to know what happens between a link click and a commission line, which system decides at each step, and where the chain can legitimately break.

Most explanations of affiliate tracking stop at "the pixel fires". That was roughly true when tracking lived in theme JavaScript and a cookie. It has not been true on Shopify for a while, and after the checkout upgrade on August 26, 2026, theme-injected scripts on the thank-you page are gone for every plan. Attribution now has to be assembled from a sandboxed pixel event, server-side webhooks and the Admin API.

This post lays out that chain in order, explains why the decisions are made server-side, covers idempotency and conflict rules, and ends with a checklist for the ticket you were just handed.

Run your affiliate program on Shopify

Install Reveshare from the Shopify App Store and start turning customers into ambassadors.

Get the app →

1. The event chain, in order

Here is the full path from click to payout on a Reveshare-tracked store. Each row names the event, where it is observed, and what is decided at that point.

StepEventObserved byWhat is decided
1Shopper clicks the affiliate's SneakylinkReveshare redirectClick recorded against the affiliate; a short-lived discount code is minted for this shopper via Admin GraphQL; shopper is associated with the affiliate for the cookie window
2Shopper reaches the storefrontStorefrontMinted code is applied to the cart so the discount shows without typing
3checkout_completedSandboxed Web Pixel with Protected Customer Data Access scopesOrder id, discount codes and customer identifiers captured in real time and sent server-side
4orders/create webhookReveshare backendOrder confirmed as real, line items and discount codes read from the source of truth; the order is matched to an affiliate
5Commission calculationReveshare backendProgram rules applied: rate, current tier, excluded products, self-referral check; commission recorded as pending
6orders/updated, refunds/create, fulfilment eventsReveshare backendCommission adjusted for partial refunds, reversed for full refunds, released once the order clears
7Payout runReveshare backend and payment providerReleased commission across all programs paid to the affiliate's method; reversals netted against the balance

Two things stand out. The pixel at step 3 is fast but not authoritative. The webhook at step 4 is authoritative but arrives on Shopify's schedule. Attribution uses the pixel for speed and the webhook for truth, and nothing is paid until the webhook has confirmed the order.

2. Why attribution is decided server-side

There are three reasons the decision cannot live in the storefront any more, and each one on its own would be enough.

Ad blockers and privacy tooling. A meaningful share of shoppers run extensions that block third-party scripts and strip tracking cookies. A theme script that records the click in a first-party cookie and reads it back on the thank-you page silently loses those orders. A webhook from Shopify to your backend cannot be blocked by anything in the shopper's browser.

The Checkout Extensibility sandbox. Checkout and the thank-you page run app code inside isolated frames. A Web Pixel cannot read arbitrary cookies, cannot touch the page DOM, and only receives customer fields its app has been granted through Protected Customer Data Access. This is the correct design, but it means the "read the cookie, fire a request" pattern is not available.

The August 26, 2026 upgrade. Additional Scripts on the order status page were removed for Basic, Shopify and Advanced plans when legacy checkouts were upgraded. Any attribution logic that still lived there stopped running that day. Reveshare was built on Web Pixels, webhooks and Admin GraphQL from the start, so it did not need migrating, but plenty of home-grown tracking did.

3. Code on order beats click

Sometimes the two signals disagree. The shopper clicked creator A's link, which minted a code for them, then typed creator B's static code at checkout. Who gets paid?

The rule in Reveshare is that the discount code on the order wins. It is the only signal that is on the order itself, it is visible in Shopify Admin, and it is explainable to both creators. Click-based attribution fills in the cases where no affiliate code is present, which is exactly the "they clicked and forgot the code" scenario Sneakylinks exist for.

This has an implementation consequence. When the orders/create payload arrives, the handler reads discount_codes first and resolves any affiliate code before consulting click history. Only if the order carries no affiliate code does the cookie-window association apply. Do not do this in the other order, or you will pay creator A for creator B's sale and then spend a week on the dispute.

Talk to our team

Book a short call to see how Reveshare fits your store and ambassador goals.

Book a demo →

4. Idempotency, retries and signatures

Shopify delivers webhooks at least once, not exactly once. If your endpoint is slow, returns a non-2xx status, or is briefly down, Shopify retries the same event with the same payload over a period of hours. A handler that is not idempotent will create two commissions for one order.

The safe pattern is to key every write on the Shopify order id and the event type, and make the write a no-op if it has already been applied. A minimal shape looks like this:

{
  "topic": "orders/create",
  "order_id": 5678901234,
  "discount_codes": ["SARAH15"],
  "financial_status": "paid",
  "processed_at": "2026-09-14T10:22:31Z"
}

Before doing anything with that, verify the X-Shopify-Hmac-SHA256 header against the raw request body using your app's secret. A payload that fails verification is dropped, not processed. Then check whether a commission already exists for order_id; if it does, acknowledge with a 200 and return.

Webhook handler rules

  • Verify the HMAC on the raw body before parsing anything
  • Respond 200 quickly and do the work asynchronously; Shopify times out slow endpoints and retries
  • Key writes on order id plus topic so redeliveries are no-ops
  • Treat `orders/updated` as a diff, not a fresh order; recompute commission from the current line items
  • Handle `refunds/create` separately from cancellation, because partial refunds adjust rather than reverse
  • Log the webhook id and processed_at so a support ticket can be traced to a specific delivery

Reveshare runs its own handlers on these rules. If you are building anything alongside it, for example a data warehouse sync, the same rules apply to you.

Two settings govern link-based programs and they are often confused.

Code lifetime is how long the discount code minted at click time stays valid. The default is 24 hours. Its purpose is leak prevention: a code that expires tomorrow is worthless on a coupon site, so there is nothing durable to scrape. Creating and expiring these codes is done through Admin GraphQL against a price rule that belongs to the program.

Cookie window is how long the shopper stays associated with the affiliate after the click. The default is 30 days. Its purpose is fair credit: a shopper who clicks, thinks about it, and buys next week without a code is still the creator's.

24h and 30d
are the defaults for code lifetime and cookie window

The two are independent. A shopper on day 10 has an expired minted code and a live association. If they buy, they will not get the discount automatically, but the order is still attributed through the click. If they want the discount, a fresh click mints a fresh code.

Static-code programs have neither setting. The code is permanent and attribution is purely "was it on the order". That is simpler, and it is also why static codes leak.

6. How Admin GraphQL is used

The storefront never talks to Reveshare's database directly. Everything that changes state in Shopify goes through the Admin GraphQL API with the app's granted scopes, and the operations are narrow.

  • Discount codes and price rules. A program's customer discount is backed by a Shopify price rule. Minting a per-click code creates a code under that rule; expiring it deletes or deactivates the code. Campaign codes with start and end dates, usage caps, minimum subtotal and combination rules are created the same way, so Shopify enforces the limits at checkout rather than an app trying to police them afterwards.
  • Order reads. When a webhook payload is ambiguous, or a support ticket needs the current state of an order, the backend reads the order by id rather than trusting a cached copy.
  • Webhook subscriptions. Registered per shop at install, and re-registered if the app's URL or topics change.

Nothing in this list modifies the theme. That is deliberate. Theme edits break on theme updates, are invisible to the merchant's next developer, and are exactly the surface the checkout sandbox was designed to remove.

7. What you can and cannot see

A brand-side developer asking "show me the raw attribution" needs to know where each piece lives.

In Shopify Admin. The order, its discount codes, its financial and fulfilment status, refunds, and the customer. This is the source of truth for whether an order exists and what was on it.

In Reveshare. The click record, which affiliate it belongs to, the program's window and lifetime settings, the commission calculation with the rate and tier that applied, the pending or paid state, and any reversal. Dashboards show this per affiliate and per program, and exports let you pull it into your own tooling.

Not directly exposed. Internal delivery logs and the raw pixel event stream. If a ticket needs those, the Reveshare team can trace a specific order id through them.

The join between the two systems is always the Shopify order id. Every export carries it, so reconciling the affiliate ledger against Shopify's order list is a straightforward left join, and the unmatched rows are your investigation list.

8. Debugging "the sale is in Shopify but not in the dashboard"

Work top-down. Each step either explains the gap or rules something out.

  1. Confirm the order exists and is paid

    Find it in Shopify Admin by order number. Check financial status. A pending, voided or fraud-held order will not carry commission until it is paid. If it was cancelled, that is the answer.

  2. Look at the discount codes on the order

    If an affiliate code is present, attribution should be to that affiliate. If it is a different affiliate's code, the code-on-order rule explains it. If there is no affiliate code at all, attribution depends on the click record.

  3. Check the click record and the window

    Find the affiliate's clicks in Reveshare around the purchase date. No click from that shopper means no association; a click outside the cookie window means the association had expired. A click on a different device than the purchase will not link.

  4. Check exclusions

    Was the buyer the affiliate? Self-referral cleaning on plans that have it will have excluded the order. Was the product outside the program's scope? Was the order later refunded, so the commission was reversed?

  5. Check webhook delivery

    In the Shopify app's webhook delivery view, confirm orders/create for that order id was delivered and acknowledged. A failed delivery that exhausted retries is rare but real, and the fix is a manual re-sync of the order rather than a code change.

  6. Escalate with the order id

    If everything above checks out and the order is still missing, send the Shopify order id, the affiliate, and the purchase timestamp. That is enough to trace the delivery end to end.

In practice the ticket is resolved at step two or three most of the time. The order carried someone else's code, or the click was outside the window, or there was no click at all because the shopper came back through search.

Conclusion

Affiliate attribution on Shopify is no longer a script on the thank-you page. It is a chain: a click that mints a short-lived code and starts a 30-day association, a sandboxed pixel that reports the checkout quickly, an orders/create webhook that confirms it authoritatively, a commission calculated against program rules, and refund and fulfilment events that adjust it before a monthly payout. The decisions are made server-side because ad blockers, the checkout sandbox and the August 2026 upgrade all make the browser unreliable. The code on the order beats the click, handlers are idempotent on order id, and the Shopify order id is the key that joins the two systems when something needs tracing.

Have a question about your program?

Payouts, removals, tier setup, migrations. Write to the team and a real person replies, usually the same day.

Email the team →

Frequently Asked Questions