Overview
POST /v3/routing/evaluate takes a problem and a plan and tells you what that plan is worth. It computes arrival times, loads and the cost breakdown exactly as a solve would, and lists every hard constraint the plan breaks. It never moves a stop.
Typical uses:
- Check a plan a dispatcher edited by hand before you commit it.
- Compare your current planning against a solve result on the same cost model.
- Get the full timeline after you apply a suggestion.
- Re-score yesterday’s plan against today’s constraints.
The request
object
required
The same
problem object as on /v3/routing/solve. See the V3 API model.object
required
The plan to score:
routes[], one entry per vehicle shift that serves something.options: there is no search, so there is nothing to tune. An options key is rejected as an unknown field.
The plan shape
The value ofplan:
A stop is one of:
Start, end and break stops are not listed; they are placed for you. Every solve and evaluate response returns its routes in this shape as
plan, so a result’s plan can be sent back unchanged.
A task you leave out of every route is not an error. It is reported in unassigned[], with a category that says whether it could be added: see What unassigned means on evaluate.
Example
The quickstart’s two deliveries, withdelivery-1 due by 11:00 local time and a hand-made plan that visits delivery-2 first:
The response
The response has the same four parts as a solve response:summary, routes, plan and unassigned. The figures below are illustrative; yours depend on live travel times.
delivery-1 at 09:29:20 UTC, 1,760 seconds after its window closed at 09:00:00 UTC. The plan is still scored in full, so you can see what it would cost and how late it would be.
How it differs from a solve response
Everything else, including the stop timeline and
score.components, reads exactly as on solve. See the Quickstart for the stop fields and Objective function for the cost breakdown.
In a request that contains shipments, summary.jobs_assigned counts a shipment in the plan twice, once for each of its stops, and a shipment left out of the plan once. Use the length of unassigned[], which has one entry per job or shipment, to count what the plan leaves out.
What unassigned means on evaluate
Evaluate never adds a task to your plan. For each task the plan leaves out,unassigned[].category tells you whether it could be added to the plan as it stands:
A task that fits is reported with reason code
SOLVER_LIMIT and a message about a time budget. That wording comes from solve; evaluate has no time budget. To place such a task, add it to plan or ask /v3/routing/suggest for its best position. The reason codes are listed in Errors.
Violations
Each violation is{ "type", "message" }. type is a stable string to branch on; message is for people.
New types can be added as new constraints ship. Handle the ones you care about and treat anything else as a generic violation.
A time window priced as soft, through
time_windows[].per_late_hour or objective.costs.per_late_hour, is not a violation when it is missed. The lateness shows up in the stop’s lateness_s and in score.components.lateness instead.What evaluate checks, and when
Evaluate needs real travel times to compute a timeline, so it fetches them the same way solve does. What it skips is the search.- The request is validated. Schema, references and limits are checked first. A problem here is a
400and no travel times are fetched. - Travel times are fetched.
- The plan is matched to the problem. A plan that cannot be laid out at all is a
400with pointer/plan/routes/{i}or/plan/routes/{i}/stops/{k}: an unknown vehicle, shift, stop or depot id; a route or stop listed twice; a stop whosetypedoes not match the task; a shipment whose pickup and delivery are not both on one route, pickup first; a misplacedreload; more than 8 reloads at one depot; more trips thanmax_trips; alocked_countlarger than the number of stops. - The plan is scored. Everything else, such as a missed window or an overloaded vehicle, is reported as a violation in a
200response.
validate_only
POST /v3/routing/evaluate?validate_only=true runs step 1 only and returns 200 {"valid": true, "warnings": []} or the 400 the real call would return. It checks problem; it does not look at plan, because matching the plan needs the later steps.
Errors
Evaluate returns the same error body and status codes as solve. A plan with violations is not an error: it is a200 with summary.status: "infeasible".
Suggest
Find the best slot for a new task in a plan you keep.
Errors
The error body, every code, and what to do about each.
Constraints
The rules behind each violation type.
API reference
Request and response schema for every endpoint.