Skip to main content
View as Markdown

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_transit on the quote is true.
  • Each entry in estimates is anchored on the clearance leg: estimate_id is the clearance-leg estimate id (this is the id you pass when creating the order).
  • line_items are 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 cost above 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 number
  • waybill_document_link — the waybill PDF for that leg
  • tracking_link and status

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​

  1. Quote — POST /quotes with hub_transit: true → a hub quote whose estimates are collapsed and clearance-anchored.
  2. Choose — pick an estimate and read its estimate_id.
  3. Order — POST /orders with that quote_estimate_id → a two-leg hub order; both legs' estimates are accepted automatically.
  4. Waybills — POST /orders/{id}/waybills readies both legs; read each leg's waybill from shipments[].
  5. Pickup — GET /orders/{id}/pickup_availabilities then POST /orders/{id}/schedule_pickups, both for the first leg.
  6. Track — poll GET /orders/{id} and read shipments[], 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_id and line_items represent 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.
API ExplorerSandbox

Pick a call on the right and run it. Every call goes to the sandbox, so nothing is really shipped or charged. The answer appears here.