# Upsell on Confirmation Page This page explains how to enable and integrate the Kustom Upsell feature, which displays product recommendations on the order confirmation page after a purchase is completed. Upsell must be enabled for your merchant account before use. Contact Kustom to activate it. ## Overview Upsell lets you present product recommendations to shoppers immediately after they complete a purchase. Because payment is already authorized, shoppers can add items to their existing order with a single tap — no re-entering payment details. Kustom handles the UI widget, payment authorization increase, and order updates. You control what products are shown and when. ## Enabling upsell ### Enable per order Set `options.confirmation_page_upsell` to `true` when creating a checkout order. This is the recommended approach for A/B testing or selective rollout. ```json { "options": { "confirmation_page_upsell": true } } ``` The flag can be updated on the order until it reaches status `checkout_complete`. ### Enable for all sessions (global) Kustom can enable upsell at the merchant ID level so all eligible sessions show the widget without any per-order flag. You can still override per order using `options.confirmation_page_upsell: false`. Contact Kustom to enable global activation. When upsell is not applicable — for example if it is disabled or the payment method is unsupported — the confirmation push fires immediately with no delay to your existing flow. ## Choosing a recommendation engine Kustom supports two recommendation modes. ### Kustom AI engine (recommended) Provide a Google Shopping feed URL and Kustom handles enrichment, model training, and serving. No `merchant_urls.upsell` endpoint is needed. | Parameter | Details | | --- | --- | | Feed format | Google Shopping XML or CSV — public URL | | Sync frequency | Every 2–4 hours | | One feed per | Market / currency pair (e.g. SE-SEK, DE-EUR) | | Recommended filter | `min_stock=50` to avoid out-of-stock recommendations | The AI model works from day one without historical data and improves with every interaction. ### Your own endpoint Set `merchant_urls.upsell` to your recommendation endpoint. Kustom calls it before the confirmation page loads and you return the list of products to display. ```json { "merchant_urls": { "upsell": "https://www.example.com/api/upsell" } } ``` Your endpoint must respond within 2–3 seconds. A slow or failed response results in no widget being shown and the push notification fires immediately. The sections below on request/response structure apply only when using your own endpoint. ## Upsell callback When `merchant_urls.upsell` is set, Kustom sends a `POST` request to that URL before the confirmation page loads. ### Request — `upsell_request` | Field | Type | Description | | --- | --- | --- | | `upsell_possible` | boolean | `false` if the payment method does not support upsell. | | `max_upsell_amount` | integer | Maximum additional amount that can be authorized, in minor units. Pre-filter your recommendations by this value. | | `order_lines` | array | Current items in the order. | | `selected_shipping_option` | object | The shipping option selected by the customer. Use to filter out items that would break the selected shipping method. | | `billing_address` | object | Use for tax rate and locale-aware filtering. | | `shipping_address` | object | Use for tax rate and locale-aware filtering. | | `purchase_currency` | string | ISO 4217 (e.g. `SEK`, `EUR`, `USD`). | | `locale` | string | RFC 1766 (e.g. `sv-SE`, `en-US`). | | `merchant_id` | string | Your merchant ID. | | `session_id` | string | The order/session ID. Use to map the request to your internal order model. | The callback is made even when `upsell_possible` is `false`. Return an empty `upsell_lines[]` array in that case to short-circuit. These fields enable you to: - Price products correctly based on `purchase_currency` and `locale` - Apply correct tax rates based on `billing_address` / `shipping_address` - Pre-filter candidates using the `max_upsell_amount` authorization headroom - Consider shipping compatibility via `selected_shipping_option` ### Response — `upsell_service_response` | Field | Type | Required | Description | | --- | --- | --- | --- | | `upsell_lines` | array | Yes | List of products to display. Return an empty array to show no widget. | | `last_upsell_time` | string | No | ISO 8601 datetime when the upsell window expires. If omitted, Kustom uses the MID-level timeout. | | `notification_uri` | string | No | URL for push notifications when the order is updated with upsell items. Use as a trigger to re-read the order from the Order Management API (Method 1). | | `empty` | boolean | No | Signal no upsell without returning lines. | ### Upsell line fields — `upsell_line` **Required fields:** | Field | Type | Description | | --- | --- | --- | | `name` | string | Product name (max 255 characters). | | `quantity` | integer | Default offered quantity. See note below. | | `unit_price` | integer | Minor units, includes tax. | | `tax_rate` | integer | Two implicit decimals. `2500` = 25%. | | `total_amount` | integer | Minor units, includes tax and discount. Must equal `unit_price × quantity`. | | `total_tax_amount` | integer | Minor units. | | `max_allowed_quantity` | integer | Maximum quantity the shopper can add. | **Strongly recommended fields:** | Field | Description | | --- | --- | | `reference` | SKU or article number. | | `image_url` | Product card image URL (max 1024 characters). Required to render product cards. | | `product_url` | Link to the product page (max 1024 characters). | | `description` | Product description (max 1024 characters). | | `product_identifiers` | Product identifiers such as GTIN or ISBN. | | `shipping_attributes` | Weight, dimensions, and tags for shipping compatibility checks. | | `feedback_url` | Item-level signal URL called when a shopper adds the item. | Number validation `unit_price × quantity` must equal `total_amount`. Kustom re-validates and recalculates when a shopper adds an item. #### Understanding the `quantity` field `quantity` is the default offer quantity you are proposing, not the shopper's final selection. If you are unsure, set `quantity: 1` and control the upper bound via `max_allowed_quantity`. Kustom recalculates totals and re-validates authorization when the shopper adds items. ```json { "name": "Matching Phone Case", "quantity": 1, "unit_price": 19900, "max_allowed_quantity": 5, "tax_rate": 2500, "total_amount": 19900, "total_tax_amount": 3980 } ``` ### Example response ```json { "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" } ``` ## Upsell validation callback (optional) Set `merchant_urls.upsell_validation` to control whether a specific item add is allowed before Kustom processes it. This callback is not required — by default, items are added without a validation callback. ```json { "merchant_urls": { "upsell_validation": "https://www.example.com/api/upsell-validation" } } ``` The request body is the same as the standard order API model, with one addition: `upsell_order_lines`, which contains the list of upsell lines being requested for addition. The model is the same as `order_lines`. Return `2xx` to allow the add. Any other status code blocks it. Do not rely on this callback as the final confirmation. The payment authorization increase can still fail after this callback returns. Use the confirmation push to finalize the order state. ## Integration methods Because items can be added after the initial payment, you need to decide how your backend handles order creation. There are two approaches. ![Integration methods for upsell on confirmation page](/assets/upsell-integration-methods.04ea02dca8752532fb10c21250c9b1aa4cc485d7208aa315374e2e6ff37d4d2b.f02f6a03.png) ### Method 1 — Create on redirect Create the order immediately when the shopper is redirected to the confirmation page, then accept updates as items are added. 1. Payment is authenticated — KCO marks the checkout order as `CHECKOUT_COMPLETE`. 2. KCO redirects to your confirmation page URL. 3. Your backend reads the order and creates it in your system, placed on hold. 4. You load the KCO confirmation iframe. 5. KCO calls `merchant_urls.upsell` to fetch recommendations. 6. Shopper adds items — KCO triggers the upsell validation callback if configured, increases authorization, and updates the order. 7. Upsell timeout expires — KCO sends the confirmation push. 8. Your backend reads the final order state and releases the hold for fulfilment. Best for merchants who need immediate order visibility in their systems or who handle high-value items where instant inventory reservation is critical. Fulfilment timing The order must be updatable during the upsell timeout window. Do not send it to your warehouse or trigger confirmation emails until the confirmation push arrives. ### Method 2 — Create on push (recommended) Do not create the order in your system until the confirmation push arrives. This avoids mid-session order modifications entirely. 1. Payment is authenticated — KCO marks the checkout order as `CHECKOUT_COMPLETE`. 2. KCO redirects to your confirmation page URL. 3. Your backend reads the order but does **not** create it yet. 4. You load the KCO confirmation iframe. 5. KCO calls `merchant_urls.upsell` to fetch recommendations. 6. Shopper adds items — KCO handles authorization and updates the order. 7. Upsell timeout expires — KCO sends the confirmation push with the final `order_lines`. 8. Your backend creates the order once from the push payload. Best for most integrations — simpler logic, no order modification needed, faster go-live. ### Determining which flow applies The confirmation push is only delayed if upsell is enabled for the session **and** the payment method supports upsell. If either condition is false, the push fires immediately — you do not need to wait. You can determine whether upsell is active by: - Checking `options.confirmation_page_upsell` on the order object - Observing the `upsell_possible` field in the upsell callback request - Checking whether the upsell widget appears in the iframe ## Payment method support | Payment method | Upsell supported | | --- | --- | | Card | Yes | | Klarna BNPL / Pay Later | Yes | | Swish | No | | Direct bank transfer | No | When `upsell_possible` is `false`, the widget is not shown and the push fires immediately with no change to your existing flow. Kustom can increase Klarna authorization limits on a per-merchant basis. Contact Kustom to discuss your limits. Use `max_upsell_amount` in your recommendations endpoint to pre-filter products to amounts that can be authorized. ## Upsell timeout The upsell window is configured at the merchant ID level by Kustom, with a maximum of 15 minutes. The timeout cannot be set via the API — contact Kustom to configure your preferred duration. Upsell is compatible with the `minimal_confirmation` GUI option. The upsell widget overrides it automatically when upsell is enabled. ## Shipping and upsell The `upsell_request` always includes `selected_shipping_option` when available. Use this to filter out recommendations that would break the selected shipping method. Use `shipping_attributes` on your upsell lines to specify weight, dimensions, and tags. This is the recommended way to ensure suggestions are compatible with the selected shipping method. ```json { "shipping_attributes": { "weight": 500, "dimensions": { "width": 100, "height": 150, "length": 200 }, "tags": ["standard", "non-fragile"] } } ``` Shipping information is only included in the upsell request if you use Kustom Shipping Assistant. Without it, `selected_shipping_option` will not be present. If your shipping platform requires re-rating after cart changes (for example when using a third-party TMS), you have two options: - **Pre-filter (recommended):** Perform a soft compatibility check in your upsell endpoint before returning candidates. Only propose items that keep the current shipping method valid. - **Post-upsell re-rate:** After an upsell is accepted, re-rate and handle any method changes per your policy. You can optionally use the `shipping_option_update` callback for this. ## Tracking upsold items Compare `order_lines` in the original checkout order with `order_lines` in the final confirmation push to identify which items were added via upsell. Use `feedback_url` on each `upsell_line` for item-level add signals. Recommendations are fetched once before the confirmation page loads and cannot be refreshed during the upsell window. Return your best candidates in the initial response. ## FAQ **What does the `quantity` field mean in `upsell_line`?** It is the default offer quantity you are proposing, not the shopper's final selection. Set `quantity: 1` and use `max_allowed_quantity` to control the upper bound. Kustom recalculates totals and re-validates authorization when the shopper adds items. **When should we create the order — on redirect or after the push?** Both are supported. Method 2 (create on push) is simpler for most integrations. Method 1 (create on redirect) is better when immediate inventory reservation is required. See [Integration methods](#integration-methods) above. **How do we know if we should wait for a delayed push?** The push is only delayed if upsell is enabled and the payment method supports it. Check `options.confirmation_page_upsell` on the order or the `upsell_possible` field in the upsell request. **What happens if our upsell endpoint is slow or times out?** The confirmation page loads normally, the upsell widget is not shown, and the push fires immediately. Optimize your endpoint to respond within 2–3 seconds. **Can we A/B test recommendation variants?** Yes. You can swap your `merchant_urls.upsell` endpoint at any time to test variants against each other or against the Kustom AI engine. **Can we show free shipping thresholds in the widget?** No. Real-time free shipping threshold tracking within the upsell widget is not currently supported. Handle threshold logic on your side when processing the final order after the push. **What happens if the upsell amount exceeds the authorization?** Kustom validates authorization when the shopper adds items and blocks additions that exceed the limit. Pre-filtering your recommendations by `max_upsell_amount` improves conversion by avoiding recommendations that will be blocked. **How do we track which items were upsold?** Compare the original `order_lines` with the final `order_lines` from the confirmation push. Use `feedback_url` on upsell lines for item-level add signals. ## API reference - [Checkout API](/contents/api/checkout) - [Merchant Callbacks API](/contents/api/checkout-callback)