Skip to main content

Overview

POST /v3/routing/suggest answers one question: given the plan I already have, where can this new task go, and what does each option cost? You send the problem, the plan you have committed to, and the ids of the tasks to place. For each task you get back a ranked list of feasible insertion positions, best first. Use it when a new order arrives during the day and a dispatcher (or your own code) should pick a slot, rather than letting the solver rearrange routes that are already on the road. The committed plan is never changed: the API holds no state, so nothing happens until you apply an option yourself.

The request

The body has three required parts:
object
required
The same problem object as on /v3/routing/solve: jobs, shipments, vehicles, depots, relations, objective, travel. It must contain the tasks already in the plan and the tasks you want to place.
object
required
The committed plan, in the shape every solve and evaluate response returns as plan: one routes[] entry per vehicle shift, each with vehicle, shift and an ordered stops[] list. The tasks to place must not be in it. A route’s locked_count marks stops that are already dispatched: no option lands before them.
array of strings
required
The ids of the jobs or shipments to place. At least one. Each must exist in problem, appear once, and be absent from plan.
integer
default:"5"
Maximum number of ranked options per task. 0 returns every feasible placement. Positive values above 100 are lowered to 100.
integer
Search budget in seconds, at most 300. 0 is raised to 100 ms. Omit it and the search runs to completion, which is what you want for a single job. The clock starts when the search starts, so fetching travel times does not eat into it.
A task in problem that is in neither plan nor options.tasks is treated as unassigned context and gets no suggestions.

Example

One truck already serves delivery-1 and delivery-2. A third order, delivery-3, comes in and must be served between 08:00 and 11:00 local time.

The response

The figures are illustrative; yours depend on live travel times. Three positions exist on this route and only two come back: inserting delivery-3 after delivery-2 would reach it after its window closes, so that position is not feasible and is not listed. suggestions[] has one entry per task you named, in the order the tasks appear in problem (jobs first, then shipments). Tasks tied together by a relation share one entry: see Shipments and related tasks.
string
The task this entry is about.
array
Feasible placements, cheapest first. Empty when there is none; reason then says why.
string
Present only when options is empty. One of three fixed sentences: see When a task gets no options.
boolean
One flag for the whole response. true when options.time_limit_s cut the search short for any task, or when an empty list for a multi-task unit is inconclusive. The lists may then be incomplete or miss the best option. A single job with no time_limit_s is never truncated.
integer
Wall-clock time of the whole request, in milliseconds.

When a task gets no options

An empty options list is a 200, not an error. The entry’s reason is one of these three sentences: reason is written for people and logs. In code, branch on the empty options list and on truncated: when truncated is false, an empty list is final. truncated covers the whole response, so with several tasks in one request it does not say which task was cut short; send one task per request when you need that.

Reading an option

extra_cost and extra_duration_s measure different things. In the example the first option adds more driving than the second, yet costs less: the truck used to wait at delivery-1 for the window to open, and the detour fits inside that wait, so the route does not end any later. See Objective function for what the cost is made of.

Applying a suggestion

Suggest only reports options. To act on one, edit the plan yourself and send it back:
1

Find the route

Take the option’s vehicle and the shift at shift_index in that vehicle’s shifts[], and find the plan.routes[] entry with that vehicle and shift id. If the vehicle was unused, add a new route entry for it.
2

Insert the stop

Insert { "type": "job", "id": "<job_id>" } so that it becomes task stop number position of the trip. On a route without reloads that is index position in stops. For the first option above, plan becomes:
3

Confirm or re-optimise

Send the edited plan to /v3/routing/evaluate to get the full timeline and cost of the new plan, or to /v3/routing/solve as a warm start if the solver may also rearrange the other stops.
Each task is ranked on its own against the same committed plan. If you name two tasks and apply an option for the first, the options for the second were computed without it: call suggest again with the updated plan.
  • Shipments. Name the shipment id in options.tasks. Its pickup and delivery are placed together: the option’s top-level fields describe the pickup, and assignments[] lists both legs with a type of pickup or delivery. Either both legs are in plan or neither is.
  • ordered, same_resource and same_day relations. Tasks tied together by one of these relations are placed as one unit and get one entry, named after the first new task of the unit.
  • synchronized and time_lag relations. A task that takes part in one of these cannot be suggested; the request is rejected with a 400.
  • Reloads. An option can land in any existing trip of a multi-trip route, but suggest never adds a new reload trip.

Errors

Suggest returns the same error body as the other endpoints. The cases specific to it: ?validate_only=true checks problem and options without fetching travel times or searching. It does not check that plan fits the problem; only the real call does.

Evaluate

Score the plan after you apply an option.

Errors

The error body, every code, and what to do about each.

The V3 API model

The problem and plan objects in full.

API reference

Request and response schema for every endpoint.