- Checkout
- Features
Authenticate the Shipping Assistant's calls
Implement POST /auth for the Kustom Shipping Assistant: verify the sha256 nonce digest, return a bearer token, and answer 401 to make Kustom fetch a new one.
Every call Kustom makes to your TMS carries a bearer token that you issued. Kustom gets it from
POST /auth, by proving it holds the merchant’s key without sending the key itself. This page
is the contract for that call and for the headers on every other call. It’s required: without a
token, nothing else is called and every checkout falls back to the merchant’s own options.
Credentials
Section titled “Credentials”You issue each merchant two values, and the merchant enters them in the Kustom Portal:
| Portal field | Sent as | What it is |
|---|---|---|
| Identifier | identifier | Which merchant account this is, in your system |
| Key | never sent | A shared secret. Kustom sends only a hash of it |
The profile also names your TMS, which is how Kustom knows your base URL. Kustom appends
/auth, /shippingoptions and /shipment to that one URL.
Request
Section titled “Request”POST /authContent-Type: application/vdn.klarna.shipping.auth-v1+json{ "identifier": "merchant-4711", "secret": { "nonce": "n8Jq2LwXv0ZbT5mA", "digest": "8f9fc1128e9dc6e85877176a982100f0c8cea09352bfe12e9ff988d8af38eb63" }}| Field | Meaning |
|---|---|
identifier | The Identifier from the merchant’s profile |
secret.nonce | 16 random letters and digits, new on every call |
secret.digest | SHA-256 of the nonce followed by the key, as UTF-8, written as lowercase hex. The example is the digest for the key example-key |
The content type reads vdn, not vnd. That’s what Kustom sends, so match on it as written or
don’t match on it at all.
To check the digest, compute the same hash from your stored key and compare. Compare without regard to case: Kustom sends lowercase, but the API reference’s example is uppercase and some older integrations were written against that.
import { createHash, timingSafeEqual } from 'node:crypto';
function digestMatches(nonce, digest, key) { const expected = createHash('sha256').update(nonce + key, 'utf8').digest('hex'); const received = digest.toLowerCase(); return received.length === expected.length && timingSafeEqual(Buffer.from(received), Buffer.from(expected));}Response
Section titled “Response”Answer 200 or 201 with the token:
{ "token": "eyJhbGciOiJIUzI1NiJ9.bWVyY2hhbnQtNDcxMQ.k3M2..." }| Field | Required | Meaning |
|---|---|---|
token | Yes | Sent back to you as Authorization: Bearer <token> on every other call |
Anything else in the body is ignored, expires_in included. A response without token, or with
any status other than 200 or 201, is a failed authentication, and the checkout falls back to
the merchant’s options. Answer 401 for wrong credentials so the failure is easy to read in your
own logs.
How Kustom keeps the token
Section titled “How Kustom keeps the token”Kustom doesn’t authenticate up front. It calls POST /auth right before a call to your TMS that
needs a token it doesn’t have, usually the first POST /shippingoptions, and then makes that
call. It stores the token per merchant profile and reuses it across checkouts, for up to a day.
It doesn’t read an expiry from you. Instead:
- Answer
401to any call whose token you no longer accept. - Kustom drops the token, calls
POST /authagain and repeats the call once with the new token. - If that repeat also gets
401, the call fails, the same as any other error.
So expire tokens on whatever schedule suits you. An hour is a reasonable choice. Issue a separate token per merchant, so revoking one merchant doesn’t log out the others.
Headers on every call
Section titled “Headers on every call”| Header | Value |
|---|---|
Authorization | Bearer <token>, on every call except POST /auth |
Content-Type | Per call, on calls with a body — see below |
Klarna-Correlation-Id | Kustom’s id for the request, when there is one. Log it, and quote it when you contact Kustom about a call |
| Call | Content-Type |
|---|---|
POST /auth | application/vdn.klarna.shipping.auth-v1+json |
POST /shippingoptions | application/vdn.klarna.shipping.get_options-v1+json |
POST /shipment, PUT /shipment/{shipment_id} | application/vdn.klarna.shipping.select_option-v1+json |
Kustom sends no Accept header and no idempotency key. Respond with JSON.
Fields are snake_case in both directions, and Kustom ignores fields it doesn’t recognize in your
responses, so you can add your own without breaking anything.
Related
Section titled “Related”- Return shipping options — the first call that uses the token
POST /authin the API reference- Build a TMS integration — where authentication sits in the flow
This page is also available as Markdown — append .md to its URL — and is indexed in llms.txt.