Download OpenAPI specification:
Shipping order creation, tracking, and price estimation.
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.
400 country_not_served (previously every non-EU country returned 400 "Only EU countries are allowed.").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.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.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.customs_status is missing is not paid. It stays TEMP with the warning Payment not allowed — customs declaration incomplete.GET /api/order/{order_id}/documents/ returns the customs invoice PDF of a confirmed customs order (?output=base64_pdf for JSON).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.customs; their responses read customs_required: false and customs_status: not_required.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.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.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.429 too_many_invalid_lookups.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.00 kg on order create. Parcels with weight < 1.00 are bumped to 1.00 before pricing.Parcel weight below minimum (1 kg) — bumped to 1 kg for parcel(s): EPDQRZ01, EPDQRZ03./api/shipping-price/ endpoint returns prices tailored to the authenticated account.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.GET /api/order/ — list orders with filtering and pagination.GET /api/order/{order_id}/ — retrieve a single order.GET /api/order/{order_id}/labels/ — fetch shipping labels for an existing order.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./api/access/ and /api/access/refresh/.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.
POST /api/access/
Authorization: Token your-api-token-here
Response:
{
"access": "eyJhbGciOiJIUzI1NiIs...",
"refresh": "BZssDasdfjJkd1NiIs..."
}
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
The access token is valid for 30 minutes.
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.
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: [...] }),
});
The Tracking endpoint is publicly accessible without authentication. Anonymous requests are rate-limited to 10 requests per minute.
Orders go through a simple lifecycle. Understanding the statuses and the available actions at each stage will help you integrate smoothly.
| 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. |
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).
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.
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_pointdirectly in thePOST /api/order/call if you already know the PUDO ID, skipping the search step entirely.
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.
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.
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:
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.
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 }
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.
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
}
| 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 |
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.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.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.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.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 |
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.
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.
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.
| 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 |
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.
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 |
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
Returns a paginated list of your orders, newest first. Supports limit / offset pagination and optional status filter.
| 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. |
{- "count": 123,
- "results": [
- {
- "count": 42,
- "previous": null,
- "results": [
- {
- "id": "KR4PZK",
- "parcels": [
- {
- "description": "Electronics",
- "width": "30.00",
- "height": "20.00",
- "length": "40.00",
- "weight": "5.00",
- "value": "150.00"
}
], - "collection_date": "2026-03-10",
- "total_price": "7.78",
- "paid": true,
- "status": "NEW",
- "created_at": "2026-03-09T14:23:11Z"
}, - {
- "id": "7AKXY7",
- "parcels": [
- {
- "description": "Books",
- "width": "20.00",
- "height": "10.00",
- "length": "30.00",
- "weight": "3.00",
- "value": "25.00"
}
], - "collection_date": "2026-03-08",
- "total_price": "5.50",
- "paid": true,
- "status": "COMPLETED",
- "customer_reference": "REF-002",
- "created_at": "2026-03-07T09:15:00Z"
}
]
}
]
}Creates one or more shipment orders. Optionally confirms and initiates payment.
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.
|
{- "orders": [
- {
- "shipper_details": {
- "country": "LT",
- "contact_name": "John Sender",
- "address": "Gedimino pr. 1",
- "city": "Vilnius",
- "postcode": "01103",
- "phone": "+37060000000",
- "email_address": "sender@example.com"
}, - "consignee_details": {
- "country": "EE",
- "contact_name": "Jane Receiver",
- "address": "Narva mnt 10",
- "city": "Tallinn",
- "postcode": "10117",
- "phone": "+3725000001",
- "email_address": "receiver@example.com"
}, - "parcels": [
- {
- "description": "Electronics",
- "width": 30,
- "height": 20,
- "length": 40,
- "weight": 5,
- "value": 150
}
], - "package_type": "parcel",
- "transportation_type": "door_to_door",
- "customer_reference": "REF-001",
- "addons": [
- "additional_protection"
]
}
], - "confirm_and_pay": true,
- "label_format": "A6"
}total_price includes the partner customs fee; DDP adds the DDP administration fee and the prepaid import tax.
{- "orders": [
- {
- "id": "GB7XQK",
- "parcels": [
- {
- "description": "T-shirts",
- "width": "30.00",
- "height": "10.00",
- "length": "40.00",
- "weight": "2.00",
- "value": "25.00"
}
], - "collection_date": "2026-11-02",
- "total_price": "23.40",
- "paid": false,
- "status": "TEMP",
- "customs_required": true,
- "customs_status": "complete",
- "incoterms_available": [
- "dap"
], - "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"
}
]
}
}
], - "labels": null
}Returns the details of a single order by its ID.
| order_id required | string Order ID. |
{- "id": "KR4PZK",
- "parcels": [
- {
- "description": "Electronics",
- "width": "30.00",
- "height": "20.00",
- "length": "40.00",
- "weight": "5.00",
- "value": "150.00"
}
], - "collection_date": "2026-03-10",
- "total_price": "7.78",
- "paid": true,
- "status": "NEW",
- "customs_required": false,
- "customs_status": "not_required",
- "incoterms_available": [
- "dap"
], - "customer_reference": "REF-001",
- "created_at": "2026-03-09T14:23:11Z"
}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.
| order_id required | string Order ID. |
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"
|
| transportation_type | string (TransportationTypeEnum) Enum: "door_to_locker" "door_to_door" "locker_to_locker" "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: |
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 | |
| confirm_and_pay | boolean Default: false |
{- "shipper_details": {
- "phone": "+37060000000"
}, - "consignee_details": {
- "phone": "++37060000000"
}
}{- "id": "string",
- "customer_reference": "string",
- "parcels": [
- {
- "description": "string",
- "width": "string",
- "height": "string",
- "length": "string",
- "weight": "string",
- "value": "string"
}
], - "collection_date": "2019-08-24",
- "total_price": "string",
- "addons": [
- "additional_protection"
], - "paid": false,
- "status": "string",
- "customs_required": true,
- "customs_status": "not_required",
- "incoterms_available": [
- "dap"
], - "customs": {
- "contents_type": "string",
- "incoterm": "string",
- "shipper": {
- "is_company": true,
- "tax_ids": [
- {
- "type": "eori",
- "value": "string"
}
]
}, - "consignee": {
- "is_company": true,
- "tax_ids": [
- {
- "type": "eori",
- "value": "string"
}
]
}, - "items": [
- {
- "parcel_index": 0,
- "description": "string",
- "hs_code": "string",
- "quantity": 0,
- "unit_value": "string",
- "net_weight": "string",
- "origin_country": "string",
- "merchant_product_id": "string",
- "manufacturer_product_id": "string",
- "gtin": "string"
}
]
}, - "warnings": [
- "string"
]
}Deletes a TEMP order and its associated parcels. Only orders in TEMP status can be deleted.
| order_id required | string Order ID. |
{- "error": {
- "code": "order_not_found",
- "message": "Order not found.",
- "details": { }
}
}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| order_id required | string Order ID. |
| output | string Enum: "base64_pdf" "pdf"
|
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 downloadbase64_pdf — JSON response with the label as a base64-encoded stringLabel format parameter:
A4 — A4 page sizeA5 — A5 page sizeA6 — A6 page size| order_id required | string Order ID. |
| 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"
|
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.
| 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 |
[- {
- "pudo_points": [
- {
- "id": "42",
- "name": "InPost Locker Vilnius Akropolis",
- "address": "Ozo g. 25",
- "city": "Vilnius",
- "postcode": "07150",
- "country": "LT",
- "point_type": "LOCKER",
- "latitude": "54.7100000",
- "longitude": "25.2700000",
- "distance_km": 1.23
}
]
}
]Returns the cheapest shipping price for a given route and parcel dimensions.
| 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 |
{- "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": [
- "dap"
]
}Returns one best-value rate per available transportation type (door, locker, ...), sorted cheapest-first.
| 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 |
{- "is_business": true,
- "country_from": "string",
- "country_to": "string",
- "currency": "string",
- "customs_required": true,
- "rates": [
- {
- "transportation_type": "string",
- "transportation_amount": "string",
- "vat_amount": "string",
- "amount": "string",
- "delivery_time": 0,
- "incoterms_available": [
- "string"
]
}
]
}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.
| order_id required | string 6-char order code or 7-8 char parcel code |
| postcode | string Collection or delivery postcode of the order. Required unless the order belongs to the authenticated account. |
{- "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": [
- {
- "parcel_id": "KR4PZKA",
- "status": "NEW",
- "tracking_events": [
- {
- "tracked_on_time": "2026-03-09T14:30:00Z",
- "event_code": "0002",
- "event_description": "Prepared for collection",
- "event_comment": "",
- "event_country": "DE"
}
]
}
]
}