Update Order Status
/api/v1/orders/{orderId}/status
Open update order status in the API Reference.
Status reflects the Order's real fulfilment progress. Request body:
{
"status": 7,
"reason": "Packed and ready to ship",
"source": "warehouse-scanner"
}
status is required; reason and source are optional free-text fields
for your own audit trail.
Status values
| Value | Name |
|---|---|
0 | Created |
1 | Requested |
2 | OnHold |
3 | Cancelled |
4 | Fulfilled |
5 | Processing |
6 | Picked |
7 | Packed |
8 | LabelCreated |
9 | Returned |
10 | Error |
11 | Draft |
A new Order starts in Created (0).
Supported transitions for this workflow
Not every (current, target) pair is allowed — an unsupported transition
returns 409. The transitions relevant to the order-to-label journey:
| From | To | Notes |
|---|---|---|
Created, Processing, or Picked | Packed | Optional operational status update — see below |
Packed | LabelCreated | Happens automatically when a Label purchase succeeds; you do not normally PATCH this yourself |
LabelCreated | Fulfilled | The only path to Fulfilled — PATCH this after you have downloaded the Label and handed the shipment to the carrier |
LabelCreated | Packed | Applied automatically when you cancel the Label — see Cancel a Label |
Created, Processing, Picked, Packed, or OnHold | Cancelled | |
Fulfilled | Returned |
Fulfilled is enforced: it is only reachable from LabelCreated, so you
must have already purchased a Label before this call succeeds.
- cURL
- JavaScript
- C#
curl -s -X PATCH "${MAILHUB_API_BASE_URL}/api/v1/orders/${ORDER_ID}/status" \
-H "Authorization: Bearer ${MAILHUB_ACCESS_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"status": 4, "reason": "Handed to carrier"}'
const statusResponse = await fetch(`${process.env.MAILHUB_API_BASE_URL}/api/v1/orders/${orderId}/status`, {
method: 'PATCH',
headers: {
Authorization: `Bearer ${accessToken}`,
'Content-Type': 'application/json',
},
// 4 = Fulfilled — only valid once the order is LabelCreated
body: JSON.stringify({status: 4, reason: 'Handed to carrier'}),
});
const {data: result} = await statusResponse.json();
// result.previousStatus, result.currentStatus (both numeric)
var statusRequest = new HttpRequestMessage(HttpMethod.Patch, $"/api/v1/orders/{orderId}/status")
{
// 4 = Fulfilled — only valid once the order is LabelCreated
Content = JsonContent.Create(new { status = 4, reason = "Handed to carrier" }),
};
statusRequest.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
var statusResponse = await client.SendAsync(statusRequest);
Response
{
"success": true,
"data": {
"orderId": "5f2c1a10-2b3d-4e5f-8a9b-0c1d2e3f4a5b",
"previousStatus": 8,
"currentStatus": 4,
"updatedAt": "2026-01-01T12:00:00Z"
},
"errors": null
}
Errors
404 if the Order does not exist for your account. 409 ("Status
transition is not allowed") if the requested (previousStatus, status) pair
is not one of the supported transitions — retrieve the Order's current
status and correct the target rather than repeating the same request.
Continue with Get Shipping Rates, or
if the Order already has a Label, proceed straight to
Download a Label and mark it
Fulfilled afterward.