Errors and retries
Branch on the HTTP status and the shape of the body, never on the wording
of detail. Messages may change; status codes and fields will not.
Outcomes
Section titled “Outcomes”| Status | How to recognise it | Meaning | What your code should do |
|---|---|---|---|
200 |
state: "ROUTED" |
Shipped | Store shipment_id, tracking_number, carrier and the label |
400 |
Body has missing: [...] |
Contact details missing for every tier that fits | Fix the data (ask the buyer for email or phone). Don’t retry unchanged. |
401 |
detail only |
Missing or invalid API key | Configuration error. Alert a human, don’t retry. |
404 |
detail only |
Unknown id, or not your shipment | Check the id you stored |
422 |
detail is an array of field errors |
The request failed validation, e.g. country is not FI, a field is missing or has the wrong type |
A bug in your mapping. Fix the code. |
422 |
detail is a string, no shipment_id |
No routing rule fits this parcel: too heavy or large for every allowed tier, or PICKUP only |
Not shippable as specified. Broaden delivery_method or route to a human. Don’t retry. |
422 |
Has shipment_id and state: "UNROUTABLE" |
Every eligible carrier rejected the parcel | Not shippable as specified. Don’t retry. |
502 |
Has shipment_id and state: "FAILED" |
Every carrier errored (temporary) | Retry later with the same Idempotency-Key. The same shipment_id is retried. |
Timeout, connection error, other 5xx |
No usable body | Outcome unknown | Retry with the same Idempotency-Key |
A 422 is a verdict on the parcel; a 502 is a verdict on the carriers.
Example bodies
Section titled “Example bodies”{ "detail": "this parcel's routing tier requires recipient_email, recipient_phone", "field": "recipient_contact", "missing": ["recipient_email", "recipient_phone"]}{ "detail": [{ "type": "literal_error", "loc": ["body", "country"], "msg": "Input should be 'FI'" }] }{ "detail": "no routing rule matched this parcel" }{ "detail": "all carriers rejected the shipment", "shipment_id": 42, "state": "UNROUTABLE" }{ "detail": "all carriers unavailable", "shipment_id": 43, "state": "FAILED" }Idempotency keys
Section titled “Idempotency keys”Send an Idempotency-Key header on every single-shipment create. If the
response is lost (a timeout, a dropped connection), retrying with the same key
returns the original shipment instead of creating, and paying for, a second
one.
- Scope: per customer account. Length: up to 128 characters (longer
returns
422). - The key identifies the parcel; the body is not compared. Reusing a key for a different parcel silently returns the first parcel’s shipment. Never share keys between parcels.
- After
ROUTED, repeating the key returns the same shipment, tracking number and label. No second consignment is created. - After a
502, repeating the key runs the carriers again for the sameshipment_id. - After an “all carriers rejected”
422, repeating the key replays that422. If you change the parcel (for example a differentdelivery_method), use a new key. - After a
400, or a “no route” or validation422, nothing was stored. Fix the request and retry with the same key. - The batch endpoint does not support idempotency keys.
Retry schedule
Section titled “Retry schedule”For a 502 or an unknown outcome, retry after 5 s, 30 s, 2 min and 10 min,
then hand the parcel to a human queue. In a web request or job worker, enqueue
the retries rather than sleeping inline. Never retry a 4xx unchanged.