workingTimeWindow. Nothing about this requires a dedicated field: it’s a modeling pattern built from capacities and stop kind, the same primitives covered in the data model.
The core mechanic: pair each delivery with its own depot pickup
Rather than pre-computing a fixed number of “reload rounds” per vehicle, pair every delivery with a pickup of the same cargo at the depot, inside the same order:- No fixed round count. The engine is free to chain any number of these pairs on a single resource, going back to the depot as many times as the schedule and capacity allow — you don’t need to guess in advance how many rounds each vehicle will need, or leave unused
"optional": truereload orders on the table. - No per-vehicle sizing. A pickup sized to one delivery’s own weight/piles fits any resource with that much spare capacity, heterogeneous fleet or not. There’s no need for a
skills/requiredSkillspair to reserve a reload for a specific vehicle — whichever resource ends up serving the delivery automatically picks up its own matching cargo first, because both stops belong to the same order.
If you also want to force or bias which stops happen before versus after each other (for example, a fixed morning sector followed by a fixed afternoon sector), combine this with tagged phases and the
maximizePrecedences objective. That’s a separate concern from the capacity mechanic above.Charging depot time once per visit, not once per pickup
With one pickup stop per delivery, a vehicle loading five deliveries’ worth of cargo before a round would otherwise payoperationDuration five times over for what is physically a single dock visit. Keep each pickup’s operationDuration negligible (as in the example above) and instead charge the real access time once per visit with the plan-level accessDurationsByStopTag field:
Limiting simultaneous depot visits
A physical depot usually has a limited number of loading docks or bays, and can’t serve every vehicle at once. Tag every pickup stop with a shared depot tag (as in the example above) and cap simultaneous presence withoverlappingCapacitiesByStopTag at the plan level:
depot:main to 2 — matching, for example, a depot with two loading docks. Because every load in this pattern is an explicit tagged stop that’s part of an order — including the first load of the day, not just later reloads — the limit applies uniformly from the very first pickup, with nothing left uncovered. departure and arrival (see Depot vs. position) are only used for the resource’s idle start/end-of-day position; they carry no cargo and no tag, so keep the actual loading out of them entirely.
Swap-body and container-exchange orders
A different, unrelated vertical also produces multi-stop orders on a single resource: fleets where the vehicle carries exactly one exchangeable unit at a time — a container, a skip (“benne”), a tank — and each trip’s job is to exchange whatever unit is already at a site for the next one, then haul the unit away to its destination (a disposal site, a treatment plant, a depot). Roll-off/skip trucks and tanker/cistern-swap vehicles are the two most common examples. This is a distinct pattern from the depot-reload pattern above — it isn’t about a vehicle running low on capacity and returning to top up, it’s about a vehicle that only ever carries one unit and needs to swap it out at (or near) the point of use.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 OpenAPI 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 above (no exchange at the origin) — and even then, prefer whichever of the three mechanisms above 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.
See also
- Data model —
capacities, stopkind,accessDurationsByStopTag, and the rest of the constraints catalog referenced above. - Modeling advanced constraints — heterogeneous capacities, skills, breaks, and multiple time windows.
- Handling infeasibility — diagnosing a plan where demand still doesn’t fit even with multiple rounds.

