Live Replanning
Use POST /api/v1/optimize/replan to re-optimize mid-day. Pass the original request plus the current execution state (completed jobs, vehicle positions, new urgent jobs, cancellations) and receive a fresh plan for the remaining work. The endpoint handles all the bookkeeping internally.
These two scenarios continue from a baseline plan created at 07:00 with two trucks serving nine jobs. By 11:00 the situation has changed and a replan is needed. See POST /optimize/replan for the full schema reference.
Baseline plan (07:00)
Two trucks started from depot (location index 0) at 07:00 (25,200 s) with a 10-location travel matrix and nine jobs spread across the day:
{
"original_request": {
"resources": [
{ "id": "truck_a", "start_location": {"index": 0}, "end_location": {"index": 0},
"time_window": {"start": 25200, "end": 61200} },
{ "id": "truck_b", "start_location": {"index": 0}, "end_location": {"index": 0},
"time_window": {"start": 25200, "end": 61200} }
],
"jobs": [
{"id": "job1", "location_index": 1, "service_time": 1800},
{"id": "job2", "location_index": 2, "service_time": 1800},
{"id": "job3", "location_index": 3, "service_time": 1800},
{"id": "job4", "location_index": 4, "service_time": 1800, "time_windows": [{"start": 39600, "end": 43200}]},
{"id": "job5", "location_index": 5, "service_time": 1800},
{"id": "job6", "location_index": 6, "service_time": 1800},
{"id": "job7", "location_index": 7, "service_time": 1800},
{"id": "job8", "location_index": 8, "service_time": 1800, "time_windows": [{"start": 41400, "end": 46800}]},
{"id": "job9", "location_index": 9, "service_time": 1800}
],
"travel": {
"time_matrix": [[0,600,900,780,1020,900,1140,1260,1320,1440],[600,0,480,540,780,900,960,1080,1200,1320],[900,480,0,300,600,780,840,960,1020,1200],[780,540,300,0,420,600,720,840,900,1080],[1020,780,600,420,0,360,540,720,780,960],[900,900,780,600,360,0,300,480,600,780],[1140,960,840,720,540,300,0,240,420,600],[1260,1080,960,840,720,480,240,0,240,420],[1320,1200,1020,900,780,600,420,240,0,240],[1440,1320,1200,1080,960,780,600,420,240,0]],
"distance_matrix": [[0,6000,9000,7800,10200,9000,11400,12600,13200,14400],[6000,0,4800,5400,7800,9000,9600,10800,12000,13200],[9000,4800,0,3000,6000,7800,8400,9600,10200,12000],[7800,5400,3000,0,4200,6000,7200,8400,9000,10800],[10200,7800,6000,4200,0,3600,5400,7200,7800,9600],[9000,9000,7800,6000,3600,0,3000,4800,6000,7800],[11400,9600,8400,7200,5400,3000,0,2400,4200,6000],[12600,10800,9600,8400,7200,4800,2400,0,2400,4200],[13200,12000,10200,9000,7800,6000,4200,2400,0,2400],[14400,13200,12000,10800,9600,7800,6000,4200,2400,0]]
},
"options": {"time_limit_seconds": 10}
}
}
Scenario: urgent job injection + cancellation (11:00)
Situation at 11:00 (39,600 s)
- job1, job2, job4 are completed.
- job3 has been cancelled by the customer — it must disappear from the plan without appearing in
unserved. - A new urgent job has arrived at location 8 with a hard time window 11:00–13:00 (39600–46800 s) and a high penalty.
- truck_a is at location 2 (its last completed stop), truck_b is at location 4.
cancelled_job_ids jobs are excluded and not reported as unserved — they vanish from the response entirely. completed_job_ids jobs are also excluded but were actually served. The distinction is semantic/record-keeping only; neither type appears in routes or unserved.
Request
{
"original_request": { "...same as baseline above..." },
"completed_job_ids": ["job1", "job2", "job4"],
"cancelled_job_ids": ["job3"],
"vehicle_positions": [
{ "resource_id": "truck_a", "location_index": 2, "current_time": 39600 },
{ "resource_id": "truck_b", "location_index": 4, "current_time": 39600 }
],
"new_jobs": [
{
"id": "job_urgent",
"location_index": 8,
"service_time": 1800,
"time_windows": [{"start": 39600, "end": 46800}],
"mandatory": true,
"penalty": 500000
}
]
}
What the solver does
- truck_a re-plans from location 2 at 11:00 (not the depot).
- truck_b re-plans from location 4 at 11:00.
- job3 is silently dropped — it will not appear in
unserved. - job_urgent has a hard window and a 500,000 penalty, giving the solver strong incentive to assign it first.
- truck_c (if present and not listed in
vehicle_positions) retains its original depot start unchanged.
Scenario: all vehicles scattered, emergency job (14:00)
Situation at 14:00 (50,400 s)
All nine original jobs are done. All four trucks are scattered across the network. A new emergency job must be served within the next 60 minutes.
Request
{
"original_request": { "...same baseline, but with 4 trucks..." },
"completed_job_ids": [
"job1", "job2", "job3", "job4", "job5",
"job6", "job7", "job8", "job9"
],
"cancelled_job_ids": [],
"vehicle_positions": [
{ "resource_id": "truck_a", "location_index": 3, "current_time": 50400 },
{ "resource_id": "truck_b", "location_index": 5, "current_time": 50400 },
{ "resource_id": "truck_c", "location_index": 6, "current_time": 50400 },
{ "resource_id": "truck_d", "location_index": 9, "current_time": 50400 }
],
"new_jobs": [
{
"id": "job_emergency",
"location_index": 7,
"service_time": 900,
"time_windows": [{"start": 51000, "end": 54600}],
"mandatory": true,
"penalty": 1000000
}
]
}
What the solver does
- All four trucks start from their current scattered positions, not the depot.
- The solver picks whichever truck can reach location 7 within the 14:10–15:10 window at minimum cost.
- Because all other jobs are complete, the only open work is the emergency job.
- Shift end (17:00, 61,200 s from the original request) is preserved automatically.
New jobs injected via new_jobs must reference location indices that already exist in the original travel.distance_matrix / travel.time_matrix. The matrix is not extended at replan time. If you need a new location, include it in the original request upfront even if the job isn't scheduled yet.
- POST /optimize/replan — full schema reference for ReplanRequest, VehiclePosition, CompletedJob.
- Multi-Depot & Multiday — replanning composes with multi-day: put
num_daysinoriginal_request.problem.