Two examples: (1) depots as named locations, with each resource pinned to a different one via start_depot_id; (2) problem.num_days = 3 with a resource restricted to certain days via available_days and a job pinned to a single day via day.
Multi-Depot & Multiday
Example 1: Multiple depots
Instead of a single shared depot_index, declare named depots and point each resource at one via start_depot_id (and optionally a different end_depot_id for one-way routes). Here van-7 starts from the Amsterdam depot and truck-2 starts from the Rotterdam depot; each is closer to a different cluster of jobs.
{
"metadata": { "problem_id": "multi-depot-example" },
"depots": [
{ "id": "depot-amsterdam", "location": { "index": 0, "label": "Amsterdam depot" } },
{ "id": "depot-rotterdam", "location": { "index": 1, "label": "Rotterdam depot" } }
],
"travel": {
"distance_matrix": [
[0, 76000, 4800, 6200, 71000, 73500],
[76000, 0, 72000, 70500, 5100, 4300],
[4800, 72000, 0, 3900, 68200, 70800],
[6200, 70500, 3900, 0, 66900, 69200],
[71000, 5100, 68200, 66900, 0, 3600],
[73500, 4300, 70800, 69200, 3600, 0]
]
},
"resources": [
{ "id": "van-7", "start_depot_id": "depot-amsterdam", "end_depot_id": "depot-amsterdam" },
{ "id": "truck-2", "start_depot_id": "depot-rotterdam", "end_depot_id": "depot-rotterdam" }
],
"jobs": [
{ "id": "job-amsterdam-12", "location_index": 2, "service_time": 300 },
{ "id": "job-amsterdam-13", "location_index": 3, "service_time": 300 },
{ "id": "job-rotterdam-21", "location_index": 4, "service_time": 300 },
{ "id": "job-rotterdam-22", "location_index": 5, "service_time": 300 }
],
"options": { "time_limit_seconds": 5 }
}
With depot-amsterdam at index 0 only 4800-6200m from the Amsterdam jobs, and depot-rotterdam at index 1 only 3600-4300m from the Rotterdam jobs, while the inter-depot/inter-city legs run 66900-76000m, the solver assigns van-7 to the two Amsterdam jobs and truck-2 to the two Rotterdam jobs — each vehicle stays near its own depot rather than crossing the country.
Example 2: Multi-day planning
num_days is a field on ProblemConfig (problem.num_days), not on SolverOptions (options). Putting it under options is silently ignored by Pydantic (extra fields are dropped), so the request quietly runs as single-day, every available_days/day constraint becomes impossible to satisfy, and the whole solve comes back infeasible with no obvious cause. Always set "problem": { "num_days": 3 }.
When num_days > 1, each resource expands into one virtual vehicle per day it is available. available_days (0-indexed) restricts a resource to a subset of days; omitting it means available every day. A job's day field pins it to one specific day and is intersected with allowed_resource_ids — the job can only go to a resource that is both allowed and available that day. Day windows do not carry over: each vehicle gets a fresh shift every day it runs.
{
"metadata": { "problem_id": "multiday-example" },
"problem": { "num_days": 3 },
"travel": {
"distance_matrix": [
[0, 5200, 8700, 6100],
[5200, 0, 4300, 7800],
[8700, 4300, 0, 5500],
[6100, 7800, 5500, 0]
]
},
"resources": [
{ "id": "truck-1", "depot_index": 0 },
{ "id": "truck-2", "depot_index": 0, "available_days": [0, 2] }
],
"jobs": [
{ "id": "job-4821", "location_index": 1, "service_time": 300 },
{ "id": "job-4822", "location_index": 2, "service_time": 300, "day": 1 },
{ "id": "job-4823", "location_index": 3, "service_time": 300, "day": 2, "allowed_resource_ids": ["truck-2"] }
],
"options": { "time_limit_seconds": 10 }
}
Here truck-2 only runs on days 0 and 2 (available_days: [0, 2]). job-4822 is pinned to day 1, so only truck-1 (available every day by default) can take it. job-4823 is pinned to day 2 and restricted to truck-2 via allowed_resource_ids — both conditions must hold, and day 2 is one of truck-2's available days, so this is feasible. job-4821 has no day, so the solver is free to place it on whichever day/vehicle minimizes cost.
Multi-day response shape
Routes carry a day field so you can tell which virtual vehicle-day each route belongs to:
{
"status": "optimal",
"problem_id": "multiday-example",
"solve_time_ms": 64,
"routes": [
{
"resource_id": "truck-1",
"day": 1,
"activities": [
{ "type": "start", "location_index": 0, "departure_time": 0 },
{ "type": "service", "job_id": "job-4822", "location_index": 2, "arrival_time": 8700, "departure_time": 9000, "service_time": 300, "distance_from_prev": 8700, "time_from_prev": 8700 },
{ "type": "end", "location_index": 0, "arrival_time": 17700, "distance_from_prev": 8700, "time_from_prev": 8700 }
],
"summary": { "total_distance": 17400, "total_travel_time": 17400, "total_service_time": 300, "total_waiting_time": 0, "total_time": 17700, "num_stops": 1, "num_jobs": 1, "cost": 17400.0 },
"violations": []
},
{
"resource_id": "truck-2",
"day": 2,
"activities": [
{ "type": "start", "location_index": 0, "departure_time": 0 },
{ "type": "service", "job_id": "job-4823", "location_index": 3, "arrival_time": 6100, "departure_time": 6400, "service_time": 300, "distance_from_prev": 6100, "time_from_prev": 6100 },
{ "type": "end", "location_index": 0, "arrival_time": 12500, "distance_from_prev": 6100, "time_from_prev": 6100 }
],
"summary": { "total_distance": 12200, "total_travel_time": 12200, "total_service_time": 300, "total_waiting_time": 0, "total_time": 12500, "num_stops": 1, "num_jobs": 1, "cost": 12200.0 },
"violations": []
},
{
"resource_id": "truck-1",
"day": 0,
"activities": [
{ "type": "start", "location_index": 0, "departure_time": 0 },
{ "type": "service", "job_id": "job-4821", "location_index": 1, "arrival_time": 5200, "departure_time": 5500, "service_time": 300, "distance_from_prev": 5200, "time_from_prev": 5200 },
{ "type": "end", "location_index": 0, "arrival_time": 10700, "distance_from_prev": 5200, "time_from_prev": 5200 }
],
"summary": { "total_distance": 10400, "total_travel_time": 10400, "total_service_time": 300, "total_waiting_time": 0, "total_time": 10700, "num_stops": 1, "num_jobs": 1, "cost": 10400.0 },
"violations": []
}
],
"unserved": [],
"score": { "total": 40000.0, "travel_distance": 40000.0, "travel_time": 40000.0, "fixed_vehicle_cost": 0.0, "penalty_dropped": 0.0, "fairness_cost": 0.0, "num_vehicles_used": 2 },
"warnings": []
}
- Live Replanning — re-optimize a single day's route mid-execution.
- Recurring Jobs Walkthrough — auto-generate day-pinned jobs from a weekly pattern instead of hand-writing
dayon each one.