Book shipments API specification

Wuunder has multiple API’s to connect with:

1. A book shipments API

Where you can book a shipment at a specific carrier and using a specific service. We return the shipping label, track & trace url and the carrier name. If there’s a problem with the address or when the service is not possible for the shipment you want to book we return an error. 

2. A book draft shipments API

Where you can send draft shipments to Wuunder. Sending the specific carrier and service is optional. These draft shipments you can book at once using the bulk booking feature in MyWuunder. By clicking on this link you can find all relevant details. 

This technical manual contains all the information required to work with the Wuunder book draft shipments API. If you do have any questions, please, contact us.

General

The Wuunder API is a RESTful API, served over HTTPS. All data is sent and received as JSON and encoded in UTF-8.

Visit Wuunder’s API Docs for more information. 

Wuunder provides isolated staging and production environments:

API

ENVIRONMENT

BASE URL

Authentication

API calls are authenticated using API tokens.
Clients that wish to integrate with the Wuunder API will be assigned a per-client token. If you do not have an API key you can request one here.

API tokens must be included in the Authorization header.

 

     Authorization: Bearer TOKEN-HERE

 

Content type

Clients must specify the type of content they are sending through the Content-Type header. At this time, only application/json is supported:

 

     Content-Type: application/json

 

Versioning

Clients must specify the API version they wish to consume. Specifying the API version is done via the Accept header:

     Accept: application/json

Errors

Errors use the appropriate HTTP status code where possible. Missing parameters for example will respond with a 422 Unprocessable Entity response.

 
     HTTP/1.1 422 Unprocessable Entity

     {
       “errors” : [
         { “field”: “weight”, “messages”: [“can’t be blank”] }
       ]
     }
 

Providing an invalid authentication token will result in a 401 Unauthorized:

 
     HTTP/1.1 401 Unauthorized

     {
        “error”: “unauthorized”
     }
 

One of the most common errors when booking your first shipment is: no_rate_chosen

This indicates that you’re trying to book a service or carrier that is not enabled for your account. Another reason could be you’re not using a business name in the sender address. Some carriers do not perform collections at a private address.  Please send the request you’re using to our customer service and let us know what carrier and service you want to use and we’ll enable these services for you.

HTTP Verbs

Where possible, the Wuunder API strives to use appropriate HTTP verbs for each action.

Verb

Description

GET

Used for retrieving resources.

Address resources

All String fields must obey a maximum of 255 characters unless otherwise noted in the description.

NAME

REQUIRED

TYPE

DESCRIPTION

country

Y

String

ISO-2 country code, for example "NL" or "BE".

family_name

Y

String

Max length: 30.

given_name

Y

String

Max length: 30.

house_number

N

String

Number including any additions. Max length: 8.

locality

Y

String

Max length: 30.

zip_code

Y

String

Max length: 9.

street_name

Y

String

Street part of the addresses. Max length: 35.

address2

N

String

Recommended length: 35.

business

N

String

When included this addresses will be regarded a business address, otherwise a private address

email_address

N

String

Email address associated with this address. Advised to keep below 40 characters to stay compatible with most carriers

phone_number

N

String

Phone number associated with this address. E.123 format, for example +31683243251

chamber_of_commerce_number

N

String

eori_number

N

String

Sender EORI number (only for pickup_address) and required for shipping to non-EU countries (Brexit)

vat

N

String

VAT number including country code required for shipping to non-EU countries (Brexit)

When you do not have a streetname and house number in separate fields you can ask Wuunder to split it for you by not sending a house number and in the streetname both the streetname and house number. We’ll then try to split the streetname and house number for you. Be aware this will not be a 100% correct. It’s always better to use separate fields.

Please use your own e-mail address for testing purposes because Wuunder will send a booking confirmation to the booker (optional), Shipping confirmation with the label to the sender (optional) and track & trace e-mail to the receiver (optional).

Create a shipment

This API allows to create shipments in the Wuunder system in a completely automated fashion.

     POST /api/shipments 

Request body

NAME

REQUIRED

TYPE

DESCRIPTION

description

Y

String

Description of the contents of the shipment in 50 characters or less.

value

Y

Numeric

Value of the whole package, VAT excluded, in eurocents.

kind

Y

String

One of "document", "package" or "pallet"

length

Y

Numeric

Length of the whole package, in centimeters

width

Y

Numeric

Width of the whole package, in centimeters

height

Y

Numeric

Height of the whole package, in centimeters

weight

Y

Numeric

Weight of the whole package in grams

delivery_address

Y

Address

Refer to the Address resource

 

pickup_address

Y

Address

Refer to the Address resource

personal_message

N

String

Personal message for the customer that will be included in the email sent to them

picture

N

Base64 String

Picture of the shipment

customer_reference

N

String

Free form customer reference for the web shop

is_return

N

Boolean

Indicates if this is a return shipment. Defaults to false.

drop_off

N

Boolean

Indicates if this is a shipment with a parcel shop drop off. Defaults to false.

preferred_service_level

Y

String

The preferred service level label as specified by the filter configuration. Check the available filters here

parcelshop_id

N

String

Id of the parcelshop where the shipment needs to be sent to

number_of_items

N

Integer

Number of same labels to book. Should be >= 1 <= 10.

order_lines

N

List (order-line)

Order lines are used for sending information about the content of your package to the customs departments and required for shipping to non-EU countries (Brexit)​

ORDER LINES

NAME

REQUIRED

TYPE

DESCRIPTION

hs_code

N

Integer

The Harmonization Code for this good and required for shipping to non-EU countries (Brexit)​. Most carriers require the 10 digit HScode.

country_of_origin

N

String

ISO-2 country code, for example “NL” or “BE” and required for shipping to non-EU countries (Brexit)​

quantity

N

Integer

Quantity of the goods and required for shipping to non-EU countries (Brexit)​

description

N

String

Description of the goods for this order line. For instance "machine parts" and required for shipping to non-EU countries (Brexit)

value

N

Decimal

Value in Euro’s of the goods and required for shipping to non-EU countries (Brexit)

weight

N

Integer

Weight in grams of the good and required for shipping to non-EU countries (Brexit)​

sku

N

String

DSKU of the good​

ean

N

String

EAN of the good

 

Example


{
    "delivery_address": {
        "business": "Wuunder Nederland BV",
        "chamber_of_commerce_number": null,
        "country": "NL",
        "email_address": null,
        "family_name": "Janssen",
        "given_name": "Jan",
        "house_number": "8",
        "locality": "Weert",
        "phone_number": "+31612345678",
        "street_name": "Afleveradres",
        "address2": "2e etage",
        "zip_code": "6003DD",
        "vat": "NL8559.62.100"
    },
    "customer_reference": "12345678",
    "description": "test",
    "height": "50",
    "kind": "package",
    "length": "10",
    "personal_message": "test",
    "pickup_address": {
        "business": "Voorbeeldshop",
        "chamber_of_commerce_number": "21",
        "country": "NL",
        "email_address": "[email protected]",
        "family_name": "AchternaamContactpersoon",
        "given_name": "VoornaamContactpersoon",
        "house_number": "8",
        "locality": "Weert",
        "phone_number": "+31612345678",
        "street_name": "Marconilaan",
        "zip_code": "6003DD",
        "vat": "NL8559.62.100",
        "eori_number": "NL8559.62.100"
    },
    "picture": null,
    "value": "6900",
    "weight": "5000",
    "width": "20",
    "is_return": false,
    "drop_off": false,
    "preferred_service_level": "dpd:cheapest",
    "number_of_items": 1,
    "order_lines": [{
        "hs_code": "1234567890",
        "country_of_origin": "NL",
        "quantity": 4,
        "description": "Wuunder parcel",
        "value": 100,
        "weight": 1,
        "sku": "54321",
        "ean": "12345"
    }]
}
    

Successful response

{
“width”: 10,
“weight”: 5000,
“value”: 6900,
“track_and_trace_url”: “https://api.wearewuunder.com/shipments/1234565/track_and_trace?token=ABCDEFG”,
“track_and_trace_details”: {
“track_and_trace_code”: “1234567890”,
“carrier_name”: “DPD”,
“carrier_code”: “DPD”
},
“status_message”: “”,
“status”: {
“name”: “booked”
},
“picture_url”: null,
“pickup_address”: {
“zip_code”: “6003DD”,
“vat”: “NL8559.62.100”,
“street_name”: “Marconilaan”,
“street_address”: “Marconilaan 8”,
“phone_number”: “+31202615748”,
“locality”: “Weert”,
“house_number”: “8”,
“given_name”: “VoornaamContactpersoon”,
“family_name”: “AchternaamContactpersoon”,
“eori_number”: “NL8559.62.100”,
“email_address”: “[email protected]”,
“country”: “NL”,
“chamber_of_commerce_number”: “21”,
“business”: “Voorbeeldshop”,
“address2”: null
},
“personal_message”: “test”,
“parcelshop_id”: null,
“name”: “Jan Janssen”,
“length”: 10,
“label_url”: “https://api.wearewuunder.com/public/shipments/123456789”,
“kind”: “package”,
“items_track_and_trace_details”: [
{
“track_and_trace_code”: “1234567890”,
“id”: “12345-12345-12345”,
“correlation_id”: “1234567890”
}
],
“is_return”: false,
“id”: “12345-12345-12345”,
“height”: 50,
“drop_off”: false,
“description”: “test”,
“delivery_address”: {
“zip_code”: “6003DD”,
“vat”: “NL8559.62.100”,
“street_name”: “Marconilaan”,
“street_address”: “Marconilaan 8”,
“phone_number”: “+31612345678”,
“locality”: “Weert”,
“house_number”: “8”,
“given_name”: “Jan”,
“family_name”: “Janssen”,
“eori_number”: null,
“email_address”: null,
“country”: “NL”,
“chamber_of_commerce_number”: null,
“business”: “Wuunder Netherlands BV”,
“address2”: “2e etage”
},
“customer_reference”: “12345678”
}

label_url and track_and_trace_url will not be available immediately, but only after the shipment is fully processed.

picture_url will contain a publicly accessible link pointing to the picture supplied in the payload, if any.

Error responses

HTTPS STATUS CODE

ERROR DESCRIPTION

422

Shipment was invalid, for example missing required fields.

Track and Trace Webhook

 

     {
       “action”: “track_and_trace_updated”,
       “track_and_trace_code”: “S3123”,
       “carrier_code”: “TNT”,
       “carrier_name”: “TNT Express”,
       “authentication_token”: “token” // JWT token
     }

 

It is expected that the system receiving the webhook responds with a 2xx status response. Any other response will be regarded as a failure, in which case the Wuunder system will start retrying the webhook using exponential back off.

Authentication token from the webhook response

Sample data:

Token

     

     “eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJib29raW5
      nX2lkIjoiNzhkZTZjNDYtMWE2Ni00YmQxLTg0MmQtZTkzNmIw
      ZDg4MjRiIiwiZXhwIjoxNTAzNTc3MTExNjA2Ljk0OX0.Q4ED7N
      q2JUa5uZaxcaAG3m6oGI0oeFjcb_5Mz0HQOPw” 

 

Consist of:

HEADER: ALGORITHM & TOKEN TYPE

   

     json
     {
       “alg”: “HS256”,
       “typ”: “JWT”
     }

 

PAYLOAD: DATA

     

     json
     {
       “booking_id: “78de6c46-1a66-4bd1-e936b0d8824b”
       “exp”: 1503577111606.949
     }

 

Where booking_id is the identifier of processed booking and exp is
expiration time. Wuunder expects the webshop to validate that the expiry time
has not passed yet.

Webshops can verify the validity of the token by calculating the signature, using
their webshop token as the shared secret.

     

     exp = booking_inserted_at (unix time) + default_expiration_time (24 * 60 * 60)