POST /deliveries/bulk creates many deliveries in one request. It is useful when your system collects orders over time and books them together, for example a morning batch from your shop or a list exported from a spreadsheet.
Each item you send is exactly the body you would send to POST /deliveries. If you can create one delivery, you already know how to build a bulk request.
Use single
POST /deliveries when you book each order as it arrives. Use bulk when you already have a list of 2–100 deliveries ready to send at the same time.Send a batch
Wrap your deliveries in adeliveries array. Send 1–100 items. Unknown fields are rejected, just like the single create.
deliveries:write scope and an Idempotency-Key, the same as a single create. The environment still comes from the base URL and key, so the same request works on sandbox and live.
A bulk call counts as one request against your per-key and per-business rate limits, however many items it contains.
The two phases
HaulStow handles a bulk request in two steps.1. Validation: all or nothing
First, every item is checked with the same rules asPOST /deliveries. HaulStow also checks that no two items share an external_id.
If any item is invalid, nothing is created. You receive 422 BULK_VALIDATION_FAILED with one entry per problem:
index is the item’s position in your deliveries array, starting at 0. Fix the listed items and send the corrected batch with a new Idempotency-Key.
An empty array, or more than 100 items, returns 422 VALIDATION_ERROR instead.
2. Creation: one item at a time
When every item passes validation, HaulStow creates them one at a time, in the order you sent them. Each item becomes an independent delivery with its owndlv_... ID and its own delivery.created webhook.
An item can still fail at this stage. For example, a live quote may be unavailable, or its external_id may already belong to a delivery from an earlier request. That item is reported as failed and HaulStow carries on with the rest.
Read the results
data.results contains one entry for every item you sent, in the same order. Each entry has the item’s index and a status of created or failed.
A created result contains the full delivery object, the same one
POST /deliveries returns. A failed result contains an error with a stable code and a human-readable message. The codes are the same ones the single create uses; see common errors.
Retry failed items safely
HaulStow stores the complete bulk response under yourIdempotency-Key, including the failed items. If you retry with the same key and body, you receive that same response again with Idempotent-Replayed: true. The failed items are not attempted again.
This keeps a network retry safe: if your connection drops, resend the exact same request with the same key and you get the original result, with no duplicate deliveries.
To retry only the items that failed:
- Collect the failed entries from
data.resultsand look up each item by itsindex. - Decide whether each failure can be retried.
QUOTE_UNAVAILABLEcan be retried later;EXTERNAL_ID_CONFLICTmeans that delivery already exists, so fetch it withGET /deliveries?external_id=...instead. - Send a new bulk request containing just the retryable items, with a new
Idempotency-Key.
external_id on every item. It lets you match each result to your own order, find a delivery later, and stops a retry from creating a second delivery for the same order.
Try it in the sandbox
Bulk creation works the same way on the sandbox host with a test key. Each created sandbox delivery starts atbooked. Move it forward with the transition helper, one delivery and one step at a time.
Upload a CSV in the developer portal
You can also create a batch without writing code. Open the Deliveries page for your application in the developer portal and upload a CSV file.- Choose which application the deliveries belong to.
- Use the portal’s test/live switch to pick the environment. Test creates simulated sandbox deliveries; live creates real ones.
- Include up to 100 rows. The same two phases apply: if any row is invalid, nothing is created.
Each row uses either
recipient_id and recipient_address_id or the inline recipient columns, never both. This is the same rule as the API’s two recipient shapes.