V3 is in beta. General availability (GA) is planned for January 2027.
unassigned[]. A soft rule is priced into summary.score.cost
and traded off against travel.
For how the pieces of a request fit together, see
The V3 API model. For how soft costs are ranked, see
Objective function.
A field the schema does not define is rejected with HTTP
400 and
errors[].code: "unknown_field". A field the schema defines but the solver
does not honour yet is rejected with HTTP 400 and
errors[].code: "not_supported"; these are marked Not supported yet below.
Each error item names the offending value with a JSON pointer, for example
/problem/vehicles/0/shifts/0/breaks/1. See Errors.Overview
Capacity
A vehicle declares acapacity per named dimension; a job or shipment
declares a pickup. The load never exceeds capacity in any dimension at any
point along the route.
weight, volume, pallets, and so on).
Values are non-negative integers; a negative value is a 400. A request may
use at most four distinct dimension names (see Limits).
Check dimension names for typos, because a mismatch is not an error:
- A vehicle whose
capacityomits a dimension has capacity0in that dimension, so a job demanding it cannot ride that vehicle. - An empty object,
"capacity": {}, omits every dimension: the vehicle carries nothing that has apickupor adelivery. - A vehicle with no
capacityfield at all has no load limit in any dimension. To mean “no limit”, omit the field; do not send{}.
unassigned[] with
CAPACITY_EXCEEDED.
A shift can override the vehicle’s capacity with its own shifts[].capacity,
for example a van that carries less on a day it tows a trailer. Each dimension
named on the shift replaces the vehicle’s value for that dimension; dimensions
the shift does not name keep the vehicle’s capacity.
tue the vehicle carries 600 weight and still 50 volume.
The response reports load_after on each stop and load_peak on each route,
as the same named map.
Deliveries from the depot
jobs[].delivery is a quantity that leaves the vehicle at the visit, having
been loaded at the depot. The vehicle starts its trip carrying the sum of the
trip’s delivery quantities. Set pickup on the same job for what it
collects there, so one visit can both drop off and pick up:
Loading and unloading different amounts at one address
A shipment carries one quantity from its pickup to its delivery. A swap that collects one quantity and drops off another at the same address is one job with bothpickup (picked up) and delivery (dropped off), as above.
Time windows
A job or shipment leg carries one or moretime_windows. This is a jobs[]
entry with one window:
TIME_WINDOW_VIOLATED. Several windows mean
“any of these is acceptable”. A vehicle that arrives before from waits.
Write every timestamp with a UTC offset or Z. A timestamp without one is a
400 that names the field.
Four fields price timing instead of forbidding it:
number
A flat cost for serving the task in this window rather than another of
its own windows. Every window stays hard. Needs at least two windows on the
task, and once any of them has a non-zero cost the task’s windows must not
overlap. A non-zero
cost anywhere in the request cannot be combined with
objective.per_late_hour (400). From 0 to 21474.83. Reported in
score.components.window_choice.number
Fleet-wide: arriving after
to becomes allowed and priced per hour late, on
every window of every task that does not set its own rate. It applies to
task windows only; the shift end (plus max_overtime_s) stays a hard limit.
Omit the field for hard windows: an explicit 0.0 is a 400. At most
1288490. Reported in score.components.lateness and per stop as
lateness_s.number
Per window: makes this window’s late side allowed and priced at this rate,
replacing the fleet rate for the task and working whether or not a fleet
rate is set. Hard and soft windows can therefore share one request. Must be
> 0 and at most 1288490, and the task must have exactly one window.
Early arrival still waits.object
A soft deadline on one task, independent of its windows:
{ "at": "2026-04-01T10:00:00Z", "on": "arrival", "per_late_hour": 60.0 }.
on is arrival (default) or end (the end of the visit).
Reaching that instant after at is priced per hour and never makes the task
infeasible; arriving early never forces a wait. per_late_hour must be
> 0 and at most 21474.83. On a shipment the target is measured on the
delivery leg. Reported per stop as target_lateness_s and in
score.components.target.400, so a soft rule cannot be made “almost
hard” with a very large number. For a deadline that must hold, use a hard
window.
Shift hours and overtime
A vehicle works only inside a shift’sfrom and to. The whole route,
including the return leg and any unloading at the end depot, must finish by
to unless max_overtime_s grants an extension. This is a shifts[] entry:
id of 1 to 128 characters, unique within its vehicle.
It is echoed as routes[].shift in the response. Several shifts[] on one
vehicle model availability over several days.
With max_overtime_s set, to becomes a soft end and to + max_overtime_s
the hard one. Time worked past to is reported as routes[].time.overtime_s and
priced by per_overtime_hour, set fleet-wide in objective.costs and
overridable per vehicle in vehicles[].costs or per shift in
shifts[].costs; score.components.overtime is 0.0 unless a rate applies.
boolean
default:"false"
The leg from
start to the first task costs nothing: it is left out of the
objective, the schedule and the distance and duration totals. The start stop
is still reported. Has no effect unless start is set.boolean
default:"false"
The same for the leg from the last task to
end.How days are counted
Three rules on this page refer to a day: thesame_day relation,
preferred_date, and recurring period limits. Each one assigns a whole shift
to a day by the shift’s from. None of them looks at the time of an
individual stop, so a stop served after midnight belongs to the day its shift
started on. A quotas[] window is different: it counts each stop
by its own service start.
All three read the calendar the same way: a day is the calendar date of a
value in the UTC offset it is written with.
The offset a
from is written with decides its day, so write it in the
offset of the place the shift runs in. These two shifts[] entries are both
on 1 April at +10:00:
early starts at 2026-03-31T22:00:00Z and late at 2026-04-01T03:00:00Z,
but both are written with the date 1 April at +10:00, so:
same_daytreats them as the same day. Two members of onesame_dayrelation can be served one onearlyand one onlate.- A task with
preferred_date: "2026-04-01T08:00:00+10:00"is on its preferred day on either shift and pays noobjective.non_preferred_date_cost. - A period with
every: "day"puts both shifts in 1 April and applies one cap to the two together.
early as "from": "2026-03-31T22:00:00Z" keeps the same instant but
moves the shift to 31 March for all three rules: same_day no longer pairs it
with late, the task above pays non_preferred_date_cost on it, and a daily
period counts it on 31 March.
Skills
A job lists the skills it needs; the vehicle that serves it must hold every one of them.ann can serve boiler-7; bob cannot.
- Skills are objects on both sides. A bare string such as
"gas"is a400witherrors[].code: "invalid_type". - A job requirement may add
min_level(>= 1, default1). A vehicle entry may addlevel(>= 1, default1). The vehicle qualifies when its level is at least the job’smin_level. - A skill name listed twice in one vehicle’s or one shift’s
skillsis a400: give one level per skill. - A vehicle that omits
skillscan serve only jobs that require none. - A shift’s own
skillsreplace the vehicle’s for that shift;[]means the shift provides none. - Skills are always hard. A job whose requirement no vehicle meets is returned
in
unassigned[]withSKILLS_MISMATCH.
Tags, serves_tags, and preferred_tags
Tags describe zones and territories rather than capabilities. Where skills
require every listed entry, tags match on any overlap.
shifts[].serves_tags is hard: when it is set and non-empty, the shift serves
only tasks that carry at least one of the listed tags. Untagged tasks can be
served by every shift. A tagged task that no shift serves is returned in
unassigned[] with TAG_RESTRICTED.
vehicles[].preferred_tags is soft: serving a task that lacks a preferred tag
costs that entry’s violation_cost, which must be 0 or more. Entries apply
independently, so a task lacking several preferred tags is charged for each.
The total is reported in score.components.tag_preference.
Vehicle eligibility
Four fields decide which vehicle may serve a task, or what it costs. These twojobs[]
entries use them:
t1 may ride v1 or v2, and costs 15.0 more on v1 than on v2. t2
rides v1 or is left unassigned.
From hardest to softest:
-
locked_vehiclepins the task to one vehicle id. Only that vehicle may serve it, and re-optimisation never moves it to another. A pinned task that its vehicle cannot serve on any shift is returned inunassigned[]withCOMMITMENT_BLOCKED. -
eligible_vehiclesis a hard whitelist (allowed) or blacklist (excluded). Set one, not both, and do not send an emptyallowedlist. A task whose lists rule out every vehicle is returned withVEHICLE_EXCLUDED. -
preferencesis soft. Each{ vehicle, violation_cost }entry is priced on its own: a task that lists several preferred vehicles pays for every listed vehicle that did not serve it, even if another one did. Aviolation_costis0to21474.83, and the entries of one task must also sum to at most21474.83. Do not combine a preference with alocked_vehiclefor a different vehicle: the preference can never be met and its cost is paid on every solve. -
assignment_costsis soft and prices the vehicle that did serve the task, wherepreferencescharges the ones that did not. Each{ vehicle, cost }entry is an absolute price in the objective’s currency, charged once when that vehicle serves the task on any of its shifts. A vehicle with no entry costs0, acostis0to21474.83, and a vehicle id that matches no vehicle is a400. A task rated badly on every vehicle is the first one dropped when capacity runs out. The charge is reported inscore.components.other. A problem that setsassignment_costsis solved as a whole, not decomposed.
eligible_vehicles or
locked_vehicle instead of a large violation_cost.
Common 400 cases: a vehicle id that matches no vehicle, in any of the three
fields; a locked_vehicle that eligible_vehicles rules out; allowed and
excluded set together; an empty allowed list; a vehicle listed twice in
preferences; a violation_cost that is negative or above the maximum.
The same fields are available on shipments[], where they apply to the pair.
Priority and optional tasks
Every task is must-serve unless it prices its own drop. Two fields control what happens when not everything fits; these are twojobs[] entries:
integer
default:"1"
A rank among must-serve tasks,
>= 1. When not all of them fit, the solver
keeps the summed priority of the served tasks as high as possible before it
looks at any cost. With every task at the default this means serving as many
as possible. It is a rank, not money, and never makes a task droppable. The
summed priority of the must-serve tasks left out is reported as
summary.score.unserved_priority.number
What leaving the task unserved costs, in the same currency as
objective.costs. This is the only way to make a task optional: it is
served while serving it costs less than the fee. Fees the plan chose to pay
are reported in score.components.drop_fees. On a shipment the fee applies
to the pair and is charged once. A task dropped for its fee is listed in
unassigned[] with category: "priced_out".400: priority: 0; priority together with
drop_fee.
Route limits
limits sits on the vehicle, and a shift can override max_distance_m,
max_route_duration_s and max_tasks in its own limits. This is a
vehicles[] entry:
max_route_duration_s caps the whole route span, the same quantity as the
response’s routes[].time.span_s: travel, service, waiting and depot
handling all count. max_tasks counts stops: a job is one, a shipment two.
max_leg_travel_time_s caps any single travel leg instead of the whole
route: the travel_s reported on each stop, including the leg from the
start to the first task and from the last task to end. Only travel counts
(setup, service and waiting do not), and speed_factor applies first. A leg
made free by ignore_start_leg or ignore_end_leg, or absent on an open
start or end, is exempt. It is set on the vehicle only, and must be between
1 and 2,147,483,647: 0 is a 400 with out_of_range, not “no cap”.
A task kept out by max_distance_m or max_route_duration_s is returned in
unassigned[] with VEHICLE_RANGE_EXCEEDED; one kept out by max_tasks
with ALL_ROUTES_BLOCKED.
The leg cap is enforced as reachability: a leg over the cap
is treated like a leg with no road connection. A task that can only be
reached over such a leg is returned in unassigned[] with
UNREACHABLE_LOCATION, and its message speaks of a missing road connection
and the coordinate even though the cap is the cause. On /evaluate, a leg
over the cap reports an unreachable violation. On a stop, the message gives
the leg’s travel time and the cap; on the route, for the leg to end, it
reads as a missing road connection.
Period limits
limits.periods[] caps a vehicle’s totals across all of its shifts in a
period. This is a limits object:
every: "day" or every: "week" (weeks run
Monday to Sunday), or one explicit from/to window with to exclusive.
Each shift counts toward the one period that contains its from. For a
recurring period the date is read in the UTC offset that from is written
with; see How days are counted.
Set exactly one of
every or from + to, and at least one maximum. A
from/to window must contain at least one of the vehicle’s shift starts; a
period inside a single shift is not supported. Anything else is a 400. The
third entry above blocks the vehicle on 3 April: max_tasks: 0 for the shifts
that start that day.
A task kept out by a period’s max_duration_s or max_distance_m is
returned in unassigned[] with VEHICLE_RANGE_EXCEEDED; one kept out by a
period’s max_tasks with ALL_ROUTES_BLOCKED.
On /evaluate, a plan over a period’s cap reports one period_limit
violation, on the route of the period’s first used shift.
Quotas
quotas[] is a top-level field of problem. Each entry caps how many stops
of the listed job_ids the listed vehicles serve together, for example
a contractor pool that shares one install quota. A quota with one vehicle is
a per-vehicle cap on those jobs. This is a quotas array:
id,vehicles,job_idsandmax_tasksare required.job_idstakes job and shipment ids.fromandtoare optional and go together. A stop counts when its service starts in[from, to)(arrival + wait_sin the response); no calendar day is involved, and a window can sit inside one shift. Without a window, every stop counts.- Stops count as
max_taskscounts them: a job is one stop, a shipment two. - The cap is hard: a job over it is left unassigned with
ALL_ROUTES_BLOCKED. - On
/evaluate, a plan over a quota reports onequotaviolation, on the route of its first used member shift.
400 cases: a duplicate quota id; an empty vehicles or job_ids
list; an unknown vehicle, job or shipment id; an id listed twice in one list;
a window with only one end; a from or to that is not a timestamp; a to
that is not after from; and a window that overlaps no shift of the quota’s
vehicles.
Relations
relations[] ties tasks to each other. There are nine types, and all nine are
hard: a relation has no cost field. This is a relations array:
sequence: members that share a route appear on it in the listed order; other stops may sit between them. It does not by itself put the members on the same route. Members are listed instops. A single entry instopspins that job as the first stop of its route.tag_order: orders tasks by tag instead of by id.tagslists at least two tags; on every route, every task taggedtags[0]precedes every task taggedtags[1], and so on. A tag that selects a shipment is a400.same_vehicle: members are served by the same vehicle, on any of its shifts. Select members withstopsor with onetag, not both.same_day: members are served on shifts whosefromfalls on the same calendar date, read in the offset eachfromis written with. The day belongs to the shift, not to the time of the stop; see How days are counted. Select members withstopsor with onetag, not both.same_route: members are served on one route: one shift of one vehicle (V2SAME_TRIP). Reloads within the shift do not split it. Select members withstopsor with onetag, not both. Jobs only: a shipment named instopsor carrying thetagis a400.synchronized: members start withinmax_wait_sseconds of each other, on any vehicles.max_wait_sis required and>= 0;0means exactly together. Add"on": "end"to synchronise the ends of service instead of the starts. Select members withstops, up to eight members. The schedule holds an early member back; the hold is reported on the stop ashold_s.time_lag: a directional gap between two jobs on any vehicles. The time fromfromtotomust lie betweenmin_sandmax_sseconds. Each end is astopplus an optionalon, which isstart(default) orendof service.min_sdefaults to0, an absentmax_smeans no upper bound, and both may be negative.from.stopandto.stopmust differ,max_smust not be belowmin_s, and both ends must be jobs: a shipment id or a shipment leg is a400. Chain more than two jobs with one relation per consecutive pair.compatible_vehicles: every two different vehicles that serve the members must be compatible undervehicles[].compatible_with. A vehicle without the field pairs with any vehicle; a list is a veto (neither list may exclude the other); a vehicle always pairs with itself, so list only its own id to pair it with no other. Members arestopsonly, 2 to 16, each also named by asynchronizedortime_lagrelation. Tasks left without a compatible placement are unassigned.all_or_none: either every member is served or none is. It sets no timing of its own. Members arestopsonly, at least two, each also named by asynchronizedortime_lagrelation. Overlapping relations chain: one on A and B plus one on B and C serves all three or none of them. The rule is switched off for a whole group, chained members included, once one of its served members has alocked_vehicleor sits inside a plan route’slocked_count./evaluatedoes not checkall_or_none: a plan that serves only some members reports no violation for it.
same_vehicle, same_day, same_route, time_lag and compatible_vehicles constrain the members that end up
assigned: an unassigned member imposes nothing on the others.
Common 400 cases:
- a task id that does not exist, or a
tagonsame_vehicle,same_dayorsame_routethat no task carries - a relation with no member selector, or with two of them (
stopstogether withtag) - a
synchronizedrelation withoutmax_wait_s, with a negative one, or with more than eight members - a job listed twice in one relation
- a job listed in more than one
same_vehiclerelation, more than onesame_dayrelation, more than onesame_routerelation, or more than onesynchronizedrelation (merge them into one); a job may sit in any number ofsequenceandtime_lagrelations - more than one
tag_orderrelation, fewer than twotags, a tag listed twice, or a task that carries more than one of the listed tags - a
time_lagwhose two ends are the same job, whosemax_sis belowmin_s, or that names a shipment - a
same_routerelation that names or selects a shipment - a shipment leg coupled to other tasks by a
synchronizedortime_lagrelation, directly or through asequencewith a member that is - a
sequencethat puts a delivery before its own pickup, or names a delivery alone - a
compatible_vehiclesrelation with fewer than 2 or more than 16stops(invalid_valueandlimit_exceeded), that names a shipment, or that names a job which nosynchronizedortime_lagrelation also names (bothnot_supported) - an
all_or_nonerelation with fewer than 2stops(invalid_value), that names a shipment, or that names a job which nosynchronizedortime_lagrelation also names (bothnot_supported) - a
vehicles[].compatible_withthat is empty (invalid_value) or names a vehicle that does not exist (unknown_reference) - an unrecognised
type, reported witherrors[].code: "invalid_type"
Shipments in relations
To relate a shipment, name one of its legs by its stop id: the leg’spickup.id or delivery.id, by default "{shipment}:pickup" and
"{shipment}:delivery". The relation then binds that leg like a job.
["s1:pickup", "j1", "s1:delivery"] in a sequence serves j1 between the
two legs. A leg cannot be coupled across routes: a leg named by a
synchronized or time_lag relation, or in a sequence with a member that
is, is a 400.
“Accepted and ignored” means the relation still binds its job members. A
shipment’s own rule, delivery after pickup on one vehicle, needs no relation.
To tie a shipment to a vehicle, use its
locked_vehicle or
eligible_vehicles.
Shipments
Ashipments[] entry is a pickup and a delivery served by one vehicle:
relations[] entry. load is carried
on the vehicle between the two legs.
Each leg accepts its own id, location (required), setup_duration_s,
service_duration_s and time_windows. Load is the shipment’s own load, not
a per-leg field. A leg id defaults to
{shipment}:pickup or {shipment}:delivery and is what the response reports
on that stop.
drop_fee, priority, skills, tags, eligible_vehicles,
locked_vehicle, preferences, service_factors and preferred_date sit on
the shipment and apply to the pair. target is measured on the delivery leg.
Driver breaks
Breaks are listed inshifts[].breaks. Each has a type: windowed,
triggered or fixed. This shifts[] entry
carries three of them:
Break in a time window
Awindowed break lasts duration_s seconds and must
start no earlier than the window’s from and no later than its to. It
is taken wherever the vehicle is at that moment. Give exactly one window.
location. It does not change the route: no
travel to that place is planned, and the break is still taken between two
stops of the route.
Break after driving time
Atriggered break with after: "drive" is taken once the driving
time since the last break, or since the start of the shift, exceeds
threshold_s. The count starts again after the break. This is the EU
561/2006 rule of 45 minutes after 4 h 30 of driving:
Break after duty time
Withafter: "duty" the count is driving time plus service time.
This break is taken after six hours of work:
Fixed off-duty interval
Afixed break is an interval with a set from and to. Everything on the
route happens entirely before from or entirely after to. It takes those
two fields only: no duration_s, no location.
Breaks in the response
A break appears on its route as a stop of typebreak, in sequence between
the stops it falls between:
arrival is when the break starts, departure when it ends, and service_s
its length in seconds. The stop also carries load_after when the request
uses capacity. It has no id and no location, also when the request set a
location on the break.
Break rules
Common400 cases:
duration_sorthreshold_sthat is not greater than0- a window or a
fixedbreak whosefromis after itsto - a field the break type does not define, such as
locationon afixedbreak (unknown_field)
400, not_supported):
- more than one
windowsentry on a break - an empty
windowslist on awindowedbreak locationon a triggered breakresets_accumulator: false(the count always starts again after a break)break_interruptible: falseon a job or shipment leg (the defaulttrueis accepted)
Multi-trip reloads
A vehicle may return to a depot mid-shift to empty or restock and start a fresh trip. Name the depots in the shift’sreloads:
reloadslistsdepots[].idvalues, each at most once. An unknown id is a400.- Capacity is enforced per trip. Each reload appears in the response as a
stop of type
reloadcarrying the depot id. max_tripscaps trips, not reloads: trips = reloads used + 1, across all reload depots of the shift. It must be at least1; without it, trips are not capped.- Whatever
max_tripssays, one shift reloads at one depot at most eight times. List more than one reload depot to go beyond that. Aplanyou send whose route reloads at one depot more than eight times is a400. - Each reload takes the depot’s
service_duration_sand is charged itscost_per_visit, reported inscore.components.depot_visits. shifts[].initial_loaddeclares stock already on the vehicle at shift start. It occupies capacity on the first trip only and is cleared at the first reload.
Travel and service time
These fields do not forbid anything on their own, but they change how long a route takes, and so whether time windows and shift ends can be met.number
default:"1.0"
Travel time is divided by this factor: above
1.0 is a faster vehicle,
below is slower. Must be greater than 0 and at most 5.number
default:"1.0"
Service time is divided by this factor: above
1.0 is a faster worker. Same
range as speed_factor. shifts[].service_factor overrides it for one
shift. It scales a task’s service_duration_s only, never setup time, depot
handling, waiting or breaks.array
On a job or a shipment: per-vehicle overrides for one task, as
{ "vehicle": "ann", "factor": 1.25 } entries. The factor multiplies the
vehicle’s or shift’s own factor:
scheduled service time = service_duration_s / (vehicle factor × task factor).
On a shipment it scales both legs. A vehicle not listed works at its own
rate. Each factor must be greater than 0 and at most 5. An unknown
vehicle id, or a vehicle listed twice, is a 400.integer
On a job or shipment leg: time paid once on arrival at a location, on top of
service_duration_s. It is skipped when the previous stop on the route is at
the same location, so co-located stops share one setup.stops[].service_s, which differs from service_duration_s whenever a factor
applies.
Grouping and preferred dates
Two soft rules shape a plan without restricting it.objective.grouping keeps nearby tasks together. Tasks within radius_m of
each other form a group, and each time a route leaves a group and comes back
it pays cost_per_reentry. This is an objective object:
cost_per_reentry is required and must be between 0 and 1000. radius_m
defaults to 180 and must be positive. distance is straight_line
(default) or road. The total is reported in score.components.grouping.
preferred_date on a job or shipment names the day it should be served on in
a multi-day problem. It takes a full timestamp. Its calendar date, in the
offset it is written with, is matched against the calendar date of each
shift’s from in that value’s own offset, so passing the intended shift’s
from is always correct, and a local midnight such as
2026-04-02T00:00:00+02:00 is 2 April. See
How days are counted.
Serving the task on another day costs objective.non_preferred_date_cost
once; with that cost unset or 0 the field has no effect. A task is never
left unassigned to honour its preferred date.
Reachability
When the road network has no connection between two locations, no route may use that leg. The rule has no request field of its own; a vehicle’slimits.max_leg_travel_time_s adds to
it by ruling out every leg that takes longer than the cap. A task that can
only be reached through such a leg comes back unassigned with
UNREACHABLE_LOCATION: check its coordinate, which is
[longitude, latitude], and any leg cap on the vehicles. See
Distance matrices for how travel
data is looked up.
What a broken hard rule looks like
A solve response never contains a violated hard rule. A task that cannot be placed is listed inunassigned[]:
categorysays what kind of problem it is:impossible(no vehicle can ever serve the task),blocked(it does not fit alongside the tasks the plan serves),priced_out(itsdrop_feewas cheaper than serving it) orsearch_limit(a slot exists that the search did not use in its time budget).reasons[].codenames the rule. The sections above give the code for each rule.relaxations[]suggests the smallest change that would let the task fit, when one exists.
- One code per task. When different rules block different vehicles, the
code names one of them, so treat it as the place to start looking. A mix
that no single code describes is reported as
ALL_ROUTES_BLOCKED, and capacity on some vehicles plus time windows on others asCAPACITY_AND_TIME_WINDOW. - A window no shift overlaps. The code is
TIME_WINDOW_VIOLATEDwith categoryimpossible. Check the dates and UTC offsets of the window and the shifts. - Relations have no code of their own.
same_vehicleandsame_daynarrow a task to the vehicle or day its already-placed partners use, and the code then names the rule that blocks it there. When no other rule is involved, and for a task kept out bysequence, the code isALL_ROUTES_BLOCKED. priced_outandsearch_limitshare the codeSOLVER_LIMIT. Branch oncategoryto tell them apart.
relaxations[] entry has five fields:
The list is empty for rules with no such fix, for example a skills mismatch.
To check a plan you built yourself, send it to
/v3/routing/evaluate. There, broken hard rules are
reported as violations[] on the stop or route instead.
The V3 API model
How the full request is structured.
Objective function
How the soft rules above are priced and ranked.
Errors
The
400 error body, item codes and pointers.API reference
Every field of the request and response schemas.