- Hard constraints define feasibility. A plan that violates one is invalid — the job is left unassigned rather than placed illegally.
- Soft constraints are priced into the objective. They can be bent for a cost, so the solver trades them off against everything else.
The V3 schema is final, but capabilities land in phases. Rules marked
Coming soon are accepted by the schema and currently return a
400 that names
the field — never a silently-ignored constraint.Overview
Capacity
Every vehicle has a multi-dimensional capacity; every job a demand. The load on a vehicle may never exceed its capacity on any dimension. Dimensions are named and matched by name, soweight, volume, pallets line up across jobs
and vehicles regardless of declaration order.
400 — you never
get a silently-infeasible plan.
The response carries load_after on every stop as a named map ({ "weight": 30, "volume": 2 }),
matching the same dimension names.
Load compatibility
Coming soon.
vehicle.incompatible_dimensions: [["general", "glass"]] — declares pairs of load dimensions that may not share a vehicle at the same time (the waste-collection “don’t mix streams” rule).Time windows
A job may carry one or moretime_windows. A hard window must be
respected — if the vehicle can’t arrive within any window, the job is unassigned.
Multiple windows mean “any of these slots is acceptable”.
string (ISO 8601)
The allowed service window. Hard by default — arriving outside the window
makes the job unassignable. Available.
number
Cost rate per hour of arriving early. When set, early arrival is allowed
and priced instead of forbidden. (Coming soon — currently returns 400.)
number
Cost rate per hour of arriving late. When set, late arrival is allowed
and priced instead of forbidden. (Coming soon — currently returns 400.)
from == to and both cost fields set models a soft target time
(penalised on both sides). The old target_arrival field is gone — this replaces it.
Shift hours & overtime
A vehicle works only within its shift windows. Each shift has afrom/to; the
route — including travel back to the end depot — must finish by to, unless
max_overtime_s allows a costed extension.
Skills
Each job can require skills the serving vehicle must have. Matching is a subset check:job.skills[].name ⊆ vehicle.skills.
string
The required skill name. The serving vehicle must have this skill in its
skills list. Available (hard).number
When set, the skill becomes soft — assigning a vehicle that lacks it costs
violation_cost instead of being infeasible. (Coming soon — currently returns 400.)Vehicle eligibility
One object expresses every “which vehicle may serve this job” rule. Hard restrictions live ineligible_vehicles; soft preferences live in preferences.
array of strings
Hard whitelist — only these vehicles may serve the job. Omit to allow all
vehicles. Available.
array of strings
Hard blacklist — these vehicles may never serve the job. Mutually exclusive
with
allowed. Available.array
Soft vehicle affinity:
[{ "vehicle": "v2", "violation_cost": 800 }]. Serving
a non-preferred vehicle costs violation_cost against the objective.
(Coming soon — currently returns 400.)Route limits
Per-vehicle caps on the shape of a route, grouped underlimits. All hard.
Stop-count caps live separately, on the shift.
integer
Total route distance cap, metres. Available.
integer
Total driving time cap, seconds (excludes service & wait). Available.
integer
Total duty time cap, seconds (includes service & wait). Available.
integer
Maximum drive time for a single leg between consecutive stops, seconds. Available.
integer
Maximum number of job stops on the route for this shift. Lives on the
shift, not on
vehicle.limits. Available.Sequencing
Inter-job ordering and grouping are expressed through therelations array. Each
relation references jobs either explicitly (job_ids) or by tag (group), but
not both.
A relation targets jobs either via
job_ids (explicit job ids) or via the
separate group field (a tag, expanding to all jobs carrying that tag) —
never both on the same relation.
Shipments
A shipment is a pickup → delivery pair: ajob with pickup + delivery legs
instead of flat visit fields. The solver guarantees the pickup precedes the
delivery on the same route, and the carried demand counts against capacity only
between the two stops. Same-vehicle and ordering are implicit — never expressed as a
separate relation.
Available for mandatory shipments with coordinate legs, hard skills, hard time windows, and (optionally) a single allowed vehicle. Droppable shipments, soft skills/windows, multi-vehicle eligibility, and depot-referenced legs are Coming soon.
Driver breaks
Coming soon.
shift.breaks[] — floating (flexible within a window) and fixed (a mandatory off-duty interval), with an optional drive/duty trigger.The V3 API Model
How the full request is structured.
API Reference
The
POST /v3/routing/solve schema.