Skip to main content
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 a deliveries array. Send 1–100 items. Unknown fields are rejected, just like the single create.
The request needs the 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 as POST /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 own dlv_... 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.
Do not treat 207 as a failed request. Some of your deliveries were created. Save every created result before you handle the failures.

Retry failed items safely

HaulStow stores the complete bulk response under your Idempotency-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:
  1. Collect the failed entries from data.results and look up each item by its index.
  2. Decide whether each failure can be retried. QUOTE_UNAVAILABLE can be retried later; EXTERNAL_ID_CONFLICT means that delivery already exists, so fetch it with GET /deliveries?external_id=... instead.
  3. Send a new bulk request containing just the retryable items, with a new Idempotency-Key.
Put an 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 at booked. 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.
The first row must contain these column names: 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.