V3 is in beta. General availability (GA) is planned for January 2027.
problem key;
a minimal one is problem.jobs and problem.vehicles.
This page describes the request and response of
POST /v3/routing/solve.
/v3/routing/evaluate and
/v3/routing/suggest take the same problem object.
The schema is strict: a field the schema does not define, such as an old
field name or a typo, is rejected with HTTP 400 and an unknown_field item.
Its message names the field and its pointer locates it: the key itself, or
the object that contains it. See Errors.Design principles
- Few orthogonal primitives. Eligibility, skills, and sequencing are each
a single field. A pickup-then-delivery is its own
shipments[]entry type, not a flag on a job. - Everything soft is money. Lateness costs a rate per hour, dropping a task costs a fee, a non-preferred vehicle costs an amount. That is one unit to trade against, with an itemised cost breakdown in every response.
- Penalties live on the thing. A task’s drop price (
drop_fee) and preferences sit on the task object itself: read one job and you see what it costs to bend each rule for it. - Strict schema, named rejections. Unknown fields are
400s with anunknown_fielditem. Fields that are specified but not available yet are400s with anot_supporteditem, rather than being ignored.
A request at a glance
The smallest valid request serves everything and minimises travel cost:problem, plan, and
options. plan is an optional warm start (see
Warm-starting with plan) and options is
covered under Options and travel.
problem holds up to eight fields. vehicles and at least one job or
shipment are required; the rest are optional.
Conventions:
- Durations are seconds (
_s) and distances are metres (_m). - Timestamps are ISO 8601 / RFC 3339 instants. Always include a UTC offset
(
+02:00orZ): a timestamp without one is accepted and read as UTC, not as local time. Timestamps in a response are always UTC with aZsuffix. - Coordinates are
[longitude, latitude]. - Money is a plain number in your own currency.
Jobs
A job is a unit of work at one location, such as a delivery, pickup, service call, or inspection. Its visit fields (location, service_duration_s,
time_windows) sit directly on the object.
string
required
Unique across
jobs and shipments. Echoed as routes[].stops[].id and
unassigned[].job_id.Place
Where the job is performed:
{ "coordinate": [lon, lat] } (GeoJSON order),
or the bare [lon, lat] array as shorthand. Omit location for a
virtual (location-less) job: it still has service duration, time
windows, demand, and skills, but requires no travel. A job’s location is
always an inline coordinate; only a shift’s start/end can reference a
depot by id. See Vehicles and shifts.integer
Seconds paid once on arrival at a new location, distinct from
service_duration_s and skipped when the previous stop in the route is
already at this location. Not scaled by service_factor.integer
Nominal on-site service time in seconds. The serving vehicle’s
service_factor scales it, so the response’s stops[].service_s may
differ from the value you sent.array
One or more
{ "from", "to" } windows (ISO 8601). Hard by default: a job
that cannot be reached inside any of its windows is left unassigned. Two
optional per-window fields:per_late_hourmakes this window soft on its late side: arriving aftertois allowed and priced at this rate per hour, in place of the fleet-wideobjective.costs.per_late_hour. Must be> 0, and only on a task with exactly one window.costis a flat price for being served in this window rather than one of the task’s other windows. It grades preference among alternatives (the task needs at least two windows) and does not soften any of them.
object
Load that goes onto the vehicle at this visit, as named non-negative
amounts such as
{ "weight": 30, "volume": 2 }. It stays on board until
the next reload or the end of the route, so load_after rises at the stop.
Use it for goods you collect. For goods you drop off, use delivery.Dimensions are matched against vehicles[].capacity by name; order never
matters. A vehicle whose capacity map omits a dimension has capacity 0
in it; a vehicle with no capacity at all is not capacity-limited. A
dimension name that no vehicle lists is therefore not rejected: check for
typos if a job comes back unassigned on capacity.object
Jobs only. Load that comes off the vehicle at this visit, having been
loaded at the depot, as named non-negative amounts such as
{ "weight": 10 }. The vehicle leaves each trip carrying the sum of that
trip’s delivery quantities, so load_after starts at that sum and falls
at each stop. A job can set both fields: delivery for what it drops off
and demand for what it collects.capacity:
On a route that only drops off, or only collects, the peak load is the same
total with either field, and only the direction of
load_after differs. The
choice matters when one route mixes the two: a drop-off frees room for a
later collection only when it is modelled as delivery. See
Capacity.
boolean
default:"true"
Omitted or
true = must serve. A must-serve task that cannot be placed
does not fail the request: it is returned in unassigned[] with a reason
and summary.status becomes partial. false is only accepted together
with drop_fee; without it the request is a 400.number
What leaving this task unserved costs, in the objective’s currency, and the
only way to make a task droppable: the task is served whenever serving it
is cheaper than the fee. Fees paid are reported in
score.components.drop_fees. Cannot be combined with mandatory: true or
with priority (400).integer
default:"1"
Importance of a must-serve task when not all of them fit. The solver
maximises the summed
priority of served tasks before it considers any
cost, so a higher-priority task is kept ahead of lower-priority ones. Must
be >= 1. It is a rank, not money, and never makes a task droppable. Use
drop_fee for that; setting both is a 400. The summed priority of
must-serve tasks left out is reported as score.unserved_priority.array
Hard capability requirements:
[{ "name": "fridge" }], each with an
optional min_level (default 1). The serving vehicle must provide every
named skill at that level or higher. For a soft affinity use tags with
the vehicle’s preferred_tags, or preferences (below).object
Hard filter:
allowed (whitelist, omit = all vehicles eligible) or
excluded (blacklist), mutually exclusive.string
Hard-pins the job to one vehicle id; survives re-optimisation. Stronger
than
eligible_vehicles (a set filter) and hard, unlike preferences.array
Soft vehicle affinity:
[{ "vehicle": "v2", "violation_cost": 800 }].
Each entry prices independently: a job listing several preferred
vehicles pays every entry whose vehicle didn’t serve it.array of strings
Free-form labels used for relation
group matching and, when a shift
scopes itself via serves_tags (hard) or a vehicle prices preferred_tags
(soft), for tag matching. A task matches when it carries any one of the
listed tags, unlike skills, where all must be provided.object
Soft point deadline:
{ "at": "...", "event": "arrival", "per_late_hour": 60.0 }.
event is arrival (default) or departure (the end of the visit).
Reaching event after at is priced, never infeasible, and an early
arrival never waits for it. Independent of
objective.costs.per_late_hour. On a shipment, measured on the delivery leg.string (ISO 8601 instant)
Soft preference for which calendar day (UTC) a task lands on in a
multi-shift problem, priced by
objective.non_preferred_date_cost. Has no
effect while that cost is unset or 0.array
Per-vehicle work-rate overrides for this task:
[{ "vehicle": "v1", "factor": 1.25 }], multiplied with the serving
vehicle’s or shift’s service_factor.Shipments
A shipment is a bound pickup→delivery pair with its ownshipments[] entry
type. The solver guarantees the pickup precedes the delivery on the same
route; carried demand counts against capacity only between the two legs.
pickup and delivery each have their own required location, plus
setup_duration_s, service_duration_s, time_windows, and an optional
stop id (default "{shipment}:pickup" / "{shipment}:delivery"). Every
other job field (demand, mandatory, drop_fee, priority, skills,
eligible_vehicles, locked_vehicle, preferences, tags, target,
preferred_date, service_factors) is shared and applies to the pair as a
whole: one droppability and eligibility decision, one drop_fee charged
once.
On a shipment, delivery is the delivery stop object shown above. It is not
the jobs[].delivery load field, which exists on jobs only.
Shipments take part in relations in a limited way, and they change how the
response counts tasks. See Relations and shipments
and Counting with shipments.
Vehicles and shifts
A vehicle carries capacity, skills, a cost model, and one or more shifts. The shift is where the working window and start/end locations live. The fleet is onevehicles[] entry per physical vehicle.
string
required
Unique across
vehicles[]. Echoed as routes[].vehicle.string
default:"car"
Routing profile for this vehicle’s travel times and distances:
"car",
"truck", or "bike". Any other value is a 400.object
Named dimensions, matched by name against job
demand and delivery and
shipment demand.array
Capabilities the vehicle provides:
[{ "name": "fridge", "level": 2 }]
(level optional, default 1). Always objects: a bare string is
rejected. A shift can replace the list for its own day with
shifts[].skills.array
Soft:
[{ "tag": "north", "violation_cost": 25.0 }]. Serving a task that
lacks a preferred tag incurs that entry’s cost; entries apply
independently.object
Hard caps:
max_distance_m and max_route_duration_s per route
(whole-route span: travel, service, and waiting all count, the same
quantity as routes[].shift_duration_s), and periods[] for caps across
all of the vehicle’s shifts in a day, a week, or an explicit window. See
Constraints.object
This vehicle’s own cost terms, next to the fleet-wide rates in
objective.costs:per_travel_kmreplaces the fleetobjective.costs.per_travel_kmfor this vehicle (0makes its distance free).fixed(once for each shift the vehicle is used on),per_stop(per served stop), andper_route_hour(per hour of the route’s span) are charged on top of the fleet time and distance rates and reported inscore.components.fixed. Set at most one of these three per vehicle; a second is a400(not_supported). Whichever you set takes the place of the fleetobjective.costs.per_vehiclefor this vehicle.per_travel_kmcombines freely with whichever of the three you picked.
per_route_hour is added to the fleet rate, a vehicle with
"per_route_hour": 50 under the default fleet rate of 35 per hour costs
85 per hour of route span. To pay only per-vehicle hourly rates, set
objective.costs.per_route_hour to 0.number
default:"1.0"
Travel-speed multiplier,
(0, 5]. Effective travel time =
travel_time / speed_factor, so above 1.0 is faster.number
default:"1.0"
Work-rate multiplier for
service_duration_s only, (0, 5]: effective
service time = service_duration_s / service_factor. Never applied to
setup_duration_s, depot or reload handling time, waiting, or breaks. A
shift may override it for its own day via shifts[].service_factor.string
required
1 to 128 characters, unique within the vehicle. Echoed as
routes[].shift.string (ISO 8601 instant)
required
Start of the working window.
string (ISO 8601 instant)
required
End of the working window. The route, including any unload at
end, must
finish by to, or by to + max_overtime_s.object
Where the shift starts: exactly one of
location (an inline place) or
depot (a depots[].id), plus an optional service_duration_s
overriding the handling time there. Omit start for a route that is open
on its start side.object
Where the shift ends, in the same shape as
start. Omit end for a route
that is open on its end side.boolean
default:"false"
When
true, the leg from start to the first task costs nothing and takes
no time, while the start stop is still reported. Use it when the
vehicle’s commute should not count.boolean
default:"false"
The same for the leg from the last task to
end.integer
Seconds past
to the vehicle may run. Overtime is priced by the
fleet-wide objective.costs.per_overtime_hour; while that rate is unset,
overtime is allowed at no cost.integer
Maximum stops in this shift: a job counts as one stop, a shipment as two
(pickup and delivery).
array of strings
Hard scope: when set, this shift serves only tasks carrying at least one
of these tags (untagged tasks remain serveable by every shift).
array
Scheduled driver breaks (
floating with a window or a drive/duty
trigger, or fixed). They appear in the response as break stops. See
Constraints.array of strings
Depot ids (from
depots[]) the vehicle may return to mid-shift to empty
its load and start a fresh trip. Each visit takes that depot’s
service_duration_s and is charged its cost_per_visit, and appears in
the response as a reload stop. Cap the number of trips with max_trips.
See Constraints.object
Load already on the vehicle at shift start, per named dimension. It
occupies capacity on the first trip only.
Depots
problem.depots[] lists the places a shift can start from, end at, or
reload at, referenced by id from shifts[].start, shifts[].end, and
shifts[].reloads:
id, a location, an optional service_duration_s
(handling time per visit: the load before a shift that starts there, the
unload after a shift that ends there, and each reload) and an optional
cost_per_visit (charged on reload visits only).
Relations
A singlerelations array expresses every ordering, grouping, and timing
rule between tasks. Each relation is a tagged object with a type, and every
relation is hard.
same_resource, same_day and time_lag constrain the members that end up
assigned: an unassigned member imposes nothing on the others. On
evaluate, a plan that breaks a relation reports a
violation named after its type; the first-stop pin of a single-entry
ordered relation reports as first_job.
These five are the complete set. Any other
type value is a 400 with an
invalid_type item. See Constraints
for the selection rules and the combinations that are rejected.Relations and shipments
Relations act on jobs. What happens when a relation names a shipment depends on the type:Objective
The whole block is optional. Omit it and the solver serves every task it can, then minimises travel cost at the default rates.-
costs: fleet-wide rates.per_travel_kmis charged per kilometre driven. Default 0.20.per_route_houris charged per hour of each route’s span from start to finish (travel, service, waiting, and breaks;routes[].shift_duration_s). Default 35.per_wait_hourandper_overtime_hourare extra rates for waiting time and for time past a shift’sto. Default0.per_late_hourprices lateness on every time window that does not carry its owntime_windows[].per_late_hour, making those windows soft on the late side. Omit it to keep windows hard; an explicit0is a400.per_vehicleis a flat charge for each route that serves at least one task (a vehicle has one route per shift). Default0.currencyis an informational label echoed asscore.currency. It does not convert anything.
-
non_preferred_date_cost: flat charge for serving a task on a day other than itspreferred_date. -
grouping: keeps tasks that lie withinradius_mof each other consecutive in a route by chargingcost_per_reentry(at most1.0) each time a route leaves such a group and returns. Present = enabled.radius_mdefaults to180.distanceselects how the radius is measured:straight_line(default) orroad. -
minimize_vehicles: a boolean that is accepted but does not change the plan on its own. With or without it, the number of vehicles used is whatever minimises total cost. To consolidate work onto fewer vehicles, put a price on using one:costs.per_vehiclefor the whole fleet, orvehicles[].cost.fixedper vehicle. Sendingminimize_vehicles: truetogether withcosts.per_vehicleis a400; omit the flag or sendfalse.
drop_fee, preferences[].violation_cost, target.per_late_hour,
time_windows[].per_late_hour), not in this block. See
Objective function for how plans
are ranked, and Constraints for every hard and
soft rule.
Options and travel
options sits next to problem at the top level of a solve request:
integer
default:"1"
Search time budget in whole seconds. Maximum
300; a larger value is a
400. 0 is raised to a 100 ms minimum. This is search time only: the
travel-matrix fetch and any wait for capacity come on top.integer
default:"42"
Random seed for the search. Repeated runs with the same seed on the same
hardware usually return the same plan. Omitting
seed is the same as
sending 42, so to explore other plans you have to send different values.string
simplified or full. When set, each route carries its road geometry as
routes[].polyline (an encoded polyline, precision 5). Omitted = no
geometry./v3/routing/suggest has its own options (the tasks to place and how many
suggestions to return). See Suggest.
/v3/routing/evaluate takes no options. See
Limits for request size and count caps, and the
Performance guide for choosing a
time budget.
problem.travel controls where travel times and distances come from:
string
default:"tomtom"
tomtom uses typical traffic for the departure time. osm uses free-flow
times with no traffic. tomtom_real_time is not enabled and returns
403 feature_not_enabled.string (ISO 8601 instant)
The instant the traffic lookup is made for, such as tomorrow’s 08:00 to
plan with morning-rush travel times. Omitted = traffic at the time of
the request. Rejected with
400 alongside engine: osm.Response
A solve returns asummary, per-stop routes, the same routes as a plan,
and unassigned tasks with structured reasons. The example below serves two
jobs on one route with a driver break and a mid-shift reload, and leaves a
third job unassigned. It is a standalone illustration, priced at the default
rates (35 per hour, 0.20 per km) with no per-vehicle or depot cost:
Summary
string
solved when unassigned[] is empty, infeasible when jobs_assigned is
0, and partial otherwise. For a request with jobs only that reads:
every job is on a route, none is, or some are. All three are HTTP 200;
check status and unassigned[] rather than the status code. With
shipments, see Counting with shipments.integer
The number of entries in
routes[]. A route is one vehicle on one shift,
so a vehicle that works two shifts counts twice.integer
For a request with jobs only, the number of jobs on a route.
integer
The number of entries in
unassigned[]: one per unassigned job or
shipment.integer
The sum of
routes[].distance_m, in metres.integer
The sum of
routes[].duration_s, in seconds. This is driving time only:
it excludes service, waiting, and breaks, and it is not what the time
cost component is charged on.object
violations, unserved_priority, cost, currency, and components.
violations is always 0 on a solve. unserved_priority is the summed
priority of must-serve tasks left unassigned (a task that priced a
drop_fee counts in components.drop_fees instead). Every component is
always present and the components sum to cost. currency appears only
when you sent objective.costs.currency. See
Objective function.integer
Wall-clock time the request took, in milliseconds.
integer
The number of search iterations completed within the time budget.
Counting with shipments
jobs_assigned counts stops, not tasks. It is the number of jobs plus twice
the number of shipments in the request, minus jobs_unassigned. A served
shipment therefore adds 2 (its pickup and its delivery), and an unassigned
shipment still adds 1. For example, a request with 3 jobs and 2 shipments
that leaves one shipment unassigned returns jobs_unassigned: 1 and
jobs_assigned: 6.
When the request contains shipments:
jobs_assigned + jobs_unassignedis not the number of tasks you sent. To count the tasks served, subtract the length ofunassigned[]from the number of jobs and shipments in the request.statusispartial, notinfeasible, when nothing could be served, becausejobs_assignedis never0. An emptyroutes[]tells you that nothing was served.solvedkeeps its meaning:unassigned[]is empty.
summary of an evaluate response counts the same
way.
Routes
routes[] has one entry per route that serves at least one task. A shift
with no work has no entry.
string
The vehicle id from the request.
string
The id of the shift this route runs on.
integer
Total distance driven on the route, in metres.
integer
Driving time only, in seconds: the sum of the stops’
travel_time_s.integer
The route’s span in seconds, from the
start stop’s arrival to the
end stop’s departure. It includes service, waiting, and breaks, and is
what per_route_hour is charged on.integer
Seconds the route runs past the shift’s
to; 0 when it finishes within
the shift.object
The highest load reached on the route, as a named map. Absent when the
request names no load dimension.
string
The route’s road geometry as an encoded polyline (precision 5). Present
only when you set
options.polylines, and absent for a route whose
geometry could not be produced. distance_m and duration_s come from
the travel data the plan was built on, so rely on them if they differ from
the geometry.Stops
string
One of
start, job, pickup, delivery, break, reload, end.
Every route has one start and one end. pickup and delivery are the
two legs of a shipment.string
On a
job stop, the job id. On a pickup or delivery stop, that leg’s
own id, or "{shipment}:pickup" / "{shipment}:delivery" when it has
none.string
The
depots[].id of the stop: on every reload stop, and on a start or
end stop when the shift names a depot there.array
The stop’s coordinate as
[longitude, latitude]. Absent on break stops
and on the start or end stop of a route that is open on that side. A
virtual job reports the coordinate of its nearest located neighbour.array
The same point snapped to the road network, when available.
string (ISO 8601 instant)
Arrival at the stop, before any waiting. Service starts at
arrival + wait_s. Always UTC with a Z suffix, whatever offset the
request used.string (ISO 8601 instant)
Departure from the stop, in UTC.
integer
Seconds spent waiting before service starts: for the time window to open,
and for any hold reported in
hold_s.integer
The part of
wait_s spent holding for a synchronized or time_lag
partner on another vehicle. Present only when it is above 0.integer
Time spent at the stop, in seconds. On a task stop this is the effective
service time, after the vehicle’s
service_factor, so it can differ from
the service_duration_s you sent; setup_duration_s is not included. On
a start, end, or reload stop it is the depot handling time, and on a
break stop the length of the break.integer
Driving time from the previous stop, in seconds.
integer
Distance from the previous stop, in metres.
integer
Seconds the arrival is past the latest arrival the task’s time window
allows;
0 when on time. A target is reported separately, in
target_lateness_s.integer
Seconds past the task’s
target.at, measured at arrival or departure as
target.event says; 0 when met. Present only on a stop whose task has a
target. For a shipment it is on the delivery stop.object
The load on the vehicle after this stop, as a named map. Absent when the
request names no load dimension.
routes[].violations and routes[].stops[].violations appear only in an
evaluate response. A solve response never has them.
Plan
object
The same routes in the request’s
plan vocabulary: plan.routes[], each
with vehicle, shift, and stops[] of type job, pickup, delivery,
or reload. Feed it straight into evaluate, suggest, or a warm-started
solve. See Warm-starting with plan.Unassigned tasks
unassigned[] has one entry per job or shipment that is not on a route. It
is an empty array when everything is assigned.
string
The id of the unassigned job, or the shipment id for a shipment.
string
Always present, one of:
impossible (no vehicle can ever serve it),
blocked (a vehicle could serve it alone, but not alongside the tasks the
plan serves), priced_out (paying its drop_fee was cheaper than serving
it), or search_limit (a worthwhile placement exists but was not found
within the time budget, the one case a longer time_limit_s can help).array
{ code, message } objects from a closed set of codes
(TIME_WINDOW_VIOLATED, CAPACITY_EXCEEDED, SKILLS_MISMATCH, …). Branch
on code; message is for people. See
Reason codes.array
Best-effort suggestions for a change that would let the task be served.
Empty when no simple relaxation exists. Each entry has:
op:extend_time_windoworreduce_demand.amountandunit: the size of the change.unitissfor seconds onextend_time_windowandloadonreduce_demand.field: a label for what to change,/jobs/{id}/time_windowsor/jobs/{id}/demand.suggestion: the same advice as a sentence for people.
Pointers and field paths
Two fields look like paths into the request. They follow different rules:
Treat
field as a label: look the task up by job_id and apply op and
amount. See Errors for the pointer rules.
Warm-starting with plan
The request-level plan seeds the search from an existing plan
(re-optimisation) instead of constructing from scratch:
vehicle and a shift id (the vehicle’s shift this plan
fills), an ordered stops list, and an optional locked_count: how many
leading stops the search must not move (a dispatched prefix; cannot be
combined with a reload stop on the route). A task stop has a type of
job, pickup, or delivery and an id, as in
{ "type": "job", "id": "job-1" }. A shipment leg’s id is its own id, or
"{shipment}:pickup" / "{shipment}:delivery" when it has none. A reload
stop is { "type": "reload", "depot": "..." }, naming a depot from that
shift’s reloads and splitting the route into a new trip. Start, end, and breaks are
placed by the solver and are never listed. plan is the same shape a
solve response returns, so result.plan goes back in unchanged.
A plan that names an unknown vehicle, shift, depot, or stop id, lists a stop
twice, or is otherwise malformed is rejected with 400. The item’s
pointer is /plan/routes/{i} or, for a stop-level problem,
/plan/routes/{i}/stops/{k}.
plan is a hint, not a guarantee on solve:
- An unlocked stop that violates a hard constraint (capacity, skills,
eligibility, a limit, or a hard time window) is taken out of its position.
The search reinserts it wherever it fits, or it appears in
unassigned[]with a reason andstatusispartial. - A locked stop (inside
locked_count) that violates a hard constraint can be neither moved nor dropped, so the request fails:400, item codeinvalid_value, pointer/plan, with a message naming the stop, the vehicle, and the rule it breaks. - Time windows are hard by default, so a stop the plan serves after its
window closes is treated as violating. Price lateness
(
time_windows[].per_late_hourorobjective.costs.per_late_hour) and the stop is kept.
?validate_only=true does not cover
them.
To score a plan as it stands, without searching for a better one, send it
to POST /v3/routing/evaluate. It fetches the travel matrix like a solve
does, then returns the plan’s timing, loads, and score, with each broken
rule listed as a violations[] entry on the stop or route instead of
dropping the stop. See Evaluate.
Errors
The three endpoints report errors in one JSON body. A401 and a 429 are
the exceptions: they are answered before the request reaches the routing
service and can have a different body or none, so branch on the HTTP status
for those two. See Errors. A request that does not
parse or does not validate is HTTP 400:
errors[]has one item per problem found.errors[].codesays what kind of problem it is (unknown_field,missing_field,invalid_type,invalid_value,not_supported,out_of_range,limit_exceeded, …).errors[].pointer, when present, is a JSON Pointer into the request body you sent, such as/problem/depots/0/service_duration_s,/plan/routes/0/stops/2, or/options/time_limit_s. It usually ends at the offending key and sometimes at the object that contains it; the message always names the field. It is a different notation fromrelaxations[].field: see Pointers and field paths.- With one item, the top-level
messagerepeats that item’s message; with several it reads"{n} problems in request". - Branch on
codeanderrors[].code; the messages are for people.
See Errors for every item code, retry guidance, and
authentication failures.
Constraints
Every constraint field, hard vs. soft, with examples.
Evaluate
Score an existing plan and list the rules it breaks.
Suggest
Rank insertion positions for new tasks in an existing plan.
Objective function
How plans are ranked and how
score is built.Solution quality
How to judge a plan from the response fields.
Distance matrices
Where travel times and distances come from.
Migration from V2
Field-by-field mapping from the V2 VRP API.
API reference
Full request and response schema for every endpoint.