- Checkout
- Features
Upselling on the confirmation page
Show product recommendations after the customer has paid and let them add items to the existing order in one tap — enabling it, feeding it recommendations and the two ways to create the order in your own system.
Upsell puts product recommendations on the confirmation page, after the customer has paid. Because the payment is already authorized, adding an item takes one tap — no payment details, no second checkout, no second order. Kustom owns the widget, the authorization increase and the order update. You decide what is recommended and, more consequentially, when your own system creates the order. That last decision is the real work on this page.
An integration has three parts, and all three are needed before you go live: turn upsell on, choose where recommendations come from and build your backend to accept an order that can still change after the customer lands on your confirmation page.
Before you start
Section titled “Before you start”Upsell must be enabled on your merchant account. Contact Kustom. Until it is, nothing on this page has any effect.
You also need to understand one consequence before you write any code: when upsell is active, the confirmation push is delayed until the upsell window closes. Anything your system does on the push — creating the order, reserving stock, emailing the customer — happens later than it used to: ten minutes by default, plus a minute’s buffer. Plan for that before you enable it.
The delay applies only when upsell is active for the order and the payment method the customer used allows it — Kustom checks that for you and only loads the widget when it does. If either is false, the push fires immediately and nothing about your existing flow changes.
Step by step
Section titled “Step by step”1. Turn it on
Section titled “1. Turn it on”Per order, which is what you want for a staged rollout or an A/B test:
{ "options": { "confirmation_page_upsell": true }}The flag can still be changed on an update, right up until the order reaches
checkout_complete.
Two other things also switch upsell on for an order, and it’s worth knowing both before you wonder why a push arrived late:
- Setting
merchant_urls.upsell. An order carrying an upsell URL has upsell on, with or without the option. - Account-level enablement. Kustom can turn upsell on for every eligible order on your
merchant ID. Once that’s set,
confirmation_page_upsell: falsedoesn’t opt an order out — ask Kustom to change the account setting instead.
2. Choose where recommendations come from
Section titled “2. Choose where recommendations come from”| Curation in Kustom Portal | Your own endpoint | |
|---|---|---|
| Effort | No development | Build and host an HTTP endpoint |
| How it works | Kustom imports your Google product feed and you pick the products to recommend | Kustom calls your endpoint and you return the products |
| Control | A fixed selection | Per order, dynamic, personalized |
| Best for | Getting started, a small set of evergreen add-ons | An existing recommendation engine or catalog-driven logic |
Curation in Portal needs nothing built. Connect your Google product feed under Upsell in Kustom Portal and choose the products to recommend. Kustom still filters the selection for each order: a product is dropped when its currency doesn’t match the order’s, when it’s out of stock, when it costs more than the authorization headroom or when the customer already has it in the order. Beyond that the selection is fixed — it doesn’t depend on what else is in the basket or on the chosen shipping option. If you need that, build an endpoint.
Your own endpoint. Set merchant_urls.upsell and Kustom calls it before the
confirmation page loads:
{ "merchant_urls": { "upsell": "https://www.example.com/api/upsell" }}Curated products win. If your account has Portal-curated products that survive the filtering above, Kustom shows those and doesn’t call your endpoint. Your endpoint is called only when there is no curated selection for the order. With neither, Kustom asks its own recommendation service instead.
Aim to respond in under 3 seconds. The hard limit is 10 seconds, the same deadline every merchant callback gets, and there’s no retry, so one slow answer is one lost widget. But the customer is waiting on a confirmation page while you think, and three seconds is about as long as that stays comfortable. Treat 3 s as your budget and 10 s as the cliff.
A slow or failed response means no widget — the customer sees an ordinary confirmation page. The push is still delayed for the full window, though: that delay is decided at completion from the upsell settings and the payment method, before your endpoint is ever called, and nothing about the call’s outcome shortens it.
3. Return recommendations
Section titled “3. Return recommendations”Kustom POSTs an upsell_request describing the order and the headroom available, and
expects a list of lines back. The full contract is a page of its own.
Four fields in the request are what make a recommendation good rather than merely valid:
max_upsell_amount— the authorization headroom, in minor units. Filter to what fits. Anything above it will be blocked when the customer taps.purchase_currencyandlocale— price and describe correctly.billing_addressandshipping_address— apply the right tax rate.selected_shipping_option— drop anything that would break the delivery method already chosen.
A minimal, valid response:
{ "upsell_lines": [ { "name": "Baseball Cap", "reference": "CAP-SAND-001", "quantity": 1, "unit_price": 40000, "tax_rate": 2500, "total_amount": 40000, "total_tax_amount": 8000, "max_allowed_quantity": 3, "image_url": "https://www.example.com/images/cap-sand.jpg", "product_url": "https://www.example.com/products/cap-sand" } ], "last_upsell_time": "2024-01-01T12:05:00Z"}Your endpoint is called more than once. Kustom calls it before the confirmation page loads and again after every item the customer adds, so the widget refreshes as the cart grows. Make the handler idempotent, and exclude what is already in the order from what you recommend.
Return upsell_lines: [] to show no widget at all. You’ll still be called when
upsell_possible is false. An empty array is the correct answer then.
4. Decide when your system creates the order
Section titled “4. Decide when your system creates the order”This is the decision that shapes your backend. Without upsell, the checkout order is final when the customer reaches your confirmation page. With upsell, it can still change until the window closes. There are two defensible ways to handle that.
Create on push — recommended, and simpler. Don’t create the order until the
confirmation push arrives. It carries the final order_lines, upsold items included, so
your system never modifies an order mid-flight.
- The payment is authorized and Kustom marks the checkout order
checkout_complete. - Kustom redirects the customer to your confirmation page.
- Your backend reads the checkout order but doesn’t create an order yet.
- You render the Kustom confirmation snippet.
- Kustom fetches recommendations and shows the widget.
- The customer adds items. Kustom increases the authorization and updates the checkout order.
- The window closes and Kustom sends the confirmation push with the final
order_lines. - Your backend creates the order once, from the push.
Create on redirect. Create the order in your system as soon as the customer lands on the confirmation page, on hold, then take updates as items are added. Choose this only when you genuinely need the order visible immediately — high-value goods where stock must be reserved the instant payment clears.
- The payment is authorized and Kustom marks the checkout order
checkout_complete. - Kustom redirects the customer to your confirmation page.
- Your backend reads the checkout order and creates it in your system, on hold.
- You render the Kustom confirmation snippet.
- Kustom fetches recommendations and shows the widget.
- The customer adds an item. Kustom runs your validation callback if you set one, increases
the authorization and updates the order, then notifies you — at
merchant_urls.upsell_notificationif it’s set, otherwise at thenotification_uriyour upsell response returned. Re-read the order through the Order Management API. - The window closes and Kustom sends the confirmation push.
- Your backend reads the final order and releases the hold for fulfillment.
If you do choose it: the order must stay updatable for the whole upsell window. Don’t send it to the warehouse and don’t send a confirmation email until the push arrives, or you’ll ship an order that is about to change.
Either way, you can tell whether to expect a delayed push from options.confirmation_page_upsell
on the order, or from upsell_possible in the upsell request.
Check that it works
Section titled “Check that it works”In Playground, with upsell enabled on the account:
- Create an order with
options.confirmation_page_upsell: trueand complete a purchase. - Your
merchant_urls.upsellendpoint is called before the confirmation page renders, and the request carriesupsell_possible: trueand a non-zeromax_upsell_amount. - The widget renders your recommendations, with images.
- Adding an item doesn’t ask for payment details, the order’s
order_linesgrow and your endpoint is called again. - The confirmation push arrives after the window closes, carrying the final lines.
- For a purchase where
upsell_possibleisfalse, no widget appears and the push arrives immediately.
To find what was upsold, diff the order_lines on the original checkout order against the
order_lines in the confirmation push. For a signal per item as it’s added, set
feedback_url on each upsell line.
Variations
Section titled “Variations”Validate each add. Set merchant_urls.upsell_validation and Kustom asks before
processing an addition. The body is the ordinary order model plus upsell_order_lines,
the lines being requested. Return 2xx to allow. Anything else blocks it.
{ "merchant_urls": { "upsell_validation": "https://www.example.com/api/upsell-validation" }}Do not treat that response as final. The authorization increase can still fail after you approve. The confirmation push remains the source of truth.
Close the widget early. Return last_upsell_time in your upsell response and the widget
stops accepting additions at that moment. It doesn’t bring the push forward: the push still
waits for the window configured on your account.
Re-rating shipping after an add. If your carrier pricing depends on the cart, either
pre-filter in your upsell endpoint so every candidate keeps the current shipping method
valid — the recommended route — or re-rate afterwards and handle a method change by your
own policy, optionally via the shipping_option_update callback.
A/B testing. Swap the merchant_urls.upsell endpoint whenever you like, or split traffic
with options.confirmation_page_upsell. Remember that curated Portal products take
precedence over an endpoint, so clear the curation to test an endpoint against it.
Minimal confirmation. Upsell is compatible with the minimal_confirmation GUI option
and overrides it automatically while the widget is shown.
Troubleshooting
Section titled “Troubleshooting”The widget never appears. Either the order didn’t qualify, for example because of the payment method — check
upsell_possible — or there was nothing to show: your endpoint was too slow or returned no
lines, or every curated product was filtered out. All of these fail silently by design.
Your endpoint is never called. Your account has Portal-curated products, and they take precedence. Remove the curation if you want the endpoint to decide.
Additions are blocked. They exceed the authorization. Pre-filter on
max_upsell_amount. Recommending what will be refused costs conversion.
selected_shipping_option is missing. It’s present whenever the order has a selected
shipping option — set from the order’s own shipping_options (the preselected one, else the
first) or by the Shipping Assistant. An order that
carries no shipping options at all has nothing to report. Filter on your own shipping knowledge
instead.
The push is late. That’s the feature. The window defaults to ten minutes, is set per
merchant account by Kustom and can’t be changed through the API. The push follows it by about
a minute. last_upsell_time closes the widget sooner but doesn’t move the push.
The push is late and you never enabled upsell. Check whether the order carries
merchant_urls.upsell, or whether Kustom enabled upsell for your whole account. Either one
turns it on without the option.
You want to show a free-shipping threshold in the widget. Not supported. Apply threshold logic on your side when you process the final order.
Related
Section titled “Related”- The upsell callback contract — every field of the request and the response
- Confirmation and push — the push this page delays
- Create and update an order — where
options.confirmation_page_upsellis set - Checkout options — the other options that change what the customer sees
- Shipping Assistant — one of the two ways a shipping option gets selected
- Checkout API reference — the order and callback schemas
This page is also available as Markdown — append .md to its URL — and is indexed in llms.txt.