Network scenarios (beta)
Compare your delivery days as they are with a new depot, a different fleet size or other shift hours.
network.site_scenario
Beta
job
How to call it
REST
POST
/v1/network/site-scenario
GraphQL
mutation
openNetworkSiteScenario
MCP
open_network_site_scenario
Route a few days of your deliveries (depots, orders, vehicles) as they are and with up to three scenarios - a new depot taking the orders within a radius, a fleet size, other shift hours - and compare km, hours, vehicles, unassigned and late orders and the cost score. Always answered as a job (poll open_job_result); usually minutes. Billed one job.
Request schema
Generated from the same validation rules the endpoint runs, so a field cannot be documented without being validated.
POST /v1/network/site-scenario
{
"type": "object",
"properties": {
"callback": {
"type": [
"object",
"null"
],
"properties": {
"events": {
"type": "array",
"items": {
"type": "string",
"maxLength": 32
}
},
"format": {
"type": [
"string",
"null"
],
"enum": [
"native",
"ce_binary",
"ce_structured"
]
},
"secret": {
"type": [
"string",
"null"
],
"minLength": 16,
"maxLength": 255,
"description": "Required when callback.signature is standard_webhooks."
},
"signature": {
"type": [
"string",
"null"
],
"enum": [
"native",
"standard_webhooks"
]
},
"url": {
"type": "string",
"maxLength": 2048,
"format": "uri",
"description": "Required when callback is present."
}
}
},
"currency": {
"type": [
"string",
"null"
],
"minLength": 3,
"maxLength": 3
},
"days": {
"type": "object",
"properties": {
"from": [],
"to": []
},
"required": [
"from",
"to"
]
},
"depots": {
"type": "array",
"minItems": 1,
"maxItems": 20,
"items": {
"type": "object",
"properties": {
"closes": [],
"id": {
"type": "string",
"maxLength": 128
},
"lat": {
"type": "number",
"minimum": -90,
"maximum": 90
},
"lng": {
"type": "number",
"minimum": -180,
"maximum": 180
},
"name": {
"type": [
"string",
"null"
],
"maxLength": 128
},
"opens": []
},
"required": [
"id",
"lat",
"lng"
]
}
},
"driver_cost": {
"type": [
"object",
"null"
],
"properties": {
"fixed_daily": {
"type": [
"number",
"null"
],
"minimum": 0
},
"per_delivery": {
"type": [
"number",
"null"
],
"minimum": 0
},
"per_hour": {
"type": [
"number",
"null"
],
"minimum": 0
},
"per_km": {
"type": [
"number",
"null"
],
"minimum": 0
}
}
},
"orders": {
"type": "array",
"minItems": 1,
"maxItems": 20000,
"items": {
"type": "object",
"properties": {
"date": [],
"depot": {
"type": [
"string",
"null"
],
"maxLength": 128
},
"id": {
"type": "string",
"maxLength": 128
},
"lat": {
"type": "number",
"minimum": -90,
"maximum": 90
},
"lng": {
"type": "number",
"minimum": -180,
"maximum": 180
},
"packages": {
"type": [
"integer",
"null"
],
"minimum": 1,
"maximum": 10000
},
"service_minutes": {
"type": [
"integer",
"null"
],
"minimum": 0,
"maximum": 600
},
"window": {
"type": [
"object",
"null"
],
"properties": {
"from": {
"description": "Required when orders.*.window is present."
},
"to": {
"description": "Required when orders.*.window is present."
}
}
}
},
"required": [
"date",
"id",
"lat",
"lng"
]
}
},
"scenarios": {
"type": "array",
"minItems": 1,
"maxItems": 3,
"items": {
"type": "object",
"properties": {
"changes": {
"type": "array",
"minItems": 1,
"maxItems": 3,
"items": {
"type": "object",
"properties": {
"depot": {
"type": [
"string",
"null"
],
"maxLength": 128
},
"factor_percent": {
"type": [
"number",
"null"
],
"minimum": 10,
"maximum": 500
},
"lat": {
"type": [
"number",
"null"
],
"minimum": -90,
"maximum": 90
},
"lng": {
"type": [
"number",
"null"
],
"minimum": -180,
"maximum": 180
},
"name": {
"type": [
"string",
"null"
],
"maxLength": 128
},
"radius_km": {
"type": [
"number",
"null"
],
"minimum": 0.1,
"maximum": 200
},
"type": {
"type": "string",
"enum": [
"add_depot",
"fleet",
"shift"
]
},
"vehicles": {
"type": [
"integer",
"null"
],
"minimum": 1,
"maximum": 10000
},
"work_from": [],
"work_to": []
},
"required": [
"type"
]
}
},
"id": {
"type": [
"string",
"null"
],
"maxLength": 64
},
"name": {
"type": "string",
"maxLength": 128
}
},
"required": [
"changes",
"name"
]
}
},
"time_zone": [],
"vehicles": {
"type": "array",
"minItems": 1,
"maxItems": 500,
"items": {
"type": "object",
"properties": {
"capacity_packages": {
"type": [
"integer",
"null"
],
"minimum": 1,
"maximum": 100000
},
"count": {
"type": "integer",
"minimum": 1,
"maximum": 1000
},
"date": [],
"depot": {
"type": "string",
"maxLength": 128
},
"work_from": [],
"work_to": []
},
"required": [
"count",
"depot"
]
}
}
},
"additionalProperties": false,
"required": [
"days",
"depots",
"orders",
"scenarios",
"time_zone",
"vehicles"
]
}
Request example
{
"time_zone": "America/Toronto",
"currency": "CAD",
"days": {
"from": "2026-11-03",
"to": "2026-11-03"
},
"depots": [
{
"id": "MTL-West",
"name": "Montreal West",
"lat": 45.4706,
"lng": -73.7408
},
{
"id": "MTL-Centre",
"name": "Montreal Centre",
"lat": 45.4972,
"lng": -73.5786
}
],
"orders": [
{
"id": "SO-2001",
"lat": 45.49524,
"lng": -73.59163,
"date": "2026-11-03",
"packages": 2
},
{
"id": "SO-2002",
"lat": 45.49518,
"lng": -73.55029,
"date": "2026-11-03",
"packages": 1
},
{
"id": "SO-2003",
"lat": 45.46847,
"lng": -73.59833,
"date": "2026-11-03",
"packages": 3
},
{
"id": "SO-2004",
"lat": 45.5114,
"lng": -73.64394,
"date": "2026-11-03",
"packages": 2
},
{
"id": "SO-2005",
"lat": 45.48034,
"lng": -73.65731,
"date": "2026-11-03",
"packages": 3
},
{
"id": "SO-2006",
"lat": 45.51345,
"lng": -73.58664,
"date": "2026-11-03",
"packages": 2
},
{
"id": "SO-2007",
"lat": 45.54648,
"lng": -73.57845,
"date": "2026-11-03",
"packages": 3
},
{
"id": "SO-2008",
"lat": 45.51501,
"lng": -73.58276,
"date": "2026-11-03",
"packages": 3
},
{
"id": "SO-2009",
"lat": 45.45632,
"lng": -73.66501,
"date": "2026-11-03",
"packages": 1
},
{
"id": "SO-2010",
"lat": 45.50996,
"lng": -73.56106,
"date": "2026-11-03",
"packages": 2
}
],
"vehicles": [
{
"depot": "MTL-West",
"count": 2,
"work_from": "08:00",
"work_to": "17:00"
},
{
"depot": "MTL-Centre",
"count": 2,
"work_from": "08:00",
"work_to": "17:00"
}
],
"driver_cost": {
"per_hour": 24,
"per_km": 0.45
},
"scenarios": [
{
"id": "east-depot",
"name": "Depot in Anjou",
"changes": [
{
"type": "add_depot",
"name": "Anjou",
"lat": 45.604,
"lng": -73.56,
"radius_km": 6,
"vehicles": 1
}
]
},
{
"id": "bigger-fleet",
"name": "Half as many vans again",
"changes": [
{
"type": "fleet",
"factor_percent": 150
}
]
}
]
}
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/network/site-scenario
Response schema
{
"description": "The base and every scenario with their figures and their difference from the base. Collected from the job's result. Billed one job.",
"data": {
"type": "object",
"properties": {
"base": {
"type": "object",
"description": "The days routed as they are.",
"properties": {
"score": {
"type": [
"number",
"null"
],
"description": "The cost score in `currency`: driver hours, distance and vehicles at the driver cost. Without `driver_cost` it holds no money."
},
"km": {
"type": [
"number",
"null"
],
"description": "Distance driven."
},
"hours": {
"type": [
"number",
"null"
],
"description": "Driver hours."
},
"vehicles": {
"type": [
"integer",
"null"
],
"description": "Vehicles used."
},
"unassigned": {
"type": [
"integer",
"null"
],
"description": "Orders left unrouted."
},
"late": {
"type": [
"integer",
"null"
],
"description": "Orders delivered after their window."
}
}
},
"scenarios": {
"type": "array",
"description": "Every scenario, in the order given.",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "The scenario id, or its number from 1."
},
"name": {
"type": "string",
"description": "The scenario name."
},
"routed": {
"type": "boolean",
"description": "Whether the scenario was routed in time; false leaves its figures empty."
},
"tier": {
"type": [
"string",
"null"
],
"description": "How thoroughly it was routed: `balanced`, or `fast` when time ran short."
},
"figures": {
"type": "object",
"description": "The days routed with the scenario's changes.",
"properties": {
"score": {
"type": [
"number",
"null"
],
"description": "The cost score in `currency`: driver hours, distance and vehicles at the driver cost. Without `driver_cost` it holds no money."
},
"km": {
"type": [
"number",
"null"
],
"description": "Distance driven."
},
"hours": {
"type": [
"number",
"null"
],
"description": "Driver hours."
},
"vehicles": {
"type": [
"integer",
"null"
],
"description": "Vehicles used."
},
"unassigned": {
"type": [
"integer",
"null"
],
"description": "Orders left unrouted."
},
"late": {
"type": [
"integer",
"null"
],
"description": "Orders delivered after their window."
}
}
},
"against_base": {
"type": "object",
"description": "The scenario's figures minus the base's; negative is less.",
"properties": {
"score": {
"type": [
"number",
"null"
],
"description": "The cost score in `currency`: driver hours, distance and vehicles at the driver cost. Without `driver_cost` it holds no money."
},
"km": {
"type": [
"number",
"null"
],
"description": "Distance driven."
},
"hours": {
"type": [
"number",
"null"
],
"description": "Driver hours."
},
"vehicles": {
"type": [
"integer",
"null"
],
"description": "Vehicles used."
},
"unassigned": {
"type": [
"integer",
"null"
],
"description": "Orders left unrouted."
},
"late": {
"type": [
"integer",
"null"
],
"description": "Orders delivered after their window."
}
}
}
}
}
},
"currency": {
"type": "string",
"description": "The currency of every score."
},
"missing": {
"type": "array",
"items": {
"type": "string"
},
"description": "What the question lacked, e.g. `driver_cost`, `vehicle_shift`."
}
},
"required": [
"base",
"scenarios",
"currency"
]
},
"meta": {
"type": "object",
"properties": {},
"additionalProperties": true
}
}
Response example
{
"request_id": "req_01k8zq3m5n7p9r1t3v5x7z9b2d",
"capability": "network.site_scenario",
"billing": {
"unit": "job",
"quantity": 1,
"counted": true,
"breakdown": {
"orders": 40,
"depots": 2,
"scenarios": 2
}
},
"data": {
"base": {
"score": 260.57,
"km": 153.3,
"hours": 8,
"vehicles": 2,
"unassigned": 0,
"late": 0
},
"scenarios": [
{
"id": "east-depot",
"name": "Depot in Anjou",
"routed": true,
"tier": "balanced",
"figures": {
"score": 267.86,
"km": 161.8,
"hours": 8.1,
"vehicles": 3,
"unassigned": 0,
"late": 0
},
"against_base": {
"score": 7.29,
"km": 8.5,
"hours": 0.1,
"vehicles": 1,
"unassigned": 0,
"late": 0
}
},
{
"id": "bigger-fleet",
"name": "Half as many vans again",
"routed": true,
"tier": "balanced",
"figures": {
"score": 260.57,
"km": 153.3,
"hours": 8,
"vehicles": 2,
"unassigned": 0,
"late": 0
},
"against_base": {
"score": 0,
"km": 0,
"hours": 0,
"vehicles": 0,
"unassigned": 0,
"late": 0
}
}
],
"currency": "CAD",
"missing": []
},
"meta": {
"partial": false,
"engine": "snet",
"mode": "async"
}
}
Example response when queued (202)
{
"request_id": "req_01k8zq3m5n7p9r1t3v5x7z9b2d",
"capability": "packing.solve",
"billing": {
"unit": "job",
"quantity": 1,
"counted": true,
"breakdown": {
"packagings": 2,
"item_rows": 2,
"units": 5
}
},
"data": {
"job": {
"id": "job_01k8zq4a6c8e0g2j4l6n8q0s2u",
"status": "queued",
"capability": "packing.solve",
"poll_after_ms": 1000,
"result_url": "/v1/jobs/job_01k8zq4a6c8e0g2j4l6n8q0s2u/result",
"cancel_url": "/v1/jobs/job_01k8zq4a6c8e0g2j4l6n8q0s2u/cancel"
}
},
"meta": {
"mode": "async"
}
}
What it costs
One solve run, whatever the item count.
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>. |
| 402 | QUOTA_EXHAUSTED |
The included allowance for this capability is used up for the current period. |
| 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 | CALLBACK_NOT_VERIFIED |
Verify the callback endpoint first: POST /v1/webhooks/verify and echo the challenge back. |
| 422 | CALLBACK_URL_UNSAFE |
The callback URL must be a public HTTPS endpoint. |
| 422 | VALIDATION_FAILED |
The request failed validation. See details. |
| 424 | ENGINE_REJECTED |
The routing engine refused this problem. Its reason is included where available. |
| 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. |
| 504 | SOLVE_TIMEOUT |
The solve exceeded the synchronous budget. Send the same request with "mode":"async" and collect the result from the job. |