Jobs
A job is something that needs to happen at one or more locations. For a simple visit (delivery, service call, inspection) place its visit fields directly on the job object. Pickup-then-delivery shipments use pickup + delivery instead — see Shipments.
{
"id": "delivery-1",
"location": { "coordinate": [4.7005, 50.8798] },
"service_duration_s": 300,
"time_windows": [
{
"from": "2026-04-01T09:00:00+02:00",
"to": "2026-04-01T12:00:00+02:00"
}
],
"demand": { "weight": 10 },
"skills": [{ "name": "refrigerated" }]
}
- location — a
Place: { "coordinate": [lon, lat] }, or a reference to a named location or depot.
- service_duration_s — seconds spent at the location, excluding travel.
- time_windows — when the job can be served (ISO 8601
from/to pairs). Hard by default — no earliness_cost_per_hour or lateness_cost_per_hour.
- demand — named capacity consumed, e.g.
{ "weight": 10 }. Matched against vehicle.capacity by name.
- skills — capability requirements, e.g.
[{ "name": "refrigerated" }]. A job can only be served by a vehicle that has all required skills.
- mandatory — set
true to require the job be served (infeasible if it cannot be). Omit or use unassigned_cost for a droppable job.
Shipments
Coming soon. Pickup-then-delivery jobs use job.pickup + job.delivery instead of flat visit fields. The solver guarantees pickup precedes delivery on the same route, and demand counts against capacity only between the two stops.
Vehicles
A vehicle represents a driver or asset with capacity, skills, a cost model, and one or more working shifts.
{
"id": "truck-1",
"capacity": { "weight": 1000, "volume": 50 },
"skills": ["refrigerated"],
"limits": {
"max_distance_m": 250000,
"max_drive_time_s": 28800
},
"shifts": [
{
"from": "2026-04-01T08:00:00+02:00",
"to": "2026-04-01T17:00:00+02:00",
"start": { "coordinate": [4.3517, 50.8503] },
"end": { "coordinate": [4.3517, 50.8503] }
}
]
}
- capacity — named dimension limits matched against job
demand by name. { "weight": 1000, "volume": 50 } is two independent caps.
- skills — capability tags the vehicle provides. A vehicle can serve a job only when its skills are a superset of the job’s required skills.
- limits — optional route caps:
max_distance_m, max_drive_time_s, max_duty_time_s, max_tasks.
- shifts — working time windows (ISO 8601
from/to). At least one shift is required. start and end are the depot Place for this shift — set inside shifts, not at vehicle top-level.
Multiple shifts on one vehicle model multi-day availability.
Distance matrices
V3 automatically fetches road-network distance and duration matrices from Solvice Maps. You don’t need to provide a matrix unless you have custom distances.
To supply a custom matrix, include it in options.runtime.matrix:
{
"options": {
"runtime": {
"matrix": {
"distances": [
[0, 1500, 3200],
[1400, 0, 2100],
[3100, 2000, 0]
],
"durations": [
[0, 600, 1200],
[550, 0, 900],
[1150, 850, 0]
]
}
}
}
}
Both matrices must be square N×N. Values are distances in metres and durations in seconds. Road-network matrices are typically asymmetric (A→B ≠ B→A).
Constraints
The solver enforces these constraints (hard — never violated unless explicitly softened):
| Constraint | Description |
|---|
| Capacity | Vehicle load never exceeds capacity on any named dimension |
| Time windows | Jobs served within their allowed time windows |
| Skills | Vehicle must have all skills required by the job |
| Vehicle range | Maximum distance or drive time per route |
| Sequence | Job A served before job B on the same route (ordered relation) |
| Same resource | A set of jobs served by the same vehicle across shifts (same_resource relation) |
| Eligible vehicles | Job may only be served by vehicles in eligible_vehicles.allowed, never by vehicles in excluded |
See the full Constraints guide for field-level details and coming-soon capabilities.
Objective
The solver minimises a lexicographic objective:
- Serve as many jobs as possible (highest priority — assign before optimising cost)
- Minimise vehicles used (use as few vehicles as possible before optimising routes)
- Minimise cost (weighted sum of travel time, distance, and any soft penalties)
The default objective is equivalent to { "priorities": ["serve_jobs", "minimize_vehicles", "minimize_cost"] }. Omit the objective block entirely to use these defaults.
{
"objective": {
"priorities": ["serve_jobs", "minimize_vehicles", "minimize_cost"],
"costs": {
"per_travel_hour": 50
}
}
}
Every response includes an itemised estimated_cost breakdown (travel, waiting, overtime, unassigned, etc.) so you never need to recompute the objective yourself.
Relations
Relations express ordering and grouping constraints between jobs using a single relations array. Each relation is a tagged object with a type.
{
"relations": [
{
"type": "ordered",
"job_ids": ["pickup-A", "delivery-A", "delivery-B"]
},
{
"type": "same_resource",
"job_ids": ["monday-visit", "tuesday-followup"]
}
]
}
| Type | Meaning | Status |
|---|
ordered | Jobs appear in listed order on one route | Available |
consecutive | Jobs appear consecutively (no stops between them) | Available |
same_resource | Jobs served by the same vehicle (across shifts / days) | Available |
same_route | Jobs on the same route in the same shift | Available |
same_day | Jobs on the same calendar day | Coming soon |
synchronized | Two vehicles meet at the same location within max_wait_s | Coming soon |
job_ids can reference a job id or a tag (jobs sharing a tag form a group). An ordered relation over tags orders the groups relative to each other while leaving order within each group free.