NAV Navbar
  • Documentation
  • Authentication
  • Availability check API
  • Order API
  • Errors
  • Documentation

    Please note, this documentation is not exhaustive, and other optional values may be returned in responses.

    If you have questions or found errors, get in touch at dev@porterbuddy.com.

    Overview

    Porterbuddy is a last mile delivery service platform. Customers of Porterbuddy can place delivery orders using this api.

    The following describes the necessary steps to do a delivery using Porterbuddy:

    1. Your retailer solution: Fetch delivery availability
    2. End user: Choose delivery time
    3. Your retailer solution: Place delivery order
    4. Porterbuddy: Deliver the parcels at the requested time

    Time

    All timestamps requires timezone to avoid ambiguity.

    Availability

    An availability object is an entity that combines pickup address, delivery address, and delivery time. To be able to guarantee our ability to fulfill an order, you first need to obtain an availability entity before submitting an order for the requested time.

    Order

    The order contains all information needed to deliver to the end user. This includes addresses and delivery time, but also the availability for the package for pick up and requirements for identification, etc.

    Testing the API

    If you want to test the API, you can send a request to dev@porterbuddy.com to receive a test API key.

    Get an API key

    To get an API key for use in production, you first have to register as a user and customer at Porterbuddy. Currently, we only support payment by invoice. As a result the process of getting access is as follows:

    1. Go to porterbuddy.com and register a user (of type business)
    2. Contact dev@porterbuddy.com to enable payment by invoice
    3. Receive your API key

    Implementation Hints

    We try to keep this API as stable as possible. However, implementing new services or features sometimes make changes necessary, for example adding new properties to API objects. To lower the risk of such a change breaking your integration, we advise to only define the properties of API responses your integration needs and always ignore unknown properties in received json objects. This is the default behaviour in Javascript (due to Javascript not being typed anyway) and Typescript when representing received API responses via interfaces. When using Jackson to implement JSON APIs in Java, the easiest way to achieve this is to use the annotation @JsonIgnoreProperties(ignoreUnknown=true)on classes representing API response data.

    Authentication

    An authenticated request includes the x-api-key header:

    curl "https://api.porterbuddy.com/..."
      -H "x-api-key: <your_api_key>"
    

    Make sure to replace <your_api_key> with your API key.

    Porterbuddy expects the API key to be included in all API requests to the server in a header that looks like the following:

    x-api-key: <your_api_key>

    Availability check API

    To verify that PorterBuddy can deliver according to your preferences you need to do an availability request. This will verify address details and return possible delivery windows. Note that even when the endpoint returns statuscode OK (200), the delivery windows array could be empty due to no delivery being possible with the specified pickup windows, or no more capacity being available.

    POST https://api.porterbuddy.com/availability

    Example Request:

    curl \
      -X POST 'https://api.porterbuddy.com/availability' \
      -H 'Content-Type: application/json' \  
      -H 'x-api-key: <your_api_key>' \
      -d '{
            "pickupWindows": [
                {"start": "2023-02-13T10:00+01:00", "end": "2023-02-13T18:00+01:00"},
                {"start": "2023-02-14T10:00+01:00", "end": "2023-02-14T18:00+01:00"}
            ],
            "originAddress": {
                "streetName": "Keysers Gate",
                "streetNumber": "3",
                "postalCode": "0165",
                "city": "Oslo",
                "country": "Norway"
            },
            "destinationAddress": {
                "streetName": "Høyenhallveien",
                "streetNumber": "25",
                "postalCode": "0678",
                "city": "Oslo",
                "country": "Norway"
            },
            "recipient": {
                "email": "testemail+recipient@porterbuddy.com",
                "phoneCountryCode": "+47",
                "phoneNumber": "65127865"
            },
            "products": [ "delivery" ],
            "parcels": [
                {
                    "widthCm": 30,
                    "heightCm": 25,
                    "depthCm": 45,
                    "weightGrams": 2000
                }
            ],
            "items": [
                {
                    "name":"Fancy Sneakers",
                    "sku":"FANCYSNEAKER43",
                    "weightGrams":"600",
                    "widthCm":20,
                    "heightCm":10,
                    "depthCm":35,
                    "description":"Fancy sneakers (red/blue) in size 43",
                    "category":"shoes",
                    "brand":"Fancy Footwear Corp.",
                    "imageUrl":"https://awesomewebshop.com/images/5fd71d6f-b0be-4480-900f-f3d008a0bc62.png",
                    "price": {
                        "fractionalDenomination":"79900",
                        "currency":"NOK"
                    },
                    "barCode": {
                        "value":"123-456-789",
                        "type":"CODE128"
                    }
                },
                {
                    "name":"Heavy Boots",
                    "sku":"HEAVYBOOTS48",
                    "weightGrams":"1400",
                    "widthCm":30,
                    "heightCm":12,
                    "depthCm":45,
                    "description":"Heavy military boots, size 48",
                    "category":"shoes",
                    "brand":"Heavy Industries Inc.",
                    "imageUrl":"https://awesomewebshop.com/images/613c37e8-64be-4483-a216-9fb1b7f7e848.png",
                    "price": {
                        "fractionalDenomination":"134900",
                        "currency":"NOK"
                    },
                    "barCode": {
                        "value":"999-888-765",
                        "type":"CODE128"
                    }
                }
            ]
        }'
    

    Example response:

    {
      "originResolvedAddress": {
        "streetName": "Keysers Gate",
        "streetNumber": "3",
        "postalCode": "0165",
        "city": "Oslo",
        "country": "Norway",
        "position": {
          "latitude": 59.9169844,
          "longitude": 10.7411912
        }
      },
      "destinationResolvedAddress": {
        "streetName": "Høyenhallveien",
        "streetNumber": "25",
        "postalCode": "0678",
        "city": "Oslo",
        "country": "Norway",
        "position": {
          "latitude": 59.9032646,
          "longitude": 10.811598
        }
      },
      "deliveryWindows": [
        {
          "product": "delivery",
          "start": "2023-02-13T17:30:00+01:00",
          "end": "2023-02-13T19:30:00+01:00",
          "price": {
            "fractionalDenomination": 14900,
            "currency": "NOK"
          },
          "expiresAt": "2023-02-13T13:00:13+01:00",
          "token": "20hRsgz8AovrLjeOldJ2Wg==:yhr85il4/swdgiEP/DG2wg==:2hBoFcmyTNLp/CTfX3sTGslOJr9sXAMxHggqq/h6tGmUuCEB2Vfy8uyNIWfg3qf6d7nj84Aj2sbwMLK2hETe14L4qgnlZHVSkBcktYPc6VCp9vEZhXErpQS3HoSyRU+mVcF2SNGP4s5TI5x7S6oq4Q==",
          "consolidated": false
        },
        {
          "product": "delivery",
          "start": "2023-02-13T19:30:00+01:00",
          "end": "2023-02-13T21:30:00+01:00",
          "price": {
            "fractionalDenomination": 14900,
            "currency": "NOK"
          },
          "expiresAt": "2023-02-13T13:00:11+01:00",
          "token": "lDQ+BXxkKR9qHwv5naf6CQ==:zpR8F/PUK3YlhUkVspoQ/w==:eV5fTEQmv/6We+KaK32ji31FHHHKaG4/GFQO8s/BX9dLKQ4QlV6gLZYtXOCQU+WfbYe+x9pJOfdjaXRfC2M4oNq+bHtNHSQKY7rfjAV3IoG2DSqvT9a+z4Lp8yuFOvonEUlzWlEiT2inTcnju+JlYQ==",
          "consolidated": false
        },
        {
          "product": "delivery",
          "start": "2023-02-14T17:30:00+01:00",
          "end": "2023-02-14T19:30:00+01:00",
          "price": {
            "fractionalDenomination": 14900,
            "currency": "NOK"
          },
          "expiresAt": "2023-02-14T13:00:27+01:00",
          "token": "SW23FPWVYkyV+xJuFfJ76w==:wZl92j141aX8vBtU/bzgtQ==:kBZeBgu+JoYMaasgk+Y+F4Bsd6ntn3Q8hkbGX9/ysfnppPTmqe24ZZeBdtDwh2364DMUhc+hpyK2Aau8jW7FC/gl4E3Q84j+7OnX3CNYuRzoNlYsXI749XGr86YbVU6wt8olw8pNkkLsyF17oNDA3g==",
          "consolidated": false
        },
        {
          "product": "delivery",
          "start": "2023-02-14T19:30:00+01:00",
          "end": "2023-02-14T21:30:00+01:00",
          "price": {
            "fractionalDenomination": 14900,
            "currency": "NOK"
          },
          "expiresAt": "2023-02-14T13:00:04+01:00",
          "token": "Kj1q+VlVFG4LX3ZAwbMPPg==:t6BkqFpGFzVnpqoTqEyKgA==:GGrsm1gUTetg8ux9sTs0/+X7mQiG/9a4y67BZoXPRjB3g8F+OK4NVyaYZAdIsLcdbO36jNta8hEyyCDxVee2qi7+95RJXBP/7wuH4Ik/S2/fq+8gHBQhISm0ba9TyO4ujytN5eAlXmoZgSQSXFG2tg==",
          "consolidated": false
        }
      ],
      "flags": ["CONSOLIDATION_ENABLED"]
    }
    

    Arguments

    Parameter Required Type Default Description
    originAddress Yes Address none The pick up location for the merchandise
    destinationAddress Yes AvailabilityDestinationAddress none The destination for the merchandise
    recipient No AvailabilityRecipient none The contact details of the delivery recipient
    pickupWindows No List<Window> none The windows when the package is available for pick-up. Specifying this allows for fine-grained control of the number of delivery windows returned, or the earliest possible delivery option, e.g. in case of preorders. If not set, pickup windows for 7 days including the current day will be generated on the backend side. We recommend sending wide windows and for multiple days. Ex: If the package is ready for pick up from 3pm on monday and the store closes at 7pm, the first window will be 3pm-7pm the first day, and the following windows should probably be the opening hours for the store the next days.
    products No List [ delivery ] The name of the product needed. Currently, only the product "delivery" is available. Multiple products here will result in windows in the list for each product. Each window has a 'product' attribute which signals which product this is for.
    parcels (*) List<Parcel> none The list of parcels that you want to transport. This will affect which couriers can fulfill the delivery. A smaller car cannot drive packages that won't fit in it's trunk.
    additionalInfo no Object null Object reserved for additional information. This argument is intended to be provided by the checkout widget and needs to be passed-through as is, without further modifications.
    items (*) List<Item> none A list of items you want to transport. Will be used to determine size and weight if no parcels are specified.

    (*) At least one of the properties "items" or "parcels" must be specified for a valid request. If only items are specified, each item must have the "weightGrams" property specified as >= 0.

    Availability Destination Address

    Parameter Required Type Default Description
    streetName No String none Name of the street
    streetNumber No String none Number of the street. Including any letters. Ex: 10d
    postalCode Yes String none The postal code of the address
    city No String none The city of the address
    country Yes String none The country of the address

    Availability Recipient

    Parameter Required Type Default Description
    email No String none Email
    phoneCountryCode No String +47 Country code for the phone number
    phoneNumber No String none Phone number

    Response

    Parameter Type Description
    originResolvedAddress Resolved Address Resolved origin. We will resolve the origin against Google\'s APIs. The exact details we find will be returned here.
    destinationResolvedAddress Resolved Address Resolved destination. Like with the origin, we will try to resolve the exact details. The result will be returned here.
    deliveryWindows List<DeliveryWindow> The available windows for the destination. This is tied to the pick up windows given in the request, so any changes to those will affect these windows as well.
    consolidatedWindow ConsolidatedDeliveryWindow Available window for the destination. Present if the recipient field was present in the request and the customer has already placed one or more orders within one of the delivery windows.
    flags List A list of flags indicating if certain features are enabled in the shop configuration. Mainly used for our own checkout widgets. This property will be omitted if the list would be empty.

    Resolved Address

    Parameter Required Type Default Description
    streetName Yes String none Name of the street
    streetNumber Yes String none Number of the street. Including any letters. Ex: 10d
    postalCode Yes String none The postal code of the address
    city Yes String none The city of the address
    country Yes String none The country of the address
    position Yes Position (lat/long) none The exact position of the address in longitude/latitude

    Delivery Window

    Parameter Type Description
    start DateTime The start of the window
    end DateTime The end of the window
    product String The product type this window is for. Example: delivery or express
    price Price The price given for the specific window
    displayPrice Price The display price given for the specific window, calculated based the store's configured base display price. Used to show the end customer a price different from the actual price. Only returned when a base display price is configured.
    expiresAt DateTime After this time this window should not be shown as an option. Either remove the window or do a full refresh of the availability.
    token String The token is to be sent back when placing and order. It makes sure that the window and price that was offered for availability at that specific point in time is what is registered on the order.
    consolidated Boolean If true, there is another order present for the same customer with that window, so the orders can be consolidated.

    Consolidated Delivery Window

    Parameter Type Description
    start DateTime The start of the window, may be omitted if anonymous consolidation is used.
    end DateTime The end of the window, may be omitted if anonymous consolidation is used.
    product String The product type this window is for. Example: delivery or express
    price Price The price given for the specific window
    displayPrice Price The display price given for the specific window, calculated based the store's configured base display price. Used to show the end customer a price different from the actual price. Only returned when a base display price is configured.
    expiresAt DateTime After this time this window should not be shown as an option. Either remove the window or do a full refresh of the availability.
    token String The token is to be sent back when placing and order. It makes sure that the window and price that was offered for availability at that specific point in time is what is registered on the order.
    consolidated Boolean If true, there is another order present for the same customer with that window, so the orders can be consolidated.

    Price

    Parameter Type Description
    fractionalDenomination Integer The price in the lowest denominator. Ex: 1 USD = 100 fractionalDenomination (cents)
    currency String The currency specified. Usually "NOK".

    Order API

    Place an order

    To place an order you need to know the destination, origin as well as the times chosen by the user and the times the package is available for pickup.

    POST https://api.porterbuddy.com/order

    Example request:

    curl -X POST \
      https://api.porterbuddy-test.com/order \
      -H 'x-api-key: <your_api_key>' \
      -H 'Content-Type: application/json' \
      -H 'Idempotency-Key: <your idempotency key>' \
      -d '{
             "origin": {
                "name": "Nils Johansen (Sender)",
                "address": {
                    "streetName": "Keysers Gate",
                    "streetNumber": "3",
                    "postalCode": "0165",
                    "city": "Oslo",
                    "country": "Norway"
                },
                "email": "testemail+sender@porterbuddy.com",
                "phoneCountryCode": "+47",
                "phoneNumber": "65127865"
            },
            "destination": {
                "name": "Roger Olsen (Recipient)",
                "address": {
                    "streetName": "Høyenhallveien",
                    "streetNumber": "25",
                    "postalCode": "0678",
                    "city": "Oslo",
                    "country": "Norway"
                },
                "email": "testemail+recipient@porterbuddy.com",
                "phoneCountryCode": "+47",
                "phoneNumber": "65789832",
                "deliveryWindow":{
                    "start": "2023-02-13T17:30:00+01:00",
                    "end": "2023-02-13T19:30:00+01:00",
                    "token": "20hRsgz8AovrLjeOldJ2Wg==:yhr85il4/swdgiEP/DG2wg==:2hBoFcmyTNLp/CTfX3sTGslOJr9sXAMxHggqq/h6tGmUuCEB2Vfy8uyNIWfg3qf6d7nj84Aj2sbwMLK2hETe14L4qgnlZHVSkBcktYPc6VCp9vEZhXErpQS3HoSyRU+mVcF2SNGP4s5TI5x7S6oq4Q=="
                },
                "verifications": {
                    "minimumAgeCheck": 16,
                    "leaveAtDoorstep": false,
                    "idCheck": true,
                    "requireSignature": false,
                    "onlyToRecipient": true
                }
            },
            "parcels" : [
                {
                    "description": "Shoes",
                    "widthCm": 30,
                    "heightCm": 25,
                    "depthCm": 45,
                    "weightGrams": 2000,
                    "parcelShipmentIdentifier": "12345"
                }
            ],
            "items": [
                {
                    "name":"Fancy Sneakers",
                    "sku":"FANCYSNEAKER43",
                    "weightGrams":"600",
                    "widthCm":20,
                    "heightCm":10,
                    "depthCm":35,
                    "description":"Fancy sneakers (red/blue) in size 43",
                    "category":"shoes",
                    "brand":"Fancy Footwear Corp.",
                    "imageUrl":"https://awesomewebshop.com/images/5fd71d6f-b0be-4480-900f-f3d008a0bc62.png",
                    "price": {
                        "fractionalDenomination":"79900",
                        "currency":"NOK"
                    },
                    "barCode": {
                        "value":"123-456-789",
                        "type":"CODE128"
                    }
                },
                {
                    "name":"Heavy Boots",
                    "sku":"HEAVYBOOTS48",
                    "weightGrams":"1400",
                    "widthCm":30,
                    "heightCm":12,
                    "depthCm":45,
                    "description":"Heavy military boots, size 48",
                    "category":"shoes",
                    "brand":"Heavy Industries Inc.",
                    "imageUrl":"https://awesomewebshop.com/images/613c37e8-64be-4483-a216-9fb1b7f7e848.png",
                    "price": {
                        "fractionalDenomination":"134900",
                        "currency":"NOK"
                    },
                    "barCode": {
                        "value":"999-888-765",
                        "type":"CODE128"
                    }
                }
            ],
            "product": "delivery",
            "courierInstructions": "Test",
            "shipmentIdentifier": "12345"
        }'
    

    Example response:

    {
      "orderId": "3520",
      "_links": {
        "self": {
            "href": "https://api.porterbuddy-test.com/order/3520"
        },
        "labelInfo": {
            "href": "https://api.porterbuddy-test.com/order/3520/label"
        },
        "status": {
            "href": "https://api.porterbuddy-test.com/order/3520/status"
        },
        "userInformation": {
            "href": "https://www.porterbuddy-test.com/orders/recipient_tracking/aDpa9ytqp86no9XATds6r2gf"
        }
      }
    }
    

    Arguments

    Parameter Required Type Default Description
    origin Yes Origin none Information about the origin location.
    destination Yes Destination none Information about the destination.
    parcels (*) List<Parcel> none Information about the parcels.
    product Yes String none The product associated with the window chosen.
    orderReference No String none Your internal order reference.
    courierInstructions No String "" A message to the courier about the delivery.
    shipmentIdentifier No Digits[5-20] none Shipping identifier. The 5 last digits will be used as a pickup pin if this is specified.
    items (*) List<Item> none Information about the items (goods) in the order.
    statusWebhookUrl No URL none Url of a webhook that gets called when the pickup date or order status changes. See Order Status Webhook

    (*) At least one of the properties "items" or "parcels" must be specified for a valid request. If only items are specified, each item must have the "weightGrams" property specified as >= 0.

    Origin

    Parameter Required Type Default Description
    name Yes String none Full name
    address Yes Address none The address where the package will be collected
    email Yes String none Email
    phoneCountryCode No String +47 Country code for the phone number
    phoneNumber Yes String none Phone number
    pickupWindows No List<Window> none The available windows to pick up the package, only relevant if destination.bestAvailableWindow is set to true. If not specified in the best available case, the backend will try to chose a delivery window within the next 10 days. If a delivery window or consolidated window is specified, this property will be ignored.

    Destination

    Parameter Required Type Default Description
    name Yes String none Full name
    address Yes Address none The address where the package will be delivered
    email Yes String none Email
    phoneCountryCode No String +47 Country code for the phone number
    phoneNumber Yes String none Phone number
    bestAvailableWindow No Boolean false IF this is true, find the "best available" window. This might be today or tomorrow, or even the next day if tomorrow is a holiday. To have some idea about when something can be delivered it is always best to verify availability through API. If a delivery window or consolidated delivery window is specified, this property is ignored.
    deliveryWindow Yes* Window none The agreed upon window to deliver within. This should be a window fetched from the /availability request.
    * This is not required if bestAvailableWindow or consolidatedWindow is used.
    consolidatedWindow No ConsolidatedWindow none if the user chose the consolidated delivery window, this field needs to be filled with the consolidated window data from the availability request.
    verifications Yes Verifications none The verifications to be performed by the courier.

    Address

    Parameter Required Type Default Description
    streetName Yes String none Name of the street
    streetNumber Yes String none Number of the street. Including any letters. Ex: 10d
    postalCode Yes String none The postal code of the address
    city Yes String none The city of the address
    country Yes String none The country of the address

    Verifications

    Some of the verifications will affect each other. In that case, the couriers will check the strictest union of the requirements to make sure nothing is delivered to the wrong person.

    Parameter Required Type Default Description
    leaveAtDoorstep Yes Boolean none Can the courier leave the package at the doorstep if no one is at home?
    requireSignature Yes Boolean none Should the recipient sign for the package?
    idCheck Yes Boolean none Should the courier check the real identity of the recipient?
    onlyToRecipient Yes Boolean none Should the courier only deliver to the named recipient? This option implies an ID check.
    minimumAgeCheck       No       Integer                           none           Should the courier verify the age of the recipient is equal to or above this value? Package will not be delivered if recipient is younger than the age provided.

    Window

    Parameter Required Type Default Description
    start Yes DateTime none The start of the window
    end Yes DateTime none The end of the window
    token No String none The token from the availability response. Only used when placing an order.

    ConsolidatedWindow

    Parameter Required Type Default Description
    token Yes String none The token from the availability response. Only used when placing an order.

    Parcel

    Parameter Required Type Default Description
    description Yes String none Description of the parcel and contents
    widthCm Yes Int none The width of the parcel in cm
    heightCm Yes Int none The height of the parcel in cm
    depthCm Yes Int none The depth of the parcel in cm
    weightGrams Yes Int none The weight of the package in grams
    parcelShipmentIdentifier No Digits[5-20] none The shipping identifier for the package. This is usually the number for the bar code.

    Item

    Parameter Required Type Default Description
    weightGrams (*) Int none Weight of the item in grams. Required if only items and no parcels are specified in the request.
    widthCm no Int none Width of the item in cm
    heightCm no Int none Height of the item in cm
    depthCm no Int none Depth of the item in cm
    name no String none The item's name
    sku no String none SKU for the item
    price no Price none Sales price per unit of the item
    cost no Price none Cost per unit of the item
    description no String none Description of the item
    category no String none A product category for the item.
    brand no String none Brand name the item is sold under
    imageUrl no String none A URL linking to an image of the product
    barCode no BarCode none A unique barcode identifying the product
    storageTemperature no Int none Temperature the item should be stored at. Note: For reference purposes only, if items need to be handled at specific temperatures, it needs to be specified as parcel restrictions and the delivery agreement with Porterbuddy must include temperature-controlled handling.

    (*) If items are specified instead of parcels in an Availability- or OrderRequest, the weightGrams property must be specified as >= 0. It can be omitted if a list of parcels is specified in the requests additionally.

    BarCode

    Parameter Required Type Default Description
    value yes String none Value encoded in the barcode. Represented as string to support different barcode formats.
    type yes String none Barcode type, e.g. Code128, UPC, ...

    Response

    Parameter Type Description
    orderId String The id of the created order
    _links OrderResponseLinks Links for this resource.
    Parameter Type Description
    self HrefLink Link to the created order.
    labelInfo HrefLink Link to information about the labels for this order. WARNING: The contents from this URL should not be stored. The addresses have limited existense because of security concerns.
    status HrefLink Link to fetch the status of the order. Returns a Order status on a GET request.
    userInformation HrefLink Address of a wep page that displays the status of the order. This link is intended for the use of the end user and is sensitive as it also gives access to modify some elements of the order.

    Order status

    Parameter Type Description
    orderId String The id of the order.
    orderStatus String Examples: [received, delivered, cancelled] The status of the order. This list is non exhaustive so please take care to display/handle any new statuses that is introduced.
    pickupDate Date The date when the order parcels will be picked up by Porterbuddy. Depending on the delivery area, the parcels may be picked up one or more days before the delivery date.
    statusUpdatedAt DateTime The timestamp of the last order status change, as ISO timestamp with time zone.
    orderReference String order reference as specified in order request / shipment details update
    Parameter Type Description
    href String Link for the given role.

    Update shipment information

    This update is supposed to be used whenever you need to update the packages and shipment information after placing the order. Note that the packages list needs to be complete, so any content here removes the information from the previous order.

    PUT https://api.porterbuddy.com/order/<orderId>/update-shipment-details

    Example request:

    curl -X PUT \
      https://api.porterbuddy-test.com/order/<orderId>/update-shipment-details \
      -H 'x-api-key: <your_api_key>' \
      -H 'Content-Type: application/json' \
      -H 'Idempotency-Key: <your idempotency key>' \
      -d '{
            "parcels" : [
                {
                    "description": "Shoes",
                    "widthCm": 1,
                    "heightCm": 40,
                    "depthCm": 1,
                    "weightGrams": 2000,
                    "parcelShipmentIdentifier": "12345"
                }
            ],
            "shipmentIdentifier": "12345",
            "orderReference": "order-#12345678"
        }'
    

    Example response:

    {
      "_links": {
        "self": {
            "href": "https://api.porterbuddy-test.com/order/3520"
        },
        "labelInfo": {
            "href": "https://api.porterbuddy-test.com/order/3520/label"
        }
      }
    }
    

    Arguments

    Parameter Required Type Default Description
    parcels Yes List<Parcel> none Information about the parcels.
    shipmentIdentifier No Digits[5-20] none Shipping identifier.
    orderReference No String none Your internal order reference. If this parameter is not set, the current order reference will not be removed.

    Response

    Parameter Type Description
    _links OrderResponseLinks Links for this resource.

    Fetch order status

    The order status can be retrieved using the URL returned from the Create Order call. The order status contains the pickup date, which is updated in case the recipient changes the delivery day, so it should be fetched regularly.

    GET https://api.porterbuddy.com/order/<orderId>/status

    Example Request

    curl -X GET \
      https://api.porterbuddy-test.com/order/<orderId>/status \
      -H 'x-api-key: <your_api_key>' \
      -H 'Accept: application/json'
    

    Example Response

    {
      "orderId": "3520",
      "orderStatus": "ready",
      "pickupDate": "2023-02-13",
      "statusUpdatedAt": "2021-03-25T10:30:35.742857Z",
      "orderReference": "order-#12345678"
    }
    

    Response

    see Order Status

    Confirmed Parcel Scan

    Note: We provide a solution to scan outgoing parcels on our partner portal. This endpoint is only intended to be used when integrating scanning of outgoing parcels into an own solution.

    This endpoint confirms that the parcels with the specified parcel shipment identifiers belonging to the specified order have been scanned and are confirmed to be sent. The list of parcel shipment identifiers is matched agains the parcel shipment identifiers specified in the parcel data of the order. In case unknown shipment identifiers are specified, the response contains a list of all unknown identifiers, and the HTTP Response code is 422.

    PUT https://api.porterbuddy.com/order/<orderId>/confirmed-scan

    Example Request

      https://api.porterbuddy-test.com/order/<orderId>/confirmed-scan \
      -H 'x-api-key: <your_api_key>' \
      -H 'Content-Type: application/json' \
      -H 'Idempotency-Key: <your idempotency key>' \
      -d '{
             "parcelShipmentIds": ["12345", "67890"]
          }
    

    Example Response

      {
        "unknownIds": []
      }
    

    Arguments

    Parameter Required Type Default Description
    parcelShipmentIds Yes List<String> none parcel shipment identifiers to confirm to have been scanned

    Place a consolidated order

    Example request:

    curl -X POST \
      https://api.porterbuddy-test.com/order \
      -H 'x-api-key: <your_api_key>' \
      -H 'Content-Type: application/json' \
      -H 'Idempotency-Key: <your idempotency key>' \
      -d '{
             "origin": {
                "name": "Nils Johansen (Sender)",
                "address": {
                    "streetName": "Keysers Gate",
                    "streetNumber": "3",
                    "postalCode": "0165",
                    "city": "Oslo",
                    "country": "Norway"
                },
                "email": "testemail+sender@porterbuddy.com",
                "phoneCountryCode": "+47",
                "phoneNumber": "65127865"
            },
            "destination": {
                "name": "Roger Olsen (Recipient)",
                "address": {
                    "streetName": "Høyenhallveien",
                    "streetNumber": "25",
                    "postalCode": "0678",
                    "city": "Oslo",
                    "country": "Norway"
                },
                "email": "testemail+recipient@porterbuddy.com",
                "phoneCountryCode": "+47",
                "phoneNumber": "65789832",
                "consolidatedWindow":{
                    "token": "20hRsgz8AovrLjeOldJ2Wg==:yhr85il4/swdgiEP/DG2wg==:2hBoFcmyTNLp/CTfX3sTGslOJr9sXAMxHggqq/h6tGmUuCEB2Vfy8uyNIWfg3qf6d7nj84Aj2sbwMLK2hETe14L4qgnlZHVSkBcktYPc6VCp9vEZhXErpQS3HoSyRU+mVcF2SNGP4s5TI5x7S6oq4Q=="
                },
                "verifications": {
                    "minimumAgeCheck": 16,
                    "leaveAtDoorstep": false,
                    "idCheck": true,
                    "requireSignature": false,
                    "onlyToRecipient": true
                }
            },
            "parcels" : [
                {
                    "description": "Shoes",
                    "widthCm": 1,
                    "heightCm": 40,
                    "depthCm": 1,
                    "weightGrams": 2000,
                    "parcelShipmentIdentifier": "12345"
                }
            ],
            "product": "delivery",
            "courierInstructions": "Test",
            "shipmentIdentifier": "12345"
        }'
    

    Consolidated orders (the "Samlevert" product) are created via the same request as regular orders, by using the token from the consolidated window returned in the availability call to fill the data for the "consolidatedWindow" property in the create order request. This should be done if the customer has chosen the consolidated delivery option in the checkout. The data model for request and response is explained in the section Place Order, so this section only contains the different example request, the response has the same structure as for the non-consolidated case.

    Note: The example request will not work as is, as to create an actually consolidated order, several conditions have to be met:

    If the consolidated window token has expired or is invalid, the order is created using the "best-available" approach.

    POST https://api.porterbuddy.com/order

    Fetching Order Data

    Data for an existing order can be fetched from the endpoint /order/<orderId>/self. This endpoint will return a basic data set for placed orders that also includes links to the label endpoint and customer tracking page. The API for this endpoint can be considered stable, so it's preferable to use this endpoint instead of /order/<orderId> to retrieve order information.

    Example Request

     curl -X GET \
      https://api.porterbuddy-test.com/order/<orderId>/self \
      -H 'x-api-key: <your_api_key>' \
      -H 'Accept: application/json'
    

    Example Response

    {
      "id": "13218",
      "created": "2021-05-31T07:13:41.525648Z",
      "deliverFrom": "2021-06-10T10:00:00Z",
      "deliverUntil": "2021-06-10T14:00:00Z",
      "pickupAddress": {
        "streetName": "Keysers Gate",
        "streetNumber": "3",
        "postalCode": "0165",
        "city": "Oslo",
        "country": "Norway",
        "location": {
          "latitude": 59.91698083192638,
          "longitude": 10.743384544175152
        },
        "addressType": "NORMAL",
        "addressValidity": "VALID",
        "fullAddressString": "Keysers Gate 3, 0165 Oslo, Norway"
      },
      "destinationAddress": {
        "streetName": "Høyenhallveien",
        "streetNumber": "25",
        "postalCode": "0678",
        "city": "Oslo",
        "country": "Norway",
        "location": {
          "latitude": 59.90325905025783,
          "longitude": 10.81379348938312
        },
        "addressType": "NORMAL",
        "addressValidity": "VALID",
        "fullAddressString": "Høyenhallveien 25, 0678 Oslo, Norway"
      },
      "pickupRegion": "OSLO",
      "destinationRegion": "OSLO",
      "recipientName": "Roger Olsen (Recipient)",
      "recipientPhoneNumber": "65789832",
      "recipientPhoneCountryCode": "+47",
      "recipientEmail": "testemail+recipient_ves4ab9qjc@porterbuddy.com",
      "pickupName": "Nils Johansen (Sender)",
      "pickupPhoneNumber": "65127865",
      "pickupPhoneCountryCode": "+47",
      "pickupEmail": "testemail+sender@porterbuddy.com",
      "pickupPin": "02766",
      "deliveryType": "SCHEDULED",
      "status": "ready",
      "xxl": false,
      "token": "1VgOBLIHOQSNNxaiS6AaVt",
      "parcels": [{
        "description": "Parcel description",
        "widthCm": 1,
        "heightCm": 40,
        "depthCm": 1,
        "weightGrams": 2000,
        "volumeCm3": null,
        "parcelShipmentIdentifier": null,
        "parcelId": "49883d8a-4319-4f7d-a081-132e0a7d1233",
        "locationToken": null,
        "originAddress": null,
        "constraints": null,
        "volume": 40
      }],
      "verifications": {
        "minimumAgeCheck": 16,
        "leaveAtDoorstep": false,
        "idCheck": true,
        "requireSignature": false,
        "onlyToRecipient": true
      },
      "orderReference": "c46e068c-a3bb-4429-ac12-f81ee3ce9e86",
      "statusUpdatedAt": "2021-05-31T07:13:41.525648Z",
      "pickupDate": "2021-06-10",
      "pickedAt": null,
      "scannedAtPartner": null,
      "scannedAtHub": null,
      "deliveredAt": null,
      "links": {
        "labelInfo": "https://api.porterbuddy.com/order/13218/label",
        "userInformation": "https://tracking.porterbuddy.com/74nBxcSLzQI0FIjJuZZHRQ"
      }
    }
    

    Response

    Parameter Type Description
    id String The Porterbuddy order id
    created DateTime Order creation data
    deliverFrom DateTime Start of the delivery window
    deliverUntil DateTime End of the delivery window
    pickupAddress Resolved Address Specified origin address for the order, usually the retailer's warehouse address
    destinationAddress Resolved Address Destination address for the order (recipient address)
    pickupRegion String Region the order will be picked up (e.g. "OSLO", "BERGEN", ...). The list is non-exhaustive, so make sure to handle new values properly.
    destinationRegion String Region the order will be delivered to
    recipientName String Name of the recipient
    recipientPhoneNumber String Recipient's phone number
    recipientPhoneCountryCode String Country code for the recipient's phone number
    recipientEmail String The recipient's email address
    pickupName String Pickup name (the sender name specified in the order create request and printed on the label)
    pickupPhoneNumber String Sender's phone number
    pickupPhoneCountryCode String Country code for the sender's phone number
    pickupEmail String Sender email as specified in the order create request - used for sender receipts if enabled
    pickupPin String 5-digit pin code from the parcel label
    deliveryType String Type of the delivery - currently always "SCHEDULED"
    status String Current order status, e.g. [received, delivered, cancelled]. The list is non-exhaustive, so make sure to handle new values properly.
    xxl Boolean Indicates that the order is oversized/overweight
    token String Order token
    parcels List<Parcel> Current parcel specification
    verifications Verifications Verifications to perform on delivery
    orderReference String External order reference as specified
    statusUpdatedAt DateTime Timestamp of the last status update
    pickupDate Date Calculated pickup date, based on the specified delivery date
    pickedAt DateTime Timestamp the order was picked up by the courier
    scannedAtPartner DateTime Timestamp the order was scanned by the partner (retailer)
    scannedAtHub DateTime Timestamp of the first time the order was scanned at a Porterbuddy hub
    deliveredAt DateTime Timestamp the order was delivered at
    links OrderInfoLinks Links for customer tracking page and label endpoint
    Parameter Type Description
    labelInfo URL Link to the label info endpoint
    userInformation URL Link to the recipient tracking page

    Order Status Webhook

    Example Order Request

    {
          "origin": {
            "name": "Nils Johansen (Sender)",
            "address": {
              "streetName": "Keysers Gate",
              "streetNumber": "3",
              "postalCode": "0165",
              "city": "Oslo",
              "country": "Norway"
            },
            "email": "testemail+sender@porterbuddy.com",
            "phoneCountryCode": "+47",
            "phoneNumber": "65127865",
            "pickupWindows":[{"start":"2023-02-13T10:00+02:00", "end":"2023-02-13T20:00+02:00"}]
          },
          "destination": {
            "name": "Roger Olsen (Recipient)",
            "address": {
              "streetName": "Høyenhallveien",
              "streetNumber": "25",
              "postalCode": "0678",
              "city": "Oslo",
              "country": "Norway"
            },
            "email": "testemail+recipient@porterbuddy.com",
            "phoneCountryCode": "+47",
            "phoneNumber": "65789832",
            "deliveryWindow":{
              "start": "2023-02-13T17:30:00+01:00",
              "end": "2023-02-13T19:30:00+01:00",
              "token": "20hRsgz8AovrLjeOldJ2Wg==:yhr85il4/swdgiEP/DG2wg==:2hBoFcmyTNLp/CTfX3sTGslOJr9sXAMxHggqq/h6tGmUuCEB2Vfy8uyNIWfg3qf6d7nj84Aj2sbwMLK2hETe14L4qgnlZHVSkBcktYPc6VCp9vEZhXErpQS3HoSyRU+mVcF2SNGP4s5TI5x7S6oq4Q=="
            },
            "verifications": {
              "minimumAgeCheck": 16,
              "leaveAtDoorstep": false,
              "idCheck": true,
              "requireSignature": false,
              "onlyToRecipient": true
            }
          },
          "parcels" : [
            {
              "description": "Shoes",
              "widthCm": 20,
              "heightCm": 10,
              "depthCm": 35,
              "weightGrams": 600,
              "parcelShipmentIdentifier": "12345"
            }
          ],
          "items": [
            {
              "name":"Fancy Sneakers",
              "sku":"FANCYSNEAKER43",
              "weightGrams":"600",
              "widthCm":20,
              "heightCm":10,
              "depthCm":35,
              "description":"Fancy sneakers (red/blue) in size 43",
              "category":"shoes",
              "brand":"Fancy Footwear Corp.",
              "imageUrl":"https://awesomewebshop.com/images/5fd71d6f-b0be-4480-900f-f3d008a0bc62.png",
              "price": {
                "fractionalDenomination":"79900",
                "currency":"NOK"
              },
              "barCode": {
                "value":"123-456-789",
                "type":"CODE128"
              }
            },
          ],
          "product": "delivery",
          "courierInstructions": "Test",
          "orderReference": "order-12345",
          "statusWebhookUrl": "https://api.myshopbackend.com/statusUpdated"
    }
    

    To receive notifications when the order status or pickup date changes, a webhook URL can be specified in the order request as property statusWebhookUrl. This URL gets called with HTTP POST every time the status or pickup date changes, and contains the same body as the Order Status Endpoint. The target endpoint must be reachable from the internet without authentication and respond within 10 seconds. If a status outside the [200..299] range is returned, or the endpoint is unreachable, the call will be retried 2 times with a rising interval. The response body will be ignored by our backend.

    Webhook call body

    Example Body

    {
      "orderId": "394326",
      "orderStatus": "canceled",
      "pickupDate": "2023-02-13",
      "statusUpdatedAt": "2021-06-08T12:16:40.32538Z",
      "orderReference": "order-12345"
    }
    

    See Order Status

    Verifying Webhook Authenticity

    To verify that a call to the status webhook was originated by Porterbuddy, we add a header x-porterbuddy-hmac-SHA256 to the webhook calls. This header contains a base64-encoded HMAC Digest of the call body, using the sender's API key as encryption secret. Authenticity can be verified by calculating the HMAC digest of the request body on the recipient side, and comparing it to the header value.

    Errors

    Http errors

    Http response code Reason
    400 Invalid data The request did not pass correctly formatted JSON
    401 Unauthenticated Invalid or missing API key
    403 Forbidden Your api key is invalid or the x-api-key header is missing
    422 Invalid schema The JSON could not be parsed with valid structure and values
    500 Server error Unknown server error :(