Skip to content

Return shipping options to the checkout

Implement POST /shippingoptions for the Kustom Shipping Assistant: required fields, and how to build home delivery, pickup point, time slot and add-on options.

The delivery choices a customer sees in the checkout are the ones your TMS sends back here, which makes this the heart of the integration. Kustom sends you the cart and the customer’s address with POST /shippingoptions, and shows the options you return. This page covers when Kustom asks, what it sends, the three fields every option needs, and each way of building an option, from a plain home delivery to pickup points, time slots, add-ons and badges. It’s required: a missing or failed answer puts the checkout on the merchant’s own fallback options.

  • The address or cart changes. That includes the moment the checkout opens, when the address may be only a country.
  • The customer searches for pickup points near another postal code (see Pickup-point searches).
  • You refused a reservation on POST /shipment, so the customer can pick again.

Kustom reuses your answer for 5 minutes when nothing in the request has changed, so you won’t see a call for every keystroke. It ignores any Cache-Control you send.

It doesn’t call you for a cart with nothing physical in it, and it stops calling you for the rest of a checkout once that checkout has fallen back.

POST /shippingoptions
Content-Type: application/vdn.klarna.shipping.get_options-v1+json
Authorization: Bearer <token>
{
"session_id": "f7b7dda8-c9a6-405d-8cca-1c3cbdfda1c0",
"locale": "sv-SE",
"currency": "SEK",
"geoblocking": false,
"recipient": {
"given_name": "Klara",
"family_name": "Joyce",
"street_address": "Sveavägen 42",
"postal_code": "111 34",
"city": "Stockholm",
"country": "SE",
"phone": "+46708800000",
"email": "customer@example.com",
"customer_type": "person"
},
"order": {
"id": "17d4cd21-99cd-4ffa-97d6-257b258cf053",
"total_amount": 79900,
"total_tax": 15980,
"total_weight": 890,
"tags": ["member"],
"merchant_reference1": "45099",
"lines": [
{
"type": "physical",
"reference": "sku-1234",
"quantity": 1,
"unit_price": 79900,
"tax_rate": 2500,
"total_tax_amount": 15980,
"total_price_including_tax": 79900,
"total_discount_amount": 0,
"attributes": {
"weight": 890,
"dimensions": { "height": 60, "width": 300, "length": 400 },
"tags": ["fragile"]
}
}
]
}
}
FieldMeaning
session_idThe checkout. The same value arrives on every call for it, POST /shipment included
locale, currencyThe customer’s language and the order currency. Return names and prices to match
geoblockingtrue when the customer’s billing country is one the merchant doesn’t ship to, so recipient holds only a country
recipientWhere the parcel goes, as far as the customer has filled it in. Name, phone and email fall back to the billing details. customer_type is person or organization
order.linesThe physical items only, without any shipping fee line. attributes carries the merchant’s weight (grams), dimensions (millimeters) and tags, when they sent them
order.total_weightWeight × quantity summed over the physical lines, in grams
order.total_amount, order.total_taxMinor units, without shipping
order.tagsThe merchant’s tags for the whole cart
order.merchant_reference1, merchant_reference2The merchant’s own order references, when they set them
selected_shipping_option_idOnly on a pickup-point search

Anything the merchant didn’t send is left out, not sent as null. Never assume a field beyond session_id, locale, currency and recipient.country is present. Fields may also be added to the request in the future, so ignore any field you don’t recognize rather than rejecting the call.

Answer 200 — not 201 — with the options in a shipping_options list. Any other status, a timeout or a body Kustom can’t read puts the checkout on the merchant’s fallback options for the rest of that session.

{
"shipping_options": [
{
"id": "pn-home",
"type": "delivery-address",
"carrier": "postnord",
"price": 4900,
"tax_rate": 2500
}
]
}

That’s a complete, valid answer. Every option needs three fields, and everything else is optional:

FieldMeaning
idYour id for the option. Non-empty. Sent back on POST /shipment and stored on the merchant’s order
priceMinor units, tax included. 0 for free. Never negative
tax_rateTwo implicit decimals: 2500 is 25%. Never negative

The checkout shows options in the order you return them, so sort them the way you want the customer to read them. An empty list is a valid answer, not a failure: the customer sees “We cannot ship to the given address” and can’t buy until the address changes.

One bad value rejects every option. Kustom validates the whole response before showing any of it. A single unknown type, carrier, class, add-on type or feature type, a malformed date, time or URL, or an option missing id, price or tax_rate, and the entire answer is discarded. The checkout falls back for the rest of that session. Send every value in lowercase, exactly as listed on this page.

Three options a customer in Stockholm might be offered for one order, each showing one part of an option: its type, its add-ons and its features. Each is complete as it stands, beside what the checkout draws for it. Return them together in one shipping_options list. The times are in UTC, so the checkout shows them two hours later, in Swedish summer time.

type decides what the customer has to choose. A pickup type needs locations: the checkout preselects the first, and the customer can pick another from a list and a map.

The Pickup point option selected: Free, delivered between 2 and 4 business days, with Bring Stockholm City chosen, SMS Notification ticked with a phone number, a Learn more link and a Traceable badge

The Choose pickup point dialog: a map of central Stockholm beside a list of Bring Stockholm City (Free), Bring Norrmalm (Free) and Bring Vasastan (19 kr), each with an address and opening hours

{
"id": "pickup-bring",
"type": "pickup-point",
"carrier": "bring",
"name": "Pickup point",
"price": 2900,
"tax_rate": 2500,
"delivery_time": { "interval": { "earliest": 2, "latest": 4 } },
"locations": [
{
"id": "loc-sto",
"name": "Bring Stockholm City",
"price": 0,
"address": {
"street_address": "Sveavägen 46",
"postal_code": "111 34",
"city": "Stockholm",
"country": "SE"
},
"coordinates": { "lat": 59.336946, "lng": 18.063095 },
"operational_hours": {
"default": [{ "open": "09:00", "close": "20:00" }],
"overrides": {
"weekday": {
"sat": [{ "open": "10:00", "close": "16:00" }],
"sun": [{ "closed": true }]
},
"date": [
{ "date": "2026-10-16", "hours": { "open": "10:00", "close": "14:00" } }
]
}
}
},
{
"id": "loc-norrmalm",
"name": "Bring Norrmalm",
"price": 0,
"address": {
"street_address": "Drottninggatan 71",
"postal_code": "111 36",
"city": "Stockholm",
"country": "SE"
},
"coordinates": { "lat": 59.33889, "lng": 18.05963 },
"operational_hours": { "default": [{ "open": "08:00", "close": "21:00" }] }
},
{
"id": "loc-vasastan",
"name": "Bring Vasastan",
"price": 1900,
"address": {
"street_address": "Odengatan 42",
"postal_code": "113 51",
"city": "Stockholm",
"country": "SE"
},
"coordinates": { "lat": 59.34327, "lng": 18.05398 },
"operational_hours": { "default": [{ "open": "09:00", "close": "19:00" }] }
}
],
"features": [
{ "type": "traceable" },
{ "type": "learn-more", "url": "https://example.com/bring-pickup" }
],
"addons": [
{ "type": "sms", "id": "sms", "preselected": true }
]
}
  • pickup-point requires locations: the customer has to choose one. Without them the checkout shows “No service point found” and the customer can’t buy with the option. If the carrier arranges the pickup point after the purchase, use generic-pickup instead. It needs no locations, and the checkout tells the customer “Your courier will get in touch with more details about your delivery.” Every type is in Option types.
  • A location’s price replaces the option’s price, and 0 counts. The option costs 29 kr, but the row says Free because the preselected location is free, and Vasastan costs 19 kr. Leave price out of a location that should cost what the option does.
  • Opening hours: the usual week, shorter on Saturdays, closed on Sundays, and a one-off change for 16 October. The rules are in Locations.

Add-ons are what the customer adds to an option: something to type for the courier, or a service to switch on. They show once the customer selects the option.

The Home delivery option selected: 49 kr, delivered Saturday 10 October from 10 to 22, with Floor, Door code and Instructions fields, No-contact delivery ticked, Leave by the door unticked, an info box and Recommended and Express badges

{
"id": "home-early-bird",
"type": "delivery-address",
"carrier": "early-bird",
"class": "express",
"name": "Home delivery",
"price": 4900,
"tax_rate": 2500,
"delivery_time": {
"earliest": "2026-10-10T08:00:00Z",
"latest": "2026-10-10T20:00:00Z",
"time_of_day": {
"earliest": "2026-10-10T08:00:00Z",
"latest": "2026-10-10T20:00:00Z"
},
"cutoff": "2026-10-09T13:00:00Z"
},
"features": [
{ "type": "recommended" },
{ "type": "express" },
{ "type": "delivery-time-extra-info" }
],
"addons": [
{ "type": "floor-number", "id": "floor", "max_length": 3 },
{ "type": "entry-code", "id": "entry-code", "max_length": 10 },
{ "type": "additional-instructions", "id": "instructions", "max_length": 100 },
{ "type": "contactless-delivery", "id": "contactless", "preselected": true },
{ "type": "leave-outside-door", "id": "leave-outside" }
]
}
  • Three text add-ons, each capped with max_length: what the customer types comes back to you on POST /shipment.
  • Two switches, with No-contact delivery ticked from the start by preselected. No-contact and Leave by the door only appear when the customer’s billing country is Sweden, Norway, Finland, Denmark or Germany.
  • All five are free. A price on an add-on is added to the option’s price when the customer takes it. Every add-on is in Add-ons.

Features decorate an option without asking the customer anything: badges, info boxes and links. Badges show on the option’s row whether or not it’s selected.

The Mailbox delivery option: 39 kr, delivered between 1 and 3 business days, with Carbon Compensated, Eco-friendly and Traceable badges

{
"id": "postal-postnord",
"type": "postal",
"carrier": "postnord",
"name": "Mailbox delivery",
"price": 3900,
"tax_rate": 2500,
"delivery_time": { "interval": { "earliest": 1, "latest": 3 } },
"features": [
{ "type": "eco-friendly" },
{ "type": "carbon-compensated" },
{ "type": "traceable" }
]
}
  • Each feature is just a type. The checkout supplies the wording and the color, in the customer’s language.
  • An info box is a feature too: the home delivery above carries delivery-time-extra-info, and a Learn more link is the pickup point’s learn-more. Every feature is in Badges and info boxes.
  • Some badges carry a logo. svanen draws the Nordic Swan Ecolabel:

The Parcel locker option: Free, delivered between 1 and 2 business days, with Eco-friendly, Fossil Free and Nordic Swan Ecolabel delivery in Sweden badges

FieldDefaultMeaning
id, price, tax_rate—Required. See Response
typegeneric-shippingThe kind of delivery — see Option types
carriergenericDrives the logo — see Carrier values
classstandardstandard, economy, express or courier
name—The title the customer sees — see Names
free_above—Order amount, in minor units, from which you ship this option free. Shows a message and a progress bar, but doesn’t change price
previewfalseSee Preview options
delivery_time—See Delivery time
locations—Required in practice for pickup types — see Locations
timeslots—See Time slots
addons—See Add-ons
features—See Badges and info boxes

type decides what the customer has to choose, and how the checkout titles the option. They fall into three groups:

KindtypeThe option needs
Home deliverydelivery-addressNothing more. Add timeslots to have the customer pick one
Pickuppickup-point, pickup-box, pickup-merchant-store, click-collect, pickup-warehouselocations, required. Without them the customer can’t buy with the option
Everything elsepostal, generic-shipping, generic-pickup, office-delivery, b2b-delivery-address, boat-delivery, crane-delivery, pallet-deliveryNothing more

Use generic-shipping or generic-pickup when the carrier arranges the details with the customer after the purchase; the checkout says the courier will be in touch. Don’t return digital: Kustom adds that itself for digital goods. What each type means is in the POST /shippingoptions reference.

name is the title the customer sees, next to the carrier’s logo, and it’s stored on the merchant’s order too. Kustom doesn’t translate it, so send it in the language of the request’s locale: “Hemleverans” for sv-SE, “Home delivery” for en-SE.

delivery_time is optional, but it’s the line the customer reads most. It takes one of two forms, and you can add a time-of-day window or an order-by time to either.

You sendThe customer sees
interval with earliest and latest in whole days“1–3 business days”
earliest and latest as ISO 8601 date-timesThe dates, such as “Tomorrow”
either, plus time_of_day with earliest and latestThe day, and a window such as 08:00–20:00. Only the time part is read
either, plus cutoffAn order-by time
  • If both are present for the same end, interval wins over the date. interval.utc_offset_in_seconds sets the time zone the days are counted in.
  • You can send only earliest or only latest.
  • delivery_time is ignored, not rejected, when it holds neither an interval nor a date, such as only a cutoff, or when latest comes before earliest. The option is still shown, with no delivery time.
  • A date that isn’t valid ISO 8601 rejects the whole response.
FieldRequiredMeaning
idYes, in practiceSent back on POST /shipment and stored on the merchant’s order
nameYes, in practiceShown in the list
addressYes, in practicestreet_address, street_address2, postal_code, city, region, country
coordinatesRecommendedlat and lng, which place the location on the map
priceNoReplaces the option’s price when this location is chosen. 0 makes it free
operational_hoursNoSee below. A malformed time rejects the whole response
delivery_timeNoOverrides the option’s delivery_time for this location

operational_hours.default is the usual week. overrides.weekday replaces it for named days (mon to sun), and overrides.date for specific dates, each { "date": "2026-12-24", "hours": { ... } }. Each entry is one of:

  • { "open": "09:00", "close": "20:00" }, and a day can hold several ranges.
  • { "always_open": true }, on its own.
  • { "closed": true }, on its own.

Times win: an entry with open and close is read as those times even if it also says always_open or closed.

FieldRequiredMeaning
idRecommendedSent back on POST /shipment as the chosen slot
start, endYesISO 8601 date-times. A malformed one rejects the whole response
priceNoReplaces the option’s price when this slot is chosen
cutoffNoISO 8601. The latest moment this slot can be booked

Slots can go on any option type. On delivery-address the customer must pick one; on other types picking one is optional. With no delivery_time, the option’s row shows the first slot:

The Pick up in store option: Free, delivered Saturday 10 October from 10 to 14, with a Recommended badge

{
"id": "clickcollect-flagship",
"type": "click-collect",
"carrier": "merchant",
"name": "Pick up in store",
"price": 0,
"tax_rate": 2500,
"locations": [
{
"id": "store-flagship",
"name": "Flagship Store Stockholm",
"address": {
"street_address": "Drottninggatan 53",
"postal_code": "111 21",
"city": "Stockholm",
"country": "SE"
},
"coordinates": { "lat": 59.3326, "lng": 18.0636 },
"operational_hours": {
"default": [{ "open": "10:00", "close": "19:00" }],
"overrides": { "weekday": { "sun": [{ "open": "12:00", "close": "16:00" }] } }
}
}
],
"timeslots": [
{
"id": "slot-morning",
"start": "2026-10-10T08:00:00Z",
"end": "2026-10-10T12:00:00Z",
"cutoff": "2026-10-09T13:00:00Z"
},
{
"id": "slot-afternoon",
"start": "2026-10-10T12:00:00Z",
"end": "2026-10-10T16:00:00Z",
"cutoff": "2026-10-09T13:00:00Z"
}
],
"features": [
{ "type": "recommended" },
{
"type": "merchant-info-text",
"merchant_info_text": "Pick up in store and skip the shipping fee."
}
]
}

A slot’s price replaces the option’s, like a location’s. Here the customer must pick a slot, Tuesday costs the option’s 59 kr, Wednesday 49 kr, and Tuesday can only be booked until 10:00 Stockholm time:

{
"id": "home-evening",
"type": "delivery-address",
"carrier": "budbee",
"name": "Evening delivery",
"price": 5900,
"tax_rate": 2500,
"timeslots": [
{
"id": "ts-tue",
"start": "2026-10-13T15:00:00Z",
"end": "2026-10-13T19:00:00Z",
"cutoff": "2026-10-13T08:00:00Z"
},
{
"id": "ts-wed",
"start": "2026-10-14T15:00:00Z",
"end": "2026-10-14T19:00:00Z",
"price": 4900
}
],
"features": [{ "type": "evening" }],
"addons": [{ "type": "sms", "id": "sms", "required": true }]
}

Add-ons are extras the customer chooses on top of an option. You decide which ones each option offers, and what each costs. The customer’s answers come back to you on POST /shipment.

FieldDefaultMeaning
type—Required. One of the types below. An unknown type rejects the whole response
price0Added to the option’s price when chosen. Minor units, tax included
requiredfalseThe customer can’t buy without it. A required contact add-on is switched on for them
preselectedfalseStarts switched on. For switches and contact add-ons
max_lengthper typeLongest text the customer may type. Text add-ons only
id—Your id for it, sent back on POST /shipment
url—A “Learn more” link next to it. A malformed URL rejects the whole response

There are three kinds, and the type decides which.

Contact — the customer agrees to be notified, and the checkout fills in their details. On POST /shipment, data.text holds the number or address.

typeThe customer sees
smsSMS Notification, with their phone number
emailEmail Notification, with their email address

Text — the customer types something for the driver. On POST /shipment, data.text holds it.

typeThe customer seesDefault max_length
entry-codeDoor code16
apartment-numberApartment Number6
floor-numberFloor2
locker-codeLocker code16
additional-instructionsInstructions280

Switch — the customer turns a service on or off. On POST /shipment, data.selected says which.

typeThe customer sees
evening-deliveryEvening delivery, between 5pm and 10pm
daytime-deliveryDaytime delivery, between 8am and 5pm
contactless-deliveryNo-contact delivery
leave-outside-doorLeave by the door
indoor-home-deliveryUnload inside the home, carried into the room of their choice
assembly-at-pickupInclude assembly, with items assembled when they collect
package-removalPackaging removal
expedited-packingExpress packing, prioritized to ship in the next delivery
sustainable-packagingSustainable packaging, reusable or easily recycled
reusable-packagingReusable packaging, sent with instructions to return it
carbon-compensatedCarbon compensated, by the merchant or the carrier

contactless-delivery and leave-outside-door are offered only when the customer’s billing country is Sweden, Norway, Finland, Denmark or Germany. Elsewhere they’re silently dropped. List each type once per option: a second one replaces the first.

features decorate an option without asking the customer anything. Each is { "type": ... }, and the type decides how it’s drawn. You may also send class (badge, info, call_to_action, custom-badge), but it must match the type or the whole response is rejected, so it’s simpler to leave it out.

Kindtype
Badge, a short colored labelrecommended, express, morning, afternoon, evening, traceable, multiple-deliveries, eco-friendly, fossil-free, carbon-compensated, svanen (with the Nordic Swan logo), bike-delivery, in-door-home-delivery, outdoor-home-delivery
Info box, a fixed sentence under the optiondistributor-will-contact, room-delivery, package-removal, delivery-time-extra-info, multiple-deliveries-info
Your own sentencemerchant-info-text, with the text in merchant_info_text. Cut at 1,000 characters, not translated
Link, with a urllearn-more, size-guide
Custom badgecustom-badge, with your label in merchant_info_text, a hex color, an optional dark_mode_color and an icon. An unsupported icon is dropped. Give each an id when an option carries more than one

The POST /shippingoptions reference has the wording of each info box and badge.

carrier picks the logo shown beside the option. Use a value from the carrier list in the POST /shippingoptions reference, or generic-postal for a generic postal network. An unknown value rejects the whole response, so send generic when you’re unsure, and ask Kustom to add a carrier you need. The reference also lists ntex, which the checkout doesn’t accept yet.

Before the customer has typed an address, Kustom may ask with only a country. Answer with your best guess for that country, and mark the options "preview": true. The customer sees them, but can’t buy with a preview option: Kustom asks again once the address is complete, and you answer with real options then. A preview option that’s still selected when the customer presses Buy blocks the purchase.

From the pickup-point map, the customer can search near a different postal code. Kustom asks you again with selected_shipping_option_id set to the option they’re looking at, and a recipient holding only country and postal_code.

From your answer, Kustom takes only the locations of the option matching the one they’re looking at, by type, class and either carrier or id. Everything else is ignored. So return the same option, with locations near the new postal code. Treat this call like a preview: it looks like an incomplete address because it is one.

This page is also available as Markdown — append .md to its URL — and is indexed in llms.txt.