Tarot Routing - Route Optimisation API¶
Request¶
RoutingProblem¶
A RoutingProblem is the body of the request sent to the Route Optimisation API endpoint.
Fields:
| Name | Type | Required | Description |
|---|---|---|---|
drivers |
list of Driver | Yes | The drivers to perform the jobs. |
jobs |
list of Job | Yes | The jobs to be optimised. |
settings |
Settings | No | null (empty settings) |
Minimal Example:
{
"drivers": [
{
"uid": "drvid1",
"shift_start": 8,
"shift_end": 17,
"start": {"lat": -33.867798, "lon": 151.166256},
"end": "start"
}
],
"jobs": [
{
"uid": "uid1",
"duration": 2,
"location": {"lat": -33.849489, "lon": 151.127482}
},
{
"uid": "uid2",
"duration": 2,
"location": {"lat": -33.880661, "lon": 151.183096}
},
{
"uid": "uid3",
"duration": 2,
"location": {"lat": -33.913168, "lon": 151.262267}
}
],
"settings": {}
}
Driver¶
A Driver represents a driver, their vehicle, or a theoretical run.
Fields:
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
uid |
string | Yes | — | Unique identifier for this driver. |
start |
Location | Yes | — | Where the driver begins their run. (Previously location) |
end |
string or Location | No | "start" |
Where the driver finishes their run. "start" = returns to start; "anywhere" = finish at last served job; a Location = fixed end different from start. No other values are allowed |
shift_start |
float | Yes* | — | Earliest time the driver may begin their (single) shift (24h float, e.g. 8.5). Omit when using multi_shift. |
shift_end |
float | Yes* | — | Latest time the driver may end their (single) shift (24h float, e.g. 18.5). Omit when using multi_shift. |
multi_shift |
MultiShift | No | — | Several Shifts in one request. Replaces shift_start / shift_end. If any Driver has this, every Driver must have it. See Multi-Shift. |
capacity |
int or str | No | — | Capacity of the vehicle. |
spec_type |
string | No | — | Only jobs with matching spec_type can be served. |
territories |
string | No | — | Only jobs with matching territories can be served. |
costs |
Costs | No | — | Cost coefficients for this driver. Required when optimisation_objective = "cost". |
speed_multiplier |
float | No | 1 |
Driving-speed multiplier for this Driver. 0.5 means half as fast (travel takes twice as long); 1.2 means 20% faster. Does not change service time, parking, waiting, or distance. Must be finite and greater than 0. See Speed multiplier. |
end_location |
Location | Deprecated | — | Deprecated legacy field. Use end instead (e.g. "start" or a Location). Ignored if end supplied. |
end_anywhere |
bool | Deprecated | false |
Deprecated legacy flag. Use "anywhere" via the unified end field. |
location |
Location | Deprecated | — | Deprecated legacy alias for start. Will be removed in a future release. |
*
shift_startandshift_endare required on a single-shift Driver. On a multi-shift Driver they are omitted andmulti_shiftis required instead. You cannot mix the two shapes in one request.Deprecated Fields:
location,end_location, andend_anywhereare accepted for backwards compatibility but will be removed in a future release. Prefer the unifiedstartandendinterface. If both new and legacy fields are supplied the request will be rejected (end+end_location/end_anywhere) or the legacy value ignored (locationifstartpresent).
Minimal Example (return to start):
{
"uid": "driver_1",
"start": {"lat": -33.867798, "lon": 151.166256},
"end": "start",
"shift_start": 9.5,
"shift_end": 18.5
}
Finish Anywhere Example (end at last job visited):
{
"uid": "driver_2",
"start": {"lat": -33.867798, "lon": 151.166256},
"end": "anywhere",
"shift_start": 9.5,
"shift_end": 18.5
}
Different Fixed End Location Example:
{
"uid": "driver_3",
"start": {"lat": -33.880661, "lon": 151.183096},
"end": {"lat": -33.900001, "lon": 151.210000},
"shift_start": 8.0,
"shift_end": 17.5
}
Multi-Shift Example (two last-mile days):
{
"uid": "driver_4",
"start": {"lat": -33.867798, "lon": 151.166256},
"end": "start",
"multi_shift": {
"mode": "reset",
"shifts": [
{
"shift_start": {"day": 1, "time": 8},
"shift_end": {"day": 1, "time": 17}
},
{
"shift_start": {"day": 2, "time": 8},
"shift_end": {"day": 2, "time": 17}
}
]
}
}
Job¶
A Job represents a task, delivery point, or customer.
Fields:
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
uid |
string | Yes | — | Unique identifier for this job. |
location |
Location | Yes | — | Where the job is. |
duration |
float | Yes | — | Minutes spent serving this job once on site (after any parking). |
parking_duration |
float | No | 0 |
Minutes of parking/docking after ETA, contiguous with duration. Applied only when driving time from the previous stop (including driver start) is nonzero. etd = eta + parking + duration when applied. |
pickup_from |
string | No | — | UID of the pickup Job that this Job carries from. Same Driver; the item stays on the vehicle. See Pickup Delivery. |
later_than |
string | No | — | UID of a Job that must be arrived at strictly earlier. Time order only — not the same Driver, and not a carried load. See Later Than. |
multi_shift |
JobMultiShift | No | — | Multi-shift-only Job fields. Currently serve_within date windows. Only valid when every Driver uses multi_shift. |
route_segment |
string|null | No | null |
Where this job may appear in a route. Allowed values: "start", "end", or null. Use "start" to fix a job to the first/start block of a route, "end" to fix it to the final block, or null to allow it anywhere. |
arrive_after |
float | No | — | Earliest ETA (arrival, before parking) as a 24h float. |
leave_by |
float | No | — | Latest ETD (after parking and service) as a 24h float. |
size |
float or str | No | — | Size for capacity constraints. May be negative on deliveries to free load. |
spec_type |
string | No | — | Only drivers with matching spec_type can serve this job. |
territories |
string | No | — | Only drivers with matching territories can serve this job. |
unserved_penalty_factor |
float | No | 1 |
How much this job matters relative to others if something must be left unserved. 2 means “worth two ordinary jobs”. See Unserved penalty factor. |
Minimal Example:
Standard Example:
{
"uid": "unique_job_id_2",
"duration": 10,
"parking_duration": 5,
"arrive_after": 10,
"leave_by": 16.5,
"location": {"lat": -33.84948962, "lon": 151.1274823}
}
Fix First/Last Job Example:
Set route_segment to "start" when a job should be served at the beginning of a route, or "end" for the final block. If multiple "start" jobs are assigned to the same route, they will form the initial block of jobs; their internal order is chosen by the optimiser. The same applies to "end" jobs at the end of the route. Use null (or omit the field) for jobs that may appear anywhere.
{
"uid": "important_first_customer",
"duration": 10,
"route_segment": "start",
"location": {"lat": -33.84948962, "lon": 151.1274823}
}
Later Than Example:
{
"uid": "install_site",
"duration": 40,
"later_than": "inspect_site",
"location": {"lat": -33.880661, "lon": 151.183096}
}
Location¶
A Location defines the geographical position of a driver or job.
| Name | Type | Required | Description |
|---|---|---|---|
lat |
float | Yes | Latitude |
lon |
float | Yes | Longitude |
Example:
PlanningTime¶
A point in a multi-shift planning horizon: a one-based Planning Day plus a clock time.
| Name | Type | Required | Description |
|---|---|---|---|
day |
int | Yes | Planning Day in this request. 1 is the first day. Maximum 100. |
time |
float or string | Yes | Clock time on that day. Accepts a 24h float (8, 8.5) or "HH:MM[:SS]". Response always uses "HH:MM:SS". |
Example:
MultiShift¶
Replaces shift_start / shift_end on a Driver when the request covers more than one Shift. See Multi-Shift.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
mode |
string | No | "reset" |
"reset" — each Shift is a new trip from start to end. "continue" — one journey; the Driver stays put between Shifts. |
shifts |
list of Shift | Yes | — | The Driver's Shifts. May be empty to mark the Driver unavailable. |
Reset Example:
{
"mode": "reset",
"shifts": [
{
"shift_start": {"day": 1, "time": 8},
"shift_end": {"day": 1, "time": 17}
},
{
"shift_start": {"day": 2, "time": 8},
"shift_end": {"day": 2, "time": 17}
}
]
}
Continue Example:
{
"mode": "continue",
"shifts": [
{
"shift_start": {"day": 1, "time": 8},
"shift_end": {"day": 1, "time": 18}
},
{
"shift_start": {"day": 2, "time": 8},
"shift_end": {"day": 2, "time": 18}
}
]
}
Shift¶
One period of availability for a multi-shift Driver.
| Name | Type | Required | Description |
|---|---|---|---|
shift_start |
PlanningTime | Yes | When this Shift begins. Must be before shift_end. |
shift_end |
PlanningTime | Yes | When this Shift ends. May be on a later day. |
Shifts on one Driver must not overlap. Back-to-back Shifts are allowed.
JobMultiShift¶
Multi-shift-only fields on a Job. Omit the object, or use an empty serve_within list, to allow any time in the planning horizon.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
serve_within |
list of ServeWithinPeriod | No | [] |
Absolute periods in which this Job may be served. Combined with OR. Each period must set arrive_after, leave_by, or both. |
Example:
{
"serve_within": [
{
"arrive_after": {"day": 2, "time": 8},
"leave_by": {"day": 2, "time": 17}
}
]
}
ServeWithinPeriod¶
One absolute window for a multi-shift Job.
| Name | Type | Required | Description |
|---|---|---|---|
arrive_after |
PlanningTime | No | Earliest arrival in this period. |
leave_by |
PlanningTime | No | Latest departure in this period. |
At least one of the two fields is required.
Together with top-level arrive_after / leave_by (recurring clock hours every Planning Day):
Settings¶
Settings convey parameters which affect the way the optimisation algorithm runs.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
time_limit |
int | No | null (No Limit) |
Seconds Max seconds allowed for optimisation. |
unimproved_time_limit |
int | No | n_locs / 20 if no time_limit else null (no limit) |
When the optimiser's best solution plateaus for this many seconds, finish the optimisation |
iter_limit |
int | No | null |
Sets a maximum number of iterations allowed. |
unimproved_iter_limit |
int | No | null |
Sets the maximum number of iterations the optimiser is allowed to continue without any improvement between iterations. |
traffic_date |
string | No | null |
yyyy-mm-dd Enables traffic optimisation for this date. In a multi-shift request this date is Planning Day 1; later Planning Days follow it. |
parallel |
string | No | auto |
Enable or disable parallel solving. Can be false, true, or auto. If auto, parallel solving is enabled if n_locs (total locations) is >= 200. |
optimisation_objective |
string | No | "time" |
The objective to minimise: "time" (default), "distance", or "cost". If "cost", requires Costs on every driver. See Concepts. |
later_shift_usage_penalty |
float | No | 0 |
Penalty on used time on later declared Shifts, per used second per day-late. Omit, null, and 0 are off. Must be finite and ≥ 0. Same addend on every optimisation_objective. Not a hard constraint. See Later shift usage penalty. |
active_vehicle_penalty |
float | No | 0 |
Lump on each compiled vehicle that serves at least one Job. Ordinary and continue: once per Driver; reset: once per Shift. Omit, null, and 0 are off. Must be finite and ≥ 0. Same unscaled addend on every optimisation_objective. Stacks with costs.run in cost mode (different units). Not a hard fleet cap. See Active vehicle penalty. |
Minimal Example:
Standard Example:
Cost Optimisation Example:
When using optimisation_objective = "cost", you must provide Costs for every driver:
{
"settings": {
"optimisation_objective": "cost",
"time_limit": 120
},
"drivers": [
{
"uid": "driver_1",
"start": {"lat": -33.867798, "lon": 151.166256},
"end": "start",
"shift_start": 9.0,
"shift_end": 17.0,
"costs": {
"km": 0.20,
"hour": 25.00,
"job": 5.00,
"run": 100.00
}
}
]
}
See Optimisation Objective in the Concepts guide for more details on when to use each objective.
Costs¶
Costs are used when Settings.optimisation_objective = "cost" and represent costs to be minimised by the optimiser. Currency is arbitrary but must be consistent across all drivers.
If you set optimisation_objective = "cost", you must provide costs for every Driver.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
km |
float | No | 0 |
Cost per kilometre driven by this driver. |
hour |
float | No | 0 |
Cost per hour that this driver is working. Includes driving, stopped at jobs, waiting for time windows, etc. |
job |
float | No | 0 |
Cost per job that this driver performs. |
run |
float | No | 0 |
Cost for this driver being active (i.e. serving any jobs). Useful for minimising the number of drivers used. |
Tip
Setting different run costs for each driver encourages the optimiser to favour certain drivers over others.
Minimal Example:
Standard Example:
Response¶
RoutingSolution¶
A RoutingSolution is the main response object returned by the API.
Fields:
| Name | Type | Description |
|---|---|---|
runs |
list of Run | The list of runs (routes) for each driver. |
unserved_jobs |
list of Job | Jobs which the optimiser could not find a way to visit. |
Run¶
A Run represents a planned route for a single driver.
Fields:
| Name | Type | Description |
|---|---|---|
driver |
Driver | The driver assigned to this run. In a multi-shift response this includes driver.multi_shift.mode and omits shift_start / shift_end. |
jobs |
list of SolutionJob | The sequence of jobs in this run. |
multi_shift |
object | Present on every Run in a multi-shift response. shift is the 1-based Shift number; planned_start / planned_end are PlanningTime values (null if the Shift is unused); planned_start_location / planned_end_location are the actual ends of this Shift. |
SolutionJob¶
A SolutionJob is identical to the original Job you sent, but with extra fields describing its placement in the solution.
Fields:
| Name | Type | Description |
|---|---|---|
route_segment |
string|null | Echoes the requested route segment: "start", "end", or null. If omitted in the request, it is returned as null. |
run |
int | 1-based index indicating which run this job is planned in. null if unserved. |
seq |
int | 1-based sequence of this job within the run. null if unserved. |
eta |
string | Arrival at the job, before parking (HH:MM:SS). null if unserved. Omitted on multi-shift Jobs; use multi_shift.eta. |
| etd | string | Departure after parking (if applied) and service (HH:MM:SS). null if unserved. Omitted on multi-shift Jobs; use multi_shift.etd. |
| later_than | string | Echo of the requested later_than uid, when set. |
| multi_shift | object | Present on every Job in a multi-shift response. eta / etd are PlanningTime values, or null if unserved. |
Example Response:
{
"runs": [
{
"driver": {
"uid": "drvid1",
"location": {"lat": -33.84948962, "lon": 151.1274823},
"shift_start": 9.5,
"shift_end": 18.5
},
"jobs": [
{
"uid": "unique_job_id_1",
"duration": 10,
"location": {"lat": -33.849489, "lon": 151.127482},
"route_segment": "start",
"run": 1,
"seq": 1,
"eta": "09:45:00",
"etd": "09:55:00"
}
]
}
],
"unserved_jobs": []
}
Asynchronous Route Optimisation API Responses¶
These response types are specific to the asynchronous Post and Poll endpoints.
PollingStartResponse¶
The response returned by POST /v2/polling/vrp with status code 202 Accepted.
Fields:
| Name | Type | Description |
|---|---|---|
uid |
string | Unique identifier for this optimization request. Use to query status and retrieve solution. |
status_url |
string | Complete URL to GET the optimization status. |
solution_url |
string | Complete URL to GET the final solution when complete. |
Example Response:
{
"uid": "01GG7AEX026F47CS4NK2JBCE5G",
"status_url": "https://opt.route.optimiser.app/v2/polling/vrp/01GG7AEX026F47CS4NK2JBCE5G/status",
"solution_url": "https://opt.route.optimiser.app/v2/polling/vrp/01GG7AEX026F47CS4NK2JBCE5G"
}
PollingStatusResponse¶
The response returned by GET /v2/polling/vrp/{uid}/status with status code 200 OK.
Fields:
| Name | Type | Description |
|---|---|---|
uid |
string | The unique identifier from the start request. |
timestamp |
string | ISO 8601 timestamp when this status was recorded. |
message |
string | Human-readable description of the current optimization state. |
solving |
boolean | true if optimization is still running, false if complete. |
cost |
number | Current best solution cost. 0 while solving hasn't started computing costs. |
next_status_eta |
string or null | ISO 8601 timestamp recommending when to check status again. null when solving is complete. |
Example Response (Solving in Progress):
{
"uid": "01GG7AEX026F47CS4NK2JBCE5G",
"timestamp": "2022-10-25T12:19:26.129294+02:00",
"message": "Started solving...",
"solving": true,
"cost": 0,
"next_status_eta": "2022-10-25T12:19:30.129134+02:00"
}
Example Response (Solving Complete):