Route refinement (beta)
Re-order the stops of chosen routes of a plan you hold around stops you lock, and receive the plan priced; a new order is kept only when it is better.
plan.refine
Beta
node
How to call it
REST
POST
/v1/plan/refine
GraphQL
query
openPlanRefine
MCP
open_plan_refine
Re-order the stops of the routes of `vehicle_ids` (every route when omitted) within and between those routes, `freeze` naming stops that stay on their route in their order, and get the plan priced as open_plan_recost does; the new order is kept only when it is better on the road, `refine.changed` says whether it was. Single jobs only. Large refines without a snapshot answer PLAN_REFINE_REFUSED: use open_routing_refine. Billed per stop priced. Every plan tool takes an optional `matrix`, a distance snapshot from open_plan_matrix_open; with it the candidates are ranked on its road matrix, and every number answered is measured on the road either way.
Request schema
Generated from the same validation rules the endpoint runs, so a field cannot be documented without being validated.
POST /v1/plan/refine
{
"type": "object",
"properties": {
"departed_at": [],
"freeze": {
"type": "array",
"maxItems": 2000,
"items": {
"type": "string",
"maxLength": 128
}
},
"jobs": {
"type": "array",
"minItems": 1,
"maxItems": 2000,
"items": {
"type": "object",
"properties": {
"amount": {
"type": [
"array",
"null"
]
},
"id": {
"type": "string",
"maxLength": 128
},
"location": {
"type": "array"
},
"service_seconds": {
"type": [
"integer",
"null"
],
"minimum": 0
},
"skills": {
"type": "array",
"items": {
"type": "string",
"maxLength": 64
}
},
"time_windows": {
"type": [
"array",
"null"
]
}
},
"required": [
"id",
"location"
]
}
},
"matrix": {
"type": [
"string",
"null"
],
"maxLength": 200
},
"options": {
"type": [
"object",
"null"
],
"properties": {
"geometry": {
"type": [
"boolean",
"null"
]
}
}
},
"profile": {
"type": [
"string",
"null"
],
"enum": [
"car",
"van",
"truck",
"bike",
"foot",
"motorcycle"
]
},
"region": {
"type": [
"string",
"null"
],
"enum": [
"NA",
"EU",
"AU",
"ASIA",
"na",
"eu",
"au",
"asia"
]
},
"routes": {
"type": "array",
"minItems": 1,
"maxItems": 200,
"items": {
"type": "object",
"properties": {
"reverse": {
"type": [
"boolean",
"null"
]
},
"stops": {
"type": "array",
"maxItems": 2000,
"items": {
"type": "string",
"maxLength": 128
}
},
"vehicle_id": {
"type": "string",
"maxLength": 128
}
},
"required": [
"vehicle_id"
]
}
},
"vehicle_ids": {
"type": "array",
"minItems": 1,
"maxItems": 200,
"items": {
"type": "string",
"maxLength": 128
}
},
"vehicles": {
"type": "array",
"minItems": 1,
"maxItems": 200,
"items": {
"type": "object",
"properties": {
"capacity": {
"type": [
"array",
"null"
]
},
"end": {
"type": [
"array",
"null"
]
},
"id": {
"type": "string",
"maxLength": 128
},
"max_distance_meters": {
"type": [
"integer",
"null"
],
"minimum": 0
},
"max_tasks": {
"type": [
"integer",
"null"
],
"minimum": 0
},
"max_travel_time_seconds": {
"type": [
"integer",
"null"
],
"minimum": 0
},
"return_to_start": {
"type": [
"boolean",
"null"
]
},
"skills": {
"type": "array",
"items": {
"type": "string",
"maxLength": 64
}
},
"start": {
"type": [
"array",
"null"
]
},
"time_window": {
"type": [
"array",
"null"
]
}
},
"required": [
"id"
]
}
}
},
"additionalProperties": false,
"required": [
"jobs",
"routes",
"vehicles"
]
}
Request example
{
"departed_at": "2026-10-23T09:30:00-04:00",
"vehicles": [
{
"id": "van-1",
"start": {
"lat": 45.5017,
"lng": -73.5673
},
"return_to_start": true,
"capacity": {
"parcels": 10
}
},
{
"id": "van-2",
"start": {
"lat": 45.5017,
"lng": -73.5673
},
"return_to_start": true,
"capacity": {
"parcels": 10
}
}
],
"jobs": [
{
"id": "order-1001",
"location": {
"lat": 45.5088,
"lng": -73.554
},
"service_seconds": 300,
"amount": {
"parcels": 2
}
},
{
"id": "order-1002",
"location": {
"lat": 45.4972,
"lng": -73.5786
},
"service_seconds": 300,
"amount": {
"parcels": 3
}
},
{
"id": "order-1003",
"location": {
"lat": 45.5231,
"lng": -73.5817
},
"service_seconds": 300,
"amount": {
"parcels": 1
}
},
{
"id": "order-1004",
"location": {
"lat": 45.5302,
"lng": -73.6125
},
"service_seconds": 300,
"amount": {
"parcels": 2
}
}
],
"routes": [
{
"vehicle_id": "van-1",
"stops": [
"order-1002",
"order-1004",
"order-1001",
"order-1003"
]
}
],
"options": {
"geometry": false
},
"matrix": "tile2.3f0c9a5e7b1d4c2a9e8f6d5c4b3a2910",
"freeze": [
"order-1002"
]
}
Response
What a successful call returns: the schema of `data` and `meta`, with a description of each field, followed by an example response from a real call.
POST /v1/plan/refine
Response schema
{
"description": "The plan with the refined order when it is better than the plan as sent, else the plan as sent, priced and judged as plan re-costing prices it, the plan itself in `plan`, and what the refine did.",
"data": {
"type": "object",
"properties": {
"routes": {
"type": "array",
"items": {
"type": "object",
"properties": {
"vehicle_id": {
"type": "string"
},
"stop_count": {
"type": "integer",
"description": "Stops including start and end."
},
"task_count": {
"type": "integer",
"description": "Jobs on the route."
},
"distance_meters": {
"type": "integer"
},
"duration_seconds": {
"type": "integer",
"description": "Driving time."
},
"service_seconds": {
"type": "integer"
},
"waiting_seconds": {
"type": "integer"
},
"departure": {
"type": "string",
"description": "ISO 8601 with offset."
},
"completion": {
"type": "string",
"description": "When the last stop (or the end) is reached. ISO 8601 with offset."
},
"unreachable_legs": {
"type": "integer"
},
"violations": {
"type": "array",
"items": {
"type": "object",
"description": "A route-level breach. Which fields are present depends on `rule`.",
"properties": {
"rule": {
"type": "string",
"enum": [
"capacity",
"max_tasks",
"max_distance",
"max_travel_time",
"vehicle_time_window"
]
},
"message": {
"type": "string"
},
"dimension": {
"type": "string",
"description": "capacity: the capacity dimension that overflows."
},
"load": {
"type": "number",
"description": "capacity: what the stops load."
},
"capacity": {
"type": "number",
"description": "capacity: what the vehicle carries."
},
"tasks": {
"type": "integer",
"description": "max_tasks: stops on the route."
},
"distance_meters": {
"type": "integer",
"description": "max_distance: distance of the route."
},
"duration_seconds": {
"type": "integer",
"description": "max_travel_time: driving time of the route."
},
"limit": {
"type": "integer",
"description": "max_tasks, max_distance, max_travel_time: the vehicle's limit."
},
"late_seconds": {
"type": "integer",
"description": "vehicle_time_window: how far past the working window the route ends."
},
"window_end": {
"type": "string",
"description": "vehicle_time_window: when the working window ends. ISO 8601 with offset."
}
}
},
"description": "Route-level breaches."
},
"violation_count": {
"type": "integer",
"description": "Route and stop breaches on this route."
},
"stops": {
"type": "array",
"items": {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": [
"start",
"job",
"end"
]
},
"id": {
"type": "string",
"description": "The caller's job id. Jobs only."
},
"sequence": {
"type": "integer",
"description": "Position of the job on the route, from 1."
},
"reachable": {
"type": "boolean",
"description": "Whether the hop into this stop could be routed. An unreachable hop has no distance or time and is counted in `unreachable_legs`."
},
"departure": {
"type": "string",
"description": "Start only: when the vehicle leaves. ISO 8601 with offset."
},
"waiting_seconds": {
"type": "integer",
"description": "Start only, when the vehicle must wait for its working window to open."
},
"arrival": {
"type": [
"string",
"null"
],
"description": "ISO 8601 with offset. Null after an unreachable hop."
},
"arrival_epoch": {
"type": [
"integer",
"null"
],
"description": "`arrival` as Unix seconds."
},
"leg_distance_meters": {
"type": [
"integer",
"null"
],
"description": "Distance of the hop into this stop."
},
"leg_duration_seconds": {
"type": [
"integer",
"null"
],
"description": "Driving time of the hop into this stop."
},
"leg_geometry": {
"type": [
"string",
"null"
],
"description": "Encoded polyline (precision 5) of the hop into this stop. Present when `options.geometry` is true (the default)."
},
"distance_meters": {
"type": [
"integer",
"null"
],
"description": "Cumulative distance from the start."
},
"duration_seconds": {
"type": [
"integer",
"null"
],
"description": "Cumulative driving time from the start."
},
"violations": {
"type": "array",
"items": {
"type": "object",
"properties": {
"rule": {
"type": "string",
"enum": [
"skills",
"time_window"
]
},
"message": {
"type": "string"
},
"missing": {
"type": "array",
"items": {
"type": "string"
},
"description": "skills: the skills the vehicle lacks."
},
"late_seconds": {
"type": "integer",
"description": "time_window: how late the arrival is."
},
"window": {
"type": "object",
"description": "time_window: the window that was missed.",
"properties": {
"from": {
"type": "string",
"description": "ISO 8601 with offset."
},
"to": {
"type": "string",
"description": "ISO 8601 with offset."
}
}
}
}
},
"description": "Constraints this stop breaks. Present only when it breaks one."
}
},
"required": [
"type"
]
}
}
}
},
"description": "The edited plan's routes priced and judged exactly as plan re-costing prices them."
},
"summary": {
"type": "object",
"properties": {
"routes": {
"type": "integer"
},
"tasks": {
"type": "integer",
"description": "Jobs across all routes."
},
"distance_meters": {
"type": "integer"
},
"duration_seconds": {
"type": "integer",
"description": "Driving time across all routes."
},
"unreachable_legs": {
"type": "integer",
"description": "Hops that could not be routed."
},
"violations": {
"type": "integer",
"description": "Breaches across all routes and stops."
}
}
},
"plan": {
"type": "object",
"description": "The edited plan in the shape it is sent: put it in place of your own. Routes keep your order; a vehicle that received its first stops is added last. Stops are in driving order, so a route sent with `reverse` comes back already reversed.",
"properties": {
"routes": {
"type": "array",
"items": {
"type": "object",
"properties": {
"vehicle_id": {
"type": "string"
},
"stops": {
"type": "array",
"items": {
"type": "string"
}
}
},
"required": [
"vehicle_id",
"stops"
]
}
},
"unassigned": {
"type": "array",
"items": {
"type": "string"
},
"description": "Every job of the plan that is on no route."
}
},
"required": [
"routes",
"unassigned"
]
},
"refine": {
"type": "object",
"properties": {
"vehicle_ids": {
"type": "array",
"items": {
"type": "string"
},
"description": "The routes refined."
},
"freeze": {
"type": "array",
"items": {
"type": "string"
},
"description": "The frozen stops."
},
"changed": {
"type": "boolean",
"description": "Whether the answered plan has a new order. False when none was found or the order found is not better."
},
"moved": {
"type": "integer",
"description": "Stops whose route or place in it is not what was sent."
},
"before": {
"type": "object",
"description": "The totals of the plan as sent.",
"properties": {
"distance_meters": {
"type": "integer"
},
"duration_seconds": {
"type": "integer",
"description": "Driving time."
},
"waiting_seconds": {
"type": "integer"
},
"violations": {
"type": "integer"
},
"unreachable_legs": {
"type": "integer"
}
}
}
},
"required": [
"vehicle_ids",
"freeze",
"changed",
"moved",
"before"
]
}
},
"required": [
"routes",
"summary",
"plan",
"refine"
]
},
"meta": {
"type": "object",
"properties": {
"region": {
"type": "string",
"description": "The engine region used."
},
"profile": {
"type": "string",
"description": "The vehicle profile used."
},
"geometry": {
"type": "boolean",
"description": "Whether leg geometry was returned."
},
"geometry_format": {
"type": [
"string",
"null"
],
"description": "`polyline` when geometry was returned."
},
"departed_at": {
"type": "string",
"description": "The departure time the plan was timed from: `departed_at`, else the current time. ISO 8601 with offset."
}
},
"additionalProperties": true
}
}
Response example
{
"request_id": "req_01k8zq3m5n7p9r1t3v5x7z9b2d",
"capability": "plan.refine",
"billing": {
"unit": "node",
"quantity": 4,
"counted": true,
"breakdown": {
"tasks": 4,
"routes": 1
}
},
"data": {
"routes": [
{
"vehicle_id": "van-1",
"stop_count": 6,
"task_count": 4,
"distance_meters": 17473,
"duration_seconds": 2286,
"service_seconds": 1200,
"waiting_seconds": 0,
"departure": "2026-10-23T09:30:00-04:00",
"completion": "2026-10-23T10:28:06-04:00",
"unreachable_legs": 0,
"violations": [],
"violation_count": 0,
"stops": [
{
"type": "start",
"departure": "2026-10-23T09:30:00-04:00"
},
{
"type": "job",
"id": "order-1002",
"sequence": 1,
"reachable": true,
"leg_distance_meters": 1611,
"leg_duration_seconds": 227,
"leg_geometry": null,
"arrival": "2026-10-23T09:33:47-04:00",
"arrival_epoch": 1792762427,
"distance_meters": 1611,
"duration_seconds": 227
},
{
"type": "job",
"id": "order-1001",
"sequence": 2,
"reachable": true,
"leg_distance_meters": 3355,
"leg_duration_seconds": 421,
"leg_geometry": null,
"arrival": "2026-10-23T09:45:48-04:00",
"arrival_epoch": 1792763148,
"distance_meters": 4966,
"duration_seconds": 648
},
{
"type": "job",
"id": "order-1003",
"sequence": 3,
"reachable": true,
"leg_distance_meters": 3500,
"leg_duration_seconds": 466,
"leg_geometry": null,
"arrival": "2026-10-23T09:58:34-04:00",
"arrival_epoch": 1792763914,
"distance_meters": 8466,
"duration_seconds": 1114
},
{
"type": "job",
"id": "order-1004",
"sequence": 4,
"reachable": true,
"leg_distance_meters": 3487,
"leg_duration_seconds": 492,
"leg_geometry": null,
"arrival": "2026-10-23T10:11:46-04:00",
"arrival_epoch": 1792764706,
"distance_meters": 11953,
"duration_seconds": 1606
},
{
"type": "end",
"reachable": true,
"leg_distance_meters": 5520,
"leg_duration_seconds": 680,
"leg_geometry": null,
"arrival": "2026-10-23T10:28:06-04:00",
"arrival_epoch": 1792765686,
"distance_meters": 17473,
"duration_seconds": 2286
}
]
}
],
"summary": {
"routes": 1,
"tasks": 4,
"distance_meters": 17473,
"duration_seconds": 2286,
"unreachable_legs": 0,
"violations": 0
},
"plan": {
"routes": [
{
"vehicle_id": "van-1",
"stops": [
"order-1002",
"order-1001",
"order-1003",
"order-1004"
]
}
],
"unassigned": []
},
"refine": {
"vehicle_ids": [
"van-1"
],
"freeze": [
"order-1002"
],
"changed": true,
"moved": 3,
"before": {
"distance_meters": 21187,
"duration_seconds": 2717,
"waiting_seconds": 0,
"violations": 0,
"unreachable_legs": 0
}
}
},
"meta": {
"region": "NA",
"profile": "car",
"geometry": false,
"geometry_format": null,
"departed_at": "2026-10-23T09:30:00-04:00",
"mode": "sync"
}
}
What it costs
Every point the solver must visit or depart from, including each vehicle's start and end.
Every billable response carries a billing block with the unit, the quantity and how the quantity was derived.
Errors it can return
| Status | Code | Description |
|---|---|---|
| 401 | TOKEN_INVALID |
Unknown or revoked API token. |
| 401 | TOKEN_MISSING |
No API token. Send Authorization: Bearer <your token>. |
| 403 | CAPABILITY_NOT_ENABLED |
This capability is not enabled for your account. See :doc to enable it. |
| 403 | CAPABILITY_NOT_GRANTED_TO_KEY |
Your account has this capability, but this API key was not granted it. |
| 403 | OPEN_API_DISABLED |
Open Platform access is not enabled for this account. |
| 422 | PLAN_REFINE_REFUSED |
The plan cannot be refined here (see `reason`). Refine it with routing.refine, with the stops to keep in `freeze`. |
| 422 | VALIDATION_FAILED |
The request failed validation. See details. |
| 429 | CONCURRENCY_LIMIT |
Too many solves are already running for this account. Retry when one finishes. |
| 429 | RATE_LIMITED |
Too many requests. Please retry after the indicated delay. |
| 500 | INTERNAL_ERROR |
An unexpected error occurred. Quote the request_id when contacting support. |
| 502 | ENGINE_UNAVAILABLE |
The routing engine is temporarily unreachable. Please retry. |