Two stop shapes, decided by what actually happens at the site
Whether a given exchange needs two stops or three is a question about the data, not a stylistic default to pick between:- Three stops, when an exchange happens at the origin. The vehicle already has a unit to drop off before it can take on the next one: a
deliverystop drops the returning/empty unit, immediately followed by apickupstop that takes on the next unit — both at the same position — and then a third stop, at a different position, delivers/hauls the newly picked-up unit to its destination. - Two stops, when no exchange happens at the origin. If the vehicle already has nothing to drop off at the pickup site (for example it starts the trip already running with an empty unit loaded, or the site has nothing to collect first), the order is a plain
pickupat the origin followed by adeliveryelsewhere — the same shape used elsewhere in this guide and in Data model.
Preventing double-booking: three legitimate mechanisms
A vehicle that carries only one unit at a time must never be assigned a second pickup before the current unit is off-loaded. There are three legitimate ways to enforce that, and they are not mutually exclusive — a real payload may use one, several, or all together, depending on what else the data needs to express:successiveStopsalone. SetsuccessiveStops: trueon the order (see Order, Stop) with no tags, nosetupDurations, and no capacity signal at all. Array order plussuccessiveStopsis already enough to guarantee the drop-off and the pickup happen back-to-back, in that order, with nothing else interleaved — which is all that’s needed to prevent double-booking when the exchange itself has no real handling-time cost worth modeling and there’s no capacity dimension the exchange needs to express. This is often the simplest correct option, and in practice the one actually used, when neither of the other two mechanisms’ extra machinery corresponds to anything real in the data — don’t reach for tags +setupDurationsby default on every swap-body order just because the pattern involves an exchange; reserve it for exchanges that genuinely carry their own timing cost (see the next option).- Tags +
setupDurations, combined withsuccessiveStops. Tag the relevant stops with something that captures the vehicle’s state (for example a tag meaning “carrying a unit” versus “empty”), and use the plan-levelsetupDurationsarray to charge a duration whenever a resource transitions from a stop tagged one way to a stop tagged another way — see the field’s own definition in the API reference (SetupDuration:fromStopTag,toStopTag,setupDuration). SetsuccessiveStops: trueon the order so nothing from another order can be interleaved between the drop-off and the pickup. Together, these charge a realistic handling/swap duration for the exchange itself and guarantee the two visits happen back-to-back, in that order. - Signed
capacitiespairs. Track the unit with two capacity dimensions that move in lockstep and opposite directions — one incrementing, one decrementing at each step (for example a stop that changescapacitiesby{"unit": 1, "missingUnit": -1}, and the matching drop-off stop by the inverse). The running capacity balance itself then prevents a second pickup from happening before the current unit is dropped off, because doing so would push a dimension out of range. This is the same family of mechanism as shared capacity pools, applied within a single order rather than across several.
capacities: {"units": 1} on the resource and on every stop that adds or removes a unit, with no successiveStops, no tags/setupDurations, and no signed pair — is not, by itself, a correct model of this pattern once a real drop-off-then-pickup step exists at the same site. A capacity ceiling only bounds how many units are aboard at any point; it doesn’t add the exchange’s own handling time, and nothing about it forces the drop-off and the pickup to happen back-to-back without another order’s stop landing in between. Use it only when the trip is genuinely the simpler two-stop shape described earlier (no exchange at the origin) — and even then, prefer whichever of the three preceding mechanisms the client’s data actually calls for once an exchange is involved.
Example: tags + setupDurations for a three-stop exchange
PT20M figure here is illustrative, not a universal constant — it stands in for whatever real handling/administrative time a given client’s exchange actually takes, which you should confirm against their own operation rather than assume. Because the drop-off and pickup stops share the same position, the resource’s travel time between them is zero; the setupDurations entry is what charges the exchange’s own realistic duration on top of that, and successiveStops: true guarantees nothing else gets scheduled in between. The final haul stop keeps the unit:loaded tag since the resource is still carrying the unit it just picked up — no further transition (and so no further setup duration) is charged until it next transitions to a differently tagged stop.
Option 2 (signed capacities pairs) doesn’t need a new worked example here — it follows the same Capacities mechanics already documented, just applied with two dimensions per unit instead of one.
Skipping operation duration for a pre-loaded resource
A resource that starts its shift already carrying a swapped unit shouldn’t pay the loading time for it again.operationDurationPolicies excludes specific operation durations from a resource’s tour:
withoutFirstOperationDuration ignores the operation duration only for the first group of tagged stops the resource encounters in its tour — the case of a driver starting already loaded. withoutOperationDurations ignores it on every matching stop for the whole tour instead. Use the plan-level operationDurationPoliciesByResourceTag variant when the rule should apply to every resource sharing a tag rather than to one named resource.
See also
- Multi-trip tours — the depot-return/reload pattern, a different and unrelated multi-stop-order pattern.
- Data model —
Order/Stopfield reference, includingsuccessiveStopsandtags. - Modeling advanced constraints — heterogeneous capacities, skills, breaks, and multiple time windows.

