Ecoparcel API (2.2.0)

Download OpenAPI specification:

Shipping order creation, tracking, and price estimation.

Changelog

The API follows Semantic Versioning: MAJOR.MINOR.PATCH. Backwards-incompatible changes bump MAJOR, additive (backwards-compatible) features bump MINOR, and bug fixes / non-breaking improvements bump PATCH.

2.2.0 — 2026-10-28

  • Shipments to and from countries outside the EU. A country is accepted when Ecoparcel ships from or to it; any other country returns 400 country_not_served (previously every non-EU country returned 400 "Only EU countries are allowed.").
  • New customs object on each order in POST /api/order/ and on PUT /api/order/{order_id}/. It is required when the shipment crosses the EU customs border and rejected otherwise: 400 customs_required or 400 customs_not_applicable. It carries the contents type, the incoterm, the shipper's and consignee's tax IDs and one line per declared item. Every rule violation in a request comes back in one 400 customs_invalid whose details list {code, path, message}. See Customs below.
  • Order responses now include customs_required, customs_status (not_required, missing or complete), incoterms_available and, once declared, customs. GET /api/shipping_price/ returns incoterms_available; GET /api/shipping_price/options/ returns customs_required and incoterms_available on each rate.
  • On customs shipments total_price includes the carrier's customs fee and, under DDP, the DDP administration fee and the prepaid import tax. All of them are charged with the order.
  • An order whose customs_status is missing is not paid. It stays TEMP with the warning Payment not allowed — customs declaration incomplete.
  • New GET /api/order/{order_id}/documents/ returns the customs invoice PDF of a confirmed customs order (?output=base64_pdf for JSON).
  • Each customs item takes optional EU product identifiers: merchant_product_id (your SKU), manufacturer_product_id and gtin (EAN, GTIN or ISBN, checked against its GS1 check digit). The first two are required on every item when goods are sold (contents_type: sale_of_goods) from outside the EU to a private consignee in the EU (product_id_required), as EU customs requires from 1 November 2026; gifts, samples and returns are exempt. Spaces and hyphens in gtin are ignored; a malformed gtin returns gtin_invalid.
  • Orders within the EU are unchanged. Send no customs; their responses read customs_required: false and customs_status: not_required.

2.1.0 — 2026-10-28

  • New optional addons array on each order object in POST /api/order/ and on PUT /api/order/{order_id}/. Values: additional_protection (Additional Parcel Protection: cover rises from the standard €100 per parcel up to the parcel's declared value, max €1,000 per parcel) and flexible_cancellation (Flexible Cancellation). Add-ons are priced from each parcel's declared value, included in total_price and charged with the order. An unknown or unavailable value, or a value sent twice, returns 400 with the error on addons. On PUT, omit addons to keep the current add-ons (re-priced when parcel values change), send [] to remove them all, or send a list to replace them. Order responses now include addons.
  • GET /api/shipping_price/options/ is now documented: one best-value rate per available transportation type for a route, sorted cheapest first, for showing live delivery options at checkout. Same parameters as GET /api/shipping_price/ without transportation_type.
  • Fix: order prices now follow the same rules as GET /api/shipping_price/. Private accounts are priced at the private rate and VAT is applied from the account's VAT rules on every order (previously every API order was priced at the business rate with 0% VAT). For the same route and parcel, an order's total_price now matches the quote's amount, plus any add-ons.

2.0.0 — 2026-08-19

  • Breaking: GET /api/tracking/{order_id}/ no longer returns tracking for a code alone. Authenticated callers get their own orders unchanged; every other lookup — unauthenticated, or an account asking about an order it did not place — must pass the order's collection or delivery postcode as ?postcode=. A request with no postcode returns 400 postcode_required naming the field to add; a wrong postcode returns 404 tracking_not_found. Both answers are identical for codes that do not exist, so neither can be used to discover valid codes. This matches the postcode gate the web tracker has always had, and stops order codes from being enumerable.
  • Rate limits now apply to authenticated tracking requests too (30/min per account), and repeated failed lookups return 429 too_many_invalid_lookups.

1.0.7 — 2026-05-07

  • Added event_code field to each tracking event in GET /api/tracking/{order_id}/. The code is a stable 4-digit identifier (see the Tracking Event Codes section below) that integrators can map to their own status taxonomy without parsing the localized event_description.

1.0.6 — 2026-05-02

  • Enforced minimum parcel weight of 1.00 kg on order create. Parcels with weight < 1.00 are bumped to 1.00 before pricing.
  • Added a per-order warning listing the affected Parcel IDs, e.g. Parcel weight below minimum (1 kg) — bumped to 1 kg for parcel(s): EPDQRZ01, EPDQRZ03.

1.0.5 — 2026-04-23

  • Shipping price calculation now applies user-specific VAT and business pricing.
  • The /api/shipping-price/ endpoint returns prices tailored to the authenticated account.

1.0.4 — 2026-04-12

  • Added optional label_format override on order create/update to force a specific label format (e.g. A4, A6) regardless of the carrier default.
  • /api/order/{order_id}/labels/ honours the per-order format override.

1.0.3 — 2026-03-27

  • New GET /api/order/ — list orders with filtering and pagination.
  • New GET /api/order/{order_id}/ — retrieve a single order.
  • New GET /api/order/{order_id}/labels/ — fetch shipping labels for an existing order.

1.0.2 — Initial public release

  • POST /api/order/ — create draft or pay-on-create orders.
  • PUT /api/order/{order_id}/ — update / confirm and pay drafts.
  • DELETE /api/order/{order_id}/ — delete draft orders.
  • GET /api/pudo/ — search PUDO points (lockers).
  • GET /api/tracking/ — public tracking endpoint.
  • JWT (Bearer) authentication via /api/access/ and /api/access/refresh/.

Authentication

The API uses Bearer (JWT) authentication.

You can find your API Token in Integrations.

Obtain a JWT token pair by exchanging your existing API Token at the /api/access/ endpoint.

Step 1 — Exchange your Token for a JWT

POST /api/access/
Authorization: Token your-api-token-here

Response:

{
  "access": "eyJhbGciOiJIUzI1NiIs...",
  "refresh": "BZssDasdfjJkd1NiIs..."
}

Step 2 — Use the Bearer token for all API requests

Authorization: Bearer eyJhbGciOiJIUzI1NiIs...

The access token is valid for 30 minutes.

Step 3 — Refresh when the access token expires

POST /api/access/refresh/
Content-Type: application/json

{"refresh": "BZssDasdfjJkd1NiIs..."}

Response:

{
  "access": "eyJhbGciOiJIUzI1NiIs...new..."
}

The refresh token is valid for 7 days and rotates on each use.

Examples

cURL:

# Get JWT tokens
curl -X POST https://www.ecoparcel.eu/api/access/ \
  -H "Authorization: Token abc123def456"

# Use Bearer token
curl -X POST https://www.ecoparcel.eu/api/order/ \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
  -H "Content-Type: application/json" \
  -d '{"orders": [...]}'

Python (requests):

import requests

# Exchange Token for JWT
token_response = requests.post(
    "https://www.ecoparcel.eu/api/access/",
    headers={"Authorization": "Token abc123def456"},
)
access_token = token_response.json()["access"]

# Use Bearer token
response = requests.post(
    "https://www.ecoparcel.eu/api/order/",
    headers={"Authorization": f"Bearer {access_token}"},
    json={"orders": [...]},
)

JavaScript (fetch):

// Exchange Token for JWT
const tokenRes = await fetch("https://www.ecoparcel.eu/api/access/", {
  method: "POST",
  headers: { "Authorization": "Token abc123def456" },
});
const { access } = await tokenRes.json();

// Use Bearer token
const res = await fetch("https://www.ecoparcel.eu/api/order/", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${access}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ orders: [...] }),
});

Public endpoints

The Tracking endpoint is publicly accessible without authentication. Anonymous requests are rate-limited to 10 requests per minute.

Rate Limiting

Client Type Limit
Anonymous 10 requests/min
Authenticated 30 requests/min

Order Flow

Orders go through a simple lifecycle. Understanding the statuses and the available actions at each stage will help you integrate smoothly.

Order Statuses

Status Meaning
TEMP Draft order — not yet paid or submitted to a carrier. Can be edited, confirmed, or deleted.
NEW Paid and queued for processing. The carrier will receive the shipment shortly.
IN_PROGRESS Handed over to the carrier. Tracking events will start appearing.
DELIVERED Successfully delivered to the recipient.
CANCELLED Cancelled due to collection failure or request.
REFUNDED Cancelled and refunded.

Typical Flows

1. Quick order (create + pay in one call)

The simplest integration — create an order and pay for it in a single request.

POST /api/order/
{ "orders": [...], "confirm_and_pay": true }

The order is created, priced, paid, and submitted to the carrier immediately. The response includes a base64-encoded shipping label PDF in the labels field.

Recommended for: production integrations where the sender and receiver addresses are known upfront and no PUDO selection is needed (door-to-door shipments).

2. Two-step order (create draft, then confirm)

Create the order first as a draft, then confirm and pay separately. Useful when you need to review pricing or make adjustments before committing.

Step 1 — Create a draft order:

POST /api/order/
{ "orders": [...], "confirm_and_pay": false }

The order is saved with status TEMP. No charge is made.

Step 2 — Confirm and pay:

PUT /api/order/{order_id}/
{ "confirm_and_pay": true }

The order is paid and submitted. Labels are returned in the response.

Recommended for: testing, or when you want to inspect the calculated price before paying.

3. PUDO point selection flow

When using locker-based transportation types (door_to_locker, locker_to_door, locker_to_locker) and you want the user to choose a specific locker, follow this flow:

Step 1 — Create a draft order without a PUDO point:

POST /api/order/
{
  "orders": [{
    ...,
    "transportation_type": "door_to_locker"
  }],
  "confirm_and_pay": false
}

The system auto-selects the nearest locker. The order is saved as TEMP.

Step 2 — Search for nearby PUDO points:

GET /api/pudo/?order_id={order_id}&for_consignee=true

Returns a list of available lockers near the consignee address, filtered by the carrier assigned to the order.

Step 3 — Update the order with the chosen PUDO and confirm:

PUT /api/order/{order_id}/
{
  "consignee_details": { "pudo_point": "87" },
  "confirm_and_pay": true
}

Note: You can also provide pudo_point directly in the POST /api/order/ call if you already know the PUDO ID, skipping the search step entirely.

4. Editing a draft order

Any TEMP order can be updated — change addresses, parcel dimensions, transportation type, or anything else.

PUT /api/order/{order_id}/
{
  "consignee_details": { "address": "New Street 5", "city": "Tallinn", "postcode": "10115" },
  "parcels": [{ "description": "Updated", "width": 25, "height": 15, "length": 35, "weight": 3, "value": 80 }]
}

The service card, shipping rate, VAT and add-ons are recalculated into total_price on every update. Only provided fields are changed — omitted fields keep their current values.

5. Cancelling an order

Draft orders (TEMP) can be deleted outright:

DELETE /api/order/{order_id}/

Orders that have already been paid (NEW, IN_PROGRESS) cannot be cancelled via the API. Use the Ecoparcel dashboard or contact support.

Payment

When confirm_and_pay is set to true (either on create or update), the system charges the order's total_price to your account in this order:

  1. Account balance is used first.
  2. If the balance is insufficient, your saved payment card is charged for the remaining amount.

If neither balance nor card can cover the cost, the order stays in TEMP status with a warning in the response.

total_price is the shipping rate for your account (private or business) with VAT applied from your account's VAT rules, plus any add-ons and, on customs shipments, the customs fees (see Customs). For the same route and parcel it matches the amount returned by GET /api/shipping_price/, plus add-ons.

Add-ons

Each order object accepts an optional addons array. additional_protection (Additional Parcel Protection) raises the cover from the standard €100 per parcel up to the parcel's declared value, max €1,000 per parcel; flexible_cancellation adds Flexible Cancellation. Add-ons are priced from each parcel's declared value, included in the order's total_price and charged with it. On PUT /api/order/{order_id}/, omit addons to keep the current add-ons (they are re-priced if parcel values change), send [] to remove them all, or send a list to replace them. An unknown or unavailable value, or a value sent twice, returns 400 with the error on addons. Order responses include addons.

POST /api/order/
{ "orders": [{ ..., "addons": ["additional_protection"] }], "confirm_and_pay": false }

Shipping Prices

GET /api/shipping_price/ returns the cheapest rate for a route, parcel size and transportation type. GET /api/shipping_price/options/ returns one best-value rate per available transportation type, sorted cheapest first, and is the call to make when a checkout needs to show every delivery option. Both require authentication (JWT Bearer, OAuth2 or Authorization: Token <api_token>) and are priced for your account: business accounts get business rates and VAT follows the account's VAT rules (verified EU businesses usually get 0%). Every rate carries transportation_amount (net), vat_amount and amount (total) in EUR plus delivery_time in hours. If you apply your own VAT, start from transportation_amount.

The quote is country level. The order is priced with both postcodes: some areas are priced by postcode and some are not served, in which case the order is saved as TEMP with the warning Service is not available for this route, dimensions, transportation type or package type. The draft order's total_price is the amount that is charged; it includes VAT per your account's rules and any add-ons, which the quote does not. Create a draft (confirm_and_pay: false) when you need the exact price before paying.

Customs

A shipment crosses the EU customs border when its two countries differ and at least one of them is outside the EU, for example HR to GB or GB to DE. Such an order needs a customs object; on any other order customs is rejected with 400 customs_not_applicable. customs_required on GET /api/shipping_price/options/ and on every order tells you which case applies, and incoterms_available lists the incoterms the service offers.

POST /api/order/
{
  "orders": [{
    "shipper_details": {"country": "HR", "company": "Adria Textiles d.o.o.", "contact_name": "Ivana Horvat", "address": "Ilica 10", "city": "Zagreb", "postcode": "10000", "phone": "+385991234567", "email_address": "sender@example.com"},
    "consignee_details": {"country": "GB", "contact_name": "Jane Receiver", "address": "10 Downing Street", "city": "London", "postcode": "SW1A 2AA", "phone": "+447911123456", "email_address": "receiver@example.com"},
    "parcels": [{"description": "T-shirts", "width": 30, "height": 10, "length": 40, "weight": 2, "value": 25}],
    "package_type": "parcel",
    "transportation_type": "door_to_door",
    "customs": {
      "contents_type": "sale_of_goods",
      "incoterm": "dap",
      "shipper": {"is_company": true, "tax_ids": [{"type": "eori", "value": "HR12345678901"}]},
      "consignee": {"is_company": false, "tax_ids": []},
      "items": [{"parcel_index": 0, "description": "Cotton T-shirt", "hs_code": "61091000", "quantity": 2, "unit_value": "12.50", "net_weight": "0.400", "origin_country": "HR", "merchant_product_id": "TS-COT-M", "manufacturer_product_id": "B-5000-M", "gtin": "4006381333931"}]
    }
  }],
  "confirm_and_pay": false
}

Customs Fields

Field Description
contents_type Reason for export: sale_of_goods, gift, commercial_sample, returned_goods, personal_effects, temporary_export, not_sold or intra_company_transfer
incoterm dap (the consignee pays import taxes and duties on delivery) or ddp (the shipper prepays them with the order). Must be one of the order's incoterms_available
shipper, consignee {is_company, tax_ids}. is_company defaults to true when the address has a company. Omit the shipper's tax_ids to use the VAT, EORI and IOSS numbers saved on your account that are valid on this lane; send [] for none. The consignee's tax_ids default to none
tax_ids[] {type, value} with type one of eori, vat, ioss, voec, personal_id. Spaces, dots and dashes are removed, letters upper-cased and VAT numbers get their country prefix; the prefix tells a VAT number's country
items[] One line per kind of goods; every parcel needs at least one
items[].parcel_index Zero-based position in parcels of the parcel holding the item
items[].description What the goods are, precisely: "Cotton T-shirt", not "Clothing" or "Gift"
items[].hs_code Harmonised System tariff code, 6, 8 or 10 digits, checked against the EU Combined Nomenclature
items[].quantity Number of units, at least 1
items[].unit_value Value of one unit in EUR
items[].net_weight Optional. Net weight of the whole line in kg
items[].origin_country Optional. ISO 3166-1 alpha-2 country the goods were made in; defaults to the shipper's country
items[].merchant_product_id Optional, up to 64 characters. Your own product ID, usually the SKU (EU customs data element C127)
items[].manufacturer_product_id Optional, up to 64 characters. The manufacturer's product or model number (C128)
items[].gtin Optional. EAN, GTIN or ISBN-13 (C129): 8, 12, 13 or 14 digits with a valid GS1 check digit. Leave it out when the goods have none; customs then records that no standard identifier exists

Customs Rules

  • An EU business shipper needs an eori. A business consignee in the EU or GB needs an eori. Private persons need no tax ID.
  • ioss is the shipper's and only applies to a private consignee in the EU with goods worth up to €150 in total. A GB vat of a shipper outside GB is its UK VAT registration and only applies to GB consignees, voec only to Norway consignees. personal_id belongs to the consignee only.
  • Per parcel, the sum of quantity × unit_value must match the parcel's value within 1% (at least €0.05), and the items' net_weight may not add up to more than the parcel's weight.
  • Descriptions that name no goods are rejected: a single generic word such as "Gift", "Parts", "Goods" or "Electronics", numbers only, or one character repeated.
  • From 1 November 2026, Commission Implementing Regulation (EU) 2026/1200 requires merchant_product_id and manufacturer_product_id on every item of a shipment from outside the EU to a private consignee in the EU, at any value. A gtin is checked whenever it is sent.
  • EU EORI numbers are checked live against the EU EORI registry and EU VAT numbers against VIES. An EORI the registry does not know returns tax_id_invalid. A number that cannot be checked, because the registry is down or the number is not an EU number, never blocks the order: the order's warnings carry The shipper eori number HR12345678901 could not be verified.

Customs Fees and Payment

The carrier's customs fee is added to every customs order. Under ddp the order also carries the DDP administration fee and the prepaid import tax: the sum of quantity × unit_value over all items times the destination country's VAT rate. These lines are part of total_price and are charged with the order.

An order is only paid once its declaration is complete. While customs_status is missing, confirm_and_pay: true leaves the order TEMP with the warning Payment not allowed — customs declaration incomplete.

Order responses carry:

Field Description
customs_required true when the shipment crosses the EU customs border
customs_status not_required, missing (declaration incomplete, payment is blocked) or complete
incoterms_available Incoterms the selected service offers; empty when no service matched
customs The stored declaration in request shape, with normalized tax IDs; omitted when nothing is declared

Customs Errors

Missing or misplaced declarations fail the whole request with a named code: 400 customs_required (no customs, or customs: null, on a customs shipment), 400 customs_not_applicable (customs on a shipment that needs none) and 400 country_not_served (a country Ecoparcel does not ship from or to). Rule violations come back together in one 400 customs_invalid; each entry's path points at the field, starting with orders.<index>.customs. on POST and customs. on PUT.

{
  "error": {
    "code": "customs_invalid",
    "message": "Customs declaration invalid.",
    "details": [
      {"code": "incoterm_not_available", "path": "orders.0.customs.incoterm", "message": "This incoterm is not offered for the selected service."},
      {"code": "hs_code_unknown", "path": "orders.0.customs.items.0.hs_code", "message": "HS code is not in the tariff nomenclature."}
    ]
  }
}
Code Path Meaning
contents_type_invalid contents_type Unknown contents type
incoterm_not_available incoterm The selected service does not offer this incoterm
tax_id_required shipper.tax_ids, consignee.tax_ids A required EORI is missing
tax_id_format_invalid <party>.tax_ids.<n>.value The number does not have the shape of its type
tax_id_invalid <party>.tax_ids.<n>.value The EU EORI registry does not know the number
tax_id_not_allowed <party>.tax_ids.<n>.type This party cannot use this tax ID on this shipment
parcel_index_invalid items.<n>.parcel_index No parcel at that position
description_vague items.<n>.description The description names no goods
hs_code_unknown items.<n>.hs_code Not a 6, 8 or 10 digit code in the tariff nomenclature
item_value_invalid items.<n>.quantity, .unit_value, .net_weight Quantity below 1, or a value or weight that is not above 0
items_total_mismatch items A parcel's items do not add up to its value
net_weight_exceeds_parcel items A parcel's items weigh more than the parcel
gtin_invalid items.<n>.gtin Not 8, 12, 13 or 14 digits, or the GS1 check digit is wrong
product_id_required items.<n>.merchant_product_id, .manufacturer_product_id Missing on goods sold into the EU to a private consignee

A malformed field, such as an unknown tax ID type or origin_country, returns the usual 400 validation_error with the field in details.

Customs on Order Updates

On PUT /api/order/{order_id}/, omit customs to keep the stored declaration. It is validated again against the updated parcels and service, so changing a parcel's value without its items returns items_total_mismatch, and removing a parcel that items point to returns parcel_index_invalid. Send customs to replace the declaration. customs: null on a customs shipment returns 400 customs_required. When an update moves the shipment inside the EU, the stored declaration and its fees are dropped.

Customs Documents

GET /api/order/{order_id}/documents/ returns the customs invoice of a confirmed customs order as a PDF (customs-{order_id}.pdf). With ?output=base64_pdf it returns {"customs_invoice": "<base64>"}. Orders that are TEMP, cancelled or refunded, or that need no customs, return 404 document_not_available.

Transportation Types

Value Description
door_to_door Door to Door
door_to_locker Door to Locker
locker_to_door Locker to Door
locker_to_locker Locker to Locker

PUDO points (lockers)

For locker-based transportation types, a pudo_point field can be included inside shipper_details and/or consignee_details to specify an exact drop-off/pick-up locker (by ID).

Transportation Type shipper_details.pudo_point consignee_details.pudo_point
door_to_door Not allowed Not allowed
door_to_locker Not allowed Optional (auto-selected if omitted)
locker_to_door Optional (auto-selected if omitted) Not allowed
locker_to_locker Optional (auto-selected if omitted) Optional (auto-selected if omitted)

When pudo_point is omitted for a locker side, the system automatically selects the nearest locker based on the provided address.

Important: sending pudo_point for a side that does not require a locker (e.g. shipper side on door_to_locker) will return a 400 validation error.

Package Types

Value Description
parcel Standard parcel shipment
pallet Pallet shipment

Tracking Event Codes

Each entry in tracking_events[] returned by GET /api/tracking/{order_id}/ includes a stable 4-digit event_code. Use the code (not the human-readable event_description) to drive your own logic — descriptions are localized and may change wording, but codes are stable.

Code Description
0002 Prepared for collection
0101 Out for collection
0102 Collection successful (includes parcel locker / shop drop-off by shipper)
0103 Collection problem
0105 Collection problem — Wrong address
0107 Collection cancelled — Wrong address
0108 Collection cancelled — Shipper was absent
0109 Cancelled
0110 Collection cancelled — Insufficient customs declaration
0111 Refunded to the balance
0112 Contacted
0201 Inbound
0202 Outbound
0204 Return
0205 Non-conveyable
0301 Out for delivery
0302 Delivery successful — Consignee
0303 Delivery successful — Parcelshop / Locker (consignee-side)
0304 Delivery successful — Post Office
0305 Delivery successful — Neighbour
0306 Delivery problem — Consignee was absent
0307 Delivery problem — Wrong address
0308 Delivery problem — Consignee refused
0309 Delivery problem — Not out for delivery
0310 Delivery problem
0311 Delivery problem — Consignee refused
0501 Parcel stored in warehouse
0502 Additional information
0503 Customs
0505 Changed collection date

Failure / problem codes

The following subset indicates the parcel hit a problem state and may need attention (failed collection, failed delivery, return):

0103, 0105, 0107, 0108, 0204, 0306, 0307, 0308, 0309, 0310

Orders

List orders

Returns a paginated list of your orders, newest first. Supports limit / offset pagination and optional status filter.

Authorizations:
jwtAuth
query Parameters
limit
integer

Number of results to return (default 100).

offset
integer

Index of the first result to return.

status
string
Enum: "CANCELED" "COMPLETED" "IN_PROGRESS" "IN_QUEUE" "NEW" "PARTIALLY_COMPLETED" "REFUNDED" "REJECTED" "TEMP"

Filter by order status.

Responses

Response samples

Content type
application/json
Example
{
  • "count": 123,
  • "results": [
    ]
}

Create order(s)

Creates one or more shipment orders. Optionally confirms and initiates payment.

Authorizations:
jwtAuth
Request Body schema:
required
required
Array of objects (SingleOrder)
confirm_and_pay
required
boolean
(LabelFormatEnum (string or null)) or (NullEnum (any or null))

Override label format (A4, A5, A6). If omitted, user preference is used.

  • A4 - A4
  • A5 - A5
  • A6 - A6

Responses

Request samples

Content type
Example
{
  • "orders": [
    ],
  • "confirm_and_pay": true,
  • "label_format": "A6"
}

Response samples

Content type
application/json
Example

total_price includes the partner customs fee; DDP adds the DDP administration fee and the prepaid import tax.

{
  • "orders": [
    ],
  • "labels": null
}

Get order details

Returns the details of a single order by its ID.

Authorizations:
jwtAuth
path Parameters
order_id
required
string

Order ID.

Responses

Response samples

Content type
application/json
{
  • "id": "KR4PZK",
  • "parcels": [
    ],
  • "collection_date": "2026-03-10",
  • "total_price": "7.78",
  • "paid": true,
  • "status": "NEW",
  • "customs_required": false,
  • "customs_status": "not_required",
  • "incoterms_available": [
    ],
  • "customer_reference": "REF-001",
  • "created_at": "2026-03-09T14:23:11Z"
}

Update a TEMP order

Updates an existing TEMP order. All fields are optional — only provided fields are changed. The full pricing pipeline always runs: service card and price are recalculated.

Authorizations:
jwtAuth
path Parameters
order_id
required
string

Order ID.

Request Body schema:
object (PartialOrderDetails)

OrderDetailsSerializer with all fields optional — for partial updates.

object (PartialOrderDetails)

OrderDetailsSerializer with all fields optional — for partial updates.

Array of objects (Parcel)
package_type
string (PackageTypeEnum)
Enum: "parcel" "pallet" "truck"
  • parcel - parcel
  • pallet - pallet
  • truck - truck
transportation_type
string (TransportationTypeEnum)
Enum: "door_to_locker" "door_to_door" "locker_to_locker" "locker_to_door"
  • door_to_locker - door_to_locker
  • door_to_door - door_to_door
  • locker_to_locker - locker_to_locker
  • locker_to_door - locker_to_door
customer_reference
string <= 32 characters
addons
Array of strings (AddonsEnum)
Items Enum: "additional_protection" "flexible_cancellation"

Order add-ons to buy, by enum value: additional_protection (Additional Parcel Protection), flexible_cancellation (Flexible Cancellation). Each add-on is priced from the declared value of every parcel; additional_protection covers each parcel up to the product's maximum insured amount. Omit to keep the current add-ons (re-priced), send [] to remove them.

object or null

Customs declaration. Required when the shipment crosses the EU customs border (one side outside the EU), rejected otherwise. Errors come back as one customs_invalid 400 whose details list {code, path, message}. Omit to keep the stored declaration (re-validated against the updated parcels; dropped when the new lane needs no customs).

confirm_and_pay
boolean
Default: false

Responses

Request samples

Content type
Example
{
  • "shipper_details": {
    },
  • "consignee_details": {
    }
}

Response samples

Content type
application/json
{
  • "id": "string",
  • "customer_reference": "string",
  • "parcels": [
    ],
  • "collection_date": "2019-08-24",
  • "total_price": "string",
  • "addons": [
    ],
  • "paid": false,
  • "status": "string",
  • "customs_required": true,
  • "customs_status": "not_required",
  • "incoterms_available": [
    ],
  • "customs": {
    },
  • "warnings": [
    ]
}

Delete a TEMP order

Deletes a TEMP order and its associated parcels. Only orders in TEMP status can be deleted.

Authorizations:
jwtAuth
path Parameters
order_id
required
string

Order ID.

Responses

Response samples

Content type
application/json
{
  • "error": {
    }
}

Download order customs documents

Returns the customs invoice (commercial invoice) of a confirmed order whose shipment crosses the EU customs border. Only available for orders that need customs and are not TEMP, CANCELED or REFUNDED.

Output parameter:

  • pdf (default) — binary PDF download (customs-{order_id}.pdf)
  • base64_pdf — JSON response with the invoice as a base64-encoded string
Authorizations:
jwtAuth
path Parameters
order_id
required
string

Order ID.

query Parameters
output
string
Enum: "base64_pdf" "pdf"

pdf (default) returns a binary PDF file. base64_pdf returns JSON with a base64-encoded PDF string.

Responses

Response samples

Content type
No sample

Download order label

Returns the shipping label for a confirmed order. Only available for orders that have been paid and processed (not TEMP, CANCELED, or REFUNDED).

Output parameter:

  • pdf (default) — binary PDF download
  • base64_pdf — JSON response with the label as a base64-encoded string

Label format parameter:

  • A4 — A4 page size
  • A5 — A5 page size
  • A6 — A6 page size
  • If omitted, the user's preferred label format from profile settings is used.
Authorizations:
jwtAuth
path Parameters
order_id
required
string

Order ID.

query Parameters
label_format
string
Enum: "A4" "A5" "A6"

Override the label format. If omitted, the user's preferred format is used.

output
string
Enum: "base64_pdf" "pdf"

pdf (default) returns a binary PDF file. base64_pdf returns JSON with a base64-encoded PDF string.

Responses

Response samples

Content type
No sample

PUDO

Search nearby PUDO points

Returns nearby PUDO points filtered by the partner assigned to an existing order. Provide order_id plus one of: for_shipper=true, for_consignee=true, or an explicit address/postcode/country.

Authorizations:
jwtAuth
query Parameters
address
string

Explicit address to geocode

country
string

ISO alpha-2 country code

distance_km
integer

Search radius in km (1-50, default 5)

for_consignee
boolean

Use order consignee address

for_shipper
boolean

Use order shipper address

limit
integer

Max results (1-30, default 10)

order_id
required
string

6-char order short code

postcode
string

Postcode for explicit address

Responses

Response samples

Content type
application/json
[
  • {
    }
]

Shipping Price

Get shipping price estimate

Returns the cheapest shipping price for a given route and parcel dimensions.

Authorizations:
jwtAuthtokenAuth
query Parameters
country_from
required
string

ISO 3166-1 alpha-2 sender country code

country_to
required
string

ISO 3166-1 alpha-2 receiver country code

height
number <double>
Default: 1

Height in cm

length
number <double>
Default: 1

Length in cm

package_type
string
Default: "parcel"

parcel or pallet

transportation_type
string
Default: "door_to_door"

door_to_door, door_to_locker, locker_to_door, locker_to_locker

weight
number <double>
Default: 1

Weight in kg

width
number <double>
Default: 1

Width in cm

Responses

Response samples

Content type
application/json
{
  • "is_business": false,
  • "country_from": "DE",
  • "country_to": "ES",
  • "transportation_amount": "18.54",
  • "vat_amount": "3.52",
  • "amount": "22.06",
  • "currency": "EUR",
  • "weight": "1.00",
  • "delivery_time": 72,
  • "transportation_type": "door_to_door",
  • "package_type": "parcel",
  • "incoterms_available": [
    ]
}

List shipping options for checkout

Returns one best-value rate per available transportation type (door, locker, ...), sorted cheapest-first.

Authorizations:
jwtAuthtokenAuth
query Parameters
country_from
required
string

ISO 3166-1 alpha-2 sender country code

country_to
required
string

ISO 3166-1 alpha-2 receiver country code

height
number <double>
Default: 1

Height in cm

length
number <double>
Default: 1

Length in cm

package_type
string
Default: "parcel"

parcel or pallet

weight
number <double>
Default: 1

Weight in kg

width
number <double>
Default: 1

Width in cm

Responses

Response samples

Content type
application/json
{
  • "is_business": true,
  • "country_from": "string",
  • "country_to": "string",
  • "currency": "string",
  • "customs_required": true,
  • "rates": [
    ]
}

Tracking

Get order tracking

Returns tracking information for an order by 6-char order code or 7-8 char parcel code. Authenticated callers get their own orders directly; every other caller must pass the postcode of the order's collection or delivery address, as the public web tracker requires.

Authorizations:
jwtAuthtokenAuthNone
path Parameters
order_id
required
string

6-char order code or 7-8 char parcel code

query Parameters
postcode
string

Collection or delivery postcode of the order. Required unless the order belongs to the authenticated account.

Responses

Response samples

Content type
application/json
{
  • "order_id": "KR4PZK",
  • "country_from": "DE",
  • "country_to": "EE",
  • "status": "NEW",
  • "estimated_collection_date": "2026-03-10",
  • "estimated_delivery_date": "2026-03-13",
  • "addons": [ ],
  • "parcels": [
    ]
}