@solvice/routing 0.0.0-stage → 0.1.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/v3.d.ts ADDED
@@ -0,0 +1,4138 @@
1
+ /**
2
+ * This file was auto-generated by openapi-typescript.
3
+ * Do not make direct changes to the file.
4
+ */
5
+ export interface paths {
6
+ "/v3/routing/evaluate": {
7
+ parameters: {
8
+ query?: never;
9
+ header?: never;
10
+ path?: never;
11
+ cookie?: never;
12
+ };
13
+ get?: never;
14
+ put?: never;
15
+ /**
16
+ * Evaluate an imported solution
17
+ * @description Scores the `plan` you send against `problem` without searching for a better one. Returns the plan's cost, timing and loads, and lists each constraint violation on the stop or route where it occurs; `summary.score.violations` is 0 when there are none. Tasks that appear in no route are reported in `unassigned[]`. `problem` has the same shape as on `POST /v3/routing/solve`, and a solve response's `plan` can be sent as it stands.
18
+ */
19
+ post: operations["evaluate"];
20
+ delete?: never;
21
+ options?: never;
22
+ head?: never;
23
+ patch?: never;
24
+ trace?: never;
25
+ };
26
+ "/v3/routing/solve": {
27
+ parameters: {
28
+ query?: never;
29
+ header?: never;
30
+ path?: never;
31
+ cookie?: never;
32
+ };
33
+ get?: never;
34
+ put?: never;
35
+ /**
36
+ * Solve a vehicle routing problem
37
+ * @description Assigns the jobs and shipments in `problem` to the vehicles' shifts and returns the routes, the `plan` they follow and any tasks left unassigned, each with the reasons. The request is validated first: an invalid one is rejected with a 400 whose `errors[]` says what to fix. Travel times and distances for the request's locations are then looked up, and the search runs for `options.time_limit_s` seconds (default 1). Send a previous result's `plan` to re-optimize starting from it. `POST /v3/routing/evaluate` and `POST /v3/routing/suggest` take the same `problem`.
38
+ */
39
+ post: operations["solve"];
40
+ delete?: never;
41
+ options?: never;
42
+ head?: never;
43
+ patch?: never;
44
+ trace?: never;
45
+ };
46
+ "/v3/routing/suggest": {
47
+ parameters: {
48
+ query?: never;
49
+ header?: never;
50
+ path?: never;
51
+ cookie?: never;
52
+ };
53
+ get?: never;
54
+ put?: never;
55
+ /**
56
+ * Suggest best insertions for the tasks outside the plan
57
+ * @description Takes the same body as `POST /v3/routing/evaluate` (`problem` + committed `plan`), plus optional `options`. Every task of `problem` that is not in `plan` is a task to place: finds its feasible insertion positions and returns them as `insertions`, ranked by `delta.cost`, the change in the plan's cost in money. Each insertion lists the stops it adds (`placements`) and every plan route it changes, whole (`routes`): swap those routes into `plan` to apply it. An empty `insertions` carries a `reason_code` (`time_limit`, `candidates_exhausted` or `infeasible`; an open set). The committed plan is never modified. Limitation: suggest never opens a new reload trip on a multi-trip vehicle.
58
+ */
59
+ post: operations["suggest"];
60
+ delete?: never;
61
+ options?: never;
62
+ head?: never;
63
+ patch?: never;
64
+ trace?: never;
65
+ };
66
+ }
67
+ export type webhooks = Record<string, never>;
68
+ export interface components {
69
+ schemas: {
70
+ /**
71
+ * @description Absolute price of serving one task on one vehicle.
72
+ * @example {
73
+ * "cost": 12.5,
74
+ * "vehicle": "v1"
75
+ * }
76
+ */
77
+ AssignmentCost: {
78
+ /**
79
+ * Format: double
80
+ * @description Cost, in the objective's currency, incurred when this vehicle serves
81
+ * the task. Must be finite and `>= 0`. A vehicle not listed costs `0`.
82
+ * @example 12.5
83
+ */
84
+ cost: number;
85
+ /**
86
+ * @description Vehicle id; applies to every shift of that vehicle. An id that matches
87
+ * no vehicle is rejected with HTTP 400.
88
+ * @example v1
89
+ */
90
+ vehicle: string;
91
+ };
92
+ /**
93
+ * @description Named capacity/demand/load dimensions, e.g. `{"weight": 30, "volume": 2}`. Keys are user-chosen dimension names; values are integer quantities. Order-independent and self-documenting.
94
+ * @example {
95
+ * "volume": 2,
96
+ * "weight": 20
97
+ * }
98
+ */
99
+ BTreeMap: {
100
+ [key: string]: number;
101
+ };
102
+ /**
103
+ * @description A scheduled break within a shift. Tagged by `type`:
104
+ *
105
+ * - `windowed`: a break of `duration_s` that starts inside its window.
106
+ * - `triggered`: a break of `duration_s` taken once drive or duty time
107
+ * passes `threshold_s` (the EU-561 mechanism).
108
+ * - `fixed`: a hard off-duty interval from `from` to `to`.
109
+ *
110
+ * Breaks are injected between stops and appear in the response as `break`
111
+ * stops.
112
+ *
113
+ * **Not yet supported** — rejected with HTTP 400 `not_supported`: more than
114
+ * one `windows` entry, a `location` on a `triggered` break, and
115
+ * `resets_accumulator: false`.
116
+ *
117
+ * There is no paid/unpaid cost accounting: a break's duration is never
118
+ * separately priced, and no `paid` field exists on this schema — sending one
119
+ * is rejected as an unknown field (HTTP 400, `unknown_field`), like any other extra key.
120
+ * @example {
121
+ * "after": "drive",
122
+ * "duration_s": 1800,
123
+ * "threshold_s": 14400,
124
+ * "type": "triggered"
125
+ * }
126
+ */
127
+ Break: {
128
+ /**
129
+ * Format: int32
130
+ * @description Break duration in seconds.
131
+ */
132
+ duration_s: number;
133
+ /** @description Where the break is taken. Absent: wherever the vehicle is. */
134
+ location?: null | components["schemas"]["Place"];
135
+ /** @enum {string} */
136
+ type: "windowed";
137
+ /**
138
+ * @description The window bounding the break's **start**. Exactly one entry; a
139
+ * second one is rejected with HTTP 400 (`not_supported`).
140
+ */
141
+ windows: components["schemas"]["BreakWindow"][];
142
+ } | {
143
+ /** @description What to accumulate: drive time or duty time. */
144
+ after: components["schemas"]["TriggerAfter"];
145
+ /**
146
+ * Format: int32
147
+ * @description Break duration in seconds.
148
+ */
149
+ duration_s: number;
150
+ /** @description Not supported yet: any value is rejected with HTTP 400. */
151
+ location?: null | components["schemas"]["Place"];
152
+ /**
153
+ * @description Whether the accumulator resets after the break. Default `true` (the
154
+ * EU-561 semantics; the accumulation window restarts after a qualifying
155
+ * break). `false` is not supported yet and is rejected with HTTP 400.
156
+ * @example true
157
+ */
158
+ resets_accumulator?: boolean | null;
159
+ /**
160
+ * Format: int64
161
+ * @description Trigger threshold in seconds (strict `>` — the break fires when the
162
+ * accumulator exceeds this). EU 561/2006 drive rule: `16200` (4 h 30).
163
+ * @example 14400
164
+ */
165
+ threshold_s: number;
166
+ /** @enum {string} */
167
+ type: "triggered";
168
+ } | {
169
+ /**
170
+ * Format: date-time
171
+ * @description Start of the break (RFC 3339 instant, UTC offset required).
172
+ */
173
+ from: string;
174
+ /**
175
+ * Format: date-time
176
+ * @description End of the break (RFC 3339 instant, UTC offset required).
177
+ */
178
+ to: string;
179
+ /** @enum {string} */
180
+ type: "fixed";
181
+ };
182
+ /**
183
+ * @description The window a `windowed` break must start in. Always hard.
184
+ * @example {
185
+ * "from": "2026-07-01T12:00:00+02:00",
186
+ * "to": "2026-07-01T13:30:00+02:00"
187
+ * }
188
+ */
189
+ BreakWindow: {
190
+ /**
191
+ * Format: date-time
192
+ * @description Earliest break start (RFC 3339 instant, UTC offset required).
193
+ */
194
+ from: string;
195
+ /**
196
+ * Format: date-time
197
+ * @description Latest break start (RFC 3339 instant, UTC offset required).
198
+ */
199
+ to: string;
200
+ };
201
+ /**
202
+ * @description What running a vehicle costs, in the request's currency
203
+ * (`objective.currency` is only a label). The same type at three levels:
204
+ * `objective.costs` prices the fleet, `vehicles[].costs` replaces a field for
205
+ * one vehicle, and `shifts[].costs` replaces it for one shift. Each field
206
+ * resolves on its own: the shift's value, else the vehicle's, else the
207
+ * fleet's, else the default. Nothing is added on top of a fleet rate, and
208
+ * every field is reported under the same `score.components` key at every
209
+ * level. `0` means priced at zero; absent means inherit.
210
+ *
211
+ * Rates are rounded to the internal grid (1/100 000 of a currency unit per
212
+ * second or per metre); a nonzero rate too small to represent is rejected.
213
+ * Every value must be finite and `>= 0`.
214
+ * @example {
215
+ * "fixed": 50,
216
+ * "per_route_hour": 35,
217
+ * "per_travel_km": 0.2
218
+ * }
219
+ */
220
+ Costs: {
221
+ /**
222
+ * Format: double
223
+ * @description Flat charge for each used shift (a route with at least one task),
224
+ * reported in `score.components.fixed`. Default `0`. Pricing vehicles is
225
+ * how the solver is made to use fewer of them: the higher it is, the
226
+ * more travel it accepts to avoid opening another route.
227
+ * @example 50
228
+ */
229
+ fixed?: number | null;
230
+ /**
231
+ * Format: double
232
+ * @description Per hour a route finishes past its shift's `to` (possible only with
233
+ * `max_overtime_s`), reported in `score.components.overtime`, on top of
234
+ * `per_route_hour`. Default `0`: overtime is unpriced.
235
+ * @example 0
236
+ */
237
+ per_overtime_hour?: number | null;
238
+ /**
239
+ * Format: double
240
+ * @description Per hour of each route's wall-clock span (travel, service, waiting
241
+ * and breaks), reported in `score.components.time`. Default **35**.
242
+ * @example 35
243
+ */
244
+ per_route_hour?: number | null;
245
+ /**
246
+ * Format: double
247
+ * @description Per kilometre driven, reported in `score.components.distance`.
248
+ * Default **0.20**.
249
+ * @example 0.2
250
+ */
251
+ per_travel_km?: number | null;
252
+ /**
253
+ * Format: double
254
+ * @description Per hour spent waiting for a time window to open, reported in
255
+ * `score.components.waiting`, on top of `per_route_hour` (the span
256
+ * includes the wait). Default `0`.
257
+ * @example 0
258
+ */
259
+ per_wait_hour?: number | null;
260
+ };
261
+ /**
262
+ * @description A depot: a place a shift starts from, ends at, or reloads at mid-shift,
263
+ * referenced by `id` from `shifts[].start`, `shifts[].end` and
264
+ * `shifts[].reloads`. Each depot keeps its own handling time and visit cost.
265
+ * @example {
266
+ * "id": "gent",
267
+ * "location": [
268
+ * 3.7174,
269
+ * 51.0543
270
+ * ],
271
+ * "service_duration_s": 900
272
+ * }
273
+ */
274
+ Depot: {
275
+ /**
276
+ * Format: double
277
+ * @description Money charged once per reload visit, in the objective's currency.
278
+ * Default 0. A shift that only starts or ends here is not charged.
279
+ *
280
+ * Set this when a reload has a real per-visit price the travel terms
281
+ * cannot see — typically the driver labour for the unload itself
282
+ * (`service_duration_s` × the hourly rate). Without it, a reload that
283
+ * consumes only *slack* is free in the objective: it adds no route
284
+ * duration, so the solver will happily insert one to displace time that
285
+ * `objective.costs.per_wait_hour` would otherwise charge as idle, parking
286
+ * the vehicle at a depot for no capacity benefit.
287
+ * @example 38.66
288
+ */
289
+ cost_per_visit?: number | null;
290
+ /**
291
+ * @description Depot id: 1 to 128 characters, unique across `depots[]`.
292
+ * @example gent
293
+ */
294
+ id: string;
295
+ /** @description Where the depot is. */
296
+ location: components["schemas"]["Place"];
297
+ /**
298
+ * Format: int32
299
+ * @description Handling time in seconds per visit: the load before a shift that starts
300
+ * here, the unload after a shift that ends here, and each reload. A shift
301
+ * end's own `service_duration_s` overrides it for that end. Default 0.
302
+ * The unload after a shift must finish by that shift's `to` (or
303
+ * `to + max_overtime_s`). Not scaled by `service_factor`: this is
304
+ * depot-side handling, not the driver's on-site work rate.
305
+ * @example 900
306
+ */
307
+ service_duration_s?: number | null;
308
+ };
309
+ /**
310
+ * @description Hard vehicle eligibility: whitelist or blacklist (mutually exclusive).
311
+ * @example {
312
+ * "allowed": [
313
+ * "v1",
314
+ * "v2"
315
+ * ]
316
+ * }
317
+ */
318
+ Eligibility: {
319
+ /** @description Hard whitelist of vehicle ids. Omit = all vehicles allowed. */
320
+ allowed?: string[] | null;
321
+ /** @description Hard blacklist of vehicle ids. Mutually exclusive with `allowed`. */
322
+ excluded?: string[] | null;
323
+ };
324
+ /**
325
+ * @description Error body of every `/v3/routing/*` endpoint.
326
+ *
327
+ * `status` is the HTTP status as text, `code` says what kind of failure it
328
+ * is, `request_id` identifies the request, and `errors` holds one item per
329
+ * problem found in the request. Branch on `code`, and on `errors[].code` for
330
+ * 400s; the messages are for people.
331
+ * @example {
332
+ * "code": "invalid_request",
333
+ * "errors": [
334
+ * {
335
+ * "code": "out_of_range",
336
+ * "message": "depot 'gent': service_duration_s must be >= 0, got -60",
337
+ * "pointer": "/problem/depots/0/service_duration_s"
338
+ * }
339
+ * ],
340
+ * "message": "depot 'gent': service_duration_s must be >= 0, got -60",
341
+ * "request_id": "req_7f3a2c90d1e4b5a6",
342
+ * "status": "BAD_REQUEST"
343
+ * }
344
+ */
345
+ ErrorBody: {
346
+ /** @description What kind of failure this is. One value per HTTP status class. */
347
+ code: components["schemas"]["ErrorCode"];
348
+ /** @description One entry per problem found in the request. Empty for non-input errors. */
349
+ errors?: components["schemas"]["ErrorItem"][];
350
+ /**
351
+ * @description Human-readable summary. With exactly one item it is that item's
352
+ * message; with several, `"{n} problems in request"` (at most 100 items
353
+ * are listed, and the message says when the list is cut).
354
+ */
355
+ message: string;
356
+ /** @description The `X-Request-Id` of the request (echoed, or generated when absent). */
357
+ request_id?: string | null;
358
+ /**
359
+ * @description The HTTP status as text, e.g. `"BAD_REQUEST"` or
360
+ * `"SERVICE_UNAVAILABLE"`.
361
+ * @example BAD_REQUEST
362
+ */
363
+ status: string;
364
+ };
365
+ /**
366
+ * @description The top-level `code` of an error body.
367
+ * @enum {string}
368
+ */
369
+ ErrorCode: "invalid_request" | "feature_not_enabled" | "payload_too_large" | "unsupported_media_type" | "internal" | "matrix_provider_error" | "overloaded";
370
+ /** @description One problem in a request. */
371
+ ErrorItem: {
372
+ /**
373
+ * Format: int64
374
+ * @description `limit_exceeded` only: the count this request has.
375
+ */
376
+ actual?: number | null;
377
+ /** @description What is wrong. */
378
+ code: components["schemas"]["ErrorItemCode"];
379
+ /**
380
+ * Format: int64
381
+ * @description `limit_exceeded` only: the published cap.
382
+ */
383
+ limit?: number | null;
384
+ /** @description Human-readable description. */
385
+ message: string;
386
+ /**
387
+ * @description RFC 6901 JSON Pointer into the request body, when the problem has a
388
+ * location. It is relative to the root of the body, so a field of the
389
+ * problem reads `/problem/...` (e.g.
390
+ * `"/problem/vehicles/0/shifts/0/from"`).
391
+ */
392
+ pointer?: string | null;
393
+ };
394
+ /**
395
+ * @description The `code` of one `errors[]` item. Clients should accept values they do
396
+ * not know yet.
397
+ * @enum {string}
398
+ */
399
+ ErrorItemCode: "invalid_json" | "invalid_type" | "unknown_field" | "missing_field" | "invalid_value" | "not_supported" | "out_of_range" | "duplicate_id" | "unknown_reference" | "conflicting_fields" | "limit_exceeded";
400
+ /**
401
+ * @description Request body for `POST /v3/routing/evaluate`.
402
+ *
403
+ * Scores `plan` against `problem` without running the optimizer: objective
404
+ * totals, timing, loads and constraint violations. Tasks absent from every
405
+ * route are reported as unassigned. A solve response's `plan` goes in as it
406
+ * stands.
407
+ * @example {
408
+ * "plan": {
409
+ * "routes": [
410
+ * {
411
+ * "shift": "mon",
412
+ * "stops": [
413
+ * {
414
+ * "id": "job-1",
415
+ * "type": "job"
416
+ * }
417
+ * ],
418
+ * "vehicle": "v1"
419
+ * }
420
+ * ]
421
+ * },
422
+ * "problem": {
423
+ * "jobs": [
424
+ * {
425
+ * "id": "job-1",
426
+ * "location": [
427
+ * 3.725,
428
+ * 51.05
429
+ * ],
430
+ * "pickup": {
431
+ * "weight": 10
432
+ * },
433
+ * "service_duration_s": 300
434
+ * }
435
+ * ],
436
+ * "vehicles": [
437
+ * {
438
+ * "capacity": {
439
+ * "weight": 500
440
+ * },
441
+ * "id": "v1",
442
+ * "shifts": [
443
+ * {
444
+ * "end": {
445
+ * "location": [
446
+ * 3.7174,
447
+ * 51.0543
448
+ * ]
449
+ * },
450
+ * "from": "2026-07-01T08:00:00+02:00",
451
+ * "id": "mon",
452
+ * "start": {
453
+ * "location": [
454
+ * 3.7174,
455
+ * 51.0543
456
+ * ]
457
+ * },
458
+ * "to": "2026-07-01T17:00:00+02:00"
459
+ * }
460
+ * ]
461
+ * }
462
+ * ]
463
+ * }
464
+ * }
465
+ */
466
+ EvaluateRequest: {
467
+ /** @description The plan to score. */
468
+ plan: components["schemas"]["Plan"];
469
+ /** @description Problem definition — identical to `POST /v3/routing/solve`. */
470
+ problem: components["schemas"]["ProblemInput"];
471
+ };
472
+ /**
473
+ * @description Keep geographically clustered tasks consecutive in a route.
474
+ *
475
+ * Two tasks are in the same **group** when they lie within `radius_m` of
476
+ * each other. The solver adds `cost_per_reentry` to the objective each time
477
+ * a route leaves a group and comes back — minimising the "passing-waypoint"
478
+ * / area re-entry count last-mile fleets track. Presence of this object
479
+ * enables the feature; omit it to disable.
480
+ * @example {
481
+ * "cost_per_reentry": 0.5,
482
+ * "distance": "straight_line",
483
+ * "radius_m": 180
484
+ * }
485
+ */
486
+ Grouping: {
487
+ /**
488
+ * Format: double
489
+ * @description Cost added to the objective per group re-entry, in the objective's
490
+ * currency. At most 1000 currency units: a larger value is rejected with
491
+ * HTTP 400. Higher keeps clusters together more aggressively at the
492
+ * expense of total travel.
493
+ * @example 0.5
494
+ */
495
+ cost_per_reentry: number;
496
+ /** @description How neighbour distance is measured. Default `"straight_line"`. */
497
+ distance?: null | components["schemas"]["GroupingDistance"];
498
+ /**
499
+ * Format: int32
500
+ * @description Radius defining a group, in metres. Default `180`.
501
+ * @example 180
502
+ */
503
+ radius_m?: number | null;
504
+ };
505
+ /**
506
+ * @description How neighbour distance is measured for `objective.grouping`.
507
+ * @example straight_line
508
+ * @enum {string}
509
+ */
510
+ GroupingDistance: "straight_line" | "road";
511
+ /**
512
+ * @description How an option changes the plan: the change in its score's `cost` and in
513
+ * its routes' summed `distance_m` and `time.travel_s`. Equal to the difference
514
+ * between two `/evaluate` calls, with and without the option (`cost` up to
515
+ * floating-point rounding).
516
+ * @example {
517
+ * "cost": 12.4,
518
+ * "distance_m": 2400,
519
+ * "duration_s": 240
520
+ * }
521
+ */
522
+ InsertionDelta: {
523
+ /**
524
+ * Format: double
525
+ * @description Change in the score's `cost`, in the request's currency. Negative when
526
+ * serving a task saves more `drop_fee` than it costs to route.
527
+ * @example 12.4
528
+ */
529
+ cost: number;
530
+ /**
531
+ * Format: int64
532
+ * @description Change in the summed route `distance_m`, in metres.
533
+ * @example 2400
534
+ */
535
+ distance_m: number;
536
+ /**
537
+ * Format: int64
538
+ * @description Change in the summed route `time.travel_s` (driving time only), in
539
+ * seconds.
540
+ * @example 240
541
+ */
542
+ duration_s: number;
543
+ };
544
+ /**
545
+ * @description One feasible way to place the whole unit.
546
+ * @example {
547
+ * "delta": {
548
+ * "cost": 12.4,
549
+ * "distance_m": 2400,
550
+ * "duration_s": 240
551
+ * },
552
+ * "placements": [
553
+ * {
554
+ * "arrival": "2026-07-01T09:30:00+02:00",
555
+ * "id": "job-2",
556
+ * "latest_arrival": "2026-07-01T10:15:00+02:00",
557
+ * "shift": "mon",
558
+ * "type": "job",
559
+ * "vehicle": "v1"
560
+ * }
561
+ * ],
562
+ * "routes": [
563
+ * {
564
+ * "shift": "mon",
565
+ * "stops": [
566
+ * {
567
+ * "id": "job-1",
568
+ * "type": "job"
569
+ * },
570
+ * {
571
+ * "id": "job-2",
572
+ * "type": "job"
573
+ * }
574
+ * ],
575
+ * "vehicle": "v1"
576
+ * }
577
+ * ]
578
+ * }
579
+ */
580
+ InsertionOption: {
581
+ /** @description What the option changes: `plan` with `routes` swapped in, minus `plan`. */
582
+ delta: components["schemas"]["InsertionDelta"];
583
+ /**
584
+ * @description Other idle vehicles, each on its own shift, that give the same `delta`
585
+ * once `vehicle` and `shift` are swapped for theirs in `placements` and
586
+ * `routes`. Present only when the option opens a vehicle with no stops
587
+ * in `plan`. Not guaranteed exhaustive.
588
+ */
589
+ interchangeable_vehicles?: components["schemas"]["ShiftRef"][];
590
+ /** @description Every stop the option adds, in route order. */
591
+ placements: components["schemas"]["Placement"][];
592
+ /**
593
+ * @description Every plan route the option changes, as it reads with the option in:
594
+ * the request's route (its `locked_count` kept), or a new one, with the
595
+ * placements inserted. Replace the routes of `plan` with the same
596
+ * `vehicle` and `shift` by these to get the plan `delta` scores.
597
+ */
598
+ routes: components["schemas"]["PlanRoute"][];
599
+ };
600
+ /**
601
+ * @description Ranked insertion suggestions for a single unit.
602
+ * @example {
603
+ * "insertions": [
604
+ * {
605
+ * "delta": {
606
+ * "cost": 12.4,
607
+ * "distance_m": 2400,
608
+ * "duration_s": 240
609
+ * },
610
+ * "placements": [
611
+ * {
612
+ * "arrival": "2026-07-01T09:30:00+02:00",
613
+ * "id": "job-2",
614
+ * "latest_arrival": "2026-07-01T10:15:00+02:00",
615
+ * "shift": "mon",
616
+ * "type": "job",
617
+ * "vehicle": "v1"
618
+ * }
619
+ * ],
620
+ * "routes": [
621
+ * {
622
+ * "shift": "mon",
623
+ * "stops": [
624
+ * {
625
+ * "id": "job-1",
626
+ * "type": "job"
627
+ * },
628
+ * {
629
+ * "id": "job-2",
630
+ * "type": "job"
631
+ * }
632
+ * ],
633
+ * "vehicle": "v1"
634
+ * }
635
+ * ]
636
+ * }
637
+ * ],
638
+ * "job_id": "job-2"
639
+ * }
640
+ */
641
+ JobSuggestions: {
642
+ /**
643
+ * @description Ranked feasible insertions, lowest `delta.cost` first. Empty if none
644
+ * was found; `reason_code` then says why. Known limitation: suggest
645
+ * never opens a **new reload trip** on a multi-trip vehicle.
646
+ */
647
+ insertions: components["schemas"]["InsertionOption"][];
648
+ /**
649
+ * @description The unit's lead: its first member in placement order (the visit, the
650
+ * shipment, or the first relation member).
651
+ * @example job-2
652
+ */
653
+ job_id: string;
654
+ /**
655
+ * @description `reason_code` as an English sentence, for people and logs; present
656
+ * exactly when `reason_code` is. Do not parse the text.
657
+ * @example No feasible insertion position found
658
+ */
659
+ reason?: string | null;
660
+ /** @description Why `insertions` is empty; absent otherwise. Branch on this. */
661
+ reason_code?: null | components["schemas"]["SuggestReasonCode"];
662
+ };
663
+ /**
664
+ * @description Which road-network data prices travel times and distances.
665
+ *
666
+ * - `tomtom` (default): TomTom historical-typical traffic at the departure
667
+ * time.
668
+ * - `osm`: OpenStreetMap free-flow times. No traffic, so
669
+ * `travel.departure_time` is rejected with 400.
670
+ * - `tomtom_real_time`: TomTom live traffic at the moment of the request.
671
+ * **Not yet enabled: currently always rejected with 403 `feature_not_enabled`.** Once enabled:
672
+ * `travel.departure_time` is rejected; every vehicle must use the `car`
673
+ * profile; at most 100 distinct locations; up to ~25 s of matrix fetch at
674
+ * 100 locations.
675
+ * @example osm
676
+ * @enum {string}
677
+ */
678
+ MatrixEngine: "tomtom" | "osm" | "tomtom_real_time";
679
+ /**
680
+ * @description Top-level objective configuration.
681
+ *
682
+ * Plans are compared in a fixed order: first **serve the required tasks**
683
+ * (the summed `priority` of the required tasks left unassigned is
684
+ * minimised), then **minimise cost** — the `costs` rates plus every priced
685
+ * preference, reported as `score.cost`. There is no separate vehicle-count
686
+ * step: to use fewer vehicles, price them with `costs.fixed` or
687
+ * `vehicles[].costs.fixed`.
688
+ * @example {
689
+ * "costs": {
690
+ * "per_route_hour": 35,
691
+ * "per_travel_km": 0.2
692
+ * },
693
+ * "currency": "EUR"
694
+ * }
695
+ */
696
+ Objective: {
697
+ /**
698
+ * @description Fleet cost rates: the default for every vehicle and shift that does
699
+ * not set its own. See the `Costs` schema.
700
+ */
701
+ costs?: null | components["schemas"]["Costs"];
702
+ /**
703
+ * @description Currency label (ISO 4217, e.g. `EUR`) for every amount in the request
704
+ * and in `score`. Informational: echoed as `score.currency`, it never
705
+ * converts anything.
706
+ * @example EUR
707
+ */
708
+ currency?: string | null;
709
+ /** @description Job-proximity cluster cohesion. See the `Grouping` schema. */
710
+ grouping?: null | components["schemas"]["Grouping"];
711
+ /**
712
+ * Format: double
713
+ * @description Flat charge, in the objective's currency, for serving a task on a day
714
+ * other than its `preferred_date`.
715
+ *
716
+ * Applied once per task whose `preferred_date` resolves to a different
717
+ * calendar day (in its own offset) than the shift serving it; tasks without a `preferred_date`
718
+ * are never charged. Unset or `0.0` disables the preference entirely,
719
+ * which also makes every `preferred_date` in the request inert.
720
+ *
721
+ * Part of `score.cost`, so it trades off against travel and vehicle
722
+ * cost but never against serving a required task.
723
+ * @example 50
724
+ */
725
+ non_preferred_date_cost?: number | null;
726
+ /**
727
+ * Format: double
728
+ * @description Cost per hour of lateness: the default rate for every task window
729
+ * that does not carry its own `time_windows[].per_late_hour`. Omit for
730
+ * hard windows. An explicit `0.0` is rejected as ambiguous (free
731
+ * lateness and hard windows read opposite ways, and lateness is never
732
+ * free). There is no earliness rate. It prices task windows only: a
733
+ * shift's `to` (plus `max_overtime_s` when set) stays a hard limit.
734
+ * Setting it `> 0` alongside a nonzero `time_windows[].cost` anywhere in
735
+ * the request is rejected with HTTP 400.
736
+ * @example 60
737
+ */
738
+ per_late_hour?: number | null;
739
+ };
740
+ /**
741
+ * @description The 200 body of solve, evaluate and suggest: the call's result, or with
742
+ * `?validate_only=true` the validation verdict (`ValidateOnlyResponse`).
743
+ */
744
+ OrValidateOnly_SolveResponse: {
745
+ /**
746
+ * @description The plan these routes follow, in the request's vocabulary: valid input
747
+ * to evaluate, suggest or a warm-started solve as it stands.
748
+ */
749
+ plan: components["schemas"]["Plan"];
750
+ /** @description One entry per non-empty route. */
751
+ routes: components["schemas"]["Route"][];
752
+ /** @description Aggregate statistics for the solve run. */
753
+ summary: components["schemas"]["Summary"];
754
+ /** @description Tasks that could not be feasibly assigned. */
755
+ unassigned: components["schemas"]["Unassigned"][];
756
+ } | components["schemas"]["ValidateOnlyResponse"];
757
+ /**
758
+ * @description The 200 body of solve, evaluate and suggest: the call's result, or with
759
+ * `?validate_only=true` the validation verdict (`ValidateOnlyResponse`).
760
+ */
761
+ OrValidateOnly_SuggestResponse: {
762
+ /**
763
+ * Format: int64
764
+ * @description Wall-clock time taken by the request, in milliseconds.
765
+ * @example 8
766
+ */
767
+ elapsed_ms: number;
768
+ /**
769
+ * @description One entry per unit of tasks outside `plan`, in problem order (jobs,
770
+ * then shipments; a relation group sorts at its first-listed member).
771
+ */
772
+ suggestions: components["schemas"]["JobSuggestions"][];
773
+ /**
774
+ * @description `true` when `options.time_limit_s` cut the search short, or a
775
+ * multi-member unit came back empty after the search had discarded
776
+ * candidates (an inconclusive "not found"; its `reason_code` is
777
+ * `candidates_exhausted`). Lists may then be incomplete or missing the
778
+ * best insertion. A single visit's only level is never pruned, so its
779
+ * empty list is proof of infeasibility unless the budget cut it, and its
780
+ * `reason_code` is then `time_limit`.
781
+ */
782
+ truncated?: boolean;
783
+ } | components["schemas"]["ValidateOnlyResponse"];
784
+ /**
785
+ * @description The recurrence of a period limit (`limits.periods[].every`).
786
+ * @enum {string}
787
+ */
788
+ PeriodEvery: "day" | "week";
789
+ /**
790
+ * @description A cap on a vehicle's totals across every shift that starts in one period.
791
+ * Set exactly one of `every`, or `from` and `to`; and at least one maximum.
792
+ * @example {
793
+ * "every": "week",
794
+ * "max_duration_s": 144000,
795
+ * "max_tasks": 40
796
+ * }
797
+ */
798
+ PeriodLimit: {
799
+ /** @description A recurring period: every `day`, or every `week` (weeks start Monday). */
800
+ every?: null | components["schemas"]["PeriodEvery"];
801
+ /**
802
+ * Format: date-time
803
+ * @description Start of one explicit period (RFC 3339, UTC offset required), with `to`. The window must
804
+ * hold at least one of the vehicle's shift starts; a period inside one
805
+ * shift is not supported.
806
+ * @example 2026-10-05T00:00:00+02:00
807
+ */
808
+ from?: string | null;
809
+ /**
810
+ * Format: int64
811
+ * @description Hard cap on the summed distance driven, in metres.
812
+ * @example 900000
813
+ */
814
+ max_distance_m?: number | null;
815
+ /**
816
+ * Format: int64
817
+ * @description Hard cap on the summed route span of the period's shifts, measured as
818
+ * for `max_route_duration_s`. An unused shift adds nothing.
819
+ * @example 144000
820
+ */
821
+ max_duration_s?: number | null;
822
+ /**
823
+ * Format: int32
824
+ * @description Hard cap on served stops, counted as a shift's `max_tasks` counts
825
+ * them: a job is one stop, a shipment two (pickup and delivery).
826
+ * @example 40
827
+ */
828
+ max_tasks?: number | null;
829
+ /**
830
+ * Format: date-time
831
+ * @description End of one explicit period (RFC 3339, UTC offset required, exclusive), with `from`.
832
+ * @example 2026-10-12T00:00:00+02:00
833
+ */
834
+ to?: string | null;
835
+ };
836
+ /**
837
+ * @description A place: a coordinate `[longitude, latitude]` in GeoJSON order — exactly two numbers. Tasks at the same coordinate are treated as one location.
838
+ * @example [
839
+ * 3.725,
840
+ * 51.05
841
+ * ]
842
+ */
843
+ Place: number[];
844
+ /**
845
+ * @description One stop an option adds: the plan stop, the plan route it goes into and
846
+ * when it is reached.
847
+ * @example {
848
+ * "arrival": "2026-07-01T09:30:00+02:00",
849
+ * "id": "s1:pickup",
850
+ * "latest_arrival": "2026-07-01T10:15:00+02:00",
851
+ * "shift": "mon",
852
+ * "type": "pickup",
853
+ * "vehicle": "v1"
854
+ * }
855
+ */
856
+ Placement: {
857
+ /**
858
+ * Format: date-time
859
+ * @description Arrival at this stop (RFC 3339), in the UTC offset of `shift`'s
860
+ * `from` (`Z` when that `from` is UTC or has no offset), as on routes.
861
+ * @example 2026-07-01T09:30:00+02:00
862
+ */
863
+ arrival: string;
864
+ /** @description The plan stop's `id`: the job id, or the shipment leg's stop id. */
865
+ id: string;
866
+ /**
867
+ * Format: date-time
868
+ * @description Latest arrival that still keeps the rest of the route feasible
869
+ * (RFC 3339, in the offset of `arrival`) — the smaller of the task's own time-window latest and
870
+ * the route's forward slack here. Under priced lateness it may be
871
+ * **earlier** than `arrival`.
872
+ * @example 2026-07-01T10:15:00+02:00
873
+ */
874
+ latest_arrival: string;
875
+ /** @description Shift of that plan route. */
876
+ shift: string;
877
+ /**
878
+ * @description The plan stop's `type`: `job`, `pickup` or `delivery` (never `reload`:
879
+ * suggest does not open reload trips).
880
+ */
881
+ type: components["schemas"]["PlanStopType"];
882
+ /** @description Vehicle of the plan route the stop goes into. */
883
+ vehicle: string;
884
+ };
885
+ /**
886
+ * @description A plan: which stops each vehicle shift serves, in order. A result's `plan`
887
+ * has exactly this shape and goes back into evaluate, suggest or a
888
+ * warm-started solve unchanged.
889
+ */
890
+ Plan: {
891
+ /** @description One entry per vehicle shift that serves something. */
892
+ routes: components["schemas"]["PlanRoute"][];
893
+ };
894
+ /**
895
+ * @description One vehicle shift's route: every trip of the shift, split by `reload` stops.
896
+ * @example {
897
+ * "shift": "mon",
898
+ * "stops": [
899
+ * {
900
+ * "id": "boiler-17",
901
+ * "type": "job"
902
+ * }
903
+ * ],
904
+ * "vehicle": "van-3"
905
+ * }
906
+ */
907
+ PlanRoute: {
908
+ /**
909
+ * Format: int32
910
+ * @description On solve, the first `locked_count` stops stay where they are (already
911
+ * dispatched). Counts every listed stop. Cannot be combined with a
912
+ * `reload` stop, and must not exceed the route's stop count — both are
913
+ * validated on every endpoint that takes a plan. Otherwise inert on
914
+ * evaluate. On suggest, no insertion lands before them.
915
+ * @example 1
916
+ */
917
+ locked_count?: number | null;
918
+ /** @description Shift id, within that vehicle. */
919
+ shift: string;
920
+ /** @description Stops in visiting order. */
921
+ stops: components["schemas"]["PlanStop"][];
922
+ /** @description Vehicle id. */
923
+ vehicle: string;
924
+ };
925
+ /**
926
+ * @description One stop of a plan route. Start, end and breaks are placed by the solver
927
+ * and are not listed.
928
+ * @example {
929
+ * "id": "parts-9:pickup",
930
+ * "type": "pickup"
931
+ * }
932
+ */
933
+ PlanStop: {
934
+ /**
935
+ * @description Job id.
936
+ * @example boiler-17
937
+ */
938
+ id: string;
939
+ /** @enum {string} */
940
+ type: "job";
941
+ } | {
942
+ /**
943
+ * @description The leg's stop id: its own `id`, else `{shipment}:pickup`.
944
+ * @example parts-9:pickup
945
+ */
946
+ id: string;
947
+ /** @enum {string} */
948
+ type: "pickup";
949
+ } | {
950
+ /**
951
+ * @description The leg's stop id: its own `id`, else `{shipment}:delivery`.
952
+ * @example parts-9:delivery
953
+ */
954
+ id: string;
955
+ /** @enum {string} */
956
+ type: "delivery";
957
+ } | {
958
+ /**
959
+ * @description A depot id from the shift's `reloads`.
960
+ * @example gent
961
+ */
962
+ depot: string;
963
+ /** @enum {string} */
964
+ type: "reload";
965
+ };
966
+ /**
967
+ * @description The `type` of a plan stop.
968
+ * @enum {string}
969
+ */
970
+ PlanStopType: "job" | "pickup" | "delivery" | "reload";
971
+ /**
972
+ * @description How much road detail `routes[].polyline` carries.
973
+ *
974
+ * This is the knob that decides whether a drawn route follows the roads or
975
+ * cuts corners — **not** the polyline encoding precision, which is fixed at 5
976
+ * and is a sub-metre concern. Measured on a three-stop, 150 km route:
977
+ *
978
+ * | detail | shape points | geometry bytes | added latency |
979
+ * |--------|--------------|----------------|---------------|
980
+ * | `simplified` | 34 | ~200 | ~0 |
981
+ * | `full` | 1,728 | ~5,300 | ~0 (inside run-to-run noise) |
982
+ *
983
+ * Prefer `full` unless response size is the binding constraint: the extra
984
+ * detail is nearly free in time, and `simplified` is what makes a route look
985
+ * like straight lines between stops.
986
+ * @example full
987
+ * @enum {string}
988
+ */
989
+ PolylineDetail: "simplified" | "full";
990
+ /**
991
+ * @description A soft vehicle affinity preference, priced.
992
+ * @example {
993
+ * "vehicle": "v1",
994
+ * "violation_cost": 50
995
+ * }
996
+ */
997
+ Preference: {
998
+ /**
999
+ * @description Preferred vehicle id.
1000
+ * @example v1
1001
+ */
1002
+ vehicle: string;
1003
+ /**
1004
+ * Format: double
1005
+ * @description Cost, in the objective's currency, incurred when a different vehicle
1006
+ * serves the job. Must be finite and `>= 0`.
1007
+ * @example 50
1008
+ */
1009
+ violation_cost: number;
1010
+ };
1011
+ /**
1012
+ * @description The problem part of every V3 request body.
1013
+ * @example {
1014
+ * "depots": [
1015
+ * {
1016
+ * "id": "gent",
1017
+ * "location": [
1018
+ * 3.7174,
1019
+ * 51.0543
1020
+ * ]
1021
+ * }
1022
+ * ],
1023
+ * "jobs": [
1024
+ * {
1025
+ * "id": "job-1",
1026
+ * "location": [
1027
+ * 3.725,
1028
+ * 51.05
1029
+ * ],
1030
+ * "pickup": {
1031
+ * "weight": 20
1032
+ * },
1033
+ * "service_duration_s": 300
1034
+ * }
1035
+ * ],
1036
+ * "vehicles": [
1037
+ * {
1038
+ * "capacity": {
1039
+ * "weight": 500
1040
+ * },
1041
+ * "id": "v1",
1042
+ * "shifts": [
1043
+ * {
1044
+ * "end": {
1045
+ * "depot": "gent"
1046
+ * },
1047
+ * "from": "2026-07-01T08:00:00+02:00",
1048
+ * "id": "mon",
1049
+ * "start": {
1050
+ * "depot": "gent"
1051
+ * },
1052
+ * "to": "2026-07-01T17:00:00+02:00"
1053
+ * }
1054
+ * ]
1055
+ * }
1056
+ * ]
1057
+ * }
1058
+ */
1059
+ ProblemInput: {
1060
+ /** @description Depots that shifts start from, end at, or reload at, by id. */
1061
+ depots?: components["schemas"]["Depot"][];
1062
+ /** @description Single-stop visits to serve. */
1063
+ jobs?: components["schemas"]["Visit"][];
1064
+ /** @description Objective configuration. */
1065
+ objective?: null | components["schemas"]["Objective"];
1066
+ /** @description Stop caps shared by a set of vehicles. */
1067
+ quotas?: components["schemas"]["Quota"][];
1068
+ /** @description Ordering, grouping, and synchronization constraints. */
1069
+ relations?: components["schemas"]["Relation"][] | null;
1070
+ /** @description Bound pickup→delivery shipments to serve. */
1071
+ shipments?: components["schemas"]["Shipment"][];
1072
+ /** @description Where travel times and distances come from. */
1073
+ travel?: null | components["schemas"]["Travel"];
1074
+ /** @description Available vehicles. */
1075
+ vehicles: components["schemas"]["Vehicle"][];
1076
+ };
1077
+ /**
1078
+ * @description A vehicle's routing profile.
1079
+ * @example car
1080
+ * @enum {string}
1081
+ */
1082
+ Profile: "car" | "truck" | "bike";
1083
+ /**
1084
+ * @description A hard cap on how many stops of `job_ids` the `vehicles` serve together
1085
+ * in a window. A stop counts when its service starts in `[from, to)`
1086
+ * (`arrival + wait_s` in the response); without a window every stop counts.
1087
+ * Stops count as `max_tasks` counts them: a job one, a shipment two. An
1088
+ * over-cap job is left unassigned. Unlike `limits.periods[]`, which counts a
1089
+ * shift by its start, a quota has no `every` recurrence and caps only stop
1090
+ * counts.
1091
+ * @example {
1092
+ * "id": "pool-install",
1093
+ * "job_ids": [
1094
+ * "j1",
1095
+ * "j2",
1096
+ * "j3"
1097
+ * ],
1098
+ * "max_tasks": 2,
1099
+ * "vehicles": [
1100
+ * "c0",
1101
+ * "c1"
1102
+ * ]
1103
+ * }
1104
+ */
1105
+ Quota: {
1106
+ /**
1107
+ * Format: date-time
1108
+ * @description Window start (RFC 3339, UTC offset required), with `to`.
1109
+ * @example 2026-10-05T00:00:00+02:00
1110
+ */
1111
+ from?: string | null;
1112
+ /**
1113
+ * @description Quota id: 1 to 128 characters, unique among `quotas`; named in the
1114
+ * `/evaluate` `quota` violation.
1115
+ */
1116
+ id: string;
1117
+ /** @description Job or shipment ids whose stops count, each at most once. */
1118
+ job_ids: string[];
1119
+ /**
1120
+ * Format: int32
1121
+ * @description The cap on counted stops.
1122
+ */
1123
+ max_tasks: number;
1124
+ /**
1125
+ * Format: date-time
1126
+ * @description Window end (RFC 3339, UTC offset required, exclusive), with `from`.
1127
+ * @example 2026-10-06T00:00:00+02:00
1128
+ */
1129
+ to?: string | null;
1130
+ /** @description Vehicle ids that share the cap, each at most once. */
1131
+ vehicles: string[];
1132
+ };
1133
+ /**
1134
+ * @description A hard constraint between tasks, selected by `type`: `sequence`,
1135
+ * `tag_order`, `same_vehicle`, `same_day`, `same_route`, `synchronized`,
1136
+ * `time_lag`, `compatible_vehicles` or `all_or_none`.
1137
+ * Each type carries only its own fields; any other `type` is rejected with
1138
+ * HTTP 400.
1139
+ *
1140
+ * Members are named by `stops` (stop ids). `same_vehicle`, `same_day` and
1141
+ * `same_route` take **either** `stops` **or** a `tag`, not both. Every
1142
+ * relation is hard.
1143
+ *
1144
+ * Relations constrain jobs, not shipments. A shipment id in the `stops` of
1145
+ * a `sequence`, `same_vehicle`, `same_day` or `synchronized` relation, and
1146
+ * a shipment carrying the `tag` of a `same_vehicle` or `same_day` relation,
1147
+ * passes validation but is skipped: the request returns HTTP 200 and the
1148
+ * relation does not constrain that shipment. `same_route`, `time_lag`,
1149
+ * `compatible_vehicles`, `all_or_none` and `tag_order` reject a shipment
1150
+ * with HTTP 400.
1151
+ * @example {
1152
+ * "stops": [
1153
+ * "job-1",
1154
+ * "job-2"
1155
+ * ],
1156
+ * "type": "sequence"
1157
+ * }
1158
+ */
1159
+ Relation: {
1160
+ /**
1161
+ * @description Job or shipment ids, in the order they must be served. A shipment
1162
+ * is named by its id, not by a leg's stop id (stop-id members are
1163
+ * not resolved yet).
1164
+ */
1165
+ stops: string[];
1166
+ /** @enum {string} */
1167
+ type: "sequence";
1168
+ } | {
1169
+ /** @description Tag labels in precedence order. Requires at least 2 entries. */
1170
+ tags: string[];
1171
+ /** @enum {string} */
1172
+ type: "tag_order";
1173
+ } | {
1174
+ /**
1175
+ * @description Job or shipment ids in the relation (mutually exclusive with
1176
+ * `tag`; stop-id members are not resolved yet).
1177
+ */
1178
+ stops?: string[] | null;
1179
+ /** @description Tag selecting the relation members (mutually exclusive with `stops`). */
1180
+ tag?: string | null;
1181
+ /** @enum {string} */
1182
+ type: "same_vehicle";
1183
+ } | {
1184
+ /**
1185
+ * @description Job or shipment ids in the relation (mutually exclusive with
1186
+ * `tag`; stop-id members are not resolved yet).
1187
+ */
1188
+ stops?: string[] | null;
1189
+ /** @description Tag selecting the relation members (mutually exclusive with `stops`). */
1190
+ tag?: string | null;
1191
+ /** @enum {string} */
1192
+ type: "same_day";
1193
+ } | {
1194
+ /**
1195
+ * @description Job or shipment ids in the relation (mutually exclusive with
1196
+ * `tag`; stop-id members are not resolved yet).
1197
+ */
1198
+ stops?: string[] | null;
1199
+ /** @description Tag selecting the relation members (mutually exclusive with `stops`). */
1200
+ tag?: string | null;
1201
+ /** @enum {string} */
1202
+ type: "same_route";
1203
+ } | {
1204
+ /**
1205
+ * Format: int64
1206
+ * @description Maximum spread between the synchronized instants in seconds
1207
+ * (`0` = exact simultaneity). Required. A negative value is rejected
1208
+ * with HTTP 400 `out_of_range`.
1209
+ */
1210
+ max_wait_s: number;
1211
+ /**
1212
+ * @description Which instant is synchronized: the service `start` (default) or
1213
+ * the service `end`. `arrival` is rejected with HTTP 400.
1214
+ */
1215
+ on?: null | components["schemas"]["StopInstant"];
1216
+ /**
1217
+ * @description Participating job or shipment ids (stop-id members are not
1218
+ * resolved yet).
1219
+ */
1220
+ stops: string[];
1221
+ /** @enum {string} */
1222
+ type: "synchronized";
1223
+ } | {
1224
+ /** @description The reference end. */
1225
+ from: components["schemas"]["TimeLagEnd"];
1226
+ /**
1227
+ * Format: int64
1228
+ * @description Upper bound on `t(to) − t(from)` in seconds. Absent = unbounded.
1229
+ */
1230
+ max_s?: number | null;
1231
+ /**
1232
+ * Format: int64
1233
+ * @description Lower bound on `t(to) − t(from)` in seconds. Default `0`.
1234
+ */
1235
+ min_s?: number | null;
1236
+ /** @description The bounded end. */
1237
+ to: components["schemas"]["TimeLagEnd"];
1238
+ /** @enum {string} */
1239
+ type: "time_lag";
1240
+ } | {
1241
+ /**
1242
+ * @description Job or shipment ids, at least two (stop-id members are not
1243
+ * resolved yet).
1244
+ * @example [
1245
+ * "job-1",
1246
+ * "job-2"
1247
+ * ]
1248
+ */
1249
+ stops: string[];
1250
+ /** @enum {string} */
1251
+ type: "compatible_vehicles";
1252
+ } | {
1253
+ /**
1254
+ * @description Job ids, at least two.
1255
+ * @example [
1256
+ * "job-1",
1257
+ * "job-2"
1258
+ * ]
1259
+ */
1260
+ stops: string[];
1261
+ /** @enum {string} */
1262
+ type: "all_or_none";
1263
+ };
1264
+ /**
1265
+ * @description A best-effort, actionable suggestion for relaxing the problem so a task can be
1266
+ * assigned — derived from the smallest constraint relaxation the solver found.
1267
+ * @example {
1268
+ * "amount": 900,
1269
+ * "field": "/jobs/job-2/time_windows",
1270
+ * "op": "extend_time_window",
1271
+ * "suggestion": "Extend a time window by 900s (~15 min).",
1272
+ * "unit": "s"
1273
+ * }
1274
+ */
1275
+ Relaxation: {
1276
+ /**
1277
+ * Format: int64
1278
+ * @description Magnitude of the change, expressed in `unit`.
1279
+ * @example 900
1280
+ */
1281
+ amount: number;
1282
+ /**
1283
+ * @description Names the task and the field to change. For a job: `/jobs/{id}/pickup`
1284
+ * (`/jobs/{id}/delivery` for a job with only a `delivery`) or
1285
+ * `/jobs/{id}/time_windows`. For a shipment: `/shipments/{id}/load` or
1286
+ * `/shipments/{id}/pickup/time_windows` (either leg's window may be the
1287
+ * one to extend). `{id}` is the task id, the same value as `job_id`. It is
1288
+ * a label, not a JSON Pointer into the request body: it uses the id, not
1289
+ * the array index, and has no `/problem` prefix.
1290
+ * @example /jobs/job-2/time_windows
1291
+ */
1292
+ field: string;
1293
+ /** @description The kind of change suggested. */
1294
+ op: components["schemas"]["RelaxationOp"];
1295
+ /**
1296
+ * @description Human-readable suggestion.
1297
+ * @example Extend a time window by 900s (~15 min).
1298
+ */
1299
+ suggestion: string;
1300
+ /**
1301
+ * @description Unit of `amount`: `"s"` (seconds, for `extend_time_window`) or
1302
+ * `"load"` (demand units, for `reduce_demand`).
1303
+ * @example s
1304
+ */
1305
+ unit: string;
1306
+ };
1307
+ /**
1308
+ * @description The kind of change a relaxation suggests.
1309
+ * @example extend_time_window
1310
+ * @enum {string}
1311
+ */
1312
+ RelaxationOp: "reduce_demand" | "extend_time_window";
1313
+ /**
1314
+ * @description A single vehicle's route.
1315
+ * @example {
1316
+ * "distance_m": 8450,
1317
+ * "load_peak": {
1318
+ * "weight": 55
1319
+ * },
1320
+ * "shift": "mon",
1321
+ * "stops": [
1322
+ * {
1323
+ * "arrival": "2026-07-01T08:00:00+02:00",
1324
+ * "departure": "2026-07-01T08:00:00+02:00",
1325
+ * "depot": "gent",
1326
+ * "load_after": {
1327
+ * "weight": 0
1328
+ * },
1329
+ * "location": [
1330
+ * 3.7174,
1331
+ * 51.0543
1332
+ * ],
1333
+ * "service_s": 0,
1334
+ * "type": "start"
1335
+ * },
1336
+ * {
1337
+ * "arrival": "2026-07-01T08:10:00+02:00",
1338
+ * "departure": "2026-07-01T09:05:00+02:00",
1339
+ * "distance_m": 2600,
1340
+ * "id": "job-1",
1341
+ * "lateness_s": 0,
1342
+ * "load_after": {
1343
+ * "weight": 20
1344
+ * },
1345
+ * "location": [
1346
+ * 3.725,
1347
+ * 51.05
1348
+ * ],
1349
+ * "service_s": 300,
1350
+ * "travel_s": 600,
1351
+ * "type": "job",
1352
+ * "wait_s": 3000
1353
+ * },
1354
+ * {
1355
+ * "arrival": "2026-07-01T09:13:00+02:00",
1356
+ * "departure": "2026-07-01T09:23:00+02:00",
1357
+ * "distance_m": 2350,
1358
+ * "id": "job-2",
1359
+ * "lateness_s": 0,
1360
+ * "load_after": {
1361
+ * "weight": 55
1362
+ * },
1363
+ * "location": [
1364
+ * 3.71,
1365
+ * 51.06
1366
+ * ],
1367
+ * "service_s": 600,
1368
+ * "travel_s": 480,
1369
+ * "type": "job",
1370
+ * "wait_s": 0
1371
+ * },
1372
+ * {
1373
+ * "arrival": "2026-07-01T09:38:00+02:00",
1374
+ * "departure": "2026-07-01T09:38:00+02:00",
1375
+ * "depot": "gent",
1376
+ * "distance_m": 3500,
1377
+ * "load_after": {
1378
+ * "weight": 55
1379
+ * },
1380
+ * "location": [
1381
+ * 3.7174,
1382
+ * 51.0543
1383
+ * ],
1384
+ * "service_s": 0,
1385
+ * "travel_s": 900,
1386
+ * "type": "end"
1387
+ * }
1388
+ * ],
1389
+ * "time": {
1390
+ * "break_s": 0,
1391
+ * "depot_s": 0,
1392
+ * "overtime_s": 0,
1393
+ * "service_s": 900,
1394
+ * "span_s": 5880,
1395
+ * "travel_s": 1980,
1396
+ * "wait_s": 3000
1397
+ * },
1398
+ * "vehicle": "v1"
1399
+ * }
1400
+ */
1401
+ Route: {
1402
+ /**
1403
+ * Format: int64
1404
+ * @description Total route distance, in metres.
1405
+ * @example 8450
1406
+ */
1407
+ distance_m: number;
1408
+ /** @description Peak load reached on the route, by named dimension. */
1409
+ load_peak?: null | components["schemas"]["BTreeMap"];
1410
+ /**
1411
+ * @description Road geometry for this route as an [encoded polyline] at precision 5
1412
+ * (~1.1 m), ready to hand to a map library. Present only when
1413
+ * `options.polylines` was set, and absent for a route whose geometry the
1414
+ * routing engine could not produce.
1415
+ *
1416
+ * Traced with this vehicle's `profile` at the solve's departure time, so
1417
+ * it is the same road path the plan was costed on — not a generic car
1418
+ * route. It is a decoration, though: `distance_m` and `time` come
1419
+ * from the solver's matrix and are authoritative if the two ever disagree.
1420
+ *
1421
+ * [encoded polyline]: https://developers.google.com/maps/documentation/utilities/polylinealgorithm
1422
+ * @example _p~iF~ps|U_ulLnnqC_mqNvxq`@
1423
+ */
1424
+ polyline?: string | null;
1425
+ /**
1426
+ * @description The shift id (`vehicles[].shifts[].id`) this route runs on.
1427
+ * @example mon
1428
+ */
1429
+ shift: string;
1430
+ /** @description Ordered stops, including depot bookends. */
1431
+ stops: components["schemas"]["StopOutput"][];
1432
+ /**
1433
+ * @description Where this route's time goes. `time.span_s` is what the route's
1434
+ * `costs.per_route_hour` is charged on: the shift's rate, else its
1435
+ * vehicle's, else `objective.costs.per_route_hour`.
1436
+ */
1437
+ time: components["schemas"]["TimeTotals"];
1438
+ /** @description The vehicle id (`vehicle.id` from the request) serving this route. */
1439
+ vehicle: string;
1440
+ /**
1441
+ * @description Route-level constraint violations (e.g. `max_distance`, `max_duration`,
1442
+ * or `unreachable` for a leg whose destination is not a job stop — the
1443
+ * return to the end depot, or a leg into a reload).
1444
+ * Only populated by `POST /v3/routing/evaluate`; absent on solve responses.
1445
+ */
1446
+ violations?: components["schemas"]["Violation"][];
1447
+ };
1448
+ /**
1449
+ * @description How good a plan is. Plans compare in order: fewer `violations` first,
1450
+ * then less `unserved_priority`, then less `cost`.
1451
+ * @example {
1452
+ * "components": {
1453
+ * "depot_visits": 0,
1454
+ * "distance": 1.69,
1455
+ * "drop_fees": 0,
1456
+ * "fixed": 0,
1457
+ * "grouping": 0,
1458
+ * "lateness": 0,
1459
+ * "other": 0,
1460
+ * "overtime": 0,
1461
+ * "tag_preference": 0,
1462
+ * "target": 0,
1463
+ * "time": 57.1536,
1464
+ * "waiting": 0,
1465
+ * "window_choice": 0
1466
+ * },
1467
+ * "cost": 58.8436,
1468
+ * "currency": "EUR",
1469
+ * "unserved_priority": 0,
1470
+ * "violations": 0
1471
+ * }
1472
+ */
1473
+ Score: {
1474
+ /** @description The money of each objective term. */
1475
+ components: components["schemas"]["ScoreComponents"];
1476
+ /**
1477
+ * Format: double
1478
+ * @description Total cost, in the objective's currency: the sum of `components`.
1479
+ * @example 58.8436
1480
+ */
1481
+ cost: number;
1482
+ /**
1483
+ * @description Informational currency label, echoed from `objective.currency`.
1484
+ * @example EUR
1485
+ */
1486
+ currency?: string | null;
1487
+ /**
1488
+ * Format: int64
1489
+ * @description Summed `priority` of the required tasks left unassigned (a task that
1490
+ * priced a `drop_fee` counts in `components.drop_fees` instead).
1491
+ * @example 0
1492
+ */
1493
+ unserved_priority: number;
1494
+ /**
1495
+ * Format: int32
1496
+ * @description Hard-constraint violations. Always `0` on a solve result.
1497
+ * @example 0
1498
+ */
1499
+ violations: number;
1500
+ };
1501
+ /**
1502
+ * @description The money of each objective term, in the objective's currency. The
1503
+ * components always sum to `score.cost` exactly: every term the solver
1504
+ * charges lands in exactly one of them.
1505
+ * @example {
1506
+ * "depot_visits": 0,
1507
+ * "distance": 1.69,
1508
+ * "drop_fees": 0,
1509
+ * "fixed": 0,
1510
+ * "grouping": 0,
1511
+ * "lateness": 0,
1512
+ * "other": 0,
1513
+ * "overtime": 0,
1514
+ * "tag_preference": 0,
1515
+ * "target": 0,
1516
+ * "time": 57.1536,
1517
+ * "waiting": 0,
1518
+ * "window_choice": 0
1519
+ * }
1520
+ */
1521
+ ScoreComponents: {
1522
+ /**
1523
+ * Format: double
1524
+ * @description Cost of reload visits, in currency units (`depots[].cost_per_visit`).
1525
+ */
1526
+ depot_visits: number;
1527
+ /**
1528
+ * Format: double
1529
+ * @description Distance cost, in currency units: each route's distance priced at
1530
+ * its `costs.per_travel_km` (shift, vehicle or fleet).
1531
+ */
1532
+ distance: number;
1533
+ /**
1534
+ * Format: double
1535
+ * @description The summed `drop_fee` of the tasks left unassigned, in currency units.
1536
+ */
1537
+ drop_fees: number;
1538
+ /**
1539
+ * Format: double
1540
+ * @description Vehicle cost, in currency units: `costs.fixed` once per used shift,
1541
+ * at the shift's, vehicle's or fleet's rate.
1542
+ */
1543
+ fixed: number;
1544
+ /**
1545
+ * Format: double
1546
+ * @description Cost of splitting close tasks across routes, in currency units
1547
+ * (`objective.grouping`).
1548
+ */
1549
+ grouping: number;
1550
+ /**
1551
+ * Format: double
1552
+ * @description Cost of priced lateness past a time window, in currency units
1553
+ * (`objective.per_late_hour` or `time_windows[].per_late_hour`).
1554
+ */
1555
+ lateness: number;
1556
+ /**
1557
+ * Format: double
1558
+ * @description Everything else the solver charges, in currency units: vehicle
1559
+ * preferences (`preferences[].violation_cost`) and the
1560
+ * non-preferred-date cost.
1561
+ */
1562
+ other: number;
1563
+ /**
1564
+ * Format: double
1565
+ * @description Cost of work past a soft shift end, in currency units: priced at the
1566
+ * route's `costs.per_overtime_hour`.
1567
+ */
1568
+ overtime: number;
1569
+ /**
1570
+ * Format: double
1571
+ * @description Cost of serving tasks outside a vehicle's `preferred_tags`, in
1572
+ * currency units.
1573
+ */
1574
+ tag_preference: number;
1575
+ /**
1576
+ * Format: double
1577
+ * @description Cost of missing tasks' `target` times, in currency units
1578
+ * (`target.per_late_hour`).
1579
+ */
1580
+ target: number;
1581
+ /**
1582
+ * Format: double
1583
+ * @description Time cost, in currency units: each route's span priced at its
1584
+ * `costs.per_route_hour` (shift, vehicle or fleet).
1585
+ */
1586
+ time: number;
1587
+ /**
1588
+ * Format: double
1589
+ * @description Cost of waiting for time windows to open, in currency units: each
1590
+ * route's wait priced at its `costs.per_wait_hour`.
1591
+ */
1592
+ waiting: number;
1593
+ /**
1594
+ * Format: double
1595
+ * @description The price of the time window each task was served in, in currency
1596
+ * units (`time_windows[].cost`).
1597
+ */
1598
+ window_choice: number;
1599
+ };
1600
+ /**
1601
+ * @description Per-vehicle work-rate override for one task's `service_duration_s`.
1602
+ * @example {
1603
+ * "factor": 1.25,
1604
+ * "vehicle": "v1"
1605
+ * }
1606
+ */
1607
+ ServiceFactor: {
1608
+ /**
1609
+ * Format: double
1610
+ * @description Same meaning, direction and `(0, 5]` range as `vehicles[].service_factor`:
1611
+ * effective service time = `service_duration_s / (vehicle_or_shift_factor * factor)`.
1612
+ * `1.0` is a no-op.
1613
+ * @example 1.25
1614
+ */
1615
+ factor: number;
1616
+ /**
1617
+ * @description Vehicle id; applies to every shift of that vehicle.
1618
+ * @example v1
1619
+ */
1620
+ vehicle: string;
1621
+ };
1622
+ /**
1623
+ * @description One working shift for a vehicle.
1624
+ * @example {
1625
+ * "end": {
1626
+ * "location": [
1627
+ * 3.7174,
1628
+ * 51.0543
1629
+ * ]
1630
+ * },
1631
+ * "from": "2026-07-01T08:00:00+02:00",
1632
+ * "id": "mon",
1633
+ * "start": {
1634
+ * "location": [
1635
+ * 3.7174,
1636
+ * 51.0543
1637
+ * ]
1638
+ * },
1639
+ * "to": "2026-07-01T17:00:00+02:00"
1640
+ * }
1641
+ */
1642
+ Shift: {
1643
+ /**
1644
+ * @description Scheduled driver breaks within this shift — see the `Break` schema for
1645
+ * the supported forms. Injected breaks appear as `break` stops in the
1646
+ * response routes.
1647
+ */
1648
+ breaks?: components["schemas"]["Break"][] | null;
1649
+ /**
1650
+ * @description Capacity for this shift only. Each dimension named here replaces the
1651
+ * vehicle's value for that dimension; dimensions not named keep the
1652
+ * vehicle's `capacity`.
1653
+ */
1654
+ capacity?: null | components["schemas"]["BTreeMap"];
1655
+ /**
1656
+ * @description Costs for this shift only. Each field set here replaces the same
1657
+ * field of the vehicle's `costs` (or, when the vehicle does not set it,
1658
+ * of `objective.costs`) for this shift; fields not set keep that value.
1659
+ */
1660
+ costs?: null | components["schemas"]["Costs"];
1661
+ /** @description Where the shift ends. Absent = open end. */
1662
+ end?: null | components["schemas"]["ShiftEnd"];
1663
+ /**
1664
+ * Format: date-time
1665
+ * @description Shift start (RFC 3339 instant, UTC offset required).
1666
+ * @example 2026-07-01T08:00:00+02:00
1667
+ */
1668
+ from: string;
1669
+ /**
1670
+ * @description Shift id: 1 to 128 characters, unique within this vehicle.
1671
+ * @example mon
1672
+ */
1673
+ id: string;
1674
+ /**
1675
+ * @description Symmetric to `ignore_start_leg` for the last-task → `end` leg.
1676
+ * @example false
1677
+ */
1678
+ ignore_end_leg?: boolean | null;
1679
+ /**
1680
+ * @description When `true`, the `start` → first-task leg costs nothing (not counted
1681
+ * in objective, feasibility timing, or total distance/duration) — the
1682
+ * depot is still known and reported (a real stop, snapped coordinate,
1683
+ * departure time). Distinct from omitting `start` (a fully open route,
1684
+ * which drops the depot entirely): this keeps the depot visible while
1685
+ * making the leg to it free. Requires `start`; a no-op when `start` is
1686
+ * absent. Default `false`.
1687
+ * @example false
1688
+ */
1689
+ ignore_start_leg?: boolean | null;
1690
+ /**
1691
+ * @description Load already on the vehicle at shift start (pre-loaded), per
1692
+ * dimension. Occupies capacity on the first trip only — flushed at the
1693
+ * first reload — so it reduces first-trip room per dimension, enforced
1694
+ * by the per-trip capacity constraint. Not validated against
1695
+ * `capacity` at request time: a value at or beyond capacity is
1696
+ * accepted and surfaces as a solve-time result (jobs left unassigned,
1697
+ * or deferred until the vehicle's first reload flushes it), not an
1698
+ * HTTP 400. A dimension named here but absent from `capacity` behaves
1699
+ * the same as capacity `0` in that dimension.
1700
+ */
1701
+ initial_load?: null | components["schemas"]["BTreeMap"];
1702
+ /**
1703
+ * @description Limits for this shift only. Each field set here replaces the
1704
+ * vehicle's `limits` value; fields not set keep it.
1705
+ */
1706
+ limits?: null | components["schemas"]["ShiftLimits"];
1707
+ /**
1708
+ * Format: int32
1709
+ * @description Maximum allowed overtime beyond `to` in seconds. Overtime is reached
1710
+ * by working late, never by waiting: a `synchronized` or `time_lag`
1711
+ * hold stops at `to`.
1712
+ * @example 1800
1713
+ */
1714
+ max_overtime_s?: number | null;
1715
+ /**
1716
+ * Format: int32
1717
+ * @description Maximum trips (depot legs) in this shift, across all reload depots.
1718
+ * Counts trips, not reloads: `trips = reloads_used + 1`. `max_trips: 1`
1719
+ * means a single trip — reloads are permitted in `reloads` but the cap
1720
+ * prevents ever using one. `reloads` present with `max_trips` absent
1721
+ * means unlimited trips. A single reload depot can only be
1722
+ * revisited up to 8 times per vehicle regardless of `max_trips`;
1723
+ * a `max_trips` beyond that against one depot
1724
+ * is accepted but the extra headroom is unreachable — list more than
1725
+ * one reload depot to raise the practical ceiling.
1726
+ * @example 2
1727
+ */
1728
+ max_trips?: number | null;
1729
+ /**
1730
+ * @description Depots (by `depots[].id`, each at most once) the vehicle may return to
1731
+ * mid-shift to reload and start a fresh trip. Each visit takes that
1732
+ * depot's `service_duration_s` and `cost_per_visit`. Cap trips with
1733
+ * `max_trips`.
1734
+ */
1735
+ reloads?: string[] | null;
1736
+ /**
1737
+ * @description Hard tag scope: when set (non-empty), this shift
1738
+ * serves only tasks carrying at least one of these tags. Untagged tasks
1739
+ * remain serveable by every shift. Absent or empty = serves everything.
1740
+ */
1741
+ serves_tags?: string[] | null;
1742
+ /**
1743
+ * Format: double
1744
+ * @description Work-rate multiplier for this shift only, overriding
1745
+ * `vehicles[].service_factor` when set (absent = inherit the vehicle's
1746
+ * value, which itself defaults to 1.0).
1747
+ *
1748
+ * Same meaning, direction, and `(0, 5]` range as the vehicle-level field:
1749
+ * effective service time = `service_duration_s / service_factor`. Use it
1750
+ * when the same driver works at a different rate on different days — a
1751
+ * measured per-day efficiency, a trainee shift, a two-person crew on
1752
+ * Fridays — instead of collapsing those days into one vehicle-level mean.
1753
+ *
1754
+ * Scope is identical to the vehicle-level field: task
1755
+ * `service_duration_s` only, never `setup_duration_s`, depot handling
1756
+ * (`depots[].service_duration_s`, `start`/`end.service_duration_s`),
1757
+ * waiting, or breaks.
1758
+ */
1759
+ service_factor?: number | null;
1760
+ /**
1761
+ * @description Capabilities this shift provides, replacing the vehicle's `skills`
1762
+ * for this shift only (a technician covering another region on one
1763
+ * day, a crew that only carries a certification on some shifts). A
1764
+ * job's `skills` must all appear in the skills of the shift that
1765
+ * serves it, at a level of at least the job's `min_level`. Same entry
1766
+ * shape as `vehicles[].skills`. Absent = the vehicle's `skills`; `[]` =
1767
+ * this shift provides none.
1768
+ */
1769
+ skills?: components["schemas"]["VehicleSkill"][] | null;
1770
+ /** @description Where the shift starts. Absent = open start. */
1771
+ start?: null | components["schemas"]["ShiftEnd"];
1772
+ /**
1773
+ * Format: date-time
1774
+ * @description Shift end (RFC 3339 instant, UTC offset required; hard unless `max_overtime_s` set). The
1775
+ * route must be finished by then, end-depot unload included.
1776
+ * @example 2026-07-01T17:00:00+02:00
1777
+ */
1778
+ to: string;
1779
+ };
1780
+ /**
1781
+ * @description Where a shift starts or ends: exactly one of `location` or `depot`.
1782
+ * @example {
1783
+ * "depot": "gent"
1784
+ * }
1785
+ */
1786
+ ShiftEnd: {
1787
+ /**
1788
+ * @description A `depots[].id`. Excludes `location`.
1789
+ * @example gent
1790
+ */
1791
+ depot?: string | null;
1792
+ /** @description An inline place. Excludes `depot`. */
1793
+ location?: null | components["schemas"]["Place"];
1794
+ /**
1795
+ * Format: int32
1796
+ * @description Handling time in seconds at this end: the morning load at `start`, the
1797
+ * evening unload at `end`. Overrides the depot's `service_duration_s`;
1798
+ * default: the depot's value, or 0 for a `location` end. Not applied on
1799
+ * an ignored leg (`ignore_start_leg`/`ignore_end_leg`). Not scaled by
1800
+ * `service_factor`.
1801
+ *
1802
+ * The unload must finish by the shift's `to` (or `to + max_overtime_s`):
1803
+ * a route whose unload would run past it is infeasible. A shift whose
1804
+ * load and unload alone exceed it is rejected with HTTP 400.
1805
+ * @example 600
1806
+ */
1807
+ service_duration_s?: number | null;
1808
+ };
1809
+ /**
1810
+ * @description Per-shift limits: the route-level `limits` fields a shift may override.
1811
+ * Period limits apply across shifts, so they stay on the vehicle.
1812
+ * @example {
1813
+ * "max_tasks": 12
1814
+ * }
1815
+ */
1816
+ ShiftLimits: {
1817
+ /**
1818
+ * Format: int64
1819
+ * @description Hard cap on this shift's route distance, in metres.
1820
+ * @example 200000
1821
+ */
1822
+ max_distance_m?: number | null;
1823
+ /**
1824
+ * Format: int64
1825
+ * @description Hard cap on this shift's route span, in seconds.
1826
+ * @example 32400
1827
+ */
1828
+ max_route_duration_s?: number | null;
1829
+ /**
1830
+ * Format: int32
1831
+ * @description Maximum stops served in this shift: a job counts as one stop, a
1832
+ * shipment as two (pickup and delivery).
1833
+ * @example 50
1834
+ */
1835
+ max_tasks?: number | null;
1836
+ };
1837
+ /**
1838
+ * @description A vehicle and one of its shifts: the key of a plan route.
1839
+ * @example {
1840
+ * "shift": "mon",
1841
+ * "vehicle": "v2"
1842
+ * }
1843
+ */
1844
+ ShiftRef: {
1845
+ /** @description Shift id, within that vehicle. */
1846
+ shift: string;
1847
+ /** @description Vehicle id. */
1848
+ vehicle: string;
1849
+ };
1850
+ /**
1851
+ * @description A bound pickup→delivery shipment (the `shipments[]` entry type).
1852
+ *
1853
+ * Two stops served by one vehicle, pickup strictly before delivery, with
1854
+ * `load` carried on the vehicle between the two legs.
1855
+ *
1856
+ * `drop_fee` and vehicle eligibility (`eligible_vehicles`, allow
1857
+ * **and** exclude, any length) are fully supported and applied to both legs — a
1858
+ * shipment's pickup and delivery share one droppability/eligibility decision,
1859
+ * since one vehicle must serve both.
1860
+ * @example {
1861
+ * "delivery": {
1862
+ * "location": [
1863
+ * 3.71,
1864
+ * 51.06
1865
+ * ],
1866
+ * "service_duration_s": 300
1867
+ * },
1868
+ * "id": "ship-1",
1869
+ * "load": {
1870
+ * "weight": 20
1871
+ * },
1872
+ * "pickup": {
1873
+ * "location": [
1874
+ * 3.725,
1875
+ * 51.05
1876
+ * ],
1877
+ * "service_duration_s": 300
1878
+ * }
1879
+ * }
1880
+ */
1881
+ Shipment: {
1882
+ /**
1883
+ * @description Absolute price of serving this task on a given vehicle, in the
1884
+ * objective's currency. Charged once, when that vehicle (on any of its
1885
+ * shifts) serves the task; unlisted vehicles cost nothing. Unlike
1886
+ * `preferences`, which charges the vehicles that did NOT get the task,
1887
+ * this prices the vehicle that did, so a task rated badly everywhere is
1888
+ * the first one dropped when capacity runs out. It lives in the cost tier,
1889
+ * below `priority`, and adds to `preferences`, `drop_fee` and travel.
1890
+ * Not applied by decomposition: a problem that sets it solves monolithically.
1891
+ */
1892
+ assignment_costs?: components["schemas"]["AssignmentCost"][] | null;
1893
+ /** @description Delivery leg (load off). */
1894
+ delivery: components["schemas"]["Stop"];
1895
+ /**
1896
+ * Format: double
1897
+ * @description Drop **price** — what leaving this task unserved costs you, in the same
1898
+ * currency as `objective.costs`, and the only way to make a task
1899
+ * droppable. Tasks without a `drop_fee` must be served.
1900
+ *
1901
+ * This is money, not a priority: it enters the same cost the solver
1902
+ * minimises, so the task is served only while serving it costs less than
1903
+ * the fee. Fees the plan chose to pay are reported in
1904
+ * `score.components.drop_fees` and summed into `score.cost`.
1905
+ *
1906
+ * On a shipment the fee applies to the pair, charged once.
1907
+ * @example 25
1908
+ */
1909
+ drop_fee?: number | null;
1910
+ /** @description Hard vehicle allow/exclude. */
1911
+ eligible_vehicles?: null | components["schemas"]["Eligibility"];
1912
+ /**
1913
+ * @description Shipment id: 1 to 128 characters, unique across `jobs` + `shipments`.
1914
+ * @example ship-1
1915
+ */
1916
+ id: string;
1917
+ /**
1918
+ * @description Load carried from the pickup to the delivery: picked up at one leg and
1919
+ * the same quantity dropped off at the other. Non-negative magnitudes
1920
+ * per named dimension.
1921
+ */
1922
+ load?: null | components["schemas"]["BTreeMap"];
1923
+ /**
1924
+ * @description Hard-pin this job to one vehicle id — only that vehicle (any of its
1925
+ * shifts) may serve it, and once placed, re-optimization never removes it
1926
+ * or moves it to another vehicle. Stronger than `eligible_vehicles`
1927
+ * (which filters a set) and hard, unlike `preferences`.
1928
+ *
1929
+ * A pin means "stays on this vehicle if placed", **not** "must be
1930
+ * served": the task can still end up unassigned (for example when it does
1931
+ * not fit), and with a `drop_fee` it then pays the fee. An id that
1932
+ * matches no vehicle is rejected with HTTP 400.
1933
+ * @example vehicle-1
1934
+ */
1935
+ locked_vehicle?: string | null;
1936
+ /** @description Pickup leg (load on). */
1937
+ pickup: components["schemas"]["Stop"];
1938
+ /**
1939
+ * @description Soft vehicle affinity preferences, priced. Each entry is independent:
1940
+ * a job listing several preferred vehicles it doesn't get pays each
1941
+ * one's `violation_cost` (compounding, the same rule as a vehicle's
1942
+ * `preferred_tags`).
1943
+ */
1944
+ preferences?: components["schemas"]["Preference"][] | null;
1945
+ /**
1946
+ * Format: date-time
1947
+ * @description Soft preference for the calendar day this task is served on, in a
1948
+ * multi-day (multi-shift) problem.
1949
+ *
1950
+ * An RFC 3339 datetime, UTC offset required, on the intended day. A day is the calendar date of
1951
+ * the value **in its own UTC offset**, the same rule that dates a shift
1952
+ * from its `from`, so passing the target shift's `from` verbatim is
1953
+ * always correct.
1954
+ *
1955
+ * Soft, and priced by `objective.non_preferred_date_cost` — a flat charge
1956
+ * applied once to a task served on any other day. **With that cost unset or
1957
+ * zero this field has no effect.** The charge is part of `score.cost`,
1958
+ * which ranks below serving required tasks, so a task is never left
1959
+ * unassigned to honour its preferred date; `locked_vehicle` (hard) also
1960
+ * still wins.
1961
+ *
1962
+ * Supported on shipments as well as jobs. Both legs of a shipment ride
1963
+ * the same route, so the preferred day is a property of the pair: set it
1964
+ * on the shipment itself, and `non_preferred_date_cost` is charged once
1965
+ * for the pair rather than once per leg.
1966
+ * @example 2026-08-04T08:00:00+02:00
1967
+ */
1968
+ preferred_date?: string | null;
1969
+ /**
1970
+ * Format: int32
1971
+ * @description Importance of a must-serve task when not all of them fit: the solver
1972
+ * maximises the summed `priority` of served tasks before any cost. `>= 1`,
1973
+ * default `1` (maximise the count). A rank, not money — it never makes a
1974
+ * task droppable; use `drop_fee` for that. Both set is a 400.
1975
+ * @example 3
1976
+ */
1977
+ priority?: number | null;
1978
+ /**
1979
+ * @description Per-vehicle work-rate overrides for this task's `service_duration_s`,
1980
+ * composed multiplicatively with the serving vehicle's or shift's
1981
+ * `service_factor`. On a shipment the override scales both legs. A
1982
+ * vehicle not listed works at its own rate.
1983
+ */
1984
+ service_factors?: components["schemas"]["ServiceFactor"][] | null;
1985
+ /**
1986
+ * @description Hard skill requirements — the assigned vehicle must provide every one,
1987
+ * at or above each requirement's `min_level`.
1988
+ */
1989
+ skills?: components["schemas"]["SkillReq"][] | null;
1990
+ /**
1991
+ * @description Free-form task labels (zones/territories, grouping/relation labels).
1992
+ * Used for `tag`/relation resolution and, when a shift scopes itself via
1993
+ * `serves_tags` (hard ANY-overlap) or a vehicle prices them via
1994
+ * `preferred_tags` (soft), for tag matching. ANY-overlap — contrast with
1995
+ * `skills`, which requires ALL. Not capability matching on its own.
1996
+ */
1997
+ tags?: string[] | null;
1998
+ /**
1999
+ * @description Soft target for this task, measured at its arrival (default) or
2000
+ * departure. On a shipment it is measured on the **delivery** leg.
2001
+ */
2002
+ target?: null | components["schemas"]["Target"];
2003
+ };
2004
+ /**
2005
+ * @description A hard skill requirement — the assigned vehicle must provide it.
2006
+ * @example {
2007
+ * "name": "fridge"
2008
+ * }
2009
+ */
2010
+ SkillReq: {
2011
+ /**
2012
+ * Format: int32
2013
+ * @description Minimum level the serving vehicle must hold for this skill (`>= 1`).
2014
+ * Absent = 1. A vehicle skill with no `level` holds level 1.
2015
+ * @example 2
2016
+ */
2017
+ min_level?: number | null;
2018
+ /**
2019
+ * @description Skill name the assigned vehicle must provide.
2020
+ * @example fridge
2021
+ */
2022
+ name: string;
2023
+ };
2024
+ /**
2025
+ * @description Solve options.
2026
+ * @example {
2027
+ * "polylines": "full",
2028
+ * "seed": 42,
2029
+ * "time_limit_s": 5
2030
+ * }
2031
+ */
2032
+ SolveOptions: {
2033
+ /**
2034
+ * @description Return road geometry for each route as `routes[].polyline`:
2035
+ * `simplified` or `full`. Omitted → no geometry (the default).
2036
+ */
2037
+ polylines?: null | components["schemas"]["PolylineDetail"];
2038
+ /**
2039
+ * Format: int64
2040
+ * @description Random seed for the search. Default `42`: omitting it is the same as
2041
+ * sending `42`, so send different values to explore different plans.
2042
+ * Repeated runs on one machine usually return an identical plan; a
2043
+ * materially faster or slower machine can differ.
2044
+ * @example 42
2045
+ */
2046
+ seed?: number | null;
2047
+ /**
2048
+ * Format: double
2049
+ * @description Search time budget in seconds, fractions allowed (default 1). Maximum
2050
+ * 300: a larger value is rejected with HTTP 400. A value below 0.1 is
2051
+ * raised to a 100 ms floor. Pure search time: fetching travel times and
2052
+ * waiting for capacity are not deducted from it, so the request takes
2053
+ * longer than this value.
2054
+ * @example 2.5
2055
+ */
2056
+ time_limit_s?: number | null;
2057
+ /**
2058
+ * @description What to do when Solvice Maps cannot answer (outage, timeout,
2059
+ * overload): `estimate` (the default) solves on estimated travel and
2060
+ * sets `summary.travel_estimated`; `fail` returns the 502/503
2061
+ * instead. A request Maps rejects as invalid always fails.
2062
+ */
2063
+ travel_fallback?: null | components["schemas"]["TravelFallback"];
2064
+ };
2065
+ /**
2066
+ * @description Request body for `POST /v3/routing/solve`.
2067
+ * @example {
2068
+ * "options": {
2069
+ * "seed": 42,
2070
+ * "time_limit_s": 5
2071
+ * },
2072
+ * "problem": {
2073
+ * "depots": [
2074
+ * {
2075
+ * "id": "gent",
2076
+ * "location": [
2077
+ * 3.7174,
2078
+ * 51.0543
2079
+ * ]
2080
+ * }
2081
+ * ],
2082
+ * "jobs": [
2083
+ * {
2084
+ * "id": "job-1",
2085
+ * "location": [
2086
+ * 3.725,
2087
+ * 51.05
2088
+ * ],
2089
+ * "pickup": {
2090
+ * "weight": 20
2091
+ * },
2092
+ * "service_duration_s": 300,
2093
+ * "skills": [
2094
+ * {
2095
+ * "name": "fridge"
2096
+ * }
2097
+ * ],
2098
+ * "time_windows": [
2099
+ * {
2100
+ * "from": "2026-07-01T09:00:00+02:00",
2101
+ * "to": "2026-07-01T12:00:00+02:00"
2102
+ * }
2103
+ * ]
2104
+ * },
2105
+ * {
2106
+ * "id": "job-2",
2107
+ * "location": [
2108
+ * 3.71,
2109
+ * 51.06
2110
+ * ],
2111
+ * "pickup": {
2112
+ * "weight": 35
2113
+ * },
2114
+ * "service_duration_s": 600
2115
+ * }
2116
+ * ],
2117
+ * "objective": {
2118
+ * "costs": {
2119
+ * "per_route_hour": 35,
2120
+ * "per_travel_km": 0.2
2121
+ * },
2122
+ * "currency": "EUR"
2123
+ * },
2124
+ * "vehicles": [
2125
+ * {
2126
+ * "capacity": {
2127
+ * "weight": 500
2128
+ * },
2129
+ * "id": "v1",
2130
+ * "profile": "car",
2131
+ * "shifts": [
2132
+ * {
2133
+ * "end": {
2134
+ * "depot": "gent"
2135
+ * },
2136
+ * "from": "2026-07-01T08:00:00+02:00",
2137
+ * "id": "mon",
2138
+ * "start": {
2139
+ * "depot": "gent"
2140
+ * },
2141
+ * "to": "2026-07-01T17:00:00+02:00"
2142
+ * }
2143
+ * ],
2144
+ * "skills": [
2145
+ * {
2146
+ * "name": "fridge"
2147
+ * }
2148
+ * ]
2149
+ * }
2150
+ * ]
2151
+ * }
2152
+ * }
2153
+ */
2154
+ SolveRequest: {
2155
+ /** @description Solve options. */
2156
+ options?: null | components["schemas"]["SolveOptions"];
2157
+ /**
2158
+ * @description Warm start: the plan the search starts from instead of constructing
2159
+ * from scratch (re-optimization). A result's `plan` goes in as it stands.
2160
+ * See `plan.routes[].locked_count` for freezing a dispatched prefix.
2161
+ *
2162
+ * A **hint, not a guarantee**: an unlocked stop that violates a hard
2163
+ * constraint (e.g. capacity, skills, eligibility, `limits.*`, or a time
2164
+ * window with no `per_late_hour` set) is left unassigned and the
2165
+ * search reinserts it wherever it legitimately fits, or it surfaces in
2166
+ * `unassigned[]` with a typed reason. An infeasible locked stop is a 400.
2167
+ */
2168
+ plan?: null | components["schemas"]["Plan"];
2169
+ /** @description The problem: tasks, fleet, depots, relations, objective, travel. */
2170
+ problem: components["schemas"]["ProblemInput"];
2171
+ };
2172
+ /**
2173
+ * @description The result of `POST /v3/routing/solve` and `POST /v3/routing/evaluate`:
2174
+ * one shape for a plan the solver built and a plan it was given to score.
2175
+ * @example {
2176
+ * "plan": {
2177
+ * "routes": [
2178
+ * {
2179
+ * "shift": "mon",
2180
+ * "stops": [
2181
+ * {
2182
+ * "id": "job-1",
2183
+ * "type": "job"
2184
+ * },
2185
+ * {
2186
+ * "id": "job-2",
2187
+ * "type": "job"
2188
+ * }
2189
+ * ],
2190
+ * "vehicle": "v1"
2191
+ * }
2192
+ * ]
2193
+ * },
2194
+ * "routes": [
2195
+ * {
2196
+ * "distance_m": 8450,
2197
+ * "load_peak": {
2198
+ * "weight": 55
2199
+ * },
2200
+ * "shift": "mon",
2201
+ * "stops": [
2202
+ * {
2203
+ * "arrival": "2026-07-01T08:00:00+02:00",
2204
+ * "departure": "2026-07-01T08:00:00+02:00",
2205
+ * "depot": "gent",
2206
+ * "load_after": {
2207
+ * "weight": 0
2208
+ * },
2209
+ * "location": [
2210
+ * 3.7174,
2211
+ * 51.0543
2212
+ * ],
2213
+ * "service_s": 0,
2214
+ * "type": "start"
2215
+ * },
2216
+ * {
2217
+ * "arrival": "2026-07-01T08:10:00+02:00",
2218
+ * "departure": "2026-07-01T09:05:00+02:00",
2219
+ * "distance_m": 2600,
2220
+ * "id": "job-1",
2221
+ * "lateness_s": 0,
2222
+ * "load_after": {
2223
+ * "weight": 20
2224
+ * },
2225
+ * "location": [
2226
+ * 3.725,
2227
+ * 51.05
2228
+ * ],
2229
+ * "service_s": 300,
2230
+ * "travel_s": 600,
2231
+ * "type": "job",
2232
+ * "wait_s": 3000
2233
+ * },
2234
+ * {
2235
+ * "arrival": "2026-07-01T09:13:00+02:00",
2236
+ * "departure": "2026-07-01T09:23:00+02:00",
2237
+ * "distance_m": 2350,
2238
+ * "id": "job-2",
2239
+ * "lateness_s": 0,
2240
+ * "load_after": {
2241
+ * "weight": 55
2242
+ * },
2243
+ * "location": [
2244
+ * 3.71,
2245
+ * 51.06
2246
+ * ],
2247
+ * "service_s": 600,
2248
+ * "travel_s": 480,
2249
+ * "type": "job",
2250
+ * "wait_s": 0
2251
+ * },
2252
+ * {
2253
+ * "arrival": "2026-07-01T09:38:00+02:00",
2254
+ * "departure": "2026-07-01T09:38:00+02:00",
2255
+ * "depot": "gent",
2256
+ * "distance_m": 3500,
2257
+ * "load_after": {
2258
+ * "weight": 55
2259
+ * },
2260
+ * "location": [
2261
+ * 3.7174,
2262
+ * 51.0543
2263
+ * ],
2264
+ * "service_s": 0,
2265
+ * "travel_s": 900,
2266
+ * "type": "end"
2267
+ * }
2268
+ * ],
2269
+ * "time": {
2270
+ * "break_s": 0,
2271
+ * "depot_s": 0,
2272
+ * "overtime_s": 0,
2273
+ * "service_s": 900,
2274
+ * "span_s": 5880,
2275
+ * "travel_s": 1980,
2276
+ * "wait_s": 3000
2277
+ * },
2278
+ * "vehicle": "v1"
2279
+ * }
2280
+ * ],
2281
+ * "summary": {
2282
+ * "distance_m": 8450,
2283
+ * "score": {
2284
+ * "components": {
2285
+ * "depot_visits": 0,
2286
+ * "distance": 1.69,
2287
+ * "drop_fees": 0,
2288
+ * "fixed": 0,
2289
+ * "grouping": 0,
2290
+ * "lateness": 0,
2291
+ * "other": 0,
2292
+ * "overtime": 0,
2293
+ * "tag_preference": 0,
2294
+ * "target": 0,
2295
+ * "time": 57.1536,
2296
+ * "waiting": 0,
2297
+ * "window_choice": 0
2298
+ * },
2299
+ * "cost": 58.8436,
2300
+ * "currency": "EUR",
2301
+ * "unserved_priority": 0,
2302
+ * "violations": 0
2303
+ * },
2304
+ * "shifts_used": 1,
2305
+ * "tasks": {
2306
+ * "served": 2,
2307
+ * "unserved": 0,
2308
+ * "unserved_required": 0
2309
+ * },
2310
+ * "time": {
2311
+ * "break_s": 0,
2312
+ * "depot_s": 0,
2313
+ * "overtime_s": 0,
2314
+ * "service_s": 900,
2315
+ * "span_s": 5880,
2316
+ * "travel_s": 1980,
2317
+ * "wait_s": 3000
2318
+ * },
2319
+ * "timings": {
2320
+ * "search_ms": 100,
2321
+ * "total_ms": 142
2322
+ * },
2323
+ * "vehicles_used": 1
2324
+ * },
2325
+ * "unassigned": []
2326
+ * }
2327
+ */
2328
+ SolveResponse: {
2329
+ /**
2330
+ * @description The plan these routes follow, in the request's vocabulary: valid input
2331
+ * to evaluate, suggest or a warm-started solve as it stands.
2332
+ */
2333
+ plan: components["schemas"]["Plan"];
2334
+ /** @description One entry per non-empty route. */
2335
+ routes: components["schemas"]["Route"][];
2336
+ /** @description Aggregate statistics for the solve run. */
2337
+ summary: components["schemas"]["Summary"];
2338
+ /** @description Tasks that could not be feasibly assigned. */
2339
+ unassigned: components["schemas"]["Unassigned"][];
2340
+ };
2341
+ /**
2342
+ * @description One leg of a shipment (pickup or delivery).
2343
+ * @example {
2344
+ * "id": "ship-1:pickup",
2345
+ * "location": [
2346
+ * 3.725,
2347
+ * 51.05
2348
+ * ],
2349
+ * "service_duration_s": 300,
2350
+ * "setup_duration_s": 60,
2351
+ * "time_windows": [
2352
+ * {
2353
+ * "from": "2026-07-01T09:00:00+02:00",
2354
+ * "to": "2026-07-01T12:00:00+02:00"
2355
+ * }
2356
+ * ]
2357
+ * }
2358
+ */
2359
+ Stop: {
2360
+ /**
2361
+ * @description Whether a break may interrupt service here. Default `true`. `false`
2362
+ * is not supported yet and is rejected with HTTP 400.
2363
+ * @example true
2364
+ */
2365
+ break_interruptible?: boolean | null;
2366
+ /**
2367
+ * @description Stop id: 1 to 128 characters when set. Defaults to
2368
+ * `"{shipment}:pickup"` / `"{shipment}:delivery"`. The effective id
2369
+ * (custom or default) must be unique across all job, shipment, and stop
2370
+ * ids — a collision is rejected with HTTP 400.
2371
+ * @example ship-1:pickup
2372
+ */
2373
+ id?: string | null;
2374
+ /** @description Where this stop happens. */
2375
+ location: components["schemas"]["Place"];
2376
+ /**
2377
+ * Format: int32
2378
+ * @description On-site service time paid per job (seconds).
2379
+ *
2380
+ * This is the nominal figure. The serving vehicle's `service_factor`
2381
+ * scales it — the time actually scheduled is
2382
+ * `service_duration_s / service_factor` — so the solution's
2383
+ * `stops[].service_s` may differ from what you sent here.
2384
+ * @example 300
2385
+ */
2386
+ service_duration_s?: number | null;
2387
+ /**
2388
+ * Format: int32
2389
+ * @description Setup time paid once on arrival at a new location (seconds),
2390
+ * distinct from `service_duration_s`. Skipped when the immediate real
2391
+ * predecessor in the route already sits at this location — co-located
2392
+ * stops share one setup.
2393
+ *
2394
+ * Not scaled by the vehicle's `service_factor`: setup is fixed arrival
2395
+ * overhead (parking, walking to the door), not the work itself.
2396
+ * @example 60
2397
+ */
2398
+ setup_duration_s?: number | null;
2399
+ /** @description Time windows for this stop. */
2400
+ time_windows?: components["schemas"]["TimeWindow"][] | null;
2401
+ };
2402
+ /**
2403
+ * @description Which instant of a stop a `target` or a relation refers to. One vocabulary
2404
+ * for both, each accepting the values that mean something there.
2405
+ * @enum {string}
2406
+ */
2407
+ StopInstant: "arrival" | "start" | "end";
2408
+ /**
2409
+ * @description A single stop within a route.
2410
+ * @example {
2411
+ * "arrival": "2026-07-01T08:10:00+02:00",
2412
+ * "departure": "2026-07-01T09:05:00+02:00",
2413
+ * "distance_m": 2600,
2414
+ * "id": "job-1",
2415
+ * "lateness_s": 0,
2416
+ * "load_after": {
2417
+ * "weight": 20
2418
+ * },
2419
+ * "location": [
2420
+ * 3.725,
2421
+ * 51.05
2422
+ * ],
2423
+ * "service_s": 300,
2424
+ * "travel_s": 600,
2425
+ * "type": "job",
2426
+ * "wait_s": 3000
2427
+ * }
2428
+ */
2429
+ StopOutput: {
2430
+ /**
2431
+ * Format: date-time
2432
+ * @description Physical arrival time (RFC 3339), before any waiting; the service
2433
+ * start is `arrival + wait_s`. Every stop time is rendered in the UTC
2434
+ * offset of the route's shift `from` (`Z` when that `from` is UTC or has
2435
+ * no offset): a `+02:00` shift reports `2026-07-01T08:10:00+02:00`, the
2436
+ * same instant as `2026-07-01T06:10:00Z`.
2437
+ * @example 2026-07-01T08:10:00+02:00
2438
+ */
2439
+ arrival?: string | null;
2440
+ /**
2441
+ * Format: date-time
2442
+ * @description Departure time (RFC 3339, in the offset of `arrival`).
2443
+ * @example 2026-07-01T09:05:00+02:00
2444
+ */
2445
+ departure?: string | null;
2446
+ /**
2447
+ * @description The `depots[].id` of this stop: on a `start`/`end` stop whose shift end
2448
+ * names a depot, and on every `reload` stop. Absent on a `location` or
2449
+ * open end and on task and break stops.
2450
+ * @example gent
2451
+ */
2452
+ depot?: string | null;
2453
+ /**
2454
+ * Format: int32
2455
+ * @description Travel distance from the previous stop, in metres.
2456
+ * @example 2600
2457
+ */
2458
+ distance_m?: number | null;
2459
+ /**
2460
+ * Format: int32
2461
+ * @description The part of `wait_s` spent holding for a `synchronized` or
2462
+ * `time_lag` partner on another vehicle, beyond any wait the time
2463
+ * window already imposed. Present on task stops only when non-zero.
2464
+ * @example 0
2465
+ */
2466
+ hold_s?: number | null;
2467
+ /**
2468
+ * @description Stop id: the job id on a `job` stop, the leg's stop id on
2469
+ * `pickup`/`delivery` (its own `id`, else `{shipment}:pickup` /
2470
+ * `{shipment}:delivery`). Absent on start, end, break and reload stops.
2471
+ * @example job-1
2472
+ */
2473
+ id?: string | null;
2474
+ /**
2475
+ * Format: int32
2476
+ * @description Seconds late beyond the latest allowed arrival (`0` when on time).
2477
+ * Measures the hard time window only; a soft target is reported in
2478
+ * `target_lateness_s`.
2479
+ * @example 0
2480
+ */
2481
+ lateness_s?: number | null;
2482
+ /** @description Cumulative load carried after this stop, by named dimension. */
2483
+ load_after?: null | components["schemas"]["BTreeMap"];
2484
+ /**
2485
+ * @description Stop location `[longitude, latitude]` (GeoJSON order). Absent on
2486
+ * every `break` stop, including a break with a pinned location, and on
2487
+ * `start`/`end` stops for a genuinely open leg (no configured depot
2488
+ * coordinate for that end of the shift). A virtual (location-less) job
2489
+ * has no coordinate of its own and reports its nearest located
2490
+ * neighbor's instead, so it is absent only when the whole route is
2491
+ * virtual with open depot ends.
2492
+ * @example [
2493
+ * 3.725,
2494
+ * 51.05
2495
+ * ]
2496
+ */
2497
+ location?: number[] | null;
2498
+ /**
2499
+ * Format: int32
2500
+ * @description On-site service time at this stop, in seconds.
2501
+ *
2502
+ * The **effective** time, not the nominal `service_duration_s` from the
2503
+ * request: on a task stop it already has the serving vehicle's
2504
+ * `service_factor` applied. The timeline reconciles as
2505
+ * `departure - arrival - wait_s == service_s` only when the stop pays no
2506
+ * setup time; with `setup_duration_s` set, the difference is larger by
2507
+ * the (unscaled) setup seconds. Depot, reload, and break stops report
2508
+ * their own nominal handling/break duration, which `service_factor`
2509
+ * never scales.
2510
+ *
2511
+ * On `start`/`end`: the depot handling time applied there (0 on an
2512
+ * ignored leg); absent on an open end.
2513
+ * @example 300
2514
+ */
2515
+ service_s?: number | null;
2516
+ /**
2517
+ * Format: date-time
2518
+ * @description When service starts (RFC 3339, in the offset of `arrival`): after the
2519
+ * arrival, any windowed
2520
+ * break taken on arrival there, and the wait. On task and reload stops;
2521
+ * like `arrival`, absent on a request without time windows.
2522
+ * @example 2026-07-01T09:00:00+02:00
2523
+ */
2524
+ service_start?: string | null;
2525
+ /** @description The same point snapped to the routable graph, when available. */
2526
+ snapped_location?: number[] | null;
2527
+ /**
2528
+ * Format: int32
2529
+ * @description Seconds past this task's `target.at`, measured at the stop's arrival
2530
+ * or departure per `target.on`; `0` when met. Present only on a task
2531
+ * stop carrying a target. A shipment's target is measured
2532
+ * on its delivery leg, so it appears on the `:delivery` stop and never on
2533
+ * the `:pickup` stop.
2534
+ * @example 0
2535
+ */
2536
+ target_lateness_s?: number | null;
2537
+ /**
2538
+ * Format: int32
2539
+ * @description Travel time from the previous stop, in seconds.
2540
+ * @example 600
2541
+ */
2542
+ travel_s?: number | null;
2543
+ /** @description What kind of stop this is. */
2544
+ type: components["schemas"]["StopType"];
2545
+ /**
2546
+ * @description Constraint violations detected at this stop. Only populated by
2547
+ * `POST /v3/routing/evaluate` (imported solutions may be infeasible); a
2548
+ * solve response never emits violations, so this is absent there.
2549
+ */
2550
+ violations?: components["schemas"]["Violation"][];
2551
+ /**
2552
+ * Format: int32
2553
+ * @description Seconds spent waiting here before service: for the time window to
2554
+ * open, and for any cross-route hold (`hold_s`, included in this
2555
+ * figure).
2556
+ * @example 3000
2557
+ */
2558
+ wait_s?: number | null;
2559
+ };
2560
+ /**
2561
+ * @description The kind of a stop in a route.
2562
+ *
2563
+ * Every route has one `start` and one `end`. A served job is `job`, and a
2564
+ * shipment's legs are `pickup` and `delivery`. A driver break is `break`,
2565
+ * and a mid-shift reload at a depot is `reload`.
2566
+ * @example job
2567
+ * @enum {string}
2568
+ */
2569
+ StopType: "start" | "job" | "pickup" | "delivery" | "break" | "reload" | "end";
2570
+ /**
2571
+ * @description Suggest options.
2572
+ * @example {
2573
+ * "max_suggestions": 5,
2574
+ * "time_limit_s": 2
2575
+ * }
2576
+ */
2577
+ SuggestOptions: {
2578
+ /**
2579
+ * @description Max ranked options per task. Default 5; `0` returns every feasible
2580
+ * placement; positive values are clamped to `1..=100`.
2581
+ * @example 5
2582
+ */
2583
+ max_suggestions?: number | null;
2584
+ /**
2585
+ * Format: double
2586
+ * @description Search budget in seconds, fractions allowed (default 1, as on `/solve`;
2587
+ * minimum 0.1 — smaller values are raised to it; maximum 300). The clock
2588
+ * starts when the search starts, after the matrix fetch and any wait for a
2589
+ * solver slot. Units are searched in problem order (jobs, then
2590
+ * shipments), each with an equal share of the time left when it starts; a
2591
+ * unit that finishes early leaves its slack to the ones after it.
2592
+ * @example 2.5
2593
+ */
2594
+ time_limit_s?: number | null;
2595
+ };
2596
+ /**
2597
+ * @description Why a unit's `insertions` is empty: the machine-readable form of
2598
+ * `reason`, to branch on.
2599
+ *
2600
+ * Open set: values may be added within `/v3`. Accept values not listed here
2601
+ * and handle them generically, for example by showing `reason`.
2602
+ * @example infeasible
2603
+ * @enum {string}
2604
+ */
2605
+ SuggestReasonCode: "time_limit" | "candidates_exhausted" | "infeasible";
2606
+ /**
2607
+ * @description Request body for `POST /v3/routing/suggest`.
2608
+ *
2609
+ * The same body as `POST /v3/routing/evaluate`, plus optional `options`.
2610
+ * Given the committed `plan` over `problem`, ranks the feasible insertion
2611
+ * positions for every task of `problem` that is not in `plan` — the tasks a
2612
+ * solve would place and an evaluate would report as unassigned. Each task
2613
+ * (or unit — a shipment, or tasks tied by one `sequence`/`same_vehicle`/
2614
+ * `same_day`/`same_route` relation) is ranked independently against the same
2615
+ * committed plan; the plan is never modified.
2616
+ * @example {
2617
+ * "options": {
2618
+ * "max_suggestions": 5
2619
+ * },
2620
+ * "plan": {
2621
+ * "routes": [
2622
+ * {
2623
+ * "shift": "mon",
2624
+ * "stops": [
2625
+ * {
2626
+ * "id": "job-1",
2627
+ * "type": "job"
2628
+ * }
2629
+ * ],
2630
+ * "vehicle": "v1"
2631
+ * }
2632
+ * ]
2633
+ * },
2634
+ * "problem": {
2635
+ * "jobs": [
2636
+ * {
2637
+ * "id": "job-1",
2638
+ * "location": [
2639
+ * 3.725,
2640
+ * 51.05
2641
+ * ],
2642
+ * "pickup": {
2643
+ * "weight": 10
2644
+ * },
2645
+ * "service_duration_s": 300
2646
+ * },
2647
+ * {
2648
+ * "id": "job-2",
2649
+ * "location": [
2650
+ * 3.71,
2651
+ * 51.06
2652
+ * ],
2653
+ * "pickup": {
2654
+ * "weight": 10
2655
+ * },
2656
+ * "service_duration_s": 300
2657
+ * }
2658
+ * ],
2659
+ * "vehicles": [
2660
+ * {
2661
+ * "capacity": {
2662
+ * "weight": 500
2663
+ * },
2664
+ * "id": "v1",
2665
+ * "shifts": [
2666
+ * {
2667
+ * "end": {
2668
+ * "location": [
2669
+ * 3.7174,
2670
+ * 51.0543
2671
+ * ]
2672
+ * },
2673
+ * "from": "2026-07-01T08:00:00+02:00",
2674
+ * "id": "mon",
2675
+ * "start": {
2676
+ * "location": [
2677
+ * 3.7174,
2678
+ * 51.0543
2679
+ * ]
2680
+ * },
2681
+ * "to": "2026-07-01T17:00:00+02:00"
2682
+ * }
2683
+ * ]
2684
+ * }
2685
+ * ]
2686
+ * }
2687
+ * }
2688
+ */
2689
+ SuggestRequest: {
2690
+ /** @description Suggest options. Absent: the defaults. */
2691
+ options?: null | components["schemas"]["SuggestOptions"];
2692
+ /**
2693
+ * @description The committed plan. Never modified, never reordered. On suggest,
2694
+ * `locked_count` keeps insertions after a route's dispatched prefix.
2695
+ */
2696
+ plan: components["schemas"]["Plan"];
2697
+ /**
2698
+ * @description Problem definition — identical to `POST /v3/routing/solve`. Every task
2699
+ * of it that is not in `plan` is a task to place.
2700
+ */
2701
+ problem: components["schemas"]["ProblemInput"];
2702
+ };
2703
+ /**
2704
+ * @description Response body for `POST /v3/routing/suggest`.
2705
+ * @example {
2706
+ * "elapsed_ms": 8,
2707
+ * "suggestions": [
2708
+ * {
2709
+ * "insertions": [
2710
+ * {
2711
+ * "delta": {
2712
+ * "cost": 12.4,
2713
+ * "distance_m": 2400,
2714
+ * "duration_s": 240
2715
+ * },
2716
+ * "placements": [
2717
+ * {
2718
+ * "arrival": "2026-07-01T09:30:00+02:00",
2719
+ * "id": "job-2",
2720
+ * "latest_arrival": "2026-07-01T10:15:00+02:00",
2721
+ * "shift": "mon",
2722
+ * "type": "job",
2723
+ * "vehicle": "v1"
2724
+ * }
2725
+ * ],
2726
+ * "routes": [
2727
+ * {
2728
+ * "shift": "mon",
2729
+ * "stops": [
2730
+ * {
2731
+ * "id": "job-1",
2732
+ * "type": "job"
2733
+ * },
2734
+ * {
2735
+ * "id": "job-2",
2736
+ * "type": "job"
2737
+ * }
2738
+ * ],
2739
+ * "vehicle": "v1"
2740
+ * }
2741
+ * ]
2742
+ * }
2743
+ * ],
2744
+ * "job_id": "job-2"
2745
+ * }
2746
+ * ],
2747
+ * "truncated": false
2748
+ * }
2749
+ */
2750
+ SuggestResponse: {
2751
+ /**
2752
+ * Format: int64
2753
+ * @description Wall-clock time taken by the request, in milliseconds.
2754
+ * @example 8
2755
+ */
2756
+ elapsed_ms: number;
2757
+ /**
2758
+ * @description One entry per unit of tasks outside `plan`, in problem order (jobs,
2759
+ * then shipments; a relation group sorts at its first-listed member).
2760
+ */
2761
+ suggestions: components["schemas"]["JobSuggestions"][];
2762
+ /**
2763
+ * @description `true` when `options.time_limit_s` cut the search short, or a
2764
+ * multi-member unit came back empty after the search had discarded
2765
+ * candidates (an inconclusive "not found"; its `reason_code` is
2766
+ * `candidates_exhausted`). Lists may then be incomplete or missing the
2767
+ * best insertion. A single visit's only level is never pruned, so its
2768
+ * empty list is proof of infeasibility unless the budget cut it, and its
2769
+ * `reason_code` is then `time_limit`.
2770
+ */
2771
+ truncated?: boolean;
2772
+ };
2773
+ /**
2774
+ * @description The plan in numbers: its score, what it serves and what it takes.
2775
+ * @example {
2776
+ * "distance_m": 8450,
2777
+ * "score": {
2778
+ * "components": {
2779
+ * "depot_visits": 0,
2780
+ * "distance": 1.69,
2781
+ * "drop_fees": 0,
2782
+ * "fixed": 0,
2783
+ * "grouping": 0,
2784
+ * "lateness": 0,
2785
+ * "other": 0,
2786
+ * "overtime": 0,
2787
+ * "tag_preference": 0,
2788
+ * "target": 0,
2789
+ * "time": 57.1536,
2790
+ * "waiting": 0,
2791
+ * "window_choice": 0
2792
+ * },
2793
+ * "cost": 58.8436,
2794
+ * "currency": "EUR",
2795
+ * "unserved_priority": 0,
2796
+ * "violations": 0
2797
+ * },
2798
+ * "shifts_used": 1,
2799
+ * "tasks": {
2800
+ * "served": 2,
2801
+ * "unserved": 0,
2802
+ * "unserved_required": 0
2803
+ * },
2804
+ * "time": {
2805
+ * "break_s": 0,
2806
+ * "depot_s": 0,
2807
+ * "overtime_s": 0,
2808
+ * "service_s": 900,
2809
+ * "span_s": 5880,
2810
+ * "travel_s": 1980,
2811
+ * "wait_s": 3000
2812
+ * },
2813
+ * "timings": {
2814
+ * "search_ms": 100,
2815
+ * "total_ms": 142
2816
+ * },
2817
+ * "vehicles_used": 1
2818
+ * }
2819
+ */
2820
+ Summary: {
2821
+ /**
2822
+ * Format: int64
2823
+ * @description Sum of all route distances, in metres.
2824
+ * @example 8450
2825
+ */
2826
+ distance_m: number;
2827
+ /**
2828
+ * @description How good the plan is. On `/solve`, `score.violations` is always 0; on
2829
+ * `/evaluate` it counts the constraints the given plan breaks.
2830
+ */
2831
+ score: components["schemas"]["Score"];
2832
+ /**
2833
+ * Format: int32
2834
+ * @description Routes with at least one task stop: one per used shift.
2835
+ * @example 1
2836
+ */
2837
+ shifts_used: number;
2838
+ /** @description Tasks served and left out. */
2839
+ tasks: components["schemas"]["TaskCounts"];
2840
+ /** @description Where the fleet's time goes, summed over routes. */
2841
+ time: components["schemas"]["TimeTotals"];
2842
+ /** @description Where the request's time went. */
2843
+ timings: components["schemas"]["Timings"];
2844
+ /**
2845
+ * @description `true` when Solvice Maps could not be reached and the plan was costed
2846
+ * on estimated travel: straight-line distance × a detour factor, at a
2847
+ * typical speed for the profile. No one-way streets or traffic, so it
2848
+ * can be tens of percent off: the plan, `unassigned[].reasons` and
2849
+ * `score.violations` hold only against it, and no `routes[].polyline` is
2850
+ * returned. Solve again once Maps is back if the plan must hold on the
2851
+ * road; `options.travel_fallback: "fail"` returns the error instead.
2852
+ * Absent when travel is road-routed.
2853
+ */
2854
+ travel_estimated?: boolean;
2855
+ /**
2856
+ * Format: int32
2857
+ * @description Distinct vehicles with at least one route.
2858
+ * @example 1
2859
+ */
2860
+ vehicles_used: number;
2861
+ };
2862
+ /**
2863
+ * @description A soft tag preference, priced.
2864
+ * @example {
2865
+ * "tag": "north",
2866
+ * "violation_cost": 25
2867
+ * }
2868
+ */
2869
+ TagPreference: {
2870
+ /**
2871
+ * @description Preferred tag.
2872
+ * @example north
2873
+ */
2874
+ tag: string;
2875
+ /**
2876
+ * Format: double
2877
+ * @description Cost, in the objective's currency, incurred when a task without this
2878
+ * tag is served.
2879
+ * @example 25
2880
+ */
2881
+ violation_cost: number;
2882
+ };
2883
+ /**
2884
+ * @description Soft target: reaching the task's `on` instant after `at` is allowed but
2885
+ * priced. Distinct from a hard time window — a target is a point, never
2886
+ * causes infeasibility, and (unlike a window's `from`) never forces the
2887
+ * vehicle to wait when it is early.
2888
+ * @example {
2889
+ * "at": "2026-07-01T10:00:00+02:00",
2890
+ * "on": "arrival",
2891
+ * "per_late_hour": 60
2892
+ * }
2893
+ */
2894
+ Target: {
2895
+ /**
2896
+ * Format: date-time
2897
+ * @description Target instant (RFC 3339, UTC offset required) the `on` instant is measured against.
2898
+ * @example 2026-07-01T10:00:00+02:00
2899
+ */
2900
+ at: string;
2901
+ /**
2902
+ * @description Which instant of the task is compared to `at`: `arrival` (default) or
2903
+ * `end`. `start` is rejected with HTTP 400.
2904
+ */
2905
+ on?: null | components["schemas"]["StopInstant"];
2906
+ /**
2907
+ * Format: double
2908
+ * @description Cost, in the objective's currency, per hour of `on` beyond `at`
2909
+ * (linear, prorated per second). Must be `> 0`; independent of
2910
+ * `objective.per_late_hour` (which prices *time-window* lateness,
2911
+ * not a target point).
2912
+ * @example 60
2913
+ */
2914
+ per_late_hour: number;
2915
+ };
2916
+ /**
2917
+ * @description How many tasks the plan serves. A job is one task; a shipment (pickup
2918
+ * plus delivery) is one task.
2919
+ * @example {
2920
+ * "served": 2,
2921
+ * "unserved": 0,
2922
+ * "unserved_required": 0
2923
+ * }
2924
+ */
2925
+ TaskCounts: {
2926
+ /**
2927
+ * Format: int32
2928
+ * @description Tasks on a route.
2929
+ */
2930
+ served: number;
2931
+ /**
2932
+ * Format: int32
2933
+ * @description Tasks on no route: the length of `unassigned`.
2934
+ */
2935
+ unserved: number;
2936
+ /**
2937
+ * Format: int32
2938
+ * @description The unserved tasks that carry no `drop_fee`: tasks the plan was
2939
+ * required to serve and did not.
2940
+ */
2941
+ unserved_required: number;
2942
+ };
2943
+ /**
2944
+ * @description One end of a `time_lag` relation.
2945
+ * @example {
2946
+ * "on": "end",
2947
+ * "stop": "job-1"
2948
+ * }
2949
+ */
2950
+ TimeLagEnd: {
2951
+ /**
2952
+ * @description Which instant of the job the bound is measured at: `start` (default) or
2953
+ * `end`. `arrival` is rejected with HTTP 400.
2954
+ */
2955
+ on?: null | components["schemas"]["StopInstant"];
2956
+ /**
2957
+ * @description A job id (shipment legs are not supported yet).
2958
+ * @example job-1
2959
+ */
2960
+ stop: string;
2961
+ };
2962
+ /**
2963
+ * @description Where a route's time goes, in seconds. On the summary: the sum over
2964
+ * routes.
2965
+ *
2966
+ * `span_s` is the wall-clock span, from the start stop's `arrival` to the
2967
+ * end stop's `departure`; it is what `per_route_hour` is charged on. The
2968
+ * other fields break it down. They do not sum to `span_s` exactly: two
2969
+ * parts of the span have no field of their own, setup time
2970
+ * (`setup_duration_s`) and the idle before a windowed break's window opens
2971
+ * (`wait_s` is measured after that break). And a `fixed` break that begins
2972
+ * while the vehicle is driving overlaps that drive: the overlap is in both
2973
+ * `travel_s` and `break_s`, so on such a route the parts can exceed
2974
+ * `span_s`.
2975
+ * @example {
2976
+ * "break_s": 0,
2977
+ * "depot_s": 0,
2978
+ * "overtime_s": 0,
2979
+ * "service_s": 900,
2980
+ * "span_s": 5880,
2981
+ * "travel_s": 1980,
2982
+ * "wait_s": 3000
2983
+ * }
2984
+ */
2985
+ TimeTotals: {
2986
+ /**
2987
+ * Format: int64
2988
+ * @description Time on driver breaks.
2989
+ */
2990
+ break_s: number;
2991
+ /**
2992
+ * Format: int64
2993
+ * @description Handling time at start, end and reload stops.
2994
+ */
2995
+ depot_s: number;
2996
+ /**
2997
+ * Format: int64
2998
+ * @description The part of the span after the shift's `to` (possible only with
2999
+ * `max_overtime_s`).
3000
+ */
3001
+ overtime_s: number;
3002
+ /**
3003
+ * Format: int64
3004
+ * @description On-site service time at job, pickup and delivery stops.
3005
+ */
3006
+ service_s: number;
3007
+ /**
3008
+ * Format: int64
3009
+ * @description Wall-clock span: travel, service, setup, depot handling, waiting and
3010
+ * breaks.
3011
+ */
3012
+ span_s: number;
3013
+ /**
3014
+ * Format: int64
3015
+ * @description Driving time: the sum of the stops' `travel_s`.
3016
+ */
3017
+ travel_s: number;
3018
+ /**
3019
+ * Format: int64
3020
+ * @description Waiting before service, cross-route holds included.
3021
+ */
3022
+ wait_s: number;
3023
+ };
3024
+ /**
3025
+ * @description A time window. Hard unless it (or `objective.per_late_hour`, for
3026
+ * every window) prices lateness.
3027
+ * @example {
3028
+ * "cost": 15,
3029
+ * "from": "2026-07-01T09:00:00+02:00",
3030
+ * "to": "2026-07-01T12:00:00+02:00"
3031
+ * }
3032
+ */
3033
+ TimeWindow: {
3034
+ /**
3035
+ * Format: double
3036
+ * @description Optional flat cost, in the objective's currency, added when this
3037
+ * job/stop is served within THIS window rather than a free/cheaper
3038
+ * alternative window on the same task. Absent = 0 (no charge). Every
3039
+ * window in the list remains individually hard — a job must still land
3040
+ * in *some* window — this only grades preference among the hard
3041
+ * alternatives; it does not soften time-window feasibility. Must be
3042
+ * finite and `>= 0`, and requires at least 2 windows on the task. Once
3043
+ * any window on the task carries a *nonzero* cost, that task's windows
3044
+ * must also be pairwise non-overlapping (a task whose costs are all
3045
+ * `0`/absent has its overlapping windows merged).
3046
+ *
3047
+ * **Cannot be combined with `objective.per_late_hour > 0`** — a
3048
+ * nonzero window `cost` anywhere in the request alongside priced lateness
3049
+ * is rejected with HTTP 400 (the two read opposite ways about which
3050
+ * window was paid for). Choose one: priced alternative windows, or priced
3051
+ * lateness.
3052
+ */
3053
+ cost?: number | null;
3054
+ /**
3055
+ * Format: date-time
3056
+ * @description Earliest allowed arrival (RFC 3339 instant, UTC offset required).
3057
+ * @example 2026-07-01T09:00:00+02:00
3058
+ */
3059
+ from: string;
3060
+ /**
3061
+ * Format: double
3062
+ * @description Makes this window soft on its late side: arriving after `to` is
3063
+ * allowed and priced at this rate, in the objective's currency per hour
3064
+ * late, in place of `objective.per_late_hour` (and whether or not
3065
+ * that is set, so hard and soft windows can share one request). Must be
3066
+ * `> 0`; omit it for a hard window. Only on a
3067
+ * task with exactly one window. Arriving early still waits for `from`.
3068
+ * @example 60
3069
+ */
3070
+ per_late_hour?: number | null;
3071
+ /**
3072
+ * Format: date-time
3073
+ * @description Latest allowed arrival (RFC 3339 instant, UTC offset required).
3074
+ * @example 2026-07-01T12:00:00+02:00
3075
+ */
3076
+ to: string;
3077
+ };
3078
+ /**
3079
+ * @description Where the request's wall-clock time went, in milliseconds.
3080
+ * @example {
3081
+ * "search_ms": 100,
3082
+ * "total_ms": 142
3083
+ * }
3084
+ */
3085
+ Timings: {
3086
+ /**
3087
+ * Format: int64
3088
+ * @description Time spent in the search. Absent on `/evaluate`, which runs none.
3089
+ */
3090
+ search_ms?: number | null;
3091
+ /**
3092
+ * Format: int64
3093
+ * @description From the request's arrival to the end of the search (of the scoring,
3094
+ * on `/evaluate`): fetching travel times and waiting for capacity
3095
+ * included, building the response not.
3096
+ */
3097
+ total_ms: number;
3098
+ };
3099
+ /**
3100
+ * @description Where travel times and distances come from.
3101
+ *
3102
+ * Matrices are traffic-aware (historical-typical traffic); by default travel
3103
+ * times reflect traffic at the moment the request is processed. Set
3104
+ * `departure_time` to plan against another instant — e.g. tomorrow's 08:00
3105
+ * routes with morning-rush travel times, or a replay of a past run under its
3106
+ * original conditions.
3107
+ * @example {
3108
+ * "departure_time": "2026-07-01T08:00:00+02:00",
3109
+ * "engine": "tomtom"
3110
+ * }
3111
+ */
3112
+ Travel: {
3113
+ /**
3114
+ * Format: date-time
3115
+ * @description Departure time for the traffic lookup (RFC 3339 instant, any offset).
3116
+ * Omitted → traffic at solve time. An explicit departure time bypasses
3117
+ * the server-side matrix cache.
3118
+ * @example 2026-07-01T08:00:00+02:00
3119
+ */
3120
+ departure_time?: string | null;
3121
+ /**
3122
+ * @description Road-network data behind travel times and distances. Omitted →
3123
+ * `tomtom`.
3124
+ */
3125
+ engine?: null | components["schemas"]["MatrixEngine"];
3126
+ };
3127
+ /**
3128
+ * @description [`SolveOptions::travel_fallback`]: what a solve does when travel times
3129
+ * cannot be fetched.
3130
+ * @example estimate
3131
+ * @enum {string}
3132
+ */
3133
+ TravelFallback: "estimate" | "fail";
3134
+ /**
3135
+ * @description What a break trigger accumulates.
3136
+ * @example drive
3137
+ * @enum {string}
3138
+ */
3139
+ TriggerAfter: "drive" | "duty";
3140
+ /**
3141
+ * @description A job the solver could not assign to any vehicle.
3142
+ * @example {
3143
+ * "category": "blocked",
3144
+ * "job_id": "job-2",
3145
+ * "reasons": [
3146
+ * {
3147
+ * "code": "TIME_WINDOW_VIOLATED",
3148
+ * "message": "No vehicle can reach this job within its time window."
3149
+ * }
3150
+ * ],
3151
+ * "relaxations": [
3152
+ * {
3153
+ * "amount": 900,
3154
+ * "field": "/jobs/job-2/time_windows",
3155
+ * "op": "extend_time_window",
3156
+ * "suggestion": "Extend a time window by 900s (~15 min).",
3157
+ * "unit": "s"
3158
+ * }
3159
+ * ]
3160
+ * }
3161
+ */
3162
+ Unassigned: {
3163
+ /** @description Which kind of "unassigned" this is. */
3164
+ category?: components["schemas"]["UnassignedCategory"];
3165
+ /**
3166
+ * @description The unassigned job id.
3167
+ * @example job-2
3168
+ */
3169
+ job_id: string;
3170
+ /** @description Structured reasons the job could not be served. */
3171
+ reasons: components["schemas"]["UnassignedReason"][];
3172
+ /**
3173
+ * @description Best-effort, actionable relaxation suggestions. Empty when no simple
3174
+ * relaxation exists (e.g. a skills mismatch).
3175
+ */
3176
+ relaxations: components["schemas"]["Relaxation"][];
3177
+ };
3178
+ /**
3179
+ * @description Which kind of "unassigned" a task is.
3180
+ * @example priced_out
3181
+ * @enum {string}
3182
+ */
3183
+ UnassignedCategory: "impossible" | "blocked" | "priced_out" | "search_limit";
3184
+ /**
3185
+ * @description A machine-readable reason a task could not be assigned.
3186
+ * @example {
3187
+ * "code": "TIME_WINDOW_VIOLATED",
3188
+ * "message": "No vehicle can reach this job within its time window."
3189
+ * }
3190
+ */
3191
+ UnassignedReason: {
3192
+ /**
3193
+ * @description Stable machine code. Closed set of eleven values — exactly one of:
3194
+ * `SOLVER_LIMIT` (a feasible slot existed but the time budget ran out),
3195
+ * `CAPACITY_EXCEEDED`, `TIME_WINDOW_VIOLATED`, `CAPACITY_AND_TIME_WINDOW`,
3196
+ * `SKILLS_MISMATCH`, `TAG_RESTRICTED`, `COMMITMENT_BLOCKED`,
3197
+ * `VEHICLE_EXCLUDED`, `VEHICLE_RANGE_EXCEEDED`, `UNREACHABLE_LOCATION`,
3198
+ * or `ALL_ROUTES_BLOCKED` (a mix no single code describes).
3199
+ * `UNREACHABLE_LOCATION` outranks the others when it applies:
3200
+ * a coordinate the routing engine cannot reach is a fact about the input,
3201
+ * not about the fleet. Treat an unrecognised value as
3202
+ * `ALL_ROUTES_BLOCKED`.
3203
+ * @example TIME_WINDOW_VIOLATED
3204
+ */
3205
+ code: string;
3206
+ /**
3207
+ * @description Human-readable explanation.
3208
+ * @example No vehicle can reach this job within its time window.
3209
+ */
3210
+ message: string;
3211
+ };
3212
+ /**
3213
+ * @description The 200 body of a `?validate_only=true` call: the request passed every
3214
+ * check a real call runs before the matrix fetch.
3215
+ * @example {
3216
+ * "valid": true,
3217
+ * "warnings": []
3218
+ * }
3219
+ */
3220
+ ValidateOnlyResponse: {
3221
+ /** @description Always `true`: an invalid request gets a 400 instead. */
3222
+ valid: boolean;
3223
+ /** @description Warnings a real call would return, in the error item shape. */
3224
+ warnings: components["schemas"]["ErrorItem"][];
3225
+ };
3226
+ /**
3227
+ * @description A vehicle type available to serve tasks.
3228
+ * @example {
3229
+ * "capacity": {
3230
+ * "volume": 20,
3231
+ * "weight": 500
3232
+ * },
3233
+ * "id": "v1",
3234
+ * "profile": "car",
3235
+ * "shifts": [
3236
+ * {
3237
+ * "end": {
3238
+ * "location": [
3239
+ * 3.7174,
3240
+ * 51.0543
3241
+ * ]
3242
+ * },
3243
+ * "from": "2026-07-01T08:00:00+02:00",
3244
+ * "id": "mon",
3245
+ * "start": {
3246
+ * "location": [
3247
+ * 3.7174,
3248
+ * 51.0543
3249
+ * ]
3250
+ * },
3251
+ * "to": "2026-07-01T17:00:00+02:00"
3252
+ * }
3253
+ * ],
3254
+ * "skills": [
3255
+ * {
3256
+ * "name": "fridge"
3257
+ * }
3258
+ * ]
3259
+ * }
3260
+ */
3261
+ Vehicle: {
3262
+ /** @description Named capacity dimensions. */
3263
+ capacity?: null | components["schemas"]["BTreeMap"];
3264
+ /**
3265
+ * @description Vehicles this one may serve a `compatible_vehicles` relation's tasks
3266
+ * together with, by id. Absent: any vehicle. A list is a veto, so a
3267
+ * one-sided list still binds both vehicles: two vehicles may share a
3268
+ * relation only if neither's list excludes the other. A vehicle always
3269
+ * pairs with itself and all its shifts count as one vehicle; to pair a
3270
+ * vehicle with no other, list only its own id. An empty list is rejected
3271
+ * with HTTP 400 (it would read as "everyone" in a client that drops empty
3272
+ * arrays), and so is an id that names no vehicle. Inert unless a
3273
+ * `compatible_vehicles` relation names tasks.
3274
+ * @example [
3275
+ * "v2",
3276
+ * "v3"
3277
+ * ]
3278
+ */
3279
+ compatible_with?: string[] | null;
3280
+ /**
3281
+ * @description This vehicle's costs. Each field set here replaces the same field of
3282
+ * `objective.costs` for this vehicle; fields not set keep the fleet
3283
+ * value. A shift's own `costs` replace these per field in turn.
3284
+ */
3285
+ costs?: null | components["schemas"]["Costs"];
3286
+ /**
3287
+ * @description Vehicle id: 1 to 128 characters, unique across `vehicles[]`; echoed as
3288
+ * `routes[].vehicle`.
3289
+ * @example v1
3290
+ */
3291
+ id: string;
3292
+ /** @description Hard operating limits. */
3293
+ limits?: null | components["schemas"]["VehicleLimits"];
3294
+ /**
3295
+ * @description Soft tag preferences. Serving a task that does not
3296
+ * carry a preferred tag incurs that entry's `violation_cost`. Entries
3297
+ * apply independently: a task lacking several preferred tags incurs each
3298
+ * cost.
3299
+ */
3300
+ preferred_tags?: components["schemas"]["TagPreference"][] | null;
3301
+ /**
3302
+ * @description Routing profile: `car` (default), `truck` or `bike` — selects the road
3303
+ * network and speeds behind this vehicle's travel times and distances.
3304
+ * Any other value is rejected with HTTP 400.
3305
+ */
3306
+ profile?: null | components["schemas"]["Profile"];
3307
+ /**
3308
+ * Format: double
3309
+ * @description Work-rate multiplier for service time at stops (1.0 = base rate).
3310
+ *
3311
+ * Same direction as `speed_factor`: effective service time =
3312
+ * `service_duration_s / service_factor`, so values > 1.0 model a driver who
3313
+ * works FASTER and spends less time at each stop, and < 1.0 one who is
3314
+ * slower. Use it for crew size, equipment, or experience — how quickly the
3315
+ * work gets done, as distinct from how fast the vehicle drives.
3316
+ *
3317
+ * Applies to job/stop `service_duration_s` only. Deliberately NOT applied
3318
+ * to `setup_duration_s` (fixed arrival overhead such as parking),
3319
+ * depot handling (`depots[].service_duration_s`,
3320
+ * `start`/`end.service_duration_s`), waiting time, or break durations.
3321
+ *
3322
+ * Must be in `(0, 5]`. Default 1.0. A shift may override it for its own
3323
+ * day with `shifts[].service_factor`.
3324
+ */
3325
+ service_factor?: number | null;
3326
+ /** @description One or more working shifts. */
3327
+ shifts: components["schemas"]["Shift"][];
3328
+ /**
3329
+ * @description Capabilities the vehicle provides. A job's `skills` must all appear
3330
+ * here at a level of at least the job's `min_level`. Each entry is an
3331
+ * object `{ "name": ..., "level": N }` (`level` optional, default 1);
3332
+ * a bare string is rejected. A skill may appear once per list. Omitting
3333
+ * the field is the same as `[]`: the vehicle can only serve jobs that
3334
+ * require no skills.
3335
+ */
3336
+ skills?: components["schemas"]["VehicleSkill"][] | null;
3337
+ /**
3338
+ * Format: double
3339
+ * @description Travel-speed multiplier (1.0 = base speed). Effective travel time =
3340
+ * `base travel time / speed_factor`, so values > 1.0 model a faster
3341
+ * vehicle and < 1.0 a slower one. Must be in `(0, 5]`. Default 1.0.
3342
+ *
3343
+ * Covers driving only. Its companion `service_factor` scales the
3344
+ * work done at stops; the two are independent and share this direction and
3345
+ * range.
3346
+ */
3347
+ speed_factor?: number | null;
3348
+ };
3349
+ /**
3350
+ * @description Vehicle distance/time limits.
3351
+ * @example {
3352
+ * "max_distance_m": 200000,
3353
+ * "max_route_duration_s": 32400
3354
+ * }
3355
+ */
3356
+ VehicleLimits: {
3357
+ /**
3358
+ * Format: int64
3359
+ * @description Hard maximum distance in metres.
3360
+ * @example 200000
3361
+ */
3362
+ max_distance_m?: number | null;
3363
+ /**
3364
+ * Format: int64
3365
+ * @description Hard cap in seconds on any single travel leg of this vehicle's routes:
3366
+ * the `travel_time_s` reported on each stop, start-to-first-task and
3367
+ * last-task-to-`end` included. Travel only: setup, service and wait do
3368
+ * not count, and `speed_factor` applies first. A leg made free by
3369
+ * `ignore_start_leg`/`ignore_end_leg`, or absent on an open start or
3370
+ * end, is exempt. Absent = no cap. Must be > 0: `0` would make every
3371
+ * leg too long rather than mean "no cap", so it is rejected with HTTP
3372
+ * 400 `out_of_range`, as is a value that does not fit in 32 bits.
3373
+ * @example 3600
3374
+ */
3375
+ max_leg_travel_time_s?: number | null;
3376
+ /**
3377
+ * Format: int64
3378
+ * @description Hard maximum route duration in seconds: the whole-route span —
3379
+ * travel, service, and wait all count, depot load and unload included.
3380
+ * The same quantity as the response's `routes[].time.span_s`.
3381
+ * @example 32400
3382
+ */
3383
+ max_route_duration_s?: number | null;
3384
+ /**
3385
+ * Format: int32
3386
+ * @description Maximum stops served per shift: a job counts as one stop, a shipment
3387
+ * as two (pickup and delivery).
3388
+ * @example 50
3389
+ */
3390
+ max_tasks?: number | null;
3391
+ /**
3392
+ * @description Hard caps on this vehicle's totals across all of its shifts in a day,
3393
+ * a week or an explicit window. Each shift counts toward the one
3394
+ * period that contains its `from`, read in that shift's own UTC offset.
3395
+ * A recurring period with none of the vehicle's shifts is skipped; an
3396
+ * explicit window with no shift start is a 400.
3397
+ */
3398
+ periods?: components["schemas"]["PeriodLimit"][];
3399
+ };
3400
+ /**
3401
+ * @description A capability a vehicle or shift provides, at a proficiency level:
3402
+ * `{ "name": "gas", "level": 3 }`. Always an object; a bare string is
3403
+ * rejected at parse.
3404
+ * @example {
3405
+ * "level": 3,
3406
+ * "name": "gas"
3407
+ * }
3408
+ */
3409
+ VehicleSkill: {
3410
+ /**
3411
+ * Format: int32
3412
+ * @description Proficiency level, `>= 1`; absent = 1. Satisfies every job
3413
+ * requirement of this skill whose `min_level` is at most this value.
3414
+ * @example 3
3415
+ */
3416
+ level?: number | null;
3417
+ /**
3418
+ * @description Skill name.
3419
+ * @example gas
3420
+ */
3421
+ name: string;
3422
+ };
3423
+ /**
3424
+ * @description A single constraint violation detected while evaluating an imported solution.
3425
+ *
3426
+ * Reported on stops (`routes[].stops[].violations`) and on routes
3427
+ * (`routes[].violations`) in the `POST /v3/routing/evaluate` response.
3428
+ * @example {
3429
+ * "message": "Cumulative load 30 exceeds vehicle capacity 15 (dimension weight)",
3430
+ * "type": "capacity"
3431
+ * }
3432
+ */
3433
+ Violation: {
3434
+ /**
3435
+ * @description Human-readable description of what was violated.
3436
+ * @example Cumulative load 30 exceeds vehicle capacity 15 (dimension weight)
3437
+ */
3438
+ message: string;
3439
+ /**
3440
+ * @description Machine-readable violation type: `time_window`, `capacity`, `skills`,
3441
+ * `tags`, `first_job`, `unreachable`, `commitment`, `eligibility`,
3442
+ * `same_vehicle`, `same_day`, `same_route`, `synchronized`, `time_lag`,
3443
+ * `max_distance`, `max_duration`, `shift_end`, `sequence`, `max_tasks`,
3444
+ * `break`, `shipment`, `multi_trip`, `period_limit` (a `limits.periods`
3445
+ * cap, reported once per period on the route of its first used shift),
3446
+ * `quota` (a `quotas[]` cap, reported the same way), or `infeasible`
3447
+ * (the insertion check refused a stop without naming a constraint; not
3448
+ * expected in practice). `max_tasks`, `break`, `multi_trip`,
3449
+ * `period_limit` and `quota` are route-level; `sequence` and
3450
+ * `shipment` are on the stop.
3451
+ *
3452
+ * Deliberately an open string rather than an enum: stop-level types come
3453
+ * from the active constraint set, so a new constraint can add one without
3454
+ * a breaking schema change. Match on the values you handle and treat
3455
+ * unknown ones as generic violations.
3456
+ * @example capacity
3457
+ */
3458
+ type: string;
3459
+ };
3460
+ /**
3461
+ * @description A single-stop visit: one location served once (the `jobs[]` entry type).
3462
+ * For a pickup bound to a delivery on the same vehicle, use `shipments[]`.
3463
+ * @example {
3464
+ * "id": "job-1",
3465
+ * "location": [
3466
+ * 3.725,
3467
+ * 51.05
3468
+ * ],
3469
+ * "pickup": {
3470
+ * "weight": 20
3471
+ * },
3472
+ * "service_duration_s": 300,
3473
+ * "skills": [
3474
+ * {
3475
+ * "name": "fridge"
3476
+ * }
3477
+ * ],
3478
+ * "time_windows": [
3479
+ * {
3480
+ * "from": "2026-07-01T09:00:00+02:00",
3481
+ * "to": "2026-07-01T12:00:00+02:00"
3482
+ * }
3483
+ * ]
3484
+ * }
3485
+ */
3486
+ Visit: {
3487
+ /**
3488
+ * @description Absolute price of serving this task on a given vehicle, in the
3489
+ * objective's currency. Charged once, when that vehicle (on any of its
3490
+ * shifts) serves the task; unlisted vehicles cost nothing. Unlike
3491
+ * `preferences`, which charges the vehicles that did NOT get the task,
3492
+ * this prices the vehicle that did, so a task rated badly everywhere is
3493
+ * the first one dropped when capacity runs out. It lives in the cost tier,
3494
+ * below `priority`, and adds to `preferences`, `drop_fee` and travel.
3495
+ * Not applied by decomposition: a problem that sets it solves monolithically.
3496
+ */
3497
+ assignment_costs?: components["schemas"]["AssignmentCost"][] | null;
3498
+ /**
3499
+ * @description Whether a break may interrupt service here. Default `true`. `false`
3500
+ * is not supported yet and is rejected with HTTP 400.
3501
+ * @example true
3502
+ */
3503
+ break_interruptible?: boolean | null;
3504
+ /** @description Quantity delivered at this visit — comes off the truck here, having been loaded at a depot — as non-negative magnitudes. Direction is carried by the field name, never by sign. Absent or empty means no delivery at this visit. */
3505
+ delivery?: {
3506
+ [key: string]: number;
3507
+ } | null;
3508
+ /**
3509
+ * Format: double
3510
+ * @description Drop **price** — what leaving this task unserved costs you, in the same
3511
+ * currency as `objective.costs`, and the only way to make a task
3512
+ * droppable. Tasks without a `drop_fee` must be served.
3513
+ *
3514
+ * This is money, not a priority: it enters the same cost the solver
3515
+ * minimises, so the task is served only while serving it costs less than
3516
+ * the fee. Fees the plan chose to pay are reported in
3517
+ * `score.components.drop_fees` and summed into `score.cost`.
3518
+ *
3519
+ * On a shipment the fee applies to the pair, charged once.
3520
+ * @example 25
3521
+ */
3522
+ drop_fee?: number | null;
3523
+ /** @description Hard vehicle allow/exclude. */
3524
+ eligible_vehicles?: null | components["schemas"]["Eligibility"];
3525
+ /**
3526
+ * @description Job id: 1 to 128 characters, unique across `jobs` + `shipments`.
3527
+ * @example job-1
3528
+ */
3529
+ id: string;
3530
+ /**
3531
+ * @description Where this visit happens. Omit for a "virtual" (location-less) job:
3532
+ * it still has service duration, time windows, load, and skills, but
3533
+ * requires no travel — the vehicle's position doesn't change when it
3534
+ * serves one. Virtual and located jobs may be mixed freely in the same
3535
+ * request and route. A virtual job's stop in the solution response
3536
+ * reports the resolved location of its nearest real (located)
3537
+ * neighbor in the route — the predecessor when there is one, otherwise
3538
+ * the successor — so it is `null` only on a route that has no located
3539
+ * stop at all (an all-virtual route on an open-start/open-end shift).
3540
+ * A shipment's legs still require a `location`.
3541
+ */
3542
+ location?: null | components["schemas"]["Place"];
3543
+ /**
3544
+ * @description Hard-pin this job to one vehicle id — only that vehicle (any of its
3545
+ * shifts) may serve it, and once placed, re-optimization never removes it
3546
+ * or moves it to another vehicle. Stronger than `eligible_vehicles`
3547
+ * (which filters a set) and hard, unlike `preferences`.
3548
+ *
3549
+ * A pin means "stays on this vehicle if placed", **not** "must be
3550
+ * served": the task can still end up unassigned (for example when it does
3551
+ * not fit), and with a `drop_fee` it then pays the fee. An id that
3552
+ * matches no vehicle is rejected with HTTP 400.
3553
+ * @example vehicle-1
3554
+ */
3555
+ locked_vehicle?: string | null;
3556
+ /**
3557
+ * @description Quantity picked up at this visit: goes onto the vehicle here and stays
3558
+ * aboard until the route ends or reloads. Non-negative magnitudes per
3559
+ * named dimension. A swap that collects one quantity and drops off
3560
+ * another at the same address is one job with both `pickup` and
3561
+ * `delivery`.
3562
+ */
3563
+ pickup?: null | components["schemas"]["BTreeMap"];
3564
+ /**
3565
+ * @description Soft vehicle affinity preferences, priced. Each entry is independent:
3566
+ * a job listing several preferred vehicles it doesn't get pays each
3567
+ * one's `violation_cost` (compounding, the same rule as a vehicle's
3568
+ * `preferred_tags`).
3569
+ */
3570
+ preferences?: components["schemas"]["Preference"][] | null;
3571
+ /**
3572
+ * Format: date-time
3573
+ * @description Soft preference for the calendar day this task is served on, in a
3574
+ * multi-day (multi-shift) problem.
3575
+ *
3576
+ * An RFC 3339 datetime, UTC offset required, on the intended day. A day is the calendar date of
3577
+ * the value **in its own UTC offset**, the same rule that dates a shift
3578
+ * from its `from`, so passing the target shift's `from` verbatim is
3579
+ * always correct.
3580
+ *
3581
+ * Soft, and priced by `objective.non_preferred_date_cost` — a flat charge
3582
+ * applied once to a task served on any other day. **With that cost unset or
3583
+ * zero this field has no effect.** The charge is part of `score.cost`,
3584
+ * which ranks below serving required tasks, so a task is never left
3585
+ * unassigned to honour its preferred date; `locked_vehicle` (hard) also
3586
+ * still wins.
3587
+ *
3588
+ * Supported on shipments as well as jobs. Both legs of a shipment ride
3589
+ * the same route, so the preferred day is a property of the pair: set it
3590
+ * on the shipment itself, and `non_preferred_date_cost` is charged once
3591
+ * for the pair rather than once per leg.
3592
+ * @example 2026-08-04T08:00:00+02:00
3593
+ */
3594
+ preferred_date?: string | null;
3595
+ /**
3596
+ * Format: int32
3597
+ * @description Importance of a must-serve task when not all of them fit: the solver
3598
+ * maximises the summed `priority` of served tasks before any cost. `>= 1`,
3599
+ * default `1` (maximise the count). A rank, not money — it never makes a
3600
+ * task droppable; use `drop_fee` for that. Both set is a 400.
3601
+ * @example 3
3602
+ */
3603
+ priority?: number | null;
3604
+ /**
3605
+ * Format: int32
3606
+ * @description On-site service time paid per job (seconds).
3607
+ *
3608
+ * This is the nominal figure. The serving vehicle's `service_factor`
3609
+ * scales it — the time actually scheduled is
3610
+ * `service_duration_s / service_factor` — so the solution's
3611
+ * `stops[].service_s` may differ from what you sent here.
3612
+ * @example 300
3613
+ */
3614
+ service_duration_s?: number | null;
3615
+ /**
3616
+ * @description Per-vehicle work-rate overrides for this task's `service_duration_s`,
3617
+ * composed multiplicatively with the serving vehicle's or shift's
3618
+ * `service_factor`. On a shipment the override scales both legs. A
3619
+ * vehicle not listed works at its own rate.
3620
+ */
3621
+ service_factors?: components["schemas"]["ServiceFactor"][] | null;
3622
+ /**
3623
+ * Format: int32
3624
+ * @description Setup time paid once on arrival at a new location (seconds),
3625
+ * distinct from `service_duration_s`. Skipped when the immediate real
3626
+ * predecessor in the route already sits at this location — co-located
3627
+ * stops share one setup.
3628
+ *
3629
+ * Not scaled by the vehicle's `service_factor`: setup is fixed arrival
3630
+ * overhead (parking, walking to the door), not the work itself.
3631
+ * @example 60
3632
+ */
3633
+ setup_duration_s?: number | null;
3634
+ /**
3635
+ * @description Hard skill requirements — the assigned vehicle must provide every one,
3636
+ * at or above each requirement's `min_level`.
3637
+ */
3638
+ skills?: components["schemas"]["SkillReq"][] | null;
3639
+ /**
3640
+ * @description Free-form task labels (zones/territories, grouping/relation labels).
3641
+ * Used for `tag`/relation resolution and, when a shift scopes itself via
3642
+ * `serves_tags` (hard ANY-overlap) or a vehicle prices them via
3643
+ * `preferred_tags` (soft), for tag matching. ANY-overlap — contrast with
3644
+ * `skills`, which requires ALL. Not capability matching on its own.
3645
+ */
3646
+ tags?: string[] | null;
3647
+ /**
3648
+ * @description Soft target for this task, measured at its arrival (default) or
3649
+ * departure. On a shipment it is measured on the **delivery** leg.
3650
+ */
3651
+ target?: null | components["schemas"]["Target"];
3652
+ /** @description Time windows for this visit. */
3653
+ time_windows?: components["schemas"]["TimeWindow"][] | null;
3654
+ };
3655
+ };
3656
+ responses: never;
3657
+ parameters: never;
3658
+ requestBodies: never;
3659
+ headers: never;
3660
+ pathItems: never;
3661
+ }
3662
+ export type $defs = Record<string, never>;
3663
+ export interface operations {
3664
+ evaluate: {
3665
+ parameters: {
3666
+ query?: {
3667
+ /**
3668
+ * @description Check the request without fetching a matrix or searching. 200
3669
+ * `{"valid": true, "warnings": []}`, or a 400 for the problem, options
3670
+ * and caps; `plan` is checked only by the real call, after the fetch.
3671
+ */
3672
+ validate_only?: boolean;
3673
+ };
3674
+ header?: never;
3675
+ path?: never;
3676
+ cookie?: never;
3677
+ };
3678
+ requestBody: {
3679
+ content: {
3680
+ "application/json": components["schemas"]["EvaluateRequest"];
3681
+ };
3682
+ };
3683
+ responses: {
3684
+ /** @description Solution evaluated. With `?validate_only=true`: a `ValidateOnlyResponse`, `{"valid": true, "warnings": []}`, without a matrix fetch, not billed. */
3685
+ 200: {
3686
+ headers: {
3687
+ /** @description The request's `X-Request-Id`, echoed, or a generated `req_…` id. */
3688
+ "x-request-id"?: string;
3689
+ [name: string]: unknown;
3690
+ };
3691
+ content: {
3692
+ "application/json": components["schemas"]["OrValidateOnly_SolveResponse"];
3693
+ };
3694
+ };
3695
+ /** @description The request is invalid. `errors[]` lists what to fix, each entry with a `code` and a JSON pointer into the request body. */
3696
+ 400: {
3697
+ headers: {
3698
+ /** @description The request's `X-Request-Id`, echoed, or a generated `req_…` id. */
3699
+ "x-request-id"?: string;
3700
+ [name: string]: unknown;
3701
+ };
3702
+ content: {
3703
+ "application/json": components["schemas"]["ErrorBody"];
3704
+ };
3705
+ };
3706
+ /** @description Missing or invalid API key. Returned by the API gateway before the request reaches the solver, so it does not use the `ErrorBody` shape and can have an empty body. Branch on the HTTP status. */
3707
+ 401: {
3708
+ headers: {
3709
+ [name: string]: unknown;
3710
+ };
3711
+ content?: never;
3712
+ };
3713
+ /** @description The request uses a feature this account does not have (`tomtom_real_time`) */
3714
+ 403: {
3715
+ headers: {
3716
+ /** @description The request's `X-Request-Id`, echoed, or a generated `req_…` id. */
3717
+ "x-request-id"?: string;
3718
+ [name: string]: unknown;
3719
+ };
3720
+ content: {
3721
+ /**
3722
+ * @example {
3723
+ * "code": "feature_not_enabled",
3724
+ * "message": "travel.engine: 'tomtom_real_time' is not enabled for this account; use engine 'tomtom'",
3725
+ * "request_id": "req_7f3a2c90d1e4b5a6",
3726
+ * "status": "FORBIDDEN"
3727
+ * }
3728
+ */
3729
+ "application/json": components["schemas"]["ErrorBody"];
3730
+ };
3731
+ };
3732
+ /** @description Request body larger than 10 MiB */
3733
+ 413: {
3734
+ headers: {
3735
+ /** @description The request's `X-Request-Id`, echoed, or a generated `req_…` id. */
3736
+ "x-request-id"?: string;
3737
+ [name: string]: unknown;
3738
+ };
3739
+ content: {
3740
+ /**
3741
+ * @example {
3742
+ * "code": "payload_too_large",
3743
+ * "message": "Request body exceeds 10485760 bytes",
3744
+ * "request_id": "req_7f3a2c90d1e4b5a6",
3745
+ * "status": "PAYLOAD_TOO_LARGE"
3746
+ * }
3747
+ */
3748
+ "application/json": components["schemas"]["ErrorBody"];
3749
+ };
3750
+ };
3751
+ /** @description Request `Content-Type` is not `application/json` */
3752
+ 415: {
3753
+ headers: {
3754
+ /** @description The request's `X-Request-Id`, echoed, or a generated `req_…` id. */
3755
+ "x-request-id"?: string;
3756
+ [name: string]: unknown;
3757
+ };
3758
+ content: {
3759
+ /**
3760
+ * @example {
3761
+ * "code": "unsupported_media_type",
3762
+ * "message": "Expected request with `Content-Type: application/json`",
3763
+ * "request_id": "req_7f3a2c90d1e4b5a6",
3764
+ * "status": "UNSUPPORTED_MEDIA_TYPE"
3765
+ * }
3766
+ */
3767
+ "application/json": components["schemas"]["ErrorBody"];
3768
+ };
3769
+ };
3770
+ /** @description Rate limited. Returned by the API gateway before the request reaches the solver, so it does not use the `ErrorBody` shape. Back off and retry. */
3771
+ 429: {
3772
+ headers: {
3773
+ [name: string]: unknown;
3774
+ };
3775
+ content?: never;
3776
+ };
3777
+ /** @description Server-side error (`code: internal`). */
3778
+ 500: {
3779
+ headers: {
3780
+ /** @description The request's `X-Request-Id`, echoed, or a generated `req_…` id. */
3781
+ "x-request-id"?: string;
3782
+ [name: string]: unknown;
3783
+ };
3784
+ content: {
3785
+ "application/json": components["schemas"]["ErrorBody"];
3786
+ };
3787
+ };
3788
+ /** @description Travel times and distances could not be fetched (`code: matrix_provider_error`). The request can be retried. */
3789
+ 502: {
3790
+ headers: {
3791
+ /** @description The request's `X-Request-Id`, echoed, or a generated `req_…` id. */
3792
+ "x-request-id"?: string;
3793
+ [name: string]: unknown;
3794
+ };
3795
+ content: {
3796
+ "application/json": components["schemas"]["ErrorBody"];
3797
+ };
3798
+ };
3799
+ /** @description No capacity right now (`code: overloaded`). Retry after the number of seconds in the `Retry-After` header. */
3800
+ 503: {
3801
+ headers: {
3802
+ /** @description Seconds to wait before retrying. */
3803
+ "Retry-After"?: number;
3804
+ /** @description The request's `X-Request-Id`, echoed, or a generated `req_…` id. */
3805
+ "x-request-id"?: string;
3806
+ [name: string]: unknown;
3807
+ };
3808
+ content: {
3809
+ /**
3810
+ * @example {
3811
+ * "code": "overloaded",
3812
+ * "message": "All solver slots are busy, try again later",
3813
+ * "request_id": "req_7f3a2c90d1e4b5a6",
3814
+ * "status": "SERVICE_UNAVAILABLE"
3815
+ * }
3816
+ */
3817
+ "application/json": components["schemas"]["ErrorBody"];
3818
+ };
3819
+ };
3820
+ };
3821
+ };
3822
+ solve: {
3823
+ parameters: {
3824
+ query?: {
3825
+ /**
3826
+ * @description Check the request without fetching a matrix or searching. 200
3827
+ * `{"valid": true, "warnings": []}`, or a 400 for the problem, options
3828
+ * and caps; `plan` is checked only by the real call, after the fetch.
3829
+ */
3830
+ validate_only?: boolean;
3831
+ };
3832
+ header?: never;
3833
+ path?: never;
3834
+ cookie?: never;
3835
+ };
3836
+ requestBody: {
3837
+ content: {
3838
+ "application/json": components["schemas"]["SolveRequest"];
3839
+ };
3840
+ };
3841
+ responses: {
3842
+ /** @description Solution found. With `?validate_only=true`: a `ValidateOnlyResponse`, `{"valid": true, "warnings": []}`, without a matrix fetch or a search, not billed. */
3843
+ 200: {
3844
+ headers: {
3845
+ /** @description The request's `X-Request-Id`, echoed, or a generated `req_…` id. */
3846
+ "x-request-id"?: string;
3847
+ [name: string]: unknown;
3848
+ };
3849
+ content: {
3850
+ "application/json": components["schemas"]["OrValidateOnly_SolveResponse"];
3851
+ };
3852
+ };
3853
+ /** @description The request is invalid. `errors[]` lists what to fix, each entry with a `code` and a JSON pointer into the request body. */
3854
+ 400: {
3855
+ headers: {
3856
+ /** @description The request's `X-Request-Id`, echoed, or a generated `req_…` id. */
3857
+ "x-request-id"?: string;
3858
+ [name: string]: unknown;
3859
+ };
3860
+ content: {
3861
+ "application/json": components["schemas"]["ErrorBody"];
3862
+ };
3863
+ };
3864
+ /** @description Missing or invalid API key. Returned by the API gateway before the request reaches the solver, so it does not use the `ErrorBody` shape and can have an empty body. Branch on the HTTP status. */
3865
+ 401: {
3866
+ headers: {
3867
+ [name: string]: unknown;
3868
+ };
3869
+ content?: never;
3870
+ };
3871
+ /** @description The request uses a feature this account does not have (`tomtom_real_time`) */
3872
+ 403: {
3873
+ headers: {
3874
+ /** @description The request's `X-Request-Id`, echoed, or a generated `req_…` id. */
3875
+ "x-request-id"?: string;
3876
+ [name: string]: unknown;
3877
+ };
3878
+ content: {
3879
+ /**
3880
+ * @example {
3881
+ * "code": "feature_not_enabled",
3882
+ * "message": "travel.engine: 'tomtom_real_time' is not enabled for this account; use engine 'tomtom'",
3883
+ * "request_id": "req_7f3a2c90d1e4b5a6",
3884
+ * "status": "FORBIDDEN"
3885
+ * }
3886
+ */
3887
+ "application/json": components["schemas"]["ErrorBody"];
3888
+ };
3889
+ };
3890
+ /** @description Request body larger than 10 MiB */
3891
+ 413: {
3892
+ headers: {
3893
+ /** @description The request's `X-Request-Id`, echoed, or a generated `req_…` id. */
3894
+ "x-request-id"?: string;
3895
+ [name: string]: unknown;
3896
+ };
3897
+ content: {
3898
+ /**
3899
+ * @example {
3900
+ * "code": "payload_too_large",
3901
+ * "message": "Request body exceeds 10485760 bytes",
3902
+ * "request_id": "req_7f3a2c90d1e4b5a6",
3903
+ * "status": "PAYLOAD_TOO_LARGE"
3904
+ * }
3905
+ */
3906
+ "application/json": components["schemas"]["ErrorBody"];
3907
+ };
3908
+ };
3909
+ /** @description Request `Content-Type` is not `application/json` */
3910
+ 415: {
3911
+ headers: {
3912
+ /** @description The request's `X-Request-Id`, echoed, or a generated `req_…` id. */
3913
+ "x-request-id"?: string;
3914
+ [name: string]: unknown;
3915
+ };
3916
+ content: {
3917
+ /**
3918
+ * @example {
3919
+ * "code": "unsupported_media_type",
3920
+ * "message": "Expected request with `Content-Type: application/json`",
3921
+ * "request_id": "req_7f3a2c90d1e4b5a6",
3922
+ * "status": "UNSUPPORTED_MEDIA_TYPE"
3923
+ * }
3924
+ */
3925
+ "application/json": components["schemas"]["ErrorBody"];
3926
+ };
3927
+ };
3928
+ /** @description Rate limited. Returned by the API gateway before the request reaches the solver, so it does not use the `ErrorBody` shape. Back off and retry. */
3929
+ 429: {
3930
+ headers: {
3931
+ [name: string]: unknown;
3932
+ };
3933
+ content?: never;
3934
+ };
3935
+ /** @description Server-side error (`code: internal`). */
3936
+ 500: {
3937
+ headers: {
3938
+ /** @description The request's `X-Request-Id`, echoed, or a generated `req_…` id. */
3939
+ "x-request-id"?: string;
3940
+ [name: string]: unknown;
3941
+ };
3942
+ content: {
3943
+ "application/json": components["schemas"]["ErrorBody"];
3944
+ };
3945
+ };
3946
+ /** @description Travel times and distances could not be fetched (`code: matrix_provider_error`). The request can be retried. If Solvice Maps is only unreachable, this needs `options.travel_fallback: "fail"`; by default the plan is solved on estimated travel and `summary.travel_estimated` is set. */
3947
+ 502: {
3948
+ headers: {
3949
+ /** @description The request's `X-Request-Id`, echoed, or a generated `req_…` id. */
3950
+ "x-request-id"?: string;
3951
+ [name: string]: unknown;
3952
+ };
3953
+ content: {
3954
+ "application/json": components["schemas"]["ErrorBody"];
3955
+ };
3956
+ };
3957
+ /** @description No capacity right now (`code: overloaded`). Retry after the number of seconds in the `Retry-After` header. */
3958
+ 503: {
3959
+ headers: {
3960
+ /** @description Seconds to wait before retrying. */
3961
+ "Retry-After"?: number;
3962
+ /** @description The request's `X-Request-Id`, echoed, or a generated `req_…` id. */
3963
+ "x-request-id"?: string;
3964
+ [name: string]: unknown;
3965
+ };
3966
+ content: {
3967
+ /**
3968
+ * @example {
3969
+ * "code": "overloaded",
3970
+ * "message": "All solver slots are busy, try again later",
3971
+ * "request_id": "req_7f3a2c90d1e4b5a6",
3972
+ * "status": "SERVICE_UNAVAILABLE"
3973
+ * }
3974
+ */
3975
+ "application/json": components["schemas"]["ErrorBody"];
3976
+ };
3977
+ };
3978
+ };
3979
+ };
3980
+ suggest: {
3981
+ parameters: {
3982
+ query?: {
3983
+ /**
3984
+ * @description Check the request without fetching a matrix or searching. 200
3985
+ * `{"valid": true, "warnings": []}`, or a 400 for the problem, options
3986
+ * and caps; `plan` is checked only by the real call, after the fetch.
3987
+ */
3988
+ validate_only?: boolean;
3989
+ };
3990
+ header?: never;
3991
+ path?: never;
3992
+ cookie?: never;
3993
+ };
3994
+ requestBody: {
3995
+ content: {
3996
+ "application/json": components["schemas"]["SuggestRequest"];
3997
+ };
3998
+ };
3999
+ responses: {
4000
+ /** @description Suggestions computed. With `?validate_only=true`: a `ValidateOnlyResponse`, `{"valid": true, "warnings": []}`, without a matrix fetch or a search, not billed. */
4001
+ 200: {
4002
+ headers: {
4003
+ /** @description The request's `X-Request-Id`, echoed, or a generated `req_…` id. */
4004
+ "x-request-id"?: string;
4005
+ [name: string]: unknown;
4006
+ };
4007
+ content: {
4008
+ "application/json": components["schemas"]["OrValidateOnly_SuggestResponse"];
4009
+ };
4010
+ };
4011
+ /** @description The request is invalid. `errors[]` lists what to fix, each entry with a `code` and a JSON pointer into the request body. */
4012
+ 400: {
4013
+ headers: {
4014
+ /** @description The request's `X-Request-Id`, echoed, or a generated `req_…` id. */
4015
+ "x-request-id"?: string;
4016
+ [name: string]: unknown;
4017
+ };
4018
+ content: {
4019
+ "application/json": components["schemas"]["ErrorBody"];
4020
+ };
4021
+ };
4022
+ /** @description Missing or invalid API key. Returned by the API gateway before the request reaches the solver, so it does not use the `ErrorBody` shape and can have an empty body. Branch on the HTTP status. */
4023
+ 401: {
4024
+ headers: {
4025
+ [name: string]: unknown;
4026
+ };
4027
+ content?: never;
4028
+ };
4029
+ /** @description The request uses a feature this account does not have (`tomtom_real_time`) */
4030
+ 403: {
4031
+ headers: {
4032
+ /** @description The request's `X-Request-Id`, echoed, or a generated `req_…` id. */
4033
+ "x-request-id"?: string;
4034
+ [name: string]: unknown;
4035
+ };
4036
+ content: {
4037
+ /**
4038
+ * @example {
4039
+ * "code": "feature_not_enabled",
4040
+ * "message": "travel.engine: 'tomtom_real_time' is not enabled for this account; use engine 'tomtom'",
4041
+ * "request_id": "req_7f3a2c90d1e4b5a6",
4042
+ * "status": "FORBIDDEN"
4043
+ * }
4044
+ */
4045
+ "application/json": components["schemas"]["ErrorBody"];
4046
+ };
4047
+ };
4048
+ /** @description Request body larger than 10 MiB */
4049
+ 413: {
4050
+ headers: {
4051
+ /** @description The request's `X-Request-Id`, echoed, or a generated `req_…` id. */
4052
+ "x-request-id"?: string;
4053
+ [name: string]: unknown;
4054
+ };
4055
+ content: {
4056
+ /**
4057
+ * @example {
4058
+ * "code": "payload_too_large",
4059
+ * "message": "Request body exceeds 10485760 bytes",
4060
+ * "request_id": "req_7f3a2c90d1e4b5a6",
4061
+ * "status": "PAYLOAD_TOO_LARGE"
4062
+ * }
4063
+ */
4064
+ "application/json": components["schemas"]["ErrorBody"];
4065
+ };
4066
+ };
4067
+ /** @description Request `Content-Type` is not `application/json` */
4068
+ 415: {
4069
+ headers: {
4070
+ /** @description The request's `X-Request-Id`, echoed, or a generated `req_…` id. */
4071
+ "x-request-id"?: string;
4072
+ [name: string]: unknown;
4073
+ };
4074
+ content: {
4075
+ /**
4076
+ * @example {
4077
+ * "code": "unsupported_media_type",
4078
+ * "message": "Expected request with `Content-Type: application/json`",
4079
+ * "request_id": "req_7f3a2c90d1e4b5a6",
4080
+ * "status": "UNSUPPORTED_MEDIA_TYPE"
4081
+ * }
4082
+ */
4083
+ "application/json": components["schemas"]["ErrorBody"];
4084
+ };
4085
+ };
4086
+ /** @description Rate limited. Returned by the API gateway before the request reaches the solver, so it does not use the `ErrorBody` shape. Back off and retry. */
4087
+ 429: {
4088
+ headers: {
4089
+ [name: string]: unknown;
4090
+ };
4091
+ content?: never;
4092
+ };
4093
+ /** @description Server-side error (`code: internal`). */
4094
+ 500: {
4095
+ headers: {
4096
+ /** @description The request's `X-Request-Id`, echoed, or a generated `req_…` id. */
4097
+ "x-request-id"?: string;
4098
+ [name: string]: unknown;
4099
+ };
4100
+ content: {
4101
+ "application/json": components["schemas"]["ErrorBody"];
4102
+ };
4103
+ };
4104
+ /** @description Travel times and distances could not be fetched (`code: matrix_provider_error`). The request can be retried. */
4105
+ 502: {
4106
+ headers: {
4107
+ /** @description The request's `X-Request-Id`, echoed, or a generated `req_…` id. */
4108
+ "x-request-id"?: string;
4109
+ [name: string]: unknown;
4110
+ };
4111
+ content: {
4112
+ "application/json": components["schemas"]["ErrorBody"];
4113
+ };
4114
+ };
4115
+ /** @description No capacity right now (`code: overloaded`). Retry after the number of seconds in the `Retry-After` header. */
4116
+ 503: {
4117
+ headers: {
4118
+ /** @description Seconds to wait before retrying. */
4119
+ "Retry-After"?: number;
4120
+ /** @description The request's `X-Request-Id`, echoed, or a generated `req_…` id. */
4121
+ "x-request-id"?: string;
4122
+ [name: string]: unknown;
4123
+ };
4124
+ content: {
4125
+ /**
4126
+ * @example {
4127
+ * "code": "overloaded",
4128
+ * "message": "All solver slots are busy, try again later",
4129
+ * "request_id": "req_7f3a2c90d1e4b5a6",
4130
+ * "status": "SERVICE_UNAVAILABLE"
4131
+ * }
4132
+ */
4133
+ "application/json": components["schemas"]["ErrorBody"];
4134
+ };
4135
+ };
4136
+ };
4137
+ };
4138
+ }