Skip to main content
This walkthrough builds a more realistic plan than First API call: two vehicles starting from two different depots, delivering to stops with time windows. It assumes you’ve already authenticated — see Authentication if you haven’t.

The scenario

Two vans, each starting and ending its day at a different depot, deliver to four stops with client-facing delivery windows.

Step 1 — start minimal: resources and orders only

Get a feasible plan running before adding any constraints. Each resource needs at minimum an id, a vehicleProfile, and a workingTimeWindow; each order needs an id and at least one stop with a position.
Submit this the same way as in First API call (PUT .../agencies/{agencyId}/plans/{planId}) and confirm two tours come back in the solution, together covering all four stops. Positions here are illustrative — see Data model for why real positions must be pre-geocoded before you submit them.

Step 2 — add depots as departure/arrival

The plan above already has each van start and end at a depot position rather than at the first/last stop — this is what makes the two-depot scenario realistic. If departure/arrival are omitted, the working day starts and ends at whichever stop the engine happens to assign first/last, which is rarely what a dispatcher expects for a fleet with fixed depots.

Step 3 — add delivery time windows, and choose hard vs soft deliberately

Now add a delivery window to each stop. This is the step where it’s easy to get the modeling choice wrong: a client-facing or “contractual” window is not automatically a hard constraint. Ask what should happen if the fleet can’t hit the window exactly:
  • If a late (or early) visit is still worth making — the customer would rather get a delayed delivery than none — use preferredTimeWindows. Missing it costs delay, tracked by the minimizeDelay objective, but the stop still gets served.
  • Only use authorizedTimeWindows if a visit outside the window genuinely can’t happen (site closed, access refused). See Hard vs soft constraints for the full reasoning — the short version is: default to preferredTimeWindows for contractual windows unless you’ve explicitly confirmed otherwise with the business.
Add "minimizeDelay" to your objectives list (it’s already in the recommended default — see How the optimization engine works) so the engine actually optimizes against these windows rather than treating them as decoration.

Step 4 — read the result

Fetch the solution the same way as in First API call and check, per tour:
  • tours[].wayPoints — the assigned stops, in visiting sequence, each with a computed arrivalTime.
  • tours[].isValid — whether that specific tour respects all hard constraints; check this per-tour, not just at the plan level.
  • unaffectedStopIds — any stop that couldn’t be placed at all. With everything modeled as preferredTimeWindows above, this should be empty; if you’d used authorizedTimeWindows instead and a window were unreachable, the stop would show up here instead of arriving late.
  • tours[].distanceInKm and tours[].workingDuration, useful for a sanity check against what you’d expect for the geography.
If a stop you expected to be served ends up in unaffectedStopIds, see Handling infeasibility for how to diagnose which constraint caused it.

Common pitfalls at this stage

  • Units. Distances in the API are kilometers, not miles; durations are ISO 8601 (PT30M, not 30); make sure any values sourced from a spreadsheet are converted before submission.
  • Time zones. Datetimes without an explicit UTC offset are ambiguous — either include the offset on every datetime, or set the plan-level tz field once (e.g. "Europe/Paris") and write local, offset-free datetimes. Mixing both styles in the same plan is a common source of off-by-a-few-hours bugs.
  • Positions. position fields must already be geocoded latitude/longitude pairs — the API does not geocode addresses for you. See Data model for what to use instead.
  • Hard vs soft defaults. As shown in Step 3, don’t default a contractual time window to authorizedTimeWindows just because it’s contractual — that choice silently drops stops rather than delivering them late.

See also