Hub Shipments (Hub Transit)
Route a shipment through one of Stream's 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, Stream routes the shipment through an intermediate hub and splits 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, Stream selects an eligible hub for your origin → destination lane and builds 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 Quote request body. Everything else is identical to a direct quote.
{
"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.com",
"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.com",
"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
}
]
}
If no hub serves the route, the request fails with 422 Unprocessable Entity:
{
"success": false,
"error": "[\"Hub transit No hub is available for this route.\"]",
"errors": []
}
When that happens, fall back to a direct quote (omit hub_transit, or send false).
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.
{
"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" }
}
]
}
}
Each
costabove is the sum across both legs (clearance + local) for that line item — a hub estimate is one combined price, not two.
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 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 Waybill) produces one for both legs.
To retrieve them, read the order's shipments array, returned on Get Orders and in the waybill response.
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
"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": "..." }
]
The order's top-level waybill/waybill_document_link reflect only the clearance leg (the order's primary shipment), so for a hub order always read the per-leg links from shipments[], in route order, to get both waybills.
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.
Important notes
- Hub selection is automatic — you request hub transit; Stream picks 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.