- Checkout
- Features
The upsell callback contract
The request Kustom sends your upsell endpoint and the response it expects back — every field of upsell_request, upsell_service_response and upsell_line, and what decides whether the upsell widget is shown at all.
Kustom calls merchant_urls.upsell before the confirmation page loads, and again after
every item the customer adds, and expects a list of products back each time. Each call is given
10 seconds and isn’t retried. Aim to answer in under three, because a customer is waiting on
the confirmation page meanwhile. The endpoint isn’t called at all when your account has
Portal-curated products for the order, because those take precedence. This page is the
field-level contract for that exchange. Upselling on the confirmation page
is where the decisions are — when to enable it, where recommendations come from and when
your own system creates the order.
Request — upsell_request
Section titled “Request — upsell_request”| Field | Type | Meaning |
|---|---|---|
upsell_possible | boolean | false when the payment method can’t support upsell. You’re still called |
max_upsell_amount | integer | Authorization headroom in minor units: the original order total (at least 100 SEK, NOK or DKK, or 10 EUR or GBP), minus what has already been upsold. 0 when the payment method doesn’t support upsell. Pre-filter against it |
order_lines | array | What the customer has already bought |
selected_shipping_option | object | Their chosen delivery. Present whenever the order has one selected — from its own shipping_options or from the Shipping Assistant |
billing_address | object | For tax and locale-aware filtering |
shipping_address | object | As above |
purchase_currency | string | ISO 4217 |
locale | string | BCP 47, such as en-SE |
merchant_id | string | Your merchant ID |
session_id | string | The order identifier. Map it to your own order |
Response — upsell_service_response
Section titled “Response — upsell_service_response”| Field | Type | Required | Meaning |
|---|---|---|---|
upsell_lines | array | Yes | Products to display. Empty means no widget — return [] to signal “no recommendations” |
last_upsell_time | string | No | ISO 8601 moment the widget stops accepting additions. Falls back to the account-level window. Doesn’t move the confirmation push, which always waits for the account-level window |
notification_uri | string | No | Where Kustom pushes when the order is updated with upsell items, used only when merchant_urls.upsell_notification isn’t set — useful as a re-read trigger if you create on redirect |
Each upsell_line
Section titled “Each upsell_line”Required:
| Field | Type | Meaning |
|---|---|---|
name | string | Product name, max 255 characters |
quantity | integer | The quantity you’re offering, not the customer’s choice |
unit_price | integer | Minor units, tax included |
tax_rate | integer | Two implicit decimals — 2500 is 25% |
total_amount | integer | Minor units, tax and discount included. Must equal quantity × unit_price − total_discount_amount |
total_tax_amount | integer | Minor units |
max_allowed_quantity | integer | The most the customer may add |
Strongly recommended — without image_url in particular, the product card renders with no
picture:
| Field | Meaning |
|---|---|
reference | Your SKU |
image_url | Card image, max 1024 characters |
product_url | Product page, max 1024 characters |
description | Max 1024 characters |
product_identifiers | GTIN, ISBN and similar |
shipping_attributes | Weight, dimensions and tags, for shipping compatibility |
feedback_url | Called when a customer adds this item — an item-level signal |
quantity trips people up. It’s the default offer, not a selection: set it to 1 and
bound the customer with max_allowed_quantity. Kustom recalculates the totals and
re-validates the authorization whenever an item is added, so your arithmetic has to be
right but it isn’t the last word.
{ "shipping_attributes": { "weight": 500, "dimensions": { "width": 100, "height": 150, "length": 200 }, "tags": ["standard", "non-fragile"] }}When the widget is shown
Section titled “When the widget is shown”Kustom only loads the upsell widget when the order qualifies: upsell is enabled for it, the
payment method the customer used allows the authorization to be increased and there is
something to recommend within the headroom. You don’t need to track which payment methods
qualify. When the payment method doesn’t, upsell_possible is false, max_upsell_amount is
0, no widget appears and the push isn’t delayed. When there’s simply nothing to recommend, no
widget appears either, but the push is still delayed for the full window.
Related
Section titled “Related”- Upselling on the confirmation page — the how-to this contract serves
- Merchant URLs explained — where
merchant_urls.upsellis set - Checkout API reference — the order schema the request embeds
This page is also available as Markdown — append .md to its URL — and is indexed in llms.txt.