Plan comparison (beta)
Price and judge several alternative plans of the same vehicles and jobs and receive them ranked.
plan.what_if
Beta
node
How to call it
REST
POST
/v1/plan/what-if
GraphQL
query
openPlanWhatIf
MCP
open_plan_what_if
Score 2-10 alternative plans (`plans`, each with its `routes`) of the same vehicles and jobs, each priced and judged as open_plan_recost does, and rank them: fewer unreachable legs, then fewer violations, then the `objective` (driver time or distance). Billed per stop of every plan. 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/what-if
{
"type": "object",
"properties": {
"departed_at": [],
"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
},
"objective": {
"type": [
"string",
"null"
],
"enum": [
"duration",
"distance"
]
},
"options": {
"type": [
"object",
"null"
],
"properties": {
"geometry": {
"type": [
"boolean",
"null"
]
}
}
},
"plans": {
"type": "array",
"minItems": 2,
"maxItems": 10,
"items": {
"type": "object",
"properties": {
"id": {
"type": [
"string",
"null"
],
"maxLength": 128
},
"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"
]
}
}
},
"required": [
"routes"
]
}
},
"profile": {
"type": [
"string",
"null"
],
"enum": [
"car",
"van",
"truck",
"bike",
"foot",
"motorcycle"
]
},
"rank_only": {
"type": [
"boolean",
"null"
]
},
"region": {
"type": [
"string",
"null"
],
"enum": [
"NA",
"EU",
"AU",
"ASIA",
"na",
"eu",
"au",
"asia"
]
},
"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",
"plans",
"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
}
}
],
"options": {
"geometry": false
},
"matrix": "tile2.3f0c9a5e7b1d4c2a9e8f6d5c4b3a2910",
"plans": [
{
"id": "one-van",
"routes": [
{
"vehicle_id": "van-1",
"stops": [
"order-1001",
"order-1002",
"order-1004",
"order-1003"
]
}
]
},
{
"id": "two-vans",
"routes": [
{
"vehicle_id": "van-1",
"stops": [
"order-1001",
"order-1002"
]
},
{
"vehicle_id": "van-2",
"stops": [
"order-1003",
"order-1004"
]
}
]
}
],
"objective": "duration"
}
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/what-if
Response schema
{
"description": "Every plan priced and judged as plan re-costing prices it, with its rank: fewer unreachable legs first, then fewer violations, then the objective. With `rank_only` on a usable snapshot, the ranking only (`scored` false).",
"data": {
"type": "object",
"properties": {
"scored": {
"type": "boolean",
"description": "True when every plan was priced; false for a ranking on the snapshot's road matrix (`rank_only`)."
},
"ranking": {
"type": "array",
"items": {
"type": "integer"
},
"description": "The plan indices, best first."
},
"best": {
"type": "integer",
"description": "The index of the best plan."
},
"plans": {
"type": "array",
"description": "The plans in the order sent.",
"items": {
"type": "object",
"properties": {
"index": {
"type": "integer"
},
"id": {
"type": [
"string",
"null"
],
"description": "The plan's `id` as sent."
},
"rank": {
"type": "integer",
"description": "1 = best."
},
"objective_value": {
"type": "integer",
"description": "Driver time in seconds (driving plus waiting) for `duration`, metres for `distance`. Scored plans only."
},
"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."
}
}
},
"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."
},
"unassigned": {
"type": "array",
"items": {
"type": "string"
},
"description": "The jobs on no route in this plan. Scored plans only."
},
"warnings": {
"type": "array",
"items": {
"type": "object"
},
"description": "The plan's own warnings. Scored plans only."
},
"unreachable": {
"type": "boolean",
"description": "`rank_only`: whether the plan drives a pair the road matrix has no road for."
}
},
"required": [
"index",
"rank"
]
}
}
},
"required": [
"scored",
"ranking",
"best",
"plans"
]
},
"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."
},
"objective": {
"type": "string",
"description": "`duration` or `distance`."
}
},
"additionalProperties": true
}
}
Response example
{
"request_id": "req_01k8zq3m5n7p9r1t3v5x7z9b2d",
"capability": "plan.what_if",
"billing": {
"unit": "node",
"quantity": 8,
"counted": true,
"breakdown": {
"plans": 2,
"stops": 8
}
},
"data": {
"scored": true,
"ranking": [
0,
1
],
"best": 0,
"plans": [
{
"index": 0,
"id": "one-van",
"rank": 1,
"objective_value": 2335,
"summary": {
"routes": 1,
"tasks": 4,
"distance_meters": 18088,
"duration_seconds": 2335,
"unreachable_legs": 0,
"violations": 0
},
"routes": [
{
"vehicle_id": "van-1",
"stop_count": 6,
"task_count": 4,
"distance_meters": 18088,
"duration_seconds": 2335,
"service_seconds": 1200,
"waiting_seconds": 0,
"departure": "2026-10-23T09:30:00-04:00",
"completion": "2026-10-23T10:28:55-04:00",
"unreachable_legs": 0,
"violations": [],
"violation_count": 0,
"stops": [
{
"type": "start",
"departure": "2026-10-23T09:30:00-04:00"
},
{
"type": "job",
"id": "order-1001",
"sequence": 1,
"reachable": true,
"leg_distance_meters": 1840,
"leg_duration_seconds": 245,
"leg_geometry": null,
"arrival": "2026-10-23T09:34:05-04:00",
"arrival_epoch": 1792762445,
"distance_meters": 1840,
"duration_seconds": 245
},
{
"type": "job",
"id": "order-1002",
"sequence": 2,
"reachable": true,
"leg_distance_meters": 3108,
"leg_duration_seconds": 390,
"leg_geometry": null,
"arrival": "2026-10-23T09:45:35-04:00",
"arrival_epoch": 1792763135,
"distance_meters": 4948,
"duration_seconds": 635
},
{
"type": "job",
"id": "order-1004",
"sequence": 3,
"reachable": true,
"leg_distance_meters": 6451,
"leg_duration_seconds": 824,
"leg_geometry": null,
"arrival": "2026-10-23T10:04:19-04:00",
"arrival_epoch": 1792764259,
"distance_meters": 11399,
"duration_seconds": 1459
},
{
"type": "job",
"id": "order-1003",
"sequence": 4,
"reachable": true,
"leg_distance_meters": 3180,
"leg_duration_seconds": 442,
"leg_geometry": null,
"arrival": "2026-10-23T10:16:41-04:00",
"arrival_epoch": 1792765001,
"distance_meters": 14579,
"duration_seconds": 1901
},
{
"type": "end",
"reachable": true,
"leg_distance_meters": 3509,
"leg_duration_seconds": 434,
"leg_geometry": null,
"arrival": "2026-10-23T10:28:55-04:00",
"arrival_epoch": 1792765735,
"distance_meters": 18088,
"duration_seconds": 2335
}
]
}
],
"unassigned": [],
"warnings": []
},
{
"index": 1,
"id": "two-vans",
"rank": 2,
"objective_value": 2486,
"summary": {
"routes": 2,
"tasks": 4,
"distance_meters": 19058,
"duration_seconds": 2486,
"unreachable_legs": 0,
"violations": 0
},
"routes": [
{
"vehicle_id": "van-1",
"stop_count": 4,
"task_count": 2,
"distance_meters": 6416,
"duration_seconds": 832,
"service_seconds": 600,
"waiting_seconds": 0,
"departure": "2026-10-23T09:30:00-04:00",
"completion": "2026-10-23T09:53:52-04:00",
"unreachable_legs": 0,
"violations": [],
"violation_count": 0,
"stops": [
{
"type": "start",
"departure": "2026-10-23T09:30:00-04:00"
},
{
"type": "job",
"id": "order-1001",
"sequence": 1,
"reachable": true,
"leg_distance_meters": 1840,
"leg_duration_seconds": 245,
"leg_geometry": null,
"arrival": "2026-10-23T09:34:05-04:00",
"arrival_epoch": 1792762445,
"distance_meters": 1840,
"duration_seconds": 245
},
{
"type": "job",
"id": "order-1002",
"sequence": 2,
"reachable": true,
"leg_distance_meters": 3108,
"leg_duration_seconds": 390,
"leg_geometry": null,
"arrival": "2026-10-23T09:45:35-04:00",
"arrival_epoch": 1792763135,
"distance_meters": 4948,
"duration_seconds": 635
},
{
"type": "end",
"reachable": true,
"leg_distance_meters": 1468,
"leg_duration_seconds": 197,
"leg_geometry": null,
"arrival": "2026-10-23T09:53:52-04:00",
"arrival_epoch": 1792763632,
"distance_meters": 6416,
"duration_seconds": 832
}
]
},
{
"vehicle_id": "van-2",
"stop_count": 4,
"task_count": 2,
"distance_meters": 12642,
"duration_seconds": 1654,
"service_seconds": 600,
"waiting_seconds": 0,
"departure": "2026-10-23T09:30:00-04:00",
"completion": "2026-10-23T10:07:34-04:00",
"unreachable_legs": 0,
"violations": [],
"violation_count": 0,
"stops": [
{
"type": "start",
"departure": "2026-10-23T09:30:00-04:00"
},
{
"type": "job",
"id": "order-1003",
"sequence": 1,
"reachable": true,
"leg_distance_meters": 3635,
"leg_duration_seconds": 482,
"leg_geometry": null,
"arrival": "2026-10-23T09:38:02-04:00",
"arrival_epoch": 1792762682,
"distance_meters": 3635,
"duration_seconds": 482
},
{
"type": "job",
"id": "order-1004",
"sequence": 2,
"reachable": true,
"leg_distance_meters": 3487,
"leg_duration_seconds": 492,
"leg_geometry": null,
"arrival": "2026-10-23T09:51:14-04:00",
"arrival_epoch": 1792763474,
"distance_meters": 7122,
"duration_seconds": 974
},
{
"type": "end",
"reachable": true,
"leg_distance_meters": 5520,
"leg_duration_seconds": 680,
"leg_geometry": null,
"arrival": "2026-10-23T10:07:34-04:00",
"arrival_epoch": 1792764454,
"distance_meters": 12642,
"duration_seconds": 1654
}
]
}
],
"unassigned": [],
"warnings": []
}
]
},
"meta": {
"region": "NA",
"profile": "car",
"geometry": false,
"geometry_format": null,
"departed_at": "2026-10-23T09:30:00-04:00",
"objective": "duration",
"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 | VALIDATION_FAILED |
The request failed validation. See details. |
| 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. |