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