Every request field referenced below belongs to the
OnRouteRequest schema — resources[] (Resource), jobs[] (Job), relations[] (Relation), depots[], options, weights, and costs. Field paths below are written relative to the object they belong to, e.g. windows[].hard means job.windows[].hard.Levels and how violations are reported
Every constraint is one of three levels:- hard — a plan that violates it is infeasible. The solver leaves the offending job unassigned (with
options.partialPlanning, the default) rather than place it illegally. - medium — priced independently from hard and soft score components; used for a handful of “should hold, but a plan isn’t automatically invalid if it doesn’t” rules (min-side period rules, same-time synchronization).
- soft — priced into the objective. Always somewhat “violated” — the goal is minimizing the penalty, not eliminating it.
JobAssignment.violations/scoreExplanation, each violated constraint is named using the OnrouteConstraint enum — that’s the code shown in the Reported as column of every table below. A handful of relation-based constraints (noted inline) share a reporting bucket with a sibling constraint rather than having their own enum value.
Weights vs. costs
Soft constraints are tunable two ways — pick one per request, don’t mix mental models:weights— unitless relative importance (e.g.travelTimeWeight: 1,minimizeResourcesWeight: 3600). This is the original system; all weight fields default to small integers and are compared against each other, not against real money.costs— monetary units (EUR or your currency, treated as unitless by the solver). Whencostsis present, it’s converted internally to the equivalent weights and takes precedence overweightsfor the fields it covers.
costs.softSkillViolationCost, costs.timeWindowViolationCostPerHour, and costs.preferredResourceViolationCost are accepted by the schema but currently have no effect on live solving — soft tag and soft time-window penalties always use each item’s own weight field regardless. Use the per-item weight fields documented below instead.weights field at all — the solver applies a fixed internal weight and the constraint isn’t currently tunable except through costs where noted.
Overview
Time windows
A job can carry one or more time windows. Multiple windows mean “any of these is acceptable.”Start of the window.
End of the window.
When
true, arriving outside every window makes the job unassignable (DATE_TIME_WINDOW_CONFLICT, hard). When false, arriving outside a window is allowed but penalized (DATE_TIME_WINDOW_CONFLICT_SOFT, soft).Penalty weight for the soft path only — ignored when
hard: true.Planned commitments
Two fields pin a job to a pre-committed schedule — one hard, one soft:Fixes the job to a specific calendar date. Hard — the solver must honor it (
PLANNED_DATE).Target arrival time. Soft — deviations are penalized proportionally to
weights.plannedWeight, not forbidden (PLANNED_ARRIVAL).Wait time and urgency
Penalizes idle time between arrival and a window opening, summed across every resource (
WAIT_TIME, soft). Disabled unless set.Higher values push the job earlier in the day and earlier in the planning period. Affects execution order, not whether the job is selected (
URGENCY, soft, scaled by weights.urgencyWeight).job.padding (seconds added before/after service), job.durationSquash (reduced duration for a job immediately following another at the same location), and job.proficiency[].durationModifier (per-resource duration multiplier) aren’t constraints themselves, but they change what arrival times and windows are scored against.Shift hours and overtime
A resource works only within its declared shift windows.End of the shift. Without
overtimeEnd, finishing later is a hard violation (SHIFT_END_CONFLICT).When set (and later than
shift.from), finishing after to but before overtimeEnd becomes a soft penalty instead of infeasible, scaled via costs.overtimeCostPerHour (no direct weights field is currently exposed for this). Finishing after overtimeEnd itself is always hard (OVERTIME_END_CONFLICT), regardless of the cost/weight configuration.Cost per hour of active (driving, servicing, or waiting) time. Not a violation — an always-on cost-minimization objective (
HOURLY_COST, soft).Windowed (
WINDOWED), duty/drive-triggered (DRIVE), and unavailability (UNAVAILABILITY) breaks. Breaks aren’t scored directly — they shift the arrival-time shadow variable, which the time-window and shift-end constraints above then score. Set job.resumable: true to let a job be split by an unavailability break instead of blocked by it.Drive time, distance, and travel
Per-resource caps on driving, plus the always-on travel-time objective.Cumulative driving time cap per shift/trip (excludes service time). Hard (
MAX_DRIVE_TIME).Cumulative distance cap per shift/trip, meters. Hard (
MAX_DRIVE_DISTANCE).Cap on a single leg’s travel time (from the previous stop, or the final leg back to the shift end). Prevents excessive backtracking within one hop, distinct from the cumulative cap above. Hard (
MAX_DRIVE_TIME_JOB).Baseline objective weight for total travel time. Also covers the return leg to the shift’s end location. Equivalent
costs.drivingCostPerHour (default 25.0). Soft (TRAVEL_TIME / END_LOCATION_TRAVEL_TIME).Separate objective for minimizing total driving time specifically (as opposed to travel time generally). Soft (
DRIVE_TIME).A preferred operating location for the resource. The solver tries to keep the resource’s jobs close to it. No dedicated
weights field is currently exposed — tune via costs.outOfRegionCostPerHour (default 10.0 EUR/hour outside the region). Soft (REGION_TIME).Capacity and load compatibility
Every resource has a multi-dimensional capacity (up to 5 dimensions); every job a load.Multi-dimensional capacity, e.g.
[500, 200] for weight and volume. Hard (TRIP_CAPACITY).Load consumed on each capacity dimension. Must match the dimension count of any resource it could be assigned to.
Matrix restricting which load dimensions may coexist non-zero on the vehicle at once:
loadCompatibility[i][j] is the effective max capacity for dimension j when dimension i currently carries load. Must match capacity’s dimension count. Omit for no restriction. Hard (LOAD_COMPATIBILITY).For pickup-and-delivery requests, load is tracked cumulatively as the vehicle picks up and drops off along the route rather than accumulated per-trip — see Pickup and delivery.
Skills, rankings, and eligibility
Four independent mechanisms answer “which resource may or should serve this job,” from strict (hard tags, allow/deny lists) to soft (rankings).Skill/capability name matched against
resource.tags (or the active shift.tags, which override the resource’s tags for that shift).true: only resources with the tag may take the job (TAG_HARD, hard). false: preferred but not required, penalized by .weight if violated (TAG_SOFT, soft).Penalty for the soft path, in travel-time-equivalent units.
A resource name this job prefers.
Lower is more preferred (1 = best). Unranked resources are treated as rank 100. Soft (
RANKING_SOFT), scaled by weights.rankingWeight / costs.rankingViolationCostPerRank. Whitelist of resource names. Hard (
ALLOWED_RESOURCES), scaled by weights.allowedResourcesWeight.Blacklist of resource names. Hard (
DISALLOWED_RESOURCES), same weight field as above.Pins the job to exactly this resource — used for warm-starting or committed assignments. Hard (
PLANNED_RESOURCE).Other resource names this resource may cooperate with on linked jobs. Only enforced when a
SAME_TIME relation sets enforceCompatibility: true. Hard (RESOURCE_COMPATIBILITY).Job relations
Inter-job rules live in the top-levelrelations[] array, each referencing job names (or, for GROUP_SEQUENCE, tags).
One of
SAME_TRIP, SEQUENCE, DIRECT_SEQUENCE, NEIGHBOR, SAME_TIME, PICKUP_AND_DELIVERY, SAME_RESOURCE, SAME_DAY, GROUP_SEQUENCE — see the table below.Job names in the relation. Order matters for
SEQUENCE/DIRECT_SEQUENCE. Ignored for GROUP_SEQUENCE (membership comes from relation.tags matching job.tags instead).Optional — pins every job in the relation to one named resource.
When
true, allows some (not necessarily all) member jobs to be scheduled if the full relation can’t be satisfied.Minimum seconds between consecutive jobs.
SEQUENCE, DIRECT_SEQUENCE, and SAME_TIME only. Hard by default; set relation.hardMinWait: false to make it soft instead.Maximum seconds between consecutive jobs. Same relation types as above.
For
SAME_TIME: how far apart member arrivals may be and still count as synchronized.FROM_ARRIVAL measures the gap from service start to service start; FROM_DEPARTURE from service end to service start.GROUP_SEQUENCE only — ordered tag names; jobs carrying each tag form one group.SAME_TIME only — requires the resources ultimately assigned to member jobs to be mutually listed in each other’s compatibleResources. See Resource compatibility.Weight modifier for this specific relation instance, independent of other relations of the same type.
Pickup and delivery
Setting any relation toPICKUP_AND_DELIVERY switches the two named jobs into a pickup → delivery pair (adding pickup/delivery legs instead of a single visit) and enables cumulative load tracking for the whole request.
- Delivery on same vehicle (
DELIVERY_NOT_ON_SAME_VEHICLE) — the pickup and its paired delivery must be on the same vehicle. - Delivery after pickup (
DELIVERY_BEFORE_PICKUP) — the pickup’s arrival must precede the delivery’s arrival.
Once any
PICKUP_AND_DELIVERY relation is present, trip capacity and load compatibility switch from a flat per-trip sum to cumulative tracking — load increases at each pickup and decreases at each delivery, so capacity can never be exceeded at any point along the route, not just in total.Depots
Depots are optional intermediate stops (disposal sites, resupply points) resources can be routed through mid-route.Per-visit cost, used to prefer cheaper depots when a detour is optional. Soft (
DEPOT_COST), also settable via costs.depotCost.Multi-dimensional cap on cumulative load routed through this depot across every job that selects it. Hard (
DEPOT_CAPACITY).Opening hours. Arrival outside every window is infeasible. Hard (
DEPOT_TIME_WINDOW).Service/unloading time at the depot, in seconds. Not a constraint by itself, but extends the route’s timeline, which the shift-end and time-window constraints then score against.
shift.unloadBeforeEndShift (End depot, hard, END_DEPOT) forces the last job of a route to select a depot if the vehicle would otherwise still be carrying non-zero load at shift end.Period rules
Resources can carry rules that cap or floor total work over a longer period (a week, a month, the whole planning horizon) rather than a single shift.The sub-window the rule applies to. Omit to cover the whole planning period.
Cap/floor on total service time (job durations only) across the period, in seconds. Max is hard (
RESOURCE_PERIOD_MAX_SERVICE_TIME); min is medium (RESOURCE_PERIOD_MIN_SERVICE_TIME).Same pattern, for driving time.
RESOURCE_PERIOD_MAX_DRIVE_TIME (hard) / RESOURCE_PERIOD_MIN_DRIVE_TIME (medium).Same pattern, for work time (service + drive/travel combined).
RESOURCE_PERIOD_MAX_WORK_TIME (hard) / RESOURCE_PERIOD_MIN_WORK_TIME (medium).Cap/floor on the summed
job.complexity of jobs completed in the period. RESOURCE_PERIOD_MAX_COMPLEXITY (hard) / RESOURCE_PERIOD_MIN_COMPLEXITY (medium).Maximum count of jobs per
job.jobTypes value, for the period. Hard (JOBTYPE_VIOLATION). The equivalent shift.jobTypeLimitations applies the same cap per shift instead of per period.Turns a per-resource rule into a shared pool: every resource declaring an identical rule (same period, same field values) under the same
groupTag shares one bound across all of them, instead of each resource getting its own. A resource with a mismatched declaration is silently excluded from the pool.Period-rule penalties use a fixed internal weight — none of them currently expose a dedicated
weights field.Fairness and vehicle usage
Solver-wide objectives that shape how work is spread and how many resources get used, controlled throughoptions + weights rather than per-job/per-resource fields.
Balance service time across all resources and trips within
options.workloadSensitivity tolerance. Soft (FAIR_WORK), scaled by weights.workloadSpreadWeight / costs.workloadImbalanceCostFactor.Same balancing, aggregated per resource across all its trips/days instead of globally. Soft (
FAIR_TOTAL_WORK).Same fairness mechanism, applied to
job.complexity instead of service time. Soft (FAIR_COMPLEXITY_PER_TRIP / FAIR_COMPLEXITY_PER_RESOURCE). The tolerance is fixed internally, not currently exposed as an option.When
true, consolidate jobs onto fewer active resources, even at the cost of more travel time. Soft (RESOURCE_USAGE), scaled by weights.minimizeResourcesWeight (units: seconds of travel time an extra resource is “worth” — e.g. 3600 means one resource ≈ 1 hour of travel).When
options.partialPlanning leaves jobs unserved, higher-priority (× duration) jobs are penalized more for being left out, biasing the solver to serve them first. Medium (UNSERVED_JOBS), scaled by weights.priorityWeight.Pushes jobs toward earlier days/shifts in the planning period when possible. Soft (
JOB_DAY_INDEX).In partial-planning mode, marks a job that must be included in the solution rather than left on a dummy trip. Hard (
HARD_JOBS), scaled per-job by hardWeight.Geographic optimization
Jobs within this radius (meters) of each other are treated as geographic neighbors; the solver penalizes visiting a job whose immediately preceding stop isn’t one of its neighbors. Soft (
JOB_PROXIMITY), scaled by weights.jobProximityWeight.HAVERSINE (straight-line, fast) or REAL (routing-engine distance, slower but accurate) for the proximity radius check above.Penalizes overlapping bounding boxes between any two routes, encouraging geographically distinct territories per resource. Soft, scaled by
weights.clusteringWeight and buffered by options.clusteringThresholdMeters (default 10000m). Not currently surfaced in the score-explanation breakdown.Related
VRP Constraint System
The conceptual model: hard/medium/soft levels, weight tuning, and debugging conflicts.
Advanced Constraints
Deeper walkthroughs of drive-time limits, tag matching, and vehicle restrictions.
Request Schema
Full field reference for
OnRouteRequest.Unassigned Reasons
Why a job was left unserved — surfaces which of these constraints blocked it.