for Shopify

Support

Frequently asked questions

This is the app's own help documentation, published in full - the same answers a merchant reads inside the product, including the ones about its limits. Anything it cannot answer: [email protected].

Getting started

cbsplit does two separate jobs: it splits traffic between versions of a page to find out which sells better, and it shows one-click offers after checkout. You can use either without the other. Both need one switch turned on in Shopify first.

What does this app actually do?

It answers two questions with real money rather than opinion: which version of a page sells better, and what else a buyer will take once they have already paid. Everything else here is machinery for those two.

Turn on the theme app embed

Split tests need a small piece of code running on your storefront. Without it no visitor is assigned a version, no visit is recorded, and no order is attributed. Nothing errors - it simply does nothing, which looks identical to a test nobody has visited.

  1. Shopify admin, Online Store, Themes
  2. Customize, on the theme you actually sell from
  3. In the toolbar at the TOP LEFT, click the app embeds icon - the small blocks icon sitting to the right of the sections and settings icons. It is not in the section list below
  4. Switch on cbsplit split tests, then Save

You should see: Visit a targeted page in a private window; the test records a visit within a minute.

Theme-specific. Duplicating a theme or installing a new one starts it off again, so a redesign can quietly stop every running test. Also check you are on your LIVE theme - running the Shopify CLI creates its own development host theme, which appears in the same list and does nothing for real visitors.

Turn on the post-purchase page

Upsells need Shopify to hand the buyer to us after payment. Without this, funnels never render however well they are built - buyers go straight to the thank-you page.

  1. Shopify admin, Settings, Checkout
  2. Find Post-purchase page
  3. Select this app

You should see: A test order matching a funnel shows the offer between payment and the thank-you page.

Shopify allows exactly ONE post-purchase app per store. Another app in that slot blocks us completely, and the last one selected wins.

Why can't the app just turn those on for me?

Shopify deliberately keeps both under the merchant's control. We can hand you a link that opens the theme editor with the embed already switched on, but you still press Save; the post-purchase setting has to be chosen in checkout settings by hand. An app that could silently modify your live theme and your checkout is one you should not install.

Do I need both switches?

Only for what you use. Split tests and pre-purchase offers need the theme embed; post-purchase upsells need the checkout page. A store running upsells and nothing else can leave the embed off entirely.

What is the setup guide on the home page checking?

Five steps, and it ticks each one from evidence rather than from your word for it. Turning the storefront switch on does not tick that step; the first storefront visit our script actually serves does. Same for the order step, which waits for a real order to arrive and be ingested.

This is why a step can look undone after you have done it: nothing has exercised it yet. It is also why the guide is worth trusting, because a green tick means something was measured on your store rather than assumed. Three conditions block completion even with every step ticked, and each says so in words: checkout tracking that has never reported, an upsell page that has never checked in, and a storefront switch in your theme (the cbsplit app embed, or its Run split tests box) that is off while a split test, pre-purchase offer or free shipping bar it runs is live.

Before you install

The five questions merchants ask about any testing or upsell app, answered for this one specifically.

Will it slow my store down?

One small script loads through Shopify's theme app embed system. On a page under test it makes a single call to assign the visitor a version. If you run a pre-purchase offer it also watches for a place to show it - an order bump can appear in the cart drawer, and a drawer opens anywhere - so on those pages it reads your cart once and asks which offer applies. On every other page it does nothing further. There is no tag manager, no third-party beacon farm, and no render-blocking overlay.

Honesty about the one real cost: variants applied in the browser take effect just after the page renders, so a heavily restructured variant can briefly show the original first. Small copy, image and price changes are imperceptible in practice, and the what-it-refuses-to-do page states this limit plainly.

Do I need a developer or theme changes?

No. The storefront piece is a standard app embed you switch on in the theme editor, and offers are built in a wizard. Nothing edits your theme's code - Shopify does not even allow apps to write theme files, which is a guarantee, not a promise.

How does one-click charging actually work?

The offer appears on Shopify's own post-purchase page, between payment and the thank-you page. If the buyer accepts, Shopify charges their original payment method for the added item - no re-entering card details, and the app never sees them. Decline or close, and the order stays exactly as placed.

Every charge is signed server-side from the funnel the merchant defined. The page in the browser cannot invent a price, a product, or a discount - forged requests are refused.

Which themes and plans does it work with?

Any Online Store 2.0 theme takes the app embed, and the post-purchase page is Shopify's native surface on standard plans - none of this is Plus-only. Stores whose landing pages live outside Shopify can run tests through the headless API instead.

Two Shopify rules every upsell app shares: the post-purchase page is skipped for some express payment methods, and it appears only for orders paid in your store's own currency - so set your store currency to where you actually sell.

What does it cost?

Every store starts with a 30-day free trial. After that the fee is monthly and set by your previous month's gross sales - free under the lowest threshold, capped at the top tier, never a percentage of revenue. The exact table is on the pricing page and on the approval screen at install; both render from the same definition the billing engine charges with.

Once installed, the Plan page shows which tier you are in, the sales figure that put you there, and every charge made so far. Billing is settled in arrears, so a month is charged on what it actually did rather than on a forecast.

Split testing: how and why

Show some visitors one version of a page and the rest another, then compare what they spent. The point is not the pages - it is replacing an argument about what customers want with a measurement of what they did.

Why bother testing instead of just making the change?

Because most changes do nothing, a meaningful number make things worse, and neither is visible without a comparison. Shipping a redesign and watching revenue is not a test - the season, your ad spend and your stock levels all moved too.

How does the split work?

Each visitor is assigned a version on first arrival and keeps it for the whole visit and any return visit. That stickiness is what makes the comparison honest: someone who saw both pages belongs to neither group.

How does a sale get credited to a version?

An attribution token rides with the visitor from the page to the cart to the order. When Shopify tells us an order happened, the token says which test and version produced it. Orders that carry no token are left uncounted rather than guessed at.

Does it still work with Buy it now and Shop Pay?

Yes, but only because it is handled specially. Those build a checkout straight from the product form and never touch the cart, so the token travels as a hidden field on the form as well as on the cart. Before that was added, every accelerated-checkout order attributed to nothing.

Worth knowing if you compare our order counts against Shopify's: a mismatch concentrated in express checkouts points here rather than at your test.

What should I test first?

The thing the most people see and the fewest people scroll past. On a product page that is usually the first screen: the headline, the main image, the price presentation, the buy button. Testing a footer link is a valid test of something that does not matter.

What can't I test?

Anything Shopify owns outright. Checkout itself is not ours to change, and neither is the post-purchase moment beyond the offer we render. Test everything up to the buy button freely.

One-click upsells: how and why

An offer shown after the card is charged and before the thank-you page. One click accepts it - no card re-entry, no second checkout - because the items are added to the order that was just placed.

Why after payment rather than in the cart?

Because it cannot cost you the original sale. A cart upsell adds a decision before the money is committed, and every added decision loses some buyers. A declined post-purchase offer leaves the first order exactly as it was.

This is why aggressive offers belong here. The downside of a buyer saying no is zero, which is not true anywhere else in the funnel.

How is the offer chosen?

From what was just bought - the variants, products, SKUs and the order subtotal - matched against your funnels' targeting rules. One thing does come from the visit: if the buyer was in a split test and you bound a funnel to their version, that binding is checked first and wins. Bind no funnels to a test and the choice is made from the order alone.

Where does the money show up?

On the original order, as extra line items. There is no second order to reconcile, and Shopify's own totals, fulfilment and returns cover the upsold items as normal.

Does this work if my store is headless?

Yes, unchanged. Post-purchase attaches to Shopify's checkout, which you keep no matter where the page before it lived.

Why did no offer show on an order?

Shopify skips the post-purchase page entirely for some orders, whatever your setup says. The big one is accelerated wallets: Apple Pay, Google Pay, Shop Pay Installments, Klarna, Affirm and Afterpay all check out without one. So do gift cards and anything not paid by credit card, orders paid with more than one method, subscriptions, orders carrying duties or more than one currency, and orders created through the API or from a draft.

Wallets are the reason this looks intermittent rather than broken. If your store takes a lot of Apple Pay, a real share of orders will never be offered anything, and no amount of funnel setup changes that. To test, place a plain single-currency order on a credit card.

Thank-you page offers: one more product, added to the order

A thank-you page offer is a small card on Shopify's Thank you page: a headline, a countdown, one product with its price and saving, and a button, Add to my order. Pressing it adds the product to the order the shopper just placed and sends them to Shopify's page to pay for it. It belongs to a funnel as its last step, after the post-purchase offers.

How is it different from a post-purchase upsell?

A post-purchase upsell is one click and charges the card the shopper just used, before the Thank you page. A thank-you page offer comes after that: the product is added to the same order, and the shopper pays the difference on Shopify's own payment page, with any payment method. So it is a second small checkout on the same order, not a one-click charge.

That is Shopify's rule, not ours: nothing on the Thank you page may change the order directly, and an ordinary checkout stores no card an app could charge afterwards. Every app that offers this works the same way.

How do I set one up?

Add it to a funnel, bind the product, and place the card on your Thank you page in Shopify's checkout editor. Until the store has approved the app's order-editing permission, the card stays off and the funnel page says so.

  1. Funnels, open the funnel, then Add thank-you page offer
  2. Bind the product, set its discount if any, write the headline and choose the countdown
  3. Settings, then Checkout, then Customize, open the Thank you page and add the cbsplit block where you want it
  4. Save the funnel and make it active

You should see: Place a test order and reach the Thank you page: the card appears where you put the block, with the product and its price.

One per funnel. The card obeys the funnel's targeting and split tests exactly as the post-purchase offers do, so a shopper in arm B of a funnel test sees arm B's card.

What happens if the shopper adds it and never pays?

After 30 minutes cbsplit takes the product back off the order and restocks it. If Shopify will not let it be removed, the order is listed in your dashboard's to-do list, because the item may otherwise ship unpaid. Then collect the balance, or remove the item from the order in Shopify. Nothing is counted in the meantime: the offer's revenue is recorded only once the order's balance is back to zero.

Where does its money show?

As its own row on the funnel's steps and its own segment in the test report's stage bar, labelled Thank-you page. It is never folded into the upsells, because it is a different kind of sale and the point of running both is to see them apart.

Pre-purchase offers: an offer before checkout

A pre-purchase offer is a small add-on offer shown before checkout: beside Add to cart on the product page, or as an order bump above the checkout button in the cart and cart drawer. It is the one surface here that acts before the payment, so it can raise the order itself rather than adding to it afterwards.

What is an order bump?

A tickbox offer above the checkout button, on the cart page and inside the cart drawer. The shopper ticks it, the product joins the cart, and they check out once. Because it is pre-purchase, the money lands in the original order rather than in a second one.

Both places, not one: a theme's drawer cannot host an app block, so the widget is placed structurally rather than by you dragging it somewhere. That is why it needs no theme editing and why it appears in the drawer at all.

What is a product-page offer?

One or more add-ons a shopper can tick beside Add to cart. Ticking only selects: the add-ons ride the theme's own Add to cart, and Buy it now carries them too. They join the cart as lines nested under the product, so Shopify removes them wherever the product is removed: in the cart, at checkout, on the order status page, in emails and in the admin.

  1. Online Store, then Themes, then Customize
  2. Open the product template, then Add block, then Product-page offer under cbsplit
  3. Save

You should see: Load a product page the offer applies to and the add-ons appear beside Add to cart.

Unlike an order bump, this one is an app block you place, so it shows only where the block is. Its discount, if any, is the same automatic discount an order bump uses and needs the product it was offered with to be in the cart. One offer shows per placement, so an order bump and a product-page offer can both show on one page.

How do I create one?

Pick where it shows and the product it adds, decide who sees it, and activate. A new offer opens on a starting design you can edit, and nothing shows to a shopper until you activate it.

  1. Pre-purchase offers, then New cart and cart drawer offer or New product page offer
  2. Pick the product it adds to the cart
  3. Choose when it shows: every cart or product page, or only for certain products, collections, or cart subtotals
  4. Edit design if you want different copy, then Activate

You should see: Load a page it applies to as a shopper: an order bump appears above the checkout button, a product-page offer beside Add to cart once its block is placed.

How does the offer's discount work?

You name one figure, a percentage or an amount off. That single value is what the widget displays and what checkout charges, so the two cannot disagree. If the charge half cannot be set up, no discount is shown at all rather than a price we cannot honour.

The figure is locked while the offer is live, because Shopify's discount was created from it. Deactivate the offer to change it, then activate again: that rebuilds the charge from the new value.

Who sees it?

The same kinds of rule a funnel uses, read against the cart rather than the order. A product-page offer reads its rule against the product being viewed, and its subtotal rule against the cart.

  1. Every cart, or every product page: the catch-all
  2. Carts containing a product, or only these products: a specific variant, or any variant of a product
  3. Carts containing a collection, or products in a collection you pick
  4. By cart subtotal: a range you set, read against the cart at either placement
  5. Only as part of a split test: it shows to nobody unless a test sends them

A cart that already holds the offer's own product never sees it. Offering someone what they are already buying is the one case worth ruling out by hand.

Two offers could match the same shopper. Which one shows?

The more specific rule wins. An offer targeting a product beats one targeting a collection, which beats a subtotal range, which beats the catch-all. One offer shows at each placement, never two.

A cart holds a snowboard. You have an order bump for that snowboard and another on every cart. The snowboard bump shows, and the every-cart bump takes the carts it did not want.

Where two offers at the same placement have rules of equal reach, the more recently edited one takes all of that traffic and the other shows to nobody. The app says so on the offer that is losing, naming the one taking its traffic, because an offer reading Active with no views and no reason is indistinguishable from broken.

How do I test a pre-purchase offer?

Two shapes. Against no offer, which measures whether having one helps at all, or against a different offer, which measures which of two is better. The wizard asks which after you press the button.

  1. Open the offer, then Split test this offer
  2. Choose what to test it against: no offer, or another offer
  3. Set the split and name the test
  4. Start it

You should see: The report counts shoppers who were shown the offer rather than page visitors, and take rate is accepts over those shoppers.

In a two-offer test the CONTROL's targeting rule decides who is in the experiment, for both arms. Otherwise the arms would reach different shoppers and the test would be measuring reach as well as the offer.

Why is my pre-purchase offer not showing?

Seven causes, and the first two account for most of it.

  • The product is not published to your Online StoreAn offer verifies the product is actually buyable before it renders, and an unpublished product is not. The product picker cannot see channel publication, so it will list the product as sellable. Check the product's sales channels in Shopify.
  • The theme app embed is offOnline Store, then Themes, then Customize, then App embeds. With it off nothing of ours runs on your storefront at all.
  • Its app block is not in the product templateA product-page offer renders inside a block you place: Online Store, then Themes, then Customize, open the product template, then Add block, then Product-page offer under cbsplit. An order bump needs no block.
  • The offer is a draftA draft shows to nobody by design. Activate it.
  • Another offer outranks itA more specific rule wins outright. The offer's own page names the one taking its traffic.
  • The cart already holds that productDeliberate: an offer never shows what is already in the cart.
  • Its rule is Only as part of a split test, and no test is runningThat rule means it serves nobody passively. Either start the test or give it a real rule.

Free shipping bars: free shipping over an amount, shown and charged from one value

A free shipping bar tells shoppers how much more to add for free shipping and fills as they add it, on the cart page and in the cart drawer. The amount you set is the amount checkout gives free shipping at: one value, shown and charged, so the bar cannot promise what checkout will not do.

What counts toward the amount?

The cart's merchandise subtotal after product discounts and before any code entered at checkout, in your store's currency. Shoppers paying in another currency see the amount converted at your store's own rate, and checkout converts it the same way. An accepted pre-purchase offer counts, because it is in the cart.

A discount code entered AT checkout can lower the subtotal after the bar was read, and a merchant's own discounts that are set not to combine with shipping discounts can take precedence there. Both are Shopify's rules at checkout; the bar reads the cart as it stands.

How does checkout actually give the free shipping?

Activating a bar creates a free shipping discount in your store from the bar's amount. It applies to every parcel of a qualifying order, so a split shipment is still free. If that discount cannot be created, the bar stays off and says why, rather than showing a promise checkout would break.

If your store already gives free shipping over that amount through its shipping rates, nothing changes: the discount takes 100% off a rate that is already zero. The bar is then the messenger for a policy you already had.

The bar is active but says it is showing nothing. Why?

Its free shipping is not armed at checkout: the discount was deleted on Shopify, or the amount or rule changed while it was paused and the discount still describes the old bar. The bar serves nobody rather than promising the wrong figure. Activate it again to rebuild the charge from the current amount.

The other direction is worse and is watched too: a bar whose free shipping could not be switched OFF when it was paused says so, because qualifying carts may still ship free with no bar showing. Pausing or activating again retries.

Where does it show?

Top of the cart puts it under the cart's heading, in the drawer and on the cart page, wherever your theme has one; where it has none the bar falls back to sitting above the checkout button. Above the checkout button is the proven place on every theme, beside any order bump you have.

It is added by the same app embed the pre-purchase offers use, so it needs no block placed. If the embed is off nothing of ours runs.

Who sees it?

The same rule kinds a pre-purchase offer uses, read against the cart: every cart, carts containing a product or a collection, or a cart subtotal range. The free shipping at checkout follows the same rule, so a cart the bar would not show on does not get it either. A cart with nothing to ship sees no bar.

One bar shows per cart. Where two bars have rules of equal reach, the more recently edited one takes every cart and the other says so on its own row.

What does a free shipping bar test measure?

The OFFER: half your shoppers get the bar and the free shipping at its amount, half get neither. The report's conversion rate, revenue per shopper shown and order value compare the two. That is a test of giving free shipping with a bar announcing it, not of the bar alone.

  1. Open the bar, then Split test this bar
  2. Choose what to test it against: no offer, or another bar at a different amount
  3. Set the split and name the test
  4. Start it

It cannot run beside a page test: both claim the same orders, so one would report the other's traffic. Stop the page test first, or wait for it to finish. The Shipped free figure is read off each order's own shipping line; on a store with a native free shipping rate the control arm ships free too, and the report will show that.

Setting up a basic split test

How do I create a test?

Pick what page it applies to, pick how the versions differ, and set the split. The wizard checks your theme can actually serve what you chose before it lets the test run.

  1. Tests, then Create a test
  2. Choose the target: a product, a collection, a URL pattern, or every page
  3. Choose how the versions differ: element changes, an alternate template, or a price
  4. Set the split, then create it
  5. Edit each version to make the changes you want to test
What can a test target?

A specific product, a collection, a URL pattern, or everything. The target decides which page loads carry the test - a visitor on a page that does not match is never assigned and never counted.

How should I split the traffic?

Evenly, unless you have a reason not to. An uneven split is legitimate for limiting exposure to a risky variant, but it makes total revenue meaningless as a comparison, because the version shown to more people wins by construction. Shares always add to 100.

What are the delivery methods?

Element changes rewrite parts of the page after it loads, and are what the visual editor produces. Alternate templates serve a different Liquid or JSON template you already made, which is better for large layout differences. A price test changes nothing but the price: you name a percentage and the discounted version is generated for you, with checkout charging the same figure the page shows. Headless hands the decision to your own code.

How do I edit a version?

The visual editor opens your real storefront with an editing layer over it, so you change the real page at real width with your own theme running.

  1. Open the test, then Edit next to the version
  2. Dashed blue outlines what you can edit
  3. Solid orange marks what this version already changes
  4. Click an element, change it, and it saves as you go
Does this modify my theme?

No. Each change is stored as an instruction against a CSS selector and applied only when a visitor is put in that version. Stopping the test removes every change immediately.

The flip side: a change is anchored to an element, so a redesign that removes or renames it leaves the instruction matching nothing and the version quietly renders as the control.

Why won't it let me start a test on that template?

Because Shopify answers a request for a template that does not exist by silently serving the default one. That version would render the control, the test would report no difference, and you would conclude the change did nothing. We check first, and refuse if we cannot check.

When does a test stop?

When you stop it, or on the rule you set: a date, a number of sales or visits, or reliability. Reliability is the one to prefer - it runs until both versions carry enough traffic to support a conclusion, then judges once.

Avoid stopping the first time a version looks good. Checking repeatedly and stopping on a lead is the single most expensive mistake in testing - see the results section.

Can I edit a test while it is running?

You can, and for a typo it is fine. For anything larger, restart it: visitors before and after the edit saw different pages, and their results are pooled into one number that then describes neither.

Advanced split tests: pages you host yourself

Testing a page on your own domain that sells through your Shopify store - a Hydrogen or custom storefront, or a marketing landing page on your own CMS. Fully supported and fully documented, and the one part of the product that expects a developer.

Who is this for, and what does it cost me?

Anyone whose page lives outside Shopify. Everything it needs is documented, but wiring it into your site is your side of the line. Ask about implementation support if you would rather not do it yourself.

Do I have to give up the testing tool I already use?

No, and you should not. If your site or CMS already splits its own traffic, tell us which version it served and we supply the part it cannot see: what the order was worth after refunds, shipping, tax and cost of goods. A tool watching your frontend only knows a button was clicked.

What has to reach us?

Two things. A call when the page loads so the visit is counted, and an attribution token on every link into your store so the sale comes back.

Miss the first and every version reports a 0% conversion rate. Miss the second and the test records visits and never a sale. Both look like real results rather than missing wiring.

Which integration should I choose?

Three shapes, best first. If you already split your own traffic, just report the version you served. If you have a server or edge worker, ask us before you render. Only if you have neither should you use the drop-in script.

  1. Your own split already: report it, and we score it
  2. A server or edge worker: ask us before rendering, so there is no flicker
  3. A static site with no server: the drop-in script, which manages the flicker for you
Why does deciding the version in the browser cost me something?

The page cannot render the varying area until the answer arrives. Showing the control first and swapping would make the variant slower on every load, so the test would partly measure network latency instead of your change. We hide the varying area for every version equally: slower for everyone, biased against nobody.

Deciding on your own server avoids the cost entirely, and is always the better integration.

Testing a price

A price test sells the same product at two prices and measures which makes you more, per visitor rather than per sale. It is the one test where the change is not something you design, so there is nothing to build: you name a discount and the arm sells at it.

How do I test a lower price?

Choose Test a different price in the New test wizard, pick the product, and say how much off version B sells at. There is no variant to build and nothing to edit: one value decides both the price the page shows and the price checkout charges, so the two cannot drift apart.

  1. New test, then Test a different price
  2. Pick the product to test
  3. Set how much off version B sells at
  4. Check the variant table underneath, which lists every variant with its current price and its test price
  5. Start the test

You should see: Load the product page as a shopper and version B shows the lower price. Add it to the cart and checkout charges that price.

What does the shopper actually see at checkout?

A discount line on the order, labelled Special price. That is how the lower price is charged: Shopify applies it as a discount rather than by changing the product, so it appears in the order summary the way any discount does, and it stays on the order confirmation and the emailed receipt.

The wording is deliberately neutral. It says nothing about a sale, a promotion or an offer, because a shopper in the control arm is not in one and a label that implied otherwise would follow the discounted buyer around their paperwork.

Why is there no struck-through original price on the product page?

Because adding one would change what the test measures. A was-price is a second variable: it adds urgency on top of the lower number, and a win would no longer say whether the price or the urgency did it. The arm shows the lower price plainly.

If a struck-through price is the thing you actually want to test, that is a real test and worth running, but it is a page change rather than a price change and belongs in a template or element test.

Can I change the page and the price in the same arm?

Yes, and the wizard offers it as Also sell at a percentage off on an ordinary test. It is worth knowing what you are giving up: the arm now differs from the control in two ways at once, so a result tells you the combination won and cannot separate them.

Version B has a new hero image and sells at 10% off, and it wins. You have learned that the pair beats the control. You have not learned whether the image would have won on its own, or whether you are simply buying the sales with the discount.

The wizard says this on the row itself the moment you type a figure, rather than leaving it to be discovered in the result.

The price option says it needs a permission. Which one, and why?

Permission to manage discounts, because that is the mechanism the lower price is charged through. It is granted per store when you approve the app, so a store that installed before this existed will be asked to approve it again before the option becomes available.

Until it is granted the option is shown switched off with that reason, rather than hidden. A control that vanishes is indistinguishable from one that was never built, and a merchant cannot fix what they cannot see.

What happens on a store with several currencies or market prices?

The discount applies to whatever price that buyer's market shows, so a percentage off means the same percentage everywhere rather than a figure converted from your home currency. The variant table in the wizard lists your own market's prices, and says so.

One honest limit. On a page where we cannot read the market-adjusted price, the shopper is shown the price without the discount applied while checkout still charges the discounted one. It resolves in the shopper's favour, and it is the direction we would choose deliberately, but it means a screenshot of a product page is not proof the test is off.

Setting up a basic upsell flow

The simplest useful funnel is one offer shown to everybody. Start there, then add targeting once you know the offer converts.

How do I build my first funnel?

Create a funnel, add one step, put a product in it, and leave the targeting as global so every order sees it. You can narrow it later without rebuilding anything.

  1. Funnels, then create a funnel
  2. Add a step and choose the product to offer
  3. Set the discount, if any
  4. Leave targeting on global for now
  5. Turn the funnel on

You should see: A test order shows the offer between payment and the thank-you page.

What makes a good first offer?

Something cheap relative to what they just bought, obviously related, and needing no thought. The buyer has already decided to trust you and their card is already charged - the offer that works is the one that does not restart the decision.

What happens when they decline?

Nothing. The original order completes exactly as it would have. You can also chain a different step onto the decline, which is where advanced funnels start.

How do I test two funnels against each other?

Use the funnel test wizard. It needs no page, no target and no version editing: you pick the funnel you are running now as the control, pick the one to try against it, set the split, and start. Buyers are divided between the two funnels after checkout, and the report measures the funnels rather than any page.

  1. Funnels, then Split test this funnel - or Tests, then Create a test, then Funnel
  2. Confirm the control: the funnel you are running today
  3. Pick the challenger to test it against
  4. Set the split and start it

You should see: The report counts orders offered rather than page visitors, and revenue per order decides the winner.

The unit is an order, not a page view, because a funnel is only reached after checkout. Everything else is unchanged: the same sample size rules, the same refusal to call an early winner. If you would rather hold the funnel STILL while testing a page, that is the next answer down.

Can a test control the offer without testing it?

Yes, and it is worth doing. Bind one funnel to the WHOLE test and every buyer in it gets the same post-purchase experience, so a difference between your page versions cannot be caused by them having met different offers.

Advanced upsell flows: targeting and multi-product steps

Which funnel a buyer gets is decided by a priority hierarchy, evaluated top down. The FIRST level with any matching funnel wins, and everything below it is ignored - so a rule's position matters as much as its contents.

How do I choose which orders a funnel applies to?

Give the funnel a targeting rule. There are five kinds, and they sit at different levels of a priority hierarchy rather than combining with each other.

  1. Product: the order contains a specific variant, SKU, or any variant of a product
  2. Collection: the order contains a product in one of the collections you pick
  3. Price: the order subtotal falls inside a minimum and maximum you set
  4. Global: every order, the catch-all
  5. Only as part of a split test: no passive rule at all - the funnel serves nobody unless a test sends buyers to it
What is the priority hierarchy, exactly?

Split test this version, then split test as a whole, then product, then collection, then price, then global. Every active funnel is checked at the top level first; if any match there, those are the only funnels considered and the rest are never looked at. The two test levels are separate on purpose: a funnel bound to version B has to beat one bound to the whole test, or setting the more specific rule would lose to the broader one.

A buyer purchases a $200 jacket. You have a product-rule funnel for that jacket, a collection funnel for outerwear, and a global funnel. Only the jacket funnel is eligible - outerwear and global are never reached, even though both also match.

This is why a broad rule never steals traffic from a specific one, and also why a global funnel only ever sees orders that no narrower funnel wanted.

Can I change the order of the hierarchy?

Not today. The order is fixed: split test, then product or variant, then collection, then order subtotal, then global. If a broader rule needs to win over a narrower one, make the narrower funnel's rule match fewer orders, or turn it off - the first rung with a match wins outright.

What if two funnels match at the same level?

They are split-tested against each other, using the same weighted sticky assignment as a page test. That is the intended way to compare two offers for the same audience.

What if nothing matches?

No offer is shown, and the buyer goes straight to the thank-you page. If you want a floor, give one funnel a global rule - that is exactly what global is for.

How precisely can product targeting match?

Three ways, and any one hitting is enough: a specific variant, a SKU as text, or a product id meaning any variant of it. Use the variant when size or colour changes the right offer, and the product when it does not.

How does price targeting decide?

On the order subtotal, against a minimum and maximum. Either bound can be left open, so "over $200" and "under $50" are both expressible.

Can one step offer more than one product?

Yes, and how it behaves depends on the mode: a single product, a bundle where everything is included together, or a choice where the buyer picks one or several. The layout adapts to how many items the step carries.

How do I build a multi-step funnel?

Each step branches on whether the buyer accepted or declined, so the path can differ. Follow an accepted offer with a bigger one, and a declined offer with something cheaper.

Restraint pays here. Every extra step is another page between the buyer and their confirmation, and the second decline is usually where goodwill ends.

What happens when a product in a funnel sells out?

The step is skipped and the buyer goes on to the next one. The funnel keeps running rather than stopping, and it starts offering the step again by itself once the product can be sold. Nothing needs switching off and nothing needs switching back on.

  1. A step whose only product is out of stock is passed over entirely
  2. A step offering a choice loses just that option, and still shows with the rest
  3. The funnel page marks the step, and the home lists it under Needs attention

This is worth knowing before you read a report. A step that spent a week sold out has fewer views than the steps either side of it, and that is a supply fact rather than a result about the offer.

Charging for shipping on an upsell

Shopify ships post-purchase items free, which means the carriage on an accepted offer comes out of your margin unless you say otherwise. This is where you say otherwise, and what each of the three answers actually does.

Why does an upsell ship free unless I change something?

Because that is Shopify's own behaviour, not a setting of ours. An accepted post-purchase offer becomes its own shipment on the order, and Shopify prices those extra shipments at zero. The order page says so in as many words: the first shipment carries the carrier's rate and the rest read Free.

  1. Open the step, or the offer designer, and find the Shipping row
  2. Free leaves Shopify's behaviour alone, and is what every offer did before this control existed
  3. Flat charges a figure you set, per unit
  4. Magic estimates what the extra weight actually costs to carry

You should see: On the next test order, the accepted offer's shipping appears on the shopper's confirmation instead of reading Free.

This is the one control here that changes what a shopper is charged rather than what they see. Whatever it decides is shown on the offer page before they accept, and the same figure is what the card is charged: the page cannot quote one number and take another.

What does Magic actually work out?

What the extra weight adds to the parcel, at the per-kg rate your own orders show. It reads your store's real orders, fits the relationship between weight and what carriers charged you, and applies it to the item being offered. It is an estimate from your own history, not a live quote.

  1. It needs weights on your products in Shopify, because weight is its only input
  2. It needs a handful of past orders carrying real carrier rates to learn from
  3. It reads what this buyer paid to ship their original order as a check: a rate that does not fit the pattern your orders show means no estimate, and your fallback is charged instead

There is no live carrier quote available here and there cannot be. Shopify never re-rates a post-purchase shipment, so there is no rate to ask for. The line under the control tells you what Magic will do on your store specifically, including when the honest answer is that your rates do not vary with weight and it will therefore charge nothing.

What is the Fallback figure, and why does the same box change its name?

It is one box that means two things, depending on the mode beside it. On Flat it is the rate and it is charged every time. On Magic it is what gets charged whenever the estimate cannot be worked out, which is an ordinary occurrence rather than an exotic one.

  • The item has no weight set in ShopifyMagic has nothing to measure, so the fallback is charged. Set a weight on the variant.
  • Your store has too few orders with carrier rates for an estimate to be trustworthyThe fallback covers it until enough orders exist. The line under the control says when this is the case.
  • The buyer's own shipping cost does not fit the pattern your other orders showRather than estimate from a figure it cannot explain, it charges the fallback.

Leaving it empty on Magic means those offers ship free and nobody is told, so the app warns about it rather than accepting it quietly. A fallback is not a nicety on this control.

Can one item in a step ship differently from another?

Yes. Each product in the step has its own Shipping box, and what you put in it beats the step's setting for that product alone. This is what lets a step sell something heavy beside something that rides along in the same box.

  1. Leave it empty and the item follows the step: the step's rate on Flat, the weight estimate on Magic
  2. Type a figure and that is charged for that item, whatever the step says
  3. Type 0 and the item ships for nothing

A step offers a snowboard and a sticker pack. On Magic the snowboard is estimated from its weight, and the sticker pack is set to 0 so it rides along free.

Empty and 0 are different answers, and the difference is deliberate. Empty means you have not said, so the step decides. Zero means you have said, and it means free. A control where blank quietly meant free would make every item you had not thought about ship at your expense.

Why is an upsell still shipping free after I set this?

The setting is per step, and several things downstream of it can send the offer back to free.

  • The step is still set to FreeShipping is set on the step, not on the funnel. A funnel with four steps has four of these and they are independent.
  • It is set to Flat or Magic with no figure in the boxBoth fall back to that figure, and an empty box is zero. The rules panel on the offer flags this rather than letting it pass.
  • Magic is on, the item has no weight, and there is no fallbackThe estimate has no input and nothing to fall back to. Set a weight, a fallback, or both.
  • The order predates the changeThe first offer a shopper sees is fixed at the moment they pay, so a change made afterwards reaches the next order rather than the one you are looking at.

Designing the offer page

The post-purchase page is drawn by Shopify from a fixed set of components, not by your theme, so what can be styled is narrower than anywhere else you have designed. This is where that line falls, including the parts that are not ours to move.

What can I change about how the offer looks?

Blocks and their order, the layout, text size and weight, three semantic text colours, image framing and size, alignment, and the space around each block. You place blocks in a list; the shopper's page draws them top to bottom in that order. Alongside the text blocks there is a countdown timer, a row of trust badges, and a hero image with four sizes.

  1. Open a funnel, pick a step, then Full screen editor
  2. Start from a template if you want a whole arrangement rather than a blank page, keeping or replacing what you have already written
  3. Add blocks from the palette on the left
  4. Select any block on the canvas to edit it
  5. Open Advanced on a block for size, colour, alignment, Space and Indent

The preview is the same design drawn by a second renderer, not a screenshot of the real page. It is accurate about layout and copy, and it cannot show you the acceptance banner a shopper sees on step two, because whether that appears depends on the path they took rather than on this step's design.

What can I change about the offer itself, rather than how it looks?

The same screen sets what is actually being sold. Which variants the shopper may choose between, whether the quantity is fixed or theirs to pick and how high it can go, a compare-at price, a discount, and how any saving is worded.

  1. Saving picks how a discount is described, from six wordings, previewed with this step's own numbers
  2. Variants ticks which variants of the bound product the shopper can pick between
  3. Quantity switches between a fixed number and Shopper chooses, with a maximum you set
  4. Compare at and Discount sit on each item, and the discount carries a label that lands on the customer's order
  5. Breakdown, under Advanced, switches the item, shipping and subtotal lines on the offer summary

The quantity maximum is a real limit rather than a menu. The server clamps what it charges to that number, so lowering it genuinely stops a larger quantity being bought, which a dropdown alone would not.

Why is there no margin, line height, or control over the gap between blocks?

Because Shopify's post-purchase components do not have them. This is not a feature we skipped: there is no margin property on any component, no line height, letter spacing, font family or point size, and no free colour anywhere. Text colour is three named values (success, warning, critical) that Shopify's own theme decides the look of.

  1. For space around a block, use Space and Indent under Advanced
  2. For a gap between blocks, add a Spacer
  3. For a coloured background, use a Coloured notice - it is the only fill this page has

Worth separating two things that sound alike. PADDING is room around a block, and you have it. SPACING is the gap between blocks, or between the lines of a bullet list, and that is fixed by Shopify - the Space and Indent controls cannot reach it, and a Spacer block is the way to open a gap. We would rather show you a short list of controls that work than a familiar-looking one that quietly does nothing on a real order.

Can I change the accept and decline buttons?

The wording is Shopify's and cannot be changed, only translated: the accept button reads Pay now and the total, the decline reads Decline upsell offer. The POSITION is yours. Every page ends with a pair, and you can place a second pair partway down a long page so the price and the buttons stay in view.

We previously said the position was fixed too. That was our mistake rather than a Shopify rule - the only placement rule is that decline sits under accept and stays less prominent. If you were told the buttons could not be moved, this is the correction.

Why does my edit to the first step not show on a reload?

The first offer is fetched at checkout and parked, so reloading the post-purchase page replays that snapshot rather than asking us again. Every later step is live. This is deliberate and correct for the shopper - the offer promised at checkout is the offer honoured - but it means iterating on step one needs a new test order each time.

Steps two and beyond can be re-tested by reloading and advancing, so build the long designs there if you are iterating quickly.

Understanding the results

The hard part of testing is not collecting numbers, it is knowing when a difference is real. Most of this section exists to stop you acting on one that is not.

What is statistical significance, in plain terms?

A measure of how surprising your result would be if the two versions were actually identical. High significance means the gap you are seeing would rarely happen by chance alone - it does not mean the gap is large, or that it will hold.

Flip two fair coins 10 times each. One will usually 'win'. Flip them 10,000 times each and they converge. Significance is the arithmetic that tells you which of those two situations you are in.

What does "underpowered" mean?

That there is not yet enough traffic for a lead to mean anything. We show the sample size needed to reliably detect a difference of a given size; below it, a lead is as likely to be noise as signal, so no winner is named. A page test is sized against a 10% difference by default. Funnel and pre-purchase offer tests are sized against 25%, because they are judged on far fewer events - orders and shoppers shown an offer rather than page views - and a 10% target there would ask for traffic no store has. You can set your own figure per test.

At a 2% conversion rate you need roughly 15,000 visitors per version to detect a 10% difference. With 400 each, a version showing 3% against 2% is not a 50% winner - it is four orders against three.

The banner says what is still missing instead of showing a number that looks decisive. An underpowered test is not broken - it is unfinished.

Why won't it declare a winner when one version is clearly ahead?

Because checking repeatedly and stopping the first time a version crosses 95% declares a winner on two IDENTICAL pages about half the time. We measured that. A reliability-triggered test runs until both versions carry enough traffic, then judges once.

Is "no winner" a failure?

No, it is a result, and an actionable one: it tells you the change does not matter and your effort belongs elsewhere. Inventing a winner from a rounding difference is how you roll out a change that never worked.

Which metric should I actually judge on?

Revenue per visitor, usually. It stays correct when versions differ in both how often they sell and how much they sell, and a version can convert less often and still deservedly win.

A version converting 10.3% against 13.8% still leads if its buyers spend enough more: $342.97 per visitor against $308.78. Fewer orders, bigger baskets, more money.

Why is total revenue not the headline?

It is only trustworthy on an even split, because the version shown to more people wins by construction. Every figure on the report is one version's, compared against another - none of them are test-wide totals.

What is "probability of being best"?

How likely each version is to be the genuinely better one. It is the question people think a p-value answers. 60% is not a winner; it is a coin with a slight lean.

95% means that if you rolled this change out, you would be right about 19 times out of 20. 60% means 3 times out of 5 - barely better than guessing, and not worth a deployment.

What do the toggles change?

What a sale is worth. Refunded orders are excluded by default, because money that came back is not evidence a version sells. Shipping and tax are included by default, so the figures reconcile with the order totals Shopify already showed you.

Why does gross profit say it has no data?

Gross profit is revenue minus your product costs, after refunds and before the payment fees and shipping we cannot see. It needs a cost per item, which Shopify only has if you have entered one; without it the figure would just be revenue under a second name, so the tile says what is missing rather than printing the same number twice under two labels.

What counts as a view?

A shopper actually seeing an offer - and the word covers two counts, each labelled where it appears. Funnel views (the funnel list, the home) count shoppers who ENTERED a funnel, one per run. Step views (the flow chart, the report's Views column, an order's Offered line) count who was SHOWN that specific step, so later steps read lower: a buyer who takes upsell 1 may never see upsell 2. Step views have been counted since the Aug 2026 update; where history predates that, or an active filter is one a view cannot answer, the report shows a dash whose hover names the exact reason - never a number that is quietly too small.

How do refunds and chargebacks show up?

As their own rates per version, so a variant that sells well and returns badly is visible rather than flattering. Refunded orders are already out of the revenue figures by default.

What does the subscription renewal toggle do?

It adds money from repeat subscription charges to the version that acquired the subscriber. Off by default, because a renewal is not a result of the test the way the first order is: the test won the customer, and the renewals are what the product did afterwards.

A renewal is never counted as a sale for the test itself. It carries no version of its own and is linked back to the order that acquired the subscriber, so it can only ever be shown through this toggle. That is deliberate: it is what keeps a stopped test stopped, instead of letting an archived experiment go on collecting revenue in every base figure. A renewal we cannot trace to exactly one version reads as untraced rather than being assigned to the likelier one.

What are the figures across the top of the home page telling me?

What the offers have earned, and how orders that took one compare with orders that did not. Extra revenue is money from accepted post-purchase and pre-purchase offers since you installed. The three averages beside it are your order value with an offer taken, without one, and across the store as Shopify counts it.

The two average order values are a comparison between groups of shoppers, not a measurement of what the offers did. People who accept an upsell were already more willing to spend, so some of that gap belongs to them rather than to you. The number that answers what the offers earned is a test, and that is what the tests page is for.

Where do the referrer chips and the buyer journey come from?

From what was actually seen. The chips list only the channels your own visitors arrived through, so the set differs between stores and grows as new sources appear. The journey on an order shows where that buyer first landed, what brought them, and what they did in order.

A channel missing from the list means nobody arrived that way while the app was watching, which is not the same as nobody arriving that way. Traffic before you installed, or while the theme app embed was off, was never seen and cannot appear here.

When something looks wrong

Organised by what you are seeing, not by what is broken. Each one lists the possible causes in the order they are worth checking, because the common cause is rarely the interesting one.

Why is my test showing no traffic at all?

Something between the visitor and us is not connected. These are worth checking in order.

  • The theme app embed is offOnline Store, Themes, Customize, App embeds. Check the theme you actually sell from, not a copy.
  • The test targets a different pageOpen the test and compare its target against the URL you are visiting.
  • The test is paused or scheduledCheck its status on the tests list - a scheduled test records nothing until it starts.
  • You are looking at a filtered date rangeWiden the range on the report. A test started today shows nothing under a range ending yesterday.
Why are visits counting but no sales?

Visitors are being assigned, so the attribution token is not surviving the trip to the order.

  • The embed was switched off between the visit and the purchaseSwitch it back on. Orders placed while it was off cannot be recovered afterwards.
  • The page you are testing does not lead to a checkoutConfirm the buy path actually starts on the tested page.
  • On an externally hosted page, the links are not carrying the tokenCheck that every link into your store carries the cbs parameter.
  • The orders are genuinely not happening yetCompare against the same period before the test. Low traffic converts slowly, and this is the cause people check last.
Why is no upsell showing after checkout?

Either Shopify never handed the buyer to us, or no funnel wanted that order.

  • The post-purchase page is not set to this appSettings, Checkout, Post-purchase page. Only one app can hold that slot.
  • No funnel's targeting matched the orderCheck the hierarchy: if a narrower level matched a funnel that is switched off, lower levels are still never reached.
  • The buyer paid with an accelerated walletApple Pay, Google Pay, Shop Pay Installments, Klarna, Affirm and Afterpay all skip the post-purchase page. So do gift cards and any non-card payment. Nothing on your side changes this.
  • Shopify skipped post-purchase for that orderSubscriptions, multi-payment orders, orders with duties or more than one currency, some gateways and draft orders never show one. Try a plain single-currency card order.
  • The funnel is off, or its step has no productOpen the funnel and confirm it is on and every step carries something to sell.
Why is the wrong funnel showing?

Almost always the priority hierarchy doing exactly what it should.

  • A higher level matched firstA test binding beats product, which beats collection, which beats price, which beats global. The first level with any match wins and the rest are never evaluated.
  • Two funnels match at the same levelThat is a split test between them, by design. Assignment is sticky per buyer.
  • The hierarchy has been reorderedCheck the order in the funnel builder - it is draggable, and a past change may not be what you expect.
Why do both versions look identical to me?

Usually correct behaviour rather than a fault.

  • You are assigned to one version and staying thereUse a private window to arrive as a new visitor. Assignment is deliberately sticky.
  • The variant's changes no longer match your pageReopen the editor. A theme change can leave an instruction anchored to an element that is gone.
  • The variant serves a template that does not existShopify silently serves the default template instead. Check the suffix exists in your theme.
Why is a conversion rate above 100%?

Orders are being counted against visits that were never recorded.

  • Visit tracking started after the orders didNothing to do. It settles once both have been running together.
  • You placed the orders yourself without being trackedExpected on a demo or a first test. Real traffic dilutes it quickly.
Why did an order attribute to the wrong version?

Almost always a visitor who genuinely saw both.

  • You tested by switching versions in one browserA returning visitor keeps their FIRST version, deliberately. Someone who saw both belongs to neither, so we do not move them.
  • The test was restarted while they were mid-visitExpected, and confined to the restart window.