Hub shipments
Route a shipment through one of our consolidation hubs instead of shipping directly from origin to destination.
What is hub transit?
By default a quote ships directly from origin to destination. With hub transit, we route the shipment through an intermediate hub and split it into two legs:
- Clearance leg — the international leg from your origin into the hub region. This is the leg that clears customs.
- Local leg — the domestic / final-mile leg from the hub on to the destination.
You never choose the hub yourself. When you ask for hub transit, we select an eligible hub for your origin → destination lane and build both legs for you. Hub routing can unlock better rates and consolidated handling on lanes where it's supported.
Requesting a hub quote
Set hub_transit: true in the Create a quote request body. Everything else is identical to a direct quote.
The country_id and purpose_of_shipment_id values above are examples. Take the real ids from List countries and List purposes of shipment.
If no hub serves the route, the request fails with 422 Unprocessable Content:
When that happens, fall back to a direct quote (omit hub_transit, or send false).
{
"hub_transit": true,
"origin_address": {
"name": "John Doe",
"company": "Tech Corp",
"address_1": "123 Tech Street",
"city": "Johannesburg",
"province": "Gauteng",
"postal_code": "2196",
"country_id": 226,
"email": "john@techcorp.example",
"phone": "+27123456789",
"classification": "business"
},
"destination_address": {
"name": "Jane Smith",
"company": "UK Business",
"address_1": "456 London Road",
"city": "London",
"province": "London",
"postal_code": "SW1A 2AA",
"country_id": 70,
"email": "jane@ukbusiness.example",
"phone": "+442012345678",
"classification": "business"
},
"purpose_of_shipment_id": 8,
"value_of_goods": "600.00",
"value_of_goods_currency_id": "ZAR",
"mass_unit": "kg",
"distance_unit": "cm",
"packages": [
{
"weight": 5.5,
"width": 30.0,
"height": 20.0,
"length": 40.0
}
]
}
{
"success": false,
"error": "Hub transit No hub is available for this route.",
"errors": ["Hub transit No hub is available for this route."]
}
What a hub quote returns
A hub quote collapses the two legs into a single estimate per service, so you compare hub options exactly like direct ones — you don't see or pick legs individually.
hub_transiton the quote istrue.- Each entry in
estimatesis anchored on the clearance leg:estimate_idis the clearance-leg estimate id (this is the id you pass when creating the order). line_itemsare the combined costs of both legs, grouped by description and summed in your account currency — so the price you see is the all-in hub price, not just one leg.
Each
costabove is the sum across both legs (clearance + local) for that line item — a hub estimate is one combined price, not two.
{
"quote": {
"id": 1234,
"dirty": false,
"hub_transit": true,
"errors": [],
"estimates": [
{
"estimate_id": 55788,
"shipping_company": "FedEx",
"shipping_service": "Express Priority",
"quote_expired": false,
"line_items": [
{ "description": "Base Rate", "cost": 1450.00, "currency_id": "ZAR" },
{ "description": "Fuel Surcharge", "cost": 217.50, "currency_id": "ZAR" }
],
"billable_weights": { "...": "same breakdown as a standard estimate" }
}
]
}
}
Placing a hub order
Create the order exactly as you would for a direct quote — pass the hub estimate's estimate_id as quote_estimate_id to Create an order. There is nothing hub-specific to send. The resulting order spans both hub legs.
The hub lane is fixed at quote time
The route — origin → hub → destination — is locked when the hub quote is created. The origin_address and destination_address you send when placing the order do not re-route a hub order: they are ignored for routing (only origin_address.export_code is read, for the export licence). This keeps the order on the exact lane the rate was quoted for.
To ship a different origin or destination, request a new hub quote (hub_transit: true) for that lane and order from it — you can't re-point an existing hub order at order time.
Waybills & documents for both legs
A hub order has two legs, each with its own waybill. Generating waybills (Create a waybill) produces one for both legs.
To retrieve them, read the order's shipments array, returned by Create an order, Get an order and Create a waybill.
The array is ordered by route: the first entry is the leg leaving your origin (origin → hub), and the last is the onward leg into your destination (hub → destination). A shipment carries no leg-role field, so use this ordering to tell the legs apart — shipments[0] is always the first leg of the journey, shipments[1] the next. Each entry carries that leg's own:
waybill— the waybill / tracking numberwaybill_document_link— the waybill PDF for that legtracking_linkandstatus
On a hub order the order's top-level waybill, waybill_document_link, tracking_link and status describe a single leg, so always read those per leg from shipments[], in route order, to get both waybills.
"shipments": [
{ "shipping_service": "...", "waybill": "WB-ORIGIN-TO-HUB", "waybill_document_link": "https://.../leg-1-waybill.pdf", "tracking_link": "...", "status": "..." },
{ "shipping_service": "...", "waybill": "WB-HUB-TO-DESTINATION", "waybill_document_link": "https://.../leg-2-waybill.pdf", "tracking_link": "...", "status": "..." }
]
Documents
Documents you upload with Create an order attach once, to the leg that clears customs. On EU hub lanes the local leg is domestic or intra-EU and needs no commercial invoice, so one upload covers the order.
Pickups
Both pickup calls (List pickup slots and Book a pickup) work on the first leg, the one leaving your origin. There is nothing for you to schedule on the hub → destination leg.
Cancelling
Cancel an order cancels both legs together.
End-to-end flow
- Quote —
POST /quoteswithhub_transit: true→ a hub quote whose estimates are collapsed and clearance-anchored. - Choose — pick an estimate and read its
estimate_id. - Order —
POST /orderswith thatquote_estimate_id→ a two-leg hub order; both legs' estimates are accepted automatically. - Waybills —
POST /orders/{id}/waybillsreadies both legs; read each leg's waybill fromshipments[]. - Pickup —
GET /orders/{id}/pickup_availabilitiesthenPOST /orders/{id}/schedule_pickups, both for the first leg. - Track — poll
GET /orders/{id}and readshipments[], or register a webhook; each leg fires its own events with the same order id.
Important notes
- Hub selection is automatic — you request hub transit; we pick the hub. You can't specify one.
- One estimate, both legs — a hub estimate's
estimate_idandline_itemsrepresent the clearance leg with the local leg folded in. You never accept the local leg yourself. - Availability is lane-dependent — not every origin → destination has a serviceable hub; handle the
422 "No hub is available for this route."response. - Same order endpoint — hub vs direct is decided entirely at quote time. Ordering is identical; you just use a hub estimate's id.