# API Integration for Shipping Assistant ## Important terms - **Checkout API:** API *implemented* by Kustom. The Merchant calls this API to start an order session or manage it. - **Shipping API:** API *specified* by Kustom; must be implemented by the Integrator (TMS) (see below). The Kustom Shipping Assistant queries this API to retrieve shipping options dynamically. - **Integrator:** Implementer of the Shipping API. Usually, this is a logistics partner, like Unifaun or Consignor. In a direct integration, the Merchant can take this role. ## What is the ideal integration? An ideal integration with the Shipping API enables the following: - Shipping options are shown within the Kustom Checkout, based on real-time user data. - The user’s selected option is saved, and a preliminary shipment is created based on it. The preliminary shipment can be later used by the merchant to print the actual shipping label. - Tracking is shared with Kustom after the purchase is completed. Please note that to use the Kustom Shipping Assistant, you may have to adjust your Checkout integration. More info about that is in this section. ## Authorization This section describes how to authorize requests from the Kustom Shipping Assistant. ### Use case The user has entered the checkout, and Kustom tries to obtain authentication from the Integrator. ### Prerequisite The merchant has provided Kustom with authentication credentials. More information can be found in the Getting Started section. ### Description Communications between the Kustom Shipping Assistant and the Integrator must be authenticated using a bearer token scheme. Consequently, each request has a header like this: ```http Authorization: Bearer ``` Where `` is a secret string provided by the Integrator. To obtain it, Kustom Shipping Assistant calls `POST /auth` with credentials provided by the Merchant. This is a payload example: ```json { "identifier": < username > "secret": { "nonce": < random string > "digest": < encoded nonce + key > } } ``` The digest is calculated by concatenating the (random) `nonce` and the (Merchant-provided) key and then feeding the result into the SHA-256 algorithm. To check that the secret is correct, the Integrator must compare our value by doing the same process on their end: ```javascript digest = sha256(nonce + key) ``` We recommend the Integrator give a unique token per merchant and let the token expire after a reasonable time (1h is a good duration). If the Kustom Shipping Assistant provides an old or wrong token to a call other than `POST /auth`, the Integrator must answer with HTTP 401 (Unauthorized). If the Kustom Shipping Assistant provides the wrong credentials to `POST /auth`, the Integrator must also answer with HTTP 401 (Unauthorized). The Kustom Shipping Assistant will cache the bearer token until an HTTP 401 response is received and then fetch a new one. ## Preview This section describes what **preview options** are and how to set them. ### Use case The user has entered the checkout. ### Prerequisite No address data is available for the user. ### Description When a new user enters the checkout, Kustom has no data to pre-fill the address. To ensure a user-friendly experience, we show placeholder options based only on the country of purchase. These temporary options are called **preview options**. To make this work, the Integrator needs to be able to return shipping options for partial address data on a `POST /shippingoptions` call (described in the next section). A partial address could be only the country in cases like the above. The Integrator must mark these options with a `preview` flag.flag. Once Kustom has a full address, the Kustom Shipping Assistant will call the endpoint again with the complete information. Then the Integrator must answer with fully valid (non-preview) shipping options. ## Shipping options This section describes how to provide shipping options to the Kustom Shipping Assistant. ### Use case The user is in the checkout and has provided address data, or their address data is prefilled. ### Prerequisite Complete address data is available for the user, and the authentication call to the Shipping API was successful. ### Description The Kustom Shipping Assistant obtains shipping options from the Integrator by calling `POST /shippingoptions` every time the user updates their cart or address information. This call must be authenticated using the bearer token obtained previously, as described in the Authorization section. You can test a Shipping API implementation and see how the shipping selector looks in our test tool, [Shipwreck](https://shipwreck.playground.kustom.co/). This is an example request from Shipwreck: ```json path: /shippingoptions method: POST Headers: Content-Type: application/vdn.klarna.shipping.get_options-v1+json body: { "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": "partner@klarna.com", "customer_type": "person" }, "order": { "id": "17d4cd21-99cd-4ffa-97d6-257b258cf053", "total_amount": 1130, "total_tax": 25, "tags": ["member"], "lines": [{ "type": "physical", "reference": "sku-1234", "quantity": 1, "unit_price": 100, "tax_rate": 2500, "total_tax_amount": 25, "total_price_including_tax": 125, "total_discount_amount": 0, "attributes": { "weight": 890, "tags": ["out_of_stock"] } }, { "type": "physical", "reference": "sku-4321", "quantity": 8, "unit_price": 0, "tax_rate": 0, "total_price_including_tax": 1005, "total_discount_amount": 0 }], "total_amount_without_shipping_fee": 1130, "total_tax_without_shipping_fee": 25 } } ``` Kustom passes the order information to the Shipping API. The order information is set by the Merchant while creating a Checkout session. To enhance the shipping experience, we added shipping attributes to the order and order line level. Like the rest of the order information, these attributes will be passed on to the shipping API. The attributes can be used to set the rules about which shipping options should be shown. - Weight and dimensions. - Tags. The following is a response example from an Integrator (adapted from our test integration tool, Ghostship): ```json { "shipping_options": [{ "id": "id-1070", "type": "postal", "carrier": "postnord", "name": "Postal delivery", "price": 0, "tax_rate": 0, "delivery_time": { "interval": { "earliest": 4, "latest": 6 } }, "class": "standard" }, { "id": "id-1000", "type": "pickup-box", "carrier": "instabox", "name": "Pickup Box", "price": 100, "tax_rate": 0, "delivery_time": { "latest": "2019-10-15T19:00:00Z" }, "locations": [{ "id": "loc-2", "name": "Coop Loet", "price": 500, "address": { "street_address": "Storgatan 59", "postal_code": "97232", "city": "Luleå", "country": "SE" }, "coordinates": { "lat": 65.5850107, "lng": 22.1558937 }, "operational_hours": { "default": [{ "always_open": true }] } }, { "id": "loc-4", "name": "BURMANS BUTIK I BERGVIKEN", "price": 0, "address": { "street_address": "BLOMGATAN 17", "postal_code": "97331", "city": "Luleå", "country": "SE" }, "coordinates": { "lat": 65.5992109, "lng": 22.1487749 }, "operational_hours": { "overrides": { "weekday": { "mon": [{ "always_open": true }], "tue": [{ "open": "09:00", "close": "18:00" }], "thu": [{ "open": "09:00", "close": "19:00" }] } }, "default": [{ "open": "08:00", "close": "18:30" }] } }], "class": "economy" }, { "id": "id-1050", "type": "delivery-address", "carrier": "budbee", "name": "To door delivery", "price": 2500, "tax_rate": 2500, "delivery_time": { "earliest": "2019-10-16T16:00:00Z", "latest": "2019-10-16T20:00:00Z" }, "addons": [{ "category": "notifications", "type": "sms" }, { "category": "delivery", "type": "additional-instructions", "max_length": 100 }], "class": "standard" }] } ``` You can find the complete list of supported shipping `types`, `services` and `carriers` in our API documentation. Below you can find a non-exhaustive summary of the key information. ### Shipping types Each shipping option must have a shipping `type` indicating whether, for example, it is a home delivery option. These are some of the supported types: - `delivery-address` - Delivery to door / address. The final selected option will contain an address and can contain a timeslot. - `pickup-box` - Delivery to a pickup locker/box. The final selected option will contain a location. - `pickup-point` - Delivery to a pickup point. The final selected option will contain a location. - `pickup-merchant-store` - Delivery to a merchant store for pickup. The final selected option will contain a location. - `postal` - Delivery of a package/letter using the postal network. - `generic-pickup` - Delivery to a pickup point when location is unknown. - `generic-shipping` - Shipping option when the carrier decides on the shipping method. - `office-delivery` - Delivery to an office address. For example, to an office reception. - `boat-delivery` - Delivery by boat. - `digital` - Delivery via email or SMS. - `click-collect` - Order goods online for reservation and pick them up at a merchant store. - `pickup-warehouse` - Delivery to a merchant warehouse for pick up. The `type` attribute controls which name KSA will show in the shipping option. For example, `delivery-address` is shown in English as “To door delivery”. The banner about digital delivery will be shown if there is a digital item in the order. When the order has a digital item but no physical items, Kustom Shipping Assistant will not request any shipping options from TMS, the shipping selector will not be shown, and the shipping `type` will be set to `digital`. ### Add-ons Add-ons are additional services or information that can be added to an option. They can be marked as mandatory. Here are some examples: - `addon_notification` - Booking email or SMS notification. The final selected option will contain an email address and/or a phone number. - `addon_delivery` - Adding entry code (door code) and delivery instructions. The final selected option will contain the door code and instruction text. ### Delivery time A shipping option’s delivery time is crucial information for the users. It can be provided in two different formats: - You can use an offset in days from the current date to show intervals. For example, ```json "delivery_time": { "interval": { "earliest": 1, "latest": 3 } } ``` This will be shown as **1-3 working days**. - To show an exact time, you can use an **ISO 8601** date-time (you can try it out in [this tool](https://dencode.com/en/date/iso8601)). For example, ```json "delivery_time": { "latest":"2019-08-28T12Z" } ``` Assuming today is **2019-08-27** in Sweden (**UTC+2** in the summer), this will be shown as **“Tomorrow at 14h”**(**Z** is the same as **+00:00**. It indicates **UTC+0, Greenwich time**). ## Location search This section describes the pickup point map and the associated location search. ### Use case The user opens the pickup point map and searches for locations. ### Prerequisite The user inserted a new postal code into the location search field. ### Description Pickup locations are presented on a map view. The user can search for new locations by postal code. Whenever the user inserts a new postal code, Kustom will call `POST /shippingoptions` with `country` and `poand postal_code`. The following swimlane explains the flow: **Be careful** when implementing your `/shippingoptions` endpoint. From the Integrator’s perspective, a location search looks like a **preview options call**, since it uses this endpoint with an incomplete address. However, Kustom Shipping Service discards any changes other than the locations. ## Select shipping option This section describes how Kustom finalizes the selected shipping option. ### Use case The user is completing the purchase (pushing the buy button). ### Prerequisites A valid (non-preview) shipping option is selected. The user has not tried to finalize before. ### Pitfalls Receiving this call does not guarantee the purchase is final. If something fails, this can lead to a `PUT` operation (see Update Shipping Option section). ### Description The first time the user tries to finalize their purchase by pressing the buy button, the Kustom Shipping Assistant validates the selected shipping option with `POST /shipment`. Finalizing can fail for several reasons (for example, the payment method is rejected), so **this is not a guarantee that the shipment is final**. Depending on the details mentioned in the next section, subsequent attempts to finalize might be realized as `PUT /shipment/`. If the Integrator receives valid data, they must respond with HTTP 201 (Created) and a valid payload: ```json { "selected_shipping_option": {...}, "shipment_id": , "shipments": [...] } ``` Where `` is a unique identifier for the (pre-reserved) shipment. The response must also set the `Location` header: ```http Location: /shipment/ ``` The shipment ID must be unique per `session_id` (this is sent in the request payload). Its presence, and the presence of the location header, will be taken as confirmation that an entry has been correctly created in the Integrator’s service. This information will be forwarded to the merchant once the purchase is confirmed, and they can print the shipping label. The following image shows the ideal workflow: Kustom uses different identifiers during the API flow. To avoid confusion, please check this swimlane. The following is an example request from our test tool Shipwreck: ```json { "session_id": "9e8de95f-f8b7-4711-85d9-53ac848ee0d8", "locale": "sv-SE", "currency": "SEK", "recipient": { "given_name": "Klara", "family_name": "Joyce", "street_address": "Sveavägen 42", "postal_code": "111 34", "city": "Stockholm", "country": "SE", "phone": "+46708800000", "email": "partner.kss@klarna.com", "customer_type": "person" }, "order": { "id": "6ad9a637-866c-484f-823e-1c86a67e7be2", "total_amount": 1130, "total_tax": 25, "lines": [{ "type": "physical", "reference": "sku-1234", "quantity": 1, "unit_price": 100, "tax_rate": 2500, "total_tax_amount": 25, "total_price_including_tax": 125, "total_discount_amount": 0 }, { "type": "physical", "reference": "sku-4321", "quantity": 8, "unit_price": 0, "tax_rate": 0, "total_price_including_tax": 1005, "total_discount_amount": 0 }], "total_amount_without_shipping_fee": 1130, "total_tax_without_shipping_fee": 25 }, "selected_shipping_option": { "id": "id-1050", "name": "Budbee 2 home", "type": "delivery-address", "class": "express", "carrier": "budbee", "price": 5000, "tax_rate": 2500, "addons": [{ "category": "notifications", "type": "sms", "required": false, "data": { "text": "+46708800000", "selected": false }, "default": false }, { "category": "notifications", "type": "email", "required": false, "data": { "text": "partner.kss@klarna.com", "selected": false }, "default": false }, { "category": "delivery", "type": "phone-required", "required": false, "data": { "selected": false }, "default": false }, { "category": "delivery", "type": "entry-code", "required": false, "data": { "selected": false }, "default": false }, { "category": "delivery", "type": "additional-instructions", "max_length": 100, "required": false, "data": { "selected": false }, "default": false }], "valid": true } } ``` Example request from our test tool Shipwreck ```http { "header": "Location: https://foo.bar/c2059e35-58b1-4482-ad55-5e7ef541eae4", "body": { "shipment_id": "100", "selected_shipping_option": { "carrier": "budbee", "carrier_product": { "name": "MyPack", "identifier": "10", "addon_identifiers": ["1", "2"] } }, "shipments": [{ "carrier": "Global carrier company", "tracking_id": "12345-6" }] } } ``` Example response from our test integration tool, Ghostship ## Update shipping option This section describes how and when the Kustom Shipping Assistant updates the finalized shipping option. ### Use case The user tries to finalize after a previous finalize attempt. ### Prerequisite A previous `POST /shipment` call was successful (see Save shipping option section). ### Description Kustom validates several items when the user completes their purchase (for example, shipping options and payment method). If any of those fail, the purchase will not proceed. For example, the `POST /shipment` call may be successful, but the payment method may fail. The user then gets an error message and can change their details before trying again. This allows them to change anything, like adding items to the cart or changing the shipping address. When the user completes the checkout for the second time, the Kustom Shipping Assistant will validate the selected shipping option again to ensure the user’s choice is still valid. If the Kustom Shipping Assistant has an available shipment ID from a previous `POST /shipment` call, the subsequent attempts to finalize will be realized as `PUT /shipment/` instead. The Shipping API will not communicate whether the order is completed successfully. This means that the Kustom Shipping Service can update the shipping option several times, sometimes with long waits until the order is placed. The merchant will know when the order is completed through the Order Management API.