Upsells
Get Started
Add an upsell page that shows one offer and accepts it with a single button.
Prerequisites
- The order must be loaded in
orderStorebefore the button is enabled. This happens automatically when the SDK detects aref_idquery parameter on the page. - The order must have
supports_post_purchase_upsells: trueserver-side. If it does not, the accept button stays disabled. - The package you want to offer must have a numeric
ref_idfrom the campaign dashboard.
Mark the Page as an Upsell
Add a meta tag to the page <head>. The SDK uses it to track the page view and to wire the redirect URLs.
<meta name="next-page-type" content="upsell">
<!-- Where to go after the customer accepts -->
<meta name="next-upsell-accept-url" content="/upsell-2">
<!-- Where to go after the customer declines -->
<meta name="next-upsell-decline-url" content="/upsell-2">Add an Accept Button
The simplest pattern is a single accept button bound to a fixed package.
<div class="upsell-card">
<h2>Add an Extra Battery?</h2>
<p>Extend your flight time with a spare battery pack.</p>
<button
data-next-action="accept-upsell"
data-next-package-id="42"
data-next-quantity="1">
Yes, add for $29
</button>
<a href="/upsell-2">No thanks</a>
</div>The button is enabled only when the order is loaded and supports post-purchase upsells. On click, the SDK posts the upsell to the order API, then navigates to the URL in the next-upsell-accept-url meta tag.
The accept button is a BaseActionEnhancer, so it manages its own loading and disabled state. Do not write JavaScript to disable it during the API call — the SDK handles it.
Verify It Is Working
Load the page with a valid ?ref_id= in the URL. You should see:
- The button has no
disabledattribute and nonext-disabledclass. - Console:
[AcceptUpsellEnhancer] Initialized { packageId: 42, ... } - Clicking the button shows a full-page loading overlay, then navigates to
/upsell-2.
If the button stays disabled:
- Confirm the page URL contains a
?ref_id=query parameter. - Open the console and run
useOrderStore.getState().canAddUpsells(). If it returnsfalse, the order has not loaded or does not support upsells.
Next Steps
- The button-only pattern in depth: Accept Button
- The container-based alternative with built-in skip button: Offer Container
- Let the customer choose between options: Selectors
- Chain multiple upsell pages: Page Flow