Live Replanning

TL;DR

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 vs completed

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 must fit the existing matrix

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.

Next steps

FieldGenius VRP API documentation. Generated from the engineering source of truth (.tex docs and app/ source).