> ## 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 vehicles that reload at the depot and run several rounds in one shift, count depot time once per visit, and limit simultaneous depot visits and returns.

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.

## 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).

These snippets use local times. The plan you put them in needs `"tz"`; see [Dates and time zones](/reference/data-model#dates-and-time-zones).

```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-03 06:00", "end": "2026-08-03 10:30" }]
    }
  ]
}
```

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 bias *which* stops happen before versus after each other (for example, a morning sector before an afternoon sector), combine this with tagged phases and the `maximizePrecedences` objective. It is an objective, so the engine tries to keep that order but does not guarantee it. See [maximizePrecedences](/concepts/objectives#maximizeprecedences). That's a separate concern from the preceding capacity mechanic.

## 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]
>
> 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.

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