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

# Modeling advanced constraints

> Heterogeneous capacities, driver skills, mandatory breaks, multiple time windows.

The [data model](/reference/data-model) covers the basic shape of a plan. This guide goes one level deeper, into five constructs that come up as soon as a fleet or a business isn't fully uniform: vehicles that aren't interchangeable, drivers with different qualifications, regulatory breaks, stops with more than one valid visiting window, and optional steps that only matter if they gate a mandatory stop downstream. Each section shows the hard version first, then the soft equivalent where one exists — see [Hard vs soft constraints](/concepts/hard-vs-soft-constraints) for the general distinction.

## Heterogeneous capacities per vehicle

`capacities` is a free-form map: any key you define (`weight`, `volume`, `nbPackages`, a custom unit) is tracked independently by the engine, on both `resources` and `stops`. A resource's capacity is a hard ceiling — the cumulative load of its assigned stops can never exceed it at any point in the tour.

```json theme={null}
{
  "resources": [
    {
      "id": "van-1",
      "vehicleProfile": { "type": "car" },
      "capacities": { "weight": 800, "volume": 4.2, "nbPackages": 60 }
    }
  ],
  "orders": [
    {
      "id": "order-1",
      "stops": [
        {
          "type": "single",
          "id": "stop-1",
          "position": { "lon": 2.3522, "lat": 48.8566 },
          "kind": "delivery",
          "operationDuration": "PT5M",
          "capacities": { "weight": 40, "volume": 0.3, "nbPackages": 3 }
        }
      ]
    }
  ]
}
```

### Multi-compartment vehicles

A vehicle can have a compartment configurable in different ways before departure — for example a reefer compartment split between chilled and frozen, or run fully chilled instead — with the setup fixed for the whole tour, no reconfiguration mid-route. Model each candidate setup as its own `capacities`-scoped constraint, and combine the candidates with `atLeastOneConstraint`:

```json theme={null}
"additionalConstraints": [
  {
    "type": "atLeastOneConstraint",
    "name": "two-compartment-cold-chain-setup",
    "constraints": [
      { "type": "capacities", "capacities": { "frozenVolume": 0, "chilledVolume": 10 }, "resourceTag": "reefer-van" },
      { "type": "capacities", "capacities": { "frozenVolume": 5, "chilledVolume": 5 }, "resourceTag": "reefer-van" }
    ]
  }
]
```

<Note>
  Each `capacities`-type constraint here is an AND across its own keys, enforced throughout the tour — the same as a resource's base `capacities`, just scoped to a `resourceTag`. `atLeastOneConstraint` then requires only one of the two full setups to hold, letting the engine pick whichever configuration fits the orders actually assigned to that resource. `AdditionalConstraintCapacities` doesn't report its own named violation and isn't meant to be used on its own at the top level of `additionalConstraints` — only as a member of `atLeastOneConstraint`, as shown here.
</Note>

### Sequencing pickups after deliveries

`atLeastOneValidCapacity` is a different constraint from the `atLeastOneConstraint` used for multi-compartment vehicles in the previous section. It lists capacity thresholds and watches the resource's running load for each of them over its tour: at every stop, at least one of the listed capacities must be at or below its threshold. Because this OR is re-checked at every stop, it can express a sequencing rule, not just a feasibility check.

A typical case: a crew delivering bulky items, such as sofas, also collects old ones at some customers. The load area works last in, first out, so an old item collected early would block the items still to be delivered, and the crew would have to unload and reload it at every following delivery. The rule to express is: only collect once every delivery is done, or once no more than 300 kg is left on board.

Add two counting capacities alongside `weight`: every delivery stop carries `nbDeliveries: 1`, and every pickup stop carries `nbPickups: 1`. Declare both on the resource too, with ceilings that never bind. Since a delivery stop deducts its capacities from the load and a pickup stop adds them, the resource leaves with `nbDeliveries` equal to its number of deliveries, `nbPickups` at `0`, and `weight` equal to the total weight to deliver. Over the tour, `nbDeliveries` falls to `0`, `nbPickups` rises by one with each pickup, and `weight` follows the actual cargo:

```json theme={null}
{
  "resources": [
    {
      "id": "truck-1",
      "vehicleProfile": { "type": "truck" },
      "workingTimeWindow": { "begin": "2026-08-03T07:00:00Z", "end": "2026-08-03T17:00:00Z" },
      "capacities": { "weight": 1200, "nbDeliveries": 30, "nbPickups": 10 }
    }
  ],
  "orders": [
    {
      "id": "order-delivery-1",
      "stops": [
        {
          "type": "single",
          "id": "delivery-1",
          "position": { "lon": 2.435, "lat": 48.805 },
          "kind": "delivery",
          "operationDuration": "PT20M",
          "capacities": { "weight": 80, "nbDeliveries": 1 }
        }
      ]
    },
    {
      "id": "order-pickup-1",
      "stops": [
        {
          "type": "single",
          "id": "pickup-1",
          "position": { "lon": 2.39, "lat": 48.81 },
          "kind": "pickup",
          "operationDuration": "PT15M",
          "capacities": { "weight": 60, "nbPickups": 1 }
        }
      ]
    }
  ],
  "additionalConstraints": [
    {
      "type": "atLeastOneValidCapacity",
      "name": "pickups-once-deliveries-done-or-under-300kg",
      "capacities": { "nbDeliveries": 0, "nbPickups": 0, "weight": 300 }
    }
  ]
}
```

The constraint is checked after every stop, including stops without a pickup, and at least one of three conditions must hold there: no delivery left (`nbDeliveries` at or below `0`), no pickup made yet (`nbPickups` at or below `0`), or no more than 300 kg on board (`weight` at or below `300`). While no pickup has happened, the `nbPickups` condition holds on its own, so deliveries can run in any order at the start of the tour. A pickup always raises `nbPickups` to at least `1`, and it never goes back to `0`, so from that stop to the end of the tour the resource must either have finished its deliveries or stay at or under 300 kg — otherwise that tour is infeasible. See the [toolkit fetch example](#optional-steps-that-gate-a-mandatory-stop) further down for the same OR-at-every-stop mechanism traced step by step.

<Note>
  This models sequencing through ordinary `capacities`, not through `AlternativesStop` — no gating stop is introduced, the ordering falls directly out of how `nbDeliveries` and `nbPickups` move over the tour. To forbid pickups while any delivery remains, whatever the weight, drop the weight threshold: `{ "nbDeliveries": 0, "nbPickups": 0 }`.
</Note>

### Tolerated capacity overflow

By default, exceeding a capacity makes a stop unplannable rather than degrading gracefully. If the business would rather accept an occasional overload than drop a stop, set the resource's hard `capacities` ceiling at the absolute physical or legal limit, and add a `cost.costsByCapacity` entry that starts charging past the load you normally plan to:

```json theme={null}
{
  "id": "van-1",
  "capacities": { "weight": 1000 },
  "cost": {
    "costsByCapacity": {
      "weight": { "costFloor": 800, "costCoeff": 1.0, "overcostFloor": 900, "overcostCoeff": 50 }
    }
  }
}
```

The cost is computed on the total `weight` handled at the tour's stops, not on the vehicle's load (see [Piecewise-linear cost functions](/guides/cost-modeling#piecewise-linear-cost-functions)). On a delivery-only tour, that total is the load at departure, so the thresholds read as a load. Up to 800 kg the load costs nothing. Between 800 and 900 kg, each extra kilogram costs 1. Between 900 and 1,000 kg, the steeper `overcostCoeff` takes over at 50 per kilogram. Beyond 1,000 kg, the hard ceiling still makes the stop unplannable. With `minimizeCosts` in `objectives`, this makes overflow economically unattractive — the engine avoids it whenever another solution exists — without declaring a stop infeasible until the real limit is reached. Keep `overcostFloor` strictly below the `capacities` ceiling: a floor equal to or higher than the ceiling can never be reached, so its `overcostCoeff` never applies.

<Warning>
  This pattern only measures overload on delivery-only tours. As soon as a tour includes pickups, every kilogram loaded and then unloaded counts twice, and the thresholds no longer track what the vehicle carries: a van that leaves with 700 kg, drops it all, then picks up 150 kg never carries more than 700 kg, yet its 850 kg handled cross the 800 kg floor.
</Warning>

### Creative capacity modeling patterns

Capacities don't have to track anything physical. Two patterns reuse the same mechanism for a different purpose:

* **Bounding a number of visits.** Declare a synthetic capacity that has nothing to do with load, and increment it once per relevant stop, to cap how many such stops a single tour can include — for example, at most 3 visits to a restricted zone per tour:

```json theme={null}
{
  "resources": [
    { "id": "van-1", "capacities": { "restrictedZoneVisits": 3 } }
  ],
  "orders": [
    { "id": "order-1", "stops": [ { "id": "stop-1", "capacities": { "restrictedZoneVisits": 1 } } ] }
  ]
}
```

* **Overlapping capacity dimensions.** Two capacity keys can be declared independently on the same stops, for example a global `weight` alongside a `dangerousGoodsWeight` subset of it, each enforced on its own ceiling without one having to derive from the other.

Both are ordinary uses of `capacities` (see [Data model](/reference/data-model#capacities)), just detached from any real-world unit.

### LIFO unloading order for stacked cargo

`removalStrategy: "lifo"` enforces a physical loading order: pickups made in the order P1 → P2 → P3 must then be unloaded in the reverse order D3 → D2 → D1 — the last thing loaded is the first thing unloaded.

```json theme={null}
{
  "additionalConstraints": [
    {
      "type": "removalStrategy",
      "name": "lifo-rule-1",
      "removalStrategy": "lifo",
      "capacities": ["volume", "length"],
      "resourceTags": ["car-carrier"]
    }
  ]
}
```

Reserve this for a genuinely stacked or sequential-loading vehicle, such as a car carrier loading vehicles nose-to-tail on a single deck, or a multi-deck cage truck — see [Hard vs soft constraints](/concepts/hard-vs-soft-constraints) for `removalStrategy` among the other structural hard constraints.

## Driver skills and qualifications

`requiredSkills` on an order and `skills` on a resource are the hard match: a resource can only be assigned an order if it has *every* skill the order requires (a certification, an equipment qualification, and so on).

```json theme={null}
{
  "resources": [
    { "id": "technician-1", "skills": ["gas-certification", "electrical-certification"] }
  ],
  "orders": [
    { "id": "order-1", "requiredSkills": ["gas-certification"], "stops": [ { "...": "..." } ] }
  ]
}
```

When the match should be a preference rather than a requirement — a senior technician *should* handle a demanding job, but a generalist can still cover it if needed — tag the stop and use `preferredStopTags` on the resource with `maximizePreferredStops` in the objectives, instead of `requiredSkills`:

```json theme={null}
{
  "resources": [
    { "id": "technician-senior", "preferredStopTags": ["skill:senior-preferred"] }
  ],
  "orders": [
    { "id": "order-1", "stops": [ { "id": "stop-1", "tags": ["skill:senior-preferred"], "...": "..." } ] }
  ],
  "objectives": ["maximizeMandatoryStops", "maximizePreferredStops", "minimizeCosts"]
}
```

The engine assigns the tagged stop to a matching resource when it can do so without hurting higher-priority objectives, but falls back to any capable resource rather than leaving the stop unplanned.

## Mandatory breaks

A resource's `breaks` array accepts three break types, and they aren't mutually exclusive — combine them to model a realistic shift:

| Type | Triggered by | Typical use |
| - | - | - |
| `timeWindowBreak` | A fixed clock time | A lunch break that must happen inside a specific window, regardless of workload |
| `workingDurationSlidingBreak` | Cumulative working time | Labor-contract rules — duration varies by sector/collective agreement, don't assume a figure here without confirming it |
| `travelDurationSlidingBreak` | Cumulative driving time | Road-safety regulation — for example, EU Regulation (EC) 561/2006 sets a public, jurisdiction-wide minimum of 45 min after every 4h30 of driving for professional road transport, regardless of employer |

```json theme={null}
{
  "id": "driver-1",
  "workingTimeWindow": { "begin": "2026-08-03T05:00:00Z", "end": "2026-08-03T20:00:00Z" },
  "breaks": [
    { "type": "workingDurationSlidingBreak", "minBreakDuration": "PT30M", "maxInterBreakDuration": "PT6H" },
    { "type": "travelDurationSlidingBreak", "minBreakDuration": "PT45M", "maxInterBreakDuration": "PT4H30M" }
  ]
}
```

The two preceding rows aren't interchangeable in how confidently you can default them: the driving-time figure is set by public statute independent of any single employer, so `PT45M`/`PT4H30M` is safe to apply outright once you've established the jurisdiction (see [§4 of the agent modeling checklist](/getting-started/agent-modeling-checklist#4-do-not-guess-on-vehicle-profile-break-triggers-or-other-underspecified-fields-check-what-the-data-actually-supports)). The working-time figure is typically set by a sector- or company-level collective agreement layered on top of a lower statutory floor, so a single number here would be wrong for many employers — don't copy a duration from this table for `workingDurationSlidingBreak`; treat it as an open question for client confirmation instead.

Both sliding breaks count the same break toward their respective counters — the engine looks for a single moment that satisfies both regulations rather than scheduling them separately. For long shifts, define both the driving-time and the working-time rule together; a single sliding break only covers one of the two regulatory clocks.

Sharing a single break between two counters isn't limited to the two sliding break types — any two breaks that overlap in time count once toward both, for example a `timeWindowBreak` that coincides with a `workingDurationSlidingBreak`'s window.

### Modeling multi-day sleepovers

A `workingTimeWindow` spanning more than one calendar day doesn't by itself force any rest between working days (see the `Modelling pitfall` on `workingTimeWindow` in the [API reference](/api-reference/plan/create-a-plan#body-resources)) — encode the rest explicitly in `breaks`:

```json theme={null}
{
  "id": "driver-1",
  "workingTimeWindow": { "begin": "2026-08-03T05:00:00Z", "end": "2026-08-04T20:00:00Z" },
  "breaks": [
    { "type": "workingDurationSlidingBreak", "minBreakDuration": "PT11H", "maxInterBreakDuration": "PT13H" },
    { "type": "timeWindowBreak", "duration": "PT11H", "timeWindow": { "begin": "2026-08-03T19:00:00Z", "end": "2026-08-04T07:00:00Z" } }
  ]
}
```

A long `workingDurationSlidingBreak` (here `PT11H`, illustrative — confirm the applicable convention or regulation before publishing, same caveat as the legal-break figures earlier on this page) forces a full night's rest; the optional `timeWindowBreak` further constrains the window that rest has to fall into, for example to prevent a restart before a given hour the next day. Extend `workingTimeWindow` further for more than two days.

## Multiple time windows per stop

`authorizedTimeWindows` accepts an array, so a stop can have more than one hard, disjoint window — for example, a site open in the morning and again in the late afternoon, closed in between:

```json theme={null}
{
  "id": "stop-1",
  "authorizedTimeWindows": [
    { "begin": "2026-08-03T08:00:00Z", "end": "2026-08-03T12:00:00Z" },
    { "begin": "2026-08-03T16:00:00Z", "end": "2026-08-03T20:00:00Z" }
  ]
}
```

The engine treats these as independent options — the stop is feasible if it can be reached in *any* one of them. `preferredTimeWindows` layers a soft target on top (see [Hard vs soft constraints](/concepts/hard-vs-soft-constraints)); it's always intersected with the authorized windows, so it narrows the target without ever widening what's actually reachable.

`begin`/`end` bound the resource's *arrival* at the stop, not when it finishes — see the `Modelling pitfall` on `authorizedTimeWindows` in the [API reference](/api-reference/plan/create-a-plan#body-orders) for how to account for `operationDuration` when a strict finish-by deadline matters.

### Restricting a window to specific resources

A window can also apply only to resources carrying a given tag — for instance, an early access slot reserved for a certified subcontractor, while other resources only see the standard window:

```json theme={null}
{
  "id": "stop-1",
  "authorizedTimeWindows": [
    { "begin": "2026-08-03T06:00:00Z", "end": "2026-08-03T08:00:00Z", "resourceTags": ["subcontractorA"] },
    { "begin": "2026-08-03T08:00:00Z", "end": "2026-08-03T18:00:00Z" }
  ]
}
```

A resource without the matching tag simply doesn't have the tagged window available to it — it's constrained to whichever windows apply to everyone.

## Optional steps that gate a mandatory stop

<Tip>
  This section is more advanced than the rest of this page — skip it unless you have an order where a downstream mandatory stop's feasibility depends on whether an earlier optional step already ran. For a sequencing rule expressed through plain `capacities` instead, without an `AlternativesStop`, see [Sequencing pickups after deliveries](#sequencing-pickups-after-deliveries).
</Tip>

Not every optional step needs its own `AlternativesStop`. Only reach for it when skipping the step would make a *downstream mandatory stop* infeasible — for example an order that needs to collect a spare part or piece of equipment before a mandatory service visit, but only when the resource isn't already carrying it. If a step is either always required or never affects feasibility, a plain `SingleStop` (or no extra stop at all) is enough; `AlternativesStop` exists for the case where whether it's needed depends on context the plan itself has to resolve, not on something you can decide upfront.

**This is a per-order decision, applied unconditionally to every order with this dependency — it is not an aggregate feasibility check on whether enough of the item exists across the fleet.** Whether *this* order's assigned resource already happens to be carrying the item, on *this* specific tour, is something the engine resolves per instance; it isn't something you can decide upfront from a stock count. A fleet-wide total that looks "sufficient" says nothing about whether any single resource, on any single tour, already has the unit it needs at the point it's needed — so don't skip modeling the `AlternativesStop` for an order just because some aggregate check elsewhere says the item isn't scarce. That aggregate question — "can this load physically fit at all, across the fleet" — is a separate, complementary mechanism: see [§2 of 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) and `atLeastOneConstraint` in the [constraints catalog](/reference/data-model#constraints-catalog). Don't conflate the two: an aggregate feasibility check answers "is there enough overall"; `AlternativesStop` answers "does *this* resource need a detour, right now, on this tour" — and the two questions can have opposite answers on the same plan.

<Warning>
  **The gate and the mandatory stop it conditions are two stops of the *same* `Order` — not two separate `Order`s.** Put the `AlternativesStop` gate first in that order's `stops` array and the mandatory stop right after it: array position doubles as precedence here, same as everywhere else in the API (see [Order, Stop](/reference/data-model#order-stop)). Splitting them into two `Order`s is the single most common way to get this pattern wrong — it looks reasonable, validates against the schema, and still produces a materially different plan, because a solver-assigned mandatory stop can then land on a *different* resource, or a different position in the tour, than the gate that was supposed to precede it.
</Warning>

<Accordion title="Full worked example: toolkit fetch gating a mandatory visit">
  Model the gated step as an `AlternativesStop` with two candidates — a real "fetch" stop, and a zero-effect placeholder — as the **first** stop of the order, immediately followed by the mandatory stop it conditions as the **second** stop of that same order. An `AlternativesStop` is not optional in itself: whenever its order is planned, the engine serves exactly one of its alternatives, and it serves none only if the whole order is left unplanned. The zero-effect placeholder is what makes the detour optional: the engine picks it instead of the fetch whenever the fetch isn't necessary. The item being fetched is typically an item a resource may already be carrying: a pooled resource drawn down by the mandatory stop and topped back up by the optional fetch immediately before it, both within the same order. Because both stops belong to the same order, the resource's running capacity balance already carries from one to the next by default — no pooling flag is needed for this. `sharedCapacities` (see [Shared capacity pools](/reference/data-model#shared-capacity-pools)) is a *different*, additional mechanism: it's for when the pool must be shared *across* separate orders on the same resource, which isn't what this pattern needs.

  ```json theme={null}
  {
    "resources": [
      {
        "id": "resource-1",
        "vehicleProfile": { "type": "car" },
        "capacities": { "toolkit-available": 1, "toolkit-placed": 1 }
      }
    ],
    "orders": [
      {
        "id": "order-1",
        "stops": [
          {
            "type": "alternatives",
            "id": "fetch-if-needed",
            "alternatives": [
              {
                "type": "single",
                "id": "fetch-at-supply-point",
                "position": { "lat": 48.86, "lon": 2.35 },
                "kind": "pickup",
                "operationDuration": "PT10M",
                "capacities": { "toolkit-available": -1 }
              },
              {
                "type": "single",
                "id": "no-detour-needed",
                "position": { "lat": 48.80, "lon": 2.40 },
                "operationDuration": "PT0S"
              }
            ]
          },
          {
            "type": "single",
            "id": "mandatory-visit",
            "position": { "lat": 48.80, "lon": 2.40 },
            "kind": "delivery",
            "operationDuration": "PT30M",
            "capacities": { "toolkit-placed": 1 }
          }
        ]
      }
    ],
    "additionalConstraints": [
      {
        "type": "atLeastOneValidCapacity",
        "name": "toolkit-fetched-before-placed",
        "capacities": { "toolkit-placed": 0, "toolkit-available": -1 }
      }
    ]
  }
  ```

  <Warning>
    **Don't set an `atLeastOneValidCapacity` threshold equal to the resource's own ceiling for that key**. `atLeastOneValidCapacity` requires that, **at every stop**, at least one of the listed capacities is at or below its threshold — an OR across the listed capacities, not a requirement that they all hold together. If a threshold equals the resource's own ceiling for that key, the base `capacities` limit already guarantees that branch of the OR holds at every stop on its own, so the additional constraint is always trivially satisfied and adds nothing. Here the threshold on `toolkit-available` has to sit **strictly below** the resource's ceiling of `1` — this example uses `-1`, not `1` — precisely so that a plan which never fetches can never make that branch of the OR true. `toolkit-placed`'s threshold is tightened to `0` for the same reason, even though it isn't the one doing the discriminating below.
  </Warning>

  `order-1`'s `mandatory-visit` — listed **second**, right after the gate — needs one unit already on board to be feasible, and that unit is supplied by the *same order*'s `fetch-at-supply-point`, listed first. Array position is what makes "earlier in the tour" mean anything here — no separate order and no `sharedCapacities` flag required, since both stops already belong to one order on one resource. The engine picks `fetch-at-supply-point` — paying its extra travel and `operationDuration` — only when nothing earlier in the tour already put a unit into the pool; otherwise it picks the zero-effect `no-detour-needed` placeholder, whose position matches the very next stop so it adds no extra travel at all. This choice is made independently for every order with this dependency, from the actual, resource-specific state of the pool at that point in that tour — never from a fleet-wide count of how many units exist in total. Both alternatives are still mandatory to *evaluate* — the choice itself isn't optional, only its real-world outcome (a detour, or none) is.

  Trace the numbers to see why each branch lands where it does. `fetch-at-supply-point` is a `pickup` stop with a **negative** `capacities` value, `{ "toolkit-available": -1 }`: a `pickup` *adds* its `capacities` value to the running total, so adding `-1` nets to a decrease — the negative value reverses the pickup's usual effect (see the note on `kind` and the sign of `capacities` under [Order, Stop](/reference/data-model#order-stop)). `mandatory-visit` never lists `toolkit-available` at all, so nothing else in the order ever touches it.

  * **If `fetch-at-supply-point` is picked:** check, at every stop, whether `toolkit-placed` is at or below `0` or `toolkit-available` is at or below `-1`. Before the fetch, `toolkit-placed` is still `0`, so the first branch holds. Right after the fetch, `toolkit-available` drops to `-1`, so the second branch also holds, and `toolkit-placed` is still `0` until `mandatory-visit` runs, so the first branch keeps holding there too. Once `mandatory-visit` runs, `toolkit-placed` becomes `1` (first branch now false), but `toolkit-available` is still `-1` from the earlier fetch (second branch still true). At every stop, at least one branch holds, so `atLeastOneValidCapacity` is satisfied throughout and the plan is feasible.
  * **If `no-detour-needed` is picked instead:** `toolkit-available` never moves off `0` — nothing in this branch ever writes to it — so it's never at or below `-1`, and the second branch is false at every stop. Before `mandatory-visit`, `toolkit-placed` is still `0`, so the first branch holds and the constraint is fine so far. But once `mandatory-visit` runs, `toolkit-placed` becomes `1`: now neither branch holds at that stop (`1` isn't at or below `0`, and `toolkit-available` still isn't at or below `-1`). `atLeastOneValidCapacity` is violated at that stop, and the plan is correctly rejected as infeasible.

  That's the concrete sequencing this constraint rules out: skipping the fetch and still performing `mandatory-visit` is fully ceiling-compliant (`toolkit-available` never exceeds `1`; `toolkit-placed` never exceeds `1`) — an "otherwise valid-looking" plan by the base capacity check alone — but it's rejected anyway, because it produces a stop, right after `mandatory-visit`, where neither branch of the OR holds. A reader can re-run this trace against the preceding JSON snippet to confirm it.

  Notice the two decoupled capacity keys, `toolkit-available` (whether the fetch has already happened, tracked as a signed credit) and `toolkit-placed` (consumed by the mandatory stop), rather than a single `toolkit` key shared by both stops. `mandatory-visit` never lists `toolkit-available`, and `fetch-at-supply-point` never lists `toolkit-placed` — the two keys stay fully independent capacities, each still validated and reportable on its own terms. A single net key would collapse two different questions the solver needs to answer separately — "how much has this order already placed" and "how much is currently available to it" — into one number that only reports their difference, hiding which side is actually short whenever the pattern needs to be checked or reported on independently. It's the plan-level `atLeastOneValidCapacity` [additional constraint](/reference/data-model#constraints-catalog) — not a shared running total — that ties the two independent keys together into the "fetch before place" rule.
</Accordion>

<Note>
  `AlternativesStop` increases optimization time — see [Sizing `maxOptimizationDuration` for a large problem](/guides/handling-large-volumes#sizing-maxoptimizationduration-for-a-large-problem). Reserve it for steps whose necessity genuinely depends on the rest of the plan; don't reach for it just to express "this step is optional" when a plain `optional: true` order, or leaving the step out entirely, would do.
</Note>

## See also

* [Hard vs soft constraints](/concepts/hard-vs-soft-constraints) — the general hard/soft distinction referenced throughout this page.
* [Handling infeasibility](/guides/handling-infeasibility) — what happens when none of the preceding can be satisfied for a given stop.
* [Data model](/reference/data-model) — an overview of `capacities`, `skills`, `breaks`, time windows, and stops, each linking to its schema in the API reference.


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