Skip to main content

Create an Order and Get Rates in One Call

One request creates an Order and returns the Rates available for each of its Shipments, so you can go straight from an address pair to a Rate identifier.

What you'll build

  • One Order containing a single Shipment, from an address pair and a parcel.
  • The Rate options that Shipment can be shipped at, grouped by Shipment.
  • The rate.id your Label purchase will need.

Prerequisites

  • An access token — see Authentication.
  • Your API base URL in MAILHUB_API_BASE_URL. The portal publishes no fixed API host: use the value supplied for your account, or your configured Sandbox base URL.
  • Python examples use the requests package.

Step 1 — Create the Order and request Rates

POST/api/v2/orders

The body carries exactly one order object — a single Order per request.

curl -s -X POST "${MAILHUB_API_BASE_URL}/api/v2/orders" \
-H "Authorization: Bearer ${MAILHUB_ACCESS_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"order": {
"fromAddress": {
"name": "Jane Sender",
"street1": "123 Main St",
"city": "Austin",
"state": "TX",
"zip": "78701",
"country": "US",
"phone": "5125550100"
},
"toAddress": {
"name": "John Recipient",
"street1": "456 Oak Ave",
"city": "Denver",
"state": "CO",
"zip": "80202",
"country": "US",
"phone": "3035550100"
},
"shipments": [
{"parcel": {"weight": 16, "length": 10, "width": 8, "height": 4}}
]
}
}'

See Create an Order for the full address and parcel rules, including which fields are optional.

Response

Trimmed to the fields this workflow uses. The complete schema is in the API Reference.

{
"success": true,
"data": {
"order": {
"id": "5f2c1a10-2b3d-4e5f-8a9b-0c1d2e3f4a5b",
"status": "Created",
"...": "..."
},
"shipmentRates": [
{
"shipmentId": "b91a2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d",
"rates": [
{
"id": "c4a1b2d3-4e5f-6a7b-8c9d-0e1f2a3b4c5d",
"carrier": "USPS",
"serviceName": "USPS Ground Advantage",
"rate": 8.42,
"currency": "USD",
"...": "..."
},
{"...": "..."}
]
}
]
},
"errors": null
}

carrier, serviceName, rate, and currency are the fields to compare when your application decides. serviceCode is also returned; it is provider-dependent, so pass it through unchanged rather than parsing or switching on it.

For the untrimmed body — every declared field on the Order, its Addresses, its Parcel, and a Rate — see the complete success response.

Use this next

data.shipmentRates[].rates[].id — keep the id of the Rate your application chooses. That value is the entire body of the Label purchase request:

{ "rate": { "id": "c4a1b2d3-4e5f-6a7b-8c9d-0e1f2a3b4c5d" } }

Keep data.shipmentRates[].shipmentId too: it is the path parameter for that purchase. Continue with Buy a Label from a Selected Rate, or see Select a Rate for how to compare the options first.

Expected result

A 200 with a persisted Order, one entry in shipmentRates per created Shipment, and at least one Rate to choose from.

Common outcomes

StatusWhat it meansWhat to do
200The Order was created and Rates were returned.Pick a Rate and keep its id.
422 — invalid inputAn address or parcel field failed validation.Correct the body from the response errors, then send it again. See Validation Errors.
422 — no Rate returnedNo created Shipment returned a Rate.Read the caveat below before doing anything else.

400, 401, 403, 404, 409, and 500 follow the standard public error envelope — see Error Handling.

Optional: tag the Order to a Sub-account

If supplied, X-SubAccount-Id tags the newly created Order and its Shipments with that Sub-account. Its effect differs per operation, so consult Work with Sub-accounts before sending it elsewhere.

-H "X-SubAccount-Id: <SUB_ACCOUNT_ID>"

Next steps