> ## 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-and-do-not-resolve-a-shortfall-on-your-own) (§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 shows up as unplanned stops, not an error). Even then, this modeling is only justified if 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. The `"depot:main"` tag used below is an arbitrary stop tag your integration defines to mark same-site pickups — it doesn't reference a `Depot` API object; see [Depot vs. position](/reference/data-model#depot-vs-position).

```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" }]
    }
  ]
}
```

Each order lists its stops in a fixed sequence — the depot pickup first, then the customer delivery — and both are always served by the same vehicle. Because the pickup loads exactly what the delivery unloads, each pair leaves the vehicle's load unchanged once both stops are done; in between, it only takes up room for that one delivery's cargo. This has two advantages over planning a few large reload stops sized to the vehicle'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 needs, or leave unused `"optional": true` reload orders on the table.
* **No per-vehicle sizing.** Each pickup carries only its own delivery's cargo (here 130 kg and 3 piles), so it fits on any vehicle with room for that one delivery, whether the fleet is mixed or uniform. A large reload stop sized to one vehicle's full capacity would only fit that vehicle, or a bigger one, so you'd have to reserve it for that vehicle with a `skills` / `requiredSkills` pair. Here nothing needs reserving: whichever vehicle the engine assigns the delivery to also gets the matching pickup, 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 preceding capacity mechanic.
</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 preceding example) 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 preceding example) and limit simultaneous presence with `overlappingCapacitiesByStopTag` at the plan level. The `objectives` list below is a variant built for this example, not the platform default — see [The default list](/concepts/objectives#the-default-list) for the actual default sequence:

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

This sets a limit of 2 resources simultaneously present at any stop tagged `depot:main` — matching, for example, a depot with two loading docks. It's a soft limit: `minimizeOverOverlappingCapacitiesOnStops` minimizes any overrun, so how firmly the engine keeps to it depends on where that objective sits in `objectives` (here right after `minimizeDelay`). 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 [minimizeLargestTourDuration](/concepts/objectives#minimizelargesttourduration)) to cap how unbalanced the longest single tour can get relative to the rest of the fleet — it's placed after `minimizeResources`, described earlier, 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>

## Limiting how many times a resource returns to the depot

`overlappingCapacitiesByStopTag` (preceding section) limits how many resources are *simultaneously* at the depot — a different question from how many times *one* resource returns there over the whole tour. For the latter, use the `maxStopTagGroups` additional constraint:

```json theme={null}
{
  "additionalConstraints": [
    {
      "type": "maxStopTagGroups",
      "name": "single-daily-depot-return",
      "resourceTags": ["subcontractorA"],
      "maxGroupsByStopTag": { "depot:main": 1 }
    }
  ]
}
```

This limits how many separate visit groups a tagged resource can make to stops sharing the `depot:main` tag over the whole tour — the two mechanisms can be combined. A common pattern: leave internal resources free to return to the depot as many times as the schedule needs, but cap subcontractor resources (`resourceTags`) to a single daily depot visit.

## See also

* [Swap-body and container-exchange orders](/guides/swap-body-exchange) — a different, unrelated multi-stop-order pattern for single-unit-at-a-time fleets.
* [Data model](/reference/data-model) — `capacities`, stop `kind`, `accessDurationsByStopTag`, and the rest of the constraints catalog referenced earlier.
* [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.
* [Requirements briefing checklist](/guides/requirements-briefing#7-depot-and-multi-trip-operations) — the questions to ask before assuming this pattern applies to an operation.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.