Skip to main content

Buy a Label

POST/api/v1/shipments/{shipmentId}/labels Open label purchase in the API Reference.

Purchase a Label for a Shipment using a Rate identifier returned by Get Shipping Rates. Use Select a Rate to make the application-owned choice. The request body carries only the selected Rate's nested rate.id:

{ "rate": { "id": "c4a1b2d3-..." } }
curl -s -X POST "${MAILHUB_API_BASE_URL}/api/v1/shipments/${SHIPMENT_ID}/labels" \
-H "Authorization: Bearer ${MAILHUB_ACCESS_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"rate": {"id": "'"${SELECTED_RATE_ID}"'"}}'

Prerequisites

The Order must not already be Cancelled, already LabelCreated (a Label was already purchased for it), or Returned — any other status, including a brand-new Created Order, is accepted. A successful purchase automatically moves the Order to LabelCreated — you do not PATCH the status yourself for this transition. See Update Order Status for the full transition table.

Response

A 200 means the purchase has completed. data is the Shipment: its root data.id is the Shipment's own identifier, not a Label. Take the nested data.postageLabel.id — that is the value Download a Label and Cancel a Label accept.

{
"success": true,
"data": {
"id": "b91a2c3d-...",
"status": "Unknown",
"trackingCode": "9400111899223197428490",
"orderId": "5f2c1a10-...",
"selectedRate": {
"id": "c4a1b2d3-...",
"carrier": "USPS",
"service": "Priority Mail",
"rate": "8.42",
"currency": "USD"
},
"postageLabel": {
"id": "f6c7d8e9-...",
"labelProcessingStatus": "processing",
"labelUrl": "",
"labelPdfUrl": null,
"labelZplUrl": null,
"labelFileType": "pdf"
},
"createdAt": "...",
"updatedAt": "..."
},
"errors": null
}

When the purchase is still running

The operation waits for the purchase for a bounded window. If the purchase is still running when that window ends, it answers 202 instead of holding the request open. The purchase continues server-side; 202 is not a failure and not a signal to send the request again.

{
"success": true,
"data": {
"operationId": "9a8b7c6d-...",
"shipmentId": "b91a2c3d-...",
"status": "Processing",
"correlationId": "..."
},
"errors": null
}

The body also echoes the selected Rate's identifier. None of these fields is an input to any operation — they identify the running purchase for your own logging, not a resource you can fetch.

Observe the outcome by retrieving the Order — Get Order returns labelId once the purchase has completed, and its status moves to LabelCreated. That labelId is the same identifier a 200 returns as postageLabel.id. There is no operation-status endpoint to poll, and operationId above is diagnostic only — you never send it back. Choose your own polling cadence; the contract defines no interval.

Repeating the request

The request carries no idempotency key. Instead, Mailhub recognizes a repeat server-side from the Shipment's purchase operation and the request that started it: sending the same purchase request again does not buy a second Label. A repeat of the same request replays the original outcome — 200 with the original result once it has completed, 202 while it is still running.

A request that is not the same purchase returns 409 rather than starting a competing one: a different rate for a Shipment whose purchase is in progress, a Shipment that already has a Label, or an earlier purchase whose carrier outcome is still being resolved. Correct the request or read the Order; do not loop on a 409.

Two bounded conditions remain, so this is safe repetition rather than a blanket guarantee. Retry within your own bounded budget:

  • If the Label bought by your request was cancelled before the result reached you, the request is answered as a conflict and will not purchase a replacement. Submit a new purchase if you still need one.
  • An unresolved carrier outcome is reconciled server-side. Until it settles, a new purchase for that Shipment is refused with 409.

See Retries and Ambiguous Outcomes for the cross-operation policy.

Errors

400 for an invalid or no-longer-usable rate id. 401 for a missing or expired bearer token. 409 when a different purchase is in progress or the Shipment already has a Label. See Label Errors.

For the shortest working version of this purchase, see Buy a Label from a Selected Rate.

After purchase, continue to Download a Label. When your application needs the documented cancellation operation, see Cancel a Label. Working with USPS? See USPS with Mailhub.