Create an Order
/api/v1/orders
Open create order in the API Reference.
Create an Order to begin the standard Order workflow. The request requires a
fromAddress, a toAddress, and at least one entry in shipments — each
shipment carries the parcel Mailhub will rate and label.
Use this operation when you want standard Order creation without rating during the create request. Use Create an Order with Rates when you want one Order created and its Shipments rated in the same request.
- cURL
- JavaScript
- C#
- Python
- Java
curl -s -X POST "${MAILHUB_API_BASE_URL}/api/v1/orders" \
-H "Authorization: Bearer ${MAILHUB_ACCESS_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"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 } }
]
}'
const orderResponse = await fetch(`${process.env.MAILHUB_API_BASE_URL}/api/v1/orders`, {
method: 'POST',
headers: {
Authorization: `Bearer ${accessToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
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}}],
}),
});
const {data: order} = await orderResponse.json();
const orderId = order.id;
const shipmentId = order.shipments[0].id;
var createOrderRequest = new HttpRequestMessage(HttpMethod.Post, "/api/v1/orders")
{
Content = JsonContent.Create(new
{
fromAddress = new { name = "Jane Sender", street1 = "123 Main St", city = "Austin", state = "TX", zip = "78701", country = "US", phone = "5125550100" },
toAddress = new { name = "John Recipient", street1 = "456 Oak Ave", city = "Denver", state = "CO", zip = "80202", country = "US", phone = "3035550100" },
shipments = new[] { new { parcel = new { weight = 16, length = 10, width = 8, height = 4 } } },
}),
};
createOrderRequest.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
var orderResponse = await client.SendAsync(createOrderRequest);
using var orderPayload = JsonDocument.Parse(await orderResponse.Content.ReadAsStreamAsync());
var order = orderPayload.RootElement.GetProperty("data");
var orderId = order.GetProperty("id").GetString();
var shipmentId = order.GetProperty("shipments")[0].GetProperty("id").GetString();
import requests
order_response = requests.post(
f"{api_base_url}/api/v1/orders",
headers={"Authorization": f"Bearer {access_token}"},
json={
"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}}
],
},
)
order = order_response.json()["data"]
order_id = order["id"]
shipment_id = order["shipments"][0]["id"]
// Java 11+ java.net.http — no MailHub package to install and no third-party client.
String body = """
{
"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}}
]
}
""";
var request = HttpRequest.newBuilder()
.uri(URI.create(apiBaseUrl + "/api/v1/orders"))
.header("Authorization", "Bearer " + accessToken)
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(body))
.build();
HttpResponse<String> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
// Parse response.body() with your own JSON library, then keep data.id (the Order)
// and data.shipments[0].id (the Shipment you will rate).
The public request schema requires fromAddress, toAddress, and each
shipment's parcel.weight. The current domestic validation also requires a
name or company, street, city, state, postal code, country, and phone value;
street2, email, and the remaining optional fields can be omitted. For a
custom parcel, the current domestic validation expects positive length,
width, and height; a recognized predefined package can supply dimensions.
Response
A successful call returns 201 with the created Order, including a
server-assigned id for the Order and for each Shipment:
{
"success": true,
"data": {
"id": "5f2c1a10-2b3d-4e5f-8a9b-0c1d2e3f4a5b",
"status": "Created",
"isReturn": false,
"fromAddress": { "id": "...", "street1": "123 Main St", "city": "Austin", "state": "TX", "zip": "78701", "country": "US", "createdAt": "...", "updatedAt": "..." },
"toAddress": { "id": "...", "street1": "456 Oak Ave", "city": "Denver", "state": "CO", "zip": "80202", "country": "US", "createdAt": "...", "updatedAt": "..." },
"shipments": [
{
"id": "b91a2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d",
"status": "Unknown",
"isReturn": false,
"parcel": { "id": "...", "weight": 16, "length": 10, "width": 8, "height": 4 },
"createdAt": "...",
"updatedAt": "..."
}
],
"createdAt": "...",
"updatedAt": "..."
},
"errors": null
}
Continue the workflow
Keep both identifiers: data.id (the Order) and
data.shipments[0].id (the Shipment). The Shipment identifier is what
Get Shipping Rates and
Buy a Label require — rates and label
purchase are not gated by the Order's status, so you can request rates
immediately.
Validation failures
400 indicates the request could not be created as sent (for example, a
missing required address field or an empty shipments array). 401
indicates the bearer token is missing or no longer usable — see
Authentication.
Optional: scope to a sub-account
If your account uses sub-accounts, add X-SubAccount-Id: <sub-account-id> to
tag the new Order (and its Shipments) with that sub-account. See
Sub-account scoping.
No documented idempotency key or safe-retry contract exists for this operation — see Retries and Ambiguous Outcomes before retrying an ambiguous or failed create request.