Shipment options
Beyond a single collection and a single delivery, a shipment request supports a
few options that are easy to miss because they live on the request rather than
on the parcel. All of them apply to POST /v1/parcels/ship, and the first two
also to POST /v3/parcels/estimate, so you can quote before you commit.
Several parcels in one shipment
parcel_ids is a list. Sending several ids in one shipment request asks for one
journey that handles all of them, rather than one journey each:
{
"parcel_ids": ["…", "…", "…"],
"shipping_type_id": "…"
}Quote it the same way: POST /v3/parcels/estimate takes a parcels array and
prices them together.
This is how a single rider covers more than one delivery in a run. It is not a
general route-planning feature — what is possible depends on the shipping type
and on how far apart the points are, so quote the exact set you intend to ship
before relying on it.
Round trips: with_return
with_returnIf you need the rider to come back to where they started — returning signed
paperwork, collecting an exchange — you do not have to model it as two separate
parcels. Set with_return on the shipment request:
{
"parcel_ids": ["…"],
"shipping_type_id": "…",
"with_return": true
}The return leg is added to the same journey, after the delivery, keeping the
stops in order. POST /v3/parcels/estimate accepts with_return too, so you
can price the round trip before requesting it.
A return creates a second parcel in your accountWe model the return leg as a real parcel, not as an attribute of the original
one. When you ship withwith_return, we create an extra parcel for you and
add it to the same journey as the last stop.You will see it. It is yours, it appears in
GET /v1/parcels, and it produces
its own notifications on theparcelhook, with its own id and its own state
timeline. Plan for it rather than treating it as corrupt data.
You can recognise it without guessing:
| Field | Value on the return parcel |
|---|---|
external_id | The original parcel's external_id with |shadow appended — order_1042|shadow. If the original had no external_id, its platform id is used instead. |
pickup_info | Copied from the original parcel's pickup: same coordinates, address and contact. |
dropoff_info | The same pickup point again — it goes back where it started. |
instr | Spanish text flagging it as a return, so the rider knows. If the original parcel carried pickup instructions, they are appended after that flag. |
Three consequences worth designing for before you switch this on:
- Filter it out of your own reporting. The
|shadowsuffix onexternal_id
is the reliable way: it is derived from yours, so you can match it back to the
order it belongs to. - Do not reuse a
|shadowexternal id yourself. If you ship the same parcel
withwith_returntwice, the second attempt tries to create a return parcel
that already exists and the request fails with409 already exist. - The shipment response does not list it.
POST /v1/parcels/shipreturns
tracking URLs for the parcels you asked for, not for the return parcel. Find
it throughGET /v1/parcelsor by itsexternal_id.
With several parcels in one shipment, only one return parcel is created, and
it is built from the first parcel in parcel_ids. If those parcels were
collected at different pickup points, the return goes to the first one. Order
parcel_ids deliberately when you combine both options.
It has to be enabled for your shipping typeReturns are enabled per shipping type, not per account. Asking for
with_returnwith a shipping type that does not have it fails with400and
the messageshipping type not enabled for return. That is configuration on
our side: if you need it for a shipping type you use, ask us rather than
working around it.
Estimating does not warn you
POST /v3/parcels/estimatebehaves differently here: if the shipping type is
not enabled for returns, the estimate silently ignoreswith_returnand
quotes the one-way trip instead of failing. So a successful estimate is not a
confirmation that returns are available to you — it can be followed by a400
from the shipment request. Confirm with us that the shipping type has returns
enabled rather than inferring it from a successful quote.When it is enabled, the estimate does price the return leg: it adds the same
extra stop internally, so the amount you are quoted covers the round trip.
Scheduling: pickup_time
pickup_timepickup_time on the shipment request asks for a collection at a given time
rather than as soon as possible:
{
"parcel_ids": ["…"],
"shipping_type_id": "…",
"pickup_time": "2026-09-20T14:00:00Z"
}Send it in UTC, formatted per RFC 3339.
pickup_time applies to the express modality. For same-day and next-day
types, delivery windows are part of the shipping type rather than something you
set per shipment.
If you send pickup_time on an express shipment and a rider is nevertheless
assigned immediately, do not work around it by delaying the request on your
side — tell us, with the parcel id and the time you asked for. Silently
rescheduling from your end hides the problem and makes it harder to find.
What is not supported
- Arbitrary multi-stop routing — picking up from several different origins
in one journey is not something the API plans for you. Several parcels in one
shipment is the closest thing, and it has limits. - Changing a shipment once requested — cancel and request again.
If one of these is blocking an integration, tell us what you are trying to do
rather than modelling around it; the shape of the workaround is usually the
thing we need to hear about.
Updated 2 days ago
