API Integration

This section is a guide to implement the Shipping API.

Before starting, you should check if any of our existing partnerships would enable you to use the Klarna Shipping Service without API integration. If so, you can skip this section and go directly to Getting Started .

Important terms

  • Checkout API: API implemented by Klarna. The Merchant calls this API to start an order session or manage it.
  • Shipping API: API specified by Klarna; must be implemented by the Integrator (see below). The Klarna Shipping Service 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 Klarna 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 Klarna after the purchase is completed.

Please note that in order to use the Klarna Shipping Service you may have to adjust your Checkout integration. More info about that in this section .

1. Authorization

This section describes how to authorize requests from the Klarna Shipping Service.

Use case

User has entered the checkout and Klarna tries to obtain authentication from the Integrator.

Prerequisite

Merchant has provided Klarna with authentication credentials. See more here .

Description

Communications between Klarna Shipping Service and the Integrator need to be authenticated using a bearer token scheme. This means each request has a header like this:

1
  Authorization: Bearer <token>

Where <token> is a secret string provided by the Integrator. To obtain it, Klarna Shipping Service calls POST /auth with credentials provided by the Merchant. This is the payload:

1
2
3
4
5
6
7
{
  "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 in their end:

1
digest = sha256(nonce + key)

We recommend the Integrator to give a unique token per merchant, and to let the token expire after a reasonable time (1h is a good duration). If Klarna Shipping Service provides an old or wrong token to a call other than POST /auth, the Integrator must answer with HTTP 401 (Unauthorized). If Klarna Shipping Service provides the wrong credentials to POST /auth, the integrator must also answer with HTTP 401 (Unauthorized).

Klarna Shipping Service will cache the bearer token until an HTTP 401 response is received, and then fetch a new one.

2. Preview

This section describes what preview options are, and how to set them.

Use case

User has entered the checkout.

Prerequisite

No address data is available for the user.

Description

When a new user enters the checkout, Klarna 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.

This is how it looks like:

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). This could be only the country, in cases like the above. The Integrator must mark these options with the preview flag. Once Klarna has a full address, Klarna Shipping Service will call the endpoint again with the complete information, and the Integrator must answer with fully valid (non-preview) shipping options.

3. Shipping options

This section describes how to provide shipping options to the Klarna Shipping Service.

Use case

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.

Pitfalls

This is the same endpoint used for preview options and the location search.

Description

Klarna Shipping Service 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 in step 1.

You can test a Shipping API implementation and see how the shipping selector looks in our test tool, Shipwreck . This is an example request from Shipwreck:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
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",
      "recipient": {
          "given_name": "Klara",
          "family_name": "Joyce",
          "street_address": "Sveavägen 42",
          "postal_code": "111 34",
          "city": "Stockholm",
          "country": "SE",
          "phone": "+46708800000",
          "email": "partner.the Klarna Shipping Service@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
      }
  }

Klarna passes on 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. These attributes, just like the rest of the order information, will be passed on to the shipping API. The attributes can be used to set the rules which shipping options should be shown.

  • Weight and dimensions.
    • Provided by the Merchant through the KCO API.
    • Given as grams and millimeters (no decimals).
  • Tags.
    • Strings set by the merchant (no limitations from Klarna).
    • For example, merchants can offer free shipping for certain members. To do so, they can tag the order with e.g. "member".

This is an example response from an Integrator (adapted from our test integration, Ghostship):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
{
    "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": "11:00"
                            },
                            {
                                "open": "12: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 short 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 the currently supported types:

  • delivery-address - Delivery to door / address. Final selected option will contain an address, and can contain a timeslot.
  • pickup-box - Delivery to a pickup locker/box. Final selected option will contain a location.
  • pickup-point - Delivery to a pickup point. Final selected option will contain a location.
  • pickup-merchant-store - Delivery to a merchant store for pickup. Final selected option will contain a location.
  • postal - Best-effort delivery of a package/letter using the postal network.

The type controls which name we show in the shipping option. For example, delivery-address is shown in English as “To door delivery”.

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. Final selected option will contain an email address and/or a phone number.
  • addon_delivery - Adding entry code (door code) and delivery instructions. Final selected option will contain 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:

  • To show intervals, you can use an offset in days from the current date. For example,
    1
    2
    3
    4
    5
    6
    "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 ). For example,
    1
    2
    3
    "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).

This section describes the pickup point map and the associated location search.

Use case

User opens the pickup point map and searches for locations.

Prerequisite

User inserted a new postal code to the location search field.

Pitfalls

This search uses POST /shippingoptions like a preview options call.

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, Klarna will call POST /shippingoptions with country and 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, Klarna Shipping Service discards any changes other than the locations.

5. Select shipping option

This section describes how Klarna finalises the selected shipping option.

Use case

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 next section).

Description

The first time the user tries to finalize their purchase by pressing the buy button, Klarna Shipping Service validates the selected shipping option with POST /shipment. Finalizing can fail for a number of reasons (for example, the payment method is rejected), so this is not a guarantee that the shipment is final. Depending on details explained in the next section, next attempts to finalize might instead be realised as PUT /shipment/<shipment ID>.

If the Integrator receives valid data, they must respond with HTTP 201 (Created) and a valid payload:

1
2
3
4
5
{
    "selected_shipping_option": {...},
    "shipment_id": <shipment ID>,
    "shipments": [...]
}

Where <shipment ID> is a unique identifier for the (pre-reserved) shipment. The response must also set the Location header:

1
Location: <Integrator's base URL>/shipment/<shipment ID>

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 then be forwarded to the merchant once the purchase is confirmed, and they can print the shipping label.

This is the ideal workflow:

Klarna uses different identifiers during the API flow. To avoid confusion, please check this swimlane .

This is an example request from our test tool Shipwreck :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
{
    "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
    }
}

This is an example response from our test integration, Ghostship:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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"
            }
        ]
    }

6. Update shipping option

This section describes how and when Klarna Shipping Service updates the finalized shipping option.

Use case

User tries to finalize after a previous finalize attempt.

Prerequisite

A previous POST /shipment call was successful (see previous section).

Description

Klarna validates several items when the user completes their purchase (example shipping options, payment method). If any of those fail, the purchase will not proceed.

As an 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 gives them a chance to change anything, like adding items to the cart or changing the shipping address. When the user is completing the checkout for the second time, Klarna Shipping Service will validate the selected shipping option again to make sure the user’s choice is still valid.

If Klarna Shipping Service has an available shipment ID from a preivous POST /shipment call, the following attempts to finalize will be realised as PUT /shipment/<shipment ID> instead.

The Shipping API will not communicate whether the order is completed successfully. This means that Klarna Shipping Service can update the shipping option several times, sometimes with long waits in between, until the order is placed. The merchant will know when the order is completed through the Order Management API .

7. Tracking

This section describes how to provide Klarna with tracking information.

Use case

User has completed the purchase.

Prerequisite

Order is successfully completed.

Description

If the Integrator does not provide complete tracking information on the POST /shipment/<sipment ID> or PUT /shipment/<sipment ID> responses, Klarna Shipping Service will call GET /shipment/<sipment ID> periodically after the purchase is complete. The expected response format is the same as in the POST and PUT operations.

The response to this call should list all shipments connected to this purchase, and it should contain tracking numbers (tracking_id) for each shipment. The tracking information is used by Klarna to allow users to track their parcels and get notified upon any updates.

Alternatively, you can provide us with the tracking number and carrier information via the order management API. Just follow these instructions .