> ## Documentation Index
> Fetch the complete documentation index at: https://developers.kardinal.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Multi-trip tours (depot returns)

> Model a vehicle that reloads at the depot and runs several rounds within one shift.

Not every fleet can serve its full daily demand in a single loop. When total demand across a wave of orders exceeds the fleet's total capacity, resources need to return to the depot, reload, and go out again — potentially several times — inside their `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](/reference/data-model).

<Tip>
  Before modeling multi-trip tours, check whether you actually need them, using the exact test from the [agent modeling checklist](/getting-started/agent-modeling-checklist) (§2, "Check capacity feasibility before modeling orders"): sum your stops' `capacities` per dimension and compare against your fleet's total capacity in a single loop. That test is a strict binary, not a margin call — total demand either **fits** within total fleet capacity (single-loop, no reload needed) or **exceeds** it (single-loop is infeasible, not merely suboptimal; the shortfall will show up as unplanned stops, not an error). This page assumes that check already came out on the "exceeds" side. It isn't a separate, softer threshold of its own, and a ratio close to but under 1 doesn't independently justify reaching for this pattern — per the checklist, don't resolve a capacity shortfall by defaulting to multi-trip on your own; surface it as an open question, and only model this pattern once the client's own data or context confirms the fleet genuinely reloads at a depot mid-shift.
</Tip>

## 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:

```json theme={null}
{
  "id": "order-school-1",
  "stops": [
    {
      "type": "single",
      "id": "pickup-school-1",
      "position": { "lat": 48.888, "lon": 2.614 },
      "kind": "pickup",
      "operationDuration": "PT1S",
      "capacities": { "weight": 130, "piles": 3 },
      "tags": ["depot:main"]
    },
    {
      "type": "single",
      "id": "delivery-school-1",
      "position": { "lat": 48.899, "lon": 2.598 },
      "kind": "delivery",
      "operationDuration": "PT11M",
      "capacities": { "weight": 130, "piles": 3 },
      "preferredTimeWindows": [{ "begin": "2026-08-03T06:00:00+02:00", "end": "2026-08-03T10:30:00+02:00" }]
    }
  ]
}
```

Since an order's stops all run on the *same* resource, in array order, and the pickup adds exactly what the delivery then removes, each pair is capacity-neutral by construction — it never touches the resource's baseline load. That has two consequences that make it a better default than sizing a handful of large reload stops to a resource's full capacity:

* **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": true` reload 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` / `requiredSkills` pair 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.

<Note>
  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.
</Note>

## 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 pay `operationDuration` 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:

```json theme={null}
{
  "accessDurationsByStopTag": { "depot:main": "PT30M" }
}
```

This adds the duration once, before the first of a run of *consecutive* stops sharing the tag — matching a single loading operation at the dock, regardless of how many individual pickups happen during that visit. See [Data model](/reference/data-model#plan-level-fields) for the full 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 with `overlappingCapacitiesByStopTag` at the plan level:

```json theme={null}
{
  "overlappingCapacitiesByStopTag": { "depot:main": 2 },
  "objectives": [
    "maximizeMandatoryStops",
    "minimizeDelay",
    "minimizeOverOverlappingCapacitiesOnStops",
    "minimizeResources",
    "minimizeLargestTourDuration",
    "minimizeWorkingDuration",
    "minimizeDistance"
  ]
}
```

This caps the number of resources simultaneously present at any stop tagged `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](/reference/data-model#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.

<Tip>
  Without a fairness objective, the engine can load one or two resources up to their limit on repeated depot rounds while others in the fleet sit comparatively idle — all while still satisfying `maximizeMandatoryStops` and the delay/cost objectives ahead of it. Include `minimizeLargestTourDuration` (see [How the optimization engine works](/concepts/how-the-optimization-engine-works#what-the-engine-optimizes)) to cap how unbalanced the longest single tour can get relative to the rest of the fleet — it's placed after `minimizeResources` above so fleet size is still minimized first, but tour lengths are then balanced across whatever fleet size that settles on.
</Tip>

<Tip>
  If resources are arriving back at the depot and then waiting idle for their next reload window, consider setting the plan-level `lateDeparture: true` (see [Plan-level fields](/reference/data-model#plan-level-fields)) so the engine compacts departure timing to reduce that idle wait, instead of always having resources leave as early as possible.
</Tip>

## See also

* [Data model](/reference/data-model) — `capacities`, stop `kind`, `accessDurationsByStopTag`, and the rest of the constraints catalog referenced above.
* [Modeling advanced constraints](/guides/advanced-constraints) — heterogeneous capacities, skills, breaks, and multiple time windows.
* [Handling infeasibility](/guides/handling-infeasibility) — diagnosing a plan where demand still doesn't fit even with multiple rounds.
