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

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