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

If 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 account

We model the return leg as a real parcel, not as an attribute of the original
one. When you ship with with_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 the parcel hook, 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:

FieldValue on the return parcel
external_idThe 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_infoCopied from the original parcel's pickup: same coordinates, address and contact.
dropoff_infoThe same pickup point again — it goes back where it started.
instrSpanish 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 |shadow suffix on external_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 |shadow external id yourself. If you ship the same parcel
    with with_return twice, the second attempt tries to create a return parcel
    that already exists and the request fails with 409 already exist.
  • The shipment response does not list it. POST /v1/parcels/ship returns
    tracking URLs for the parcels you asked for, not for the return parcel. Find
    it through GET /v1/parcels or by its external_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 type

Returns are enabled per shipping type, not per account. Asking for
with_return with a shipping type that does not have it fails with 400 and
the message shipping 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/estimate behaves differently here: if the shipping type is
not enabled for returns, the estimate silently ignores with_return and
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 a 400
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_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.


Did this page help you?