Real-time planning: extended stop
There are many situations where Real-time planning is necessary.
Sometimes stops take longer than expected.
Consider the following shift schedule:
Carl has four stops scheduled for the day: Stop C, Stop A, Stop D and Stop B.
However, Stop C takes much longer than expected.
As a result, Stop A, Stop D and Stop B need to be rescheduled.
This guide explains real-time planning: extended stops with the following:
1. Batch schedule: extended stop
Learn how to configure an API Key to run the examples in this guide:
In the examples, replace |
Carl’s original schedule was generated from the following input dataset during the regular batch scheduling:
-
Input
-
Output
Try this example in Timefold Platform by saving this JSON into a file called sample.json and make the following API call:
|
curl -X POST -H "Content-type: application/json" -H 'X-API-KEY: <API_KEY>' https://app.timefold.ai/api/models/pickup-delivery-routing/v1/route-plans [email protected]
{
"config": {
"run": {
"name": "Original shift plan: extended stop example"
}
},
"modelInput": {
"drivers": [
{
"id": "Carl",
"shifts": [
{
"id": "Carl Mon",
"startLocation": [33.77284, -84.42989],
"minStartTime": "2027-02-01T09:00:00Z",
"maxEndTime": "2027-02-01T12:00:00Z"
}
]
}
],
"jobs": [
{
"id": "job1",
"stops": [
{
"id": "Stop A",
"name": "Stop A",
"location": [33.74648, -84.46461],
"duration": "PT10M"
},
{
"id": "Stop B",
"name": "Stop B",
"location": [33.65207, -84.46496],
"duration": "PT10M"
}
]
},
{
"id": "job2",
"stops": [
{
"id": "Stop C",
"name": "Stop C",
"location": [33.77911, -84.49644],
"duration": "PT10M"
},
{
"id": "Stop D",
"name": "Stop D",
"location": [33.65979, -84.46366],
"duration": "PT10M"
}
]
}
]
}
}
To request the solution, locate the ID from the response to the post operation and append it to the following API call:
|
curl -X GET -H 'X-API-KEY: <API_KEY>' https://app.timefold.ai/api/models/pickup-delivery-routing/v1/route-plans/<ID>
{
"metadata": {
"id": "ID",
"originId": "ID",
"name": "Original shift plan: extended stop example",
"submitDateTime": "2026-04-09T16:15:34.09559+02:00",
"startDateTime": "2026-04-09T16:15:34.15051+02:00",
"activeDateTime": "2026-04-09T16:15:34.152865+02:00",
"completeDateTime": "2026-04-09T16:16:04.170458+02:00",
"shutdownDateTime": "2026-04-09T16:16:04.17046+02:00",
"solverStatus": "SOLVING_COMPLETED",
"score": "0hard/0medium/-2533soft",
"validationResult": {
"summary": "OK"
}
},
"modelOutput": {
"drivers": [
{
"id": "Carl",
"shifts": [
{
"id": "Carl Mon",
"startTime": "2027-02-01T09:00:00Z",
"itinerary": [
{
"id": "Stop C",
"arrivalTime": "2027-02-01T09:07:26Z",
"startServiceTime": "2027-02-01T09:07:26Z",
"departureTime": "2027-02-01T09:17:26Z",
"effectiveDuration": "PT10M",
"travelTimeFromPreviousStandstill": "PT7M26S",
"travelDistanceMetersFromPreviousStandstill": 6190,
"minStartTravelTime": "2027-02-01T00:00:00Z",
"load": [],
"kind": "STOP"
},
{
"id": "Stop A",
"arrivalTime": "2027-02-01T09:23:02Z",
"startServiceTime": "2027-02-01T09:23:02Z",
"departureTime": "2027-02-01T09:33:02Z",
"effectiveDuration": "PT10M",
"travelTimeFromPreviousStandstill": "PT5M36S",
"travelDistanceMetersFromPreviousStandstill": 4671,
"minStartTravelTime": "2027-02-01T00:00:00Z",
"load": [],
"kind": "STOP"
},
{
"id": "Stop D",
"arrivalTime": "2027-02-01T09:44:36Z",
"startServiceTime": "2027-02-01T09:44:36Z",
"departureTime": "2027-02-01T09:54:36Z",
"effectiveDuration": "PT10M",
"travelTimeFromPreviousStandstill": "PT11M34S",
"travelDistanceMetersFromPreviousStandstill": 9640,
"minStartTravelTime": "2027-02-01T00:00:00Z",
"load": [],
"kind": "STOP"
},
{
"id": "Stop B",
"arrivalTime": "2027-02-01T09:55:38Z",
"startServiceTime": "2027-02-01T09:55:38Z",
"departureTime": "2027-02-01T10:05:38Z",
"effectiveDuration": "PT10M",
"travelTimeFromPreviousStandstill": "PT1M2S",
"travelDistanceMetersFromPreviousStandstill": 867,
"minStartTravelTime": "2027-02-01T00:00:00Z",
"load": [],
"kind": "STOP"
}
],
"metrics": {
"totalTravelTime": "PT42M13S",
"travelTimeFromStartLocationToFirstStop": "PT7M26S",
"travelTimeBetweenStops": "PT18M12S",
"travelTimeFromLastStopToEndLocation": "PT16M35S",
"totalTravelDistanceMeters": 35183,
"travelDistanceFromStartLocationToFirstStopMeters": 6190,
"travelDistanceBetweenStopsMeters": 15178,
"travelDistanceFromLastStopToEndLocationMeters": 13815,
"endLocationArrivalTime": "2027-02-01T10:22:13Z"
}
}
]
}
],
"unassignedJobs": []
},
"inputMetrics": {
"jobs": 2,
"stops": 4,
"drivers": 1,
"driverShifts": 1,
"pinnedStops": 0
},
"kpis": {
"totalTravelTime": "PT42M13S",
"totalTravelDistanceMeters": 35183,
"totalActivatedDrivers": 1,
"totalUnassignedJobs": 0,
"totalAssignedJobs": 2,
"assignedMandatoryJobs": 2,
"totalUnassignedStops": 0,
"totalAssignedStops": 4,
"assignedMandatoryStops": 4,
"travelTimeFromStartLocationToFirstStop": "PT7M26S",
"travelTimeBetweenStops": "PT18M12S",
"travelTimeFromLastStopToEndLocation": "PT16M35S",
"travelDistanceFromStartLocationToFirstStopMeters": 6190,
"travelDistanceBetweenStopsMeters": 15178,
"travelDistanceFromLastStopToEndLocationMeters": 13815
}
}
modelOutput contains Carl’s shift itinerary.
2. Real-time planning update: extended stop
The plan needs to be updated to reflect the new situation.
Stop C took thirty minutes longer than expected.
Update the duration for Stop C from 10 minutes to 40 minutes.
Add the minStartTravelTime from the most recent planning output dataset (batch or real-time) to each stop.
{
"jobs": [
{
"id": "job1",
"stops": [
{
"id": "Stop A",
"location": [33.84475, -84.63649],
"duration": "PT10M",
"minStartTravelTime": "2027-02-01T00:00:00Z"
},
{
"id": "Stop B",
"location": [33.90719, -84.28149],
"duration": "PT10M",
"minStartTravelTime": "2027-02-01T00:00:00Z"
}
]
},
{
"id": "job2",
"stops": [
{
"id": "Stop C",
"location": [33.89351, -84.00649],
"duration": "PT40M",
"minStartTravelTime": "2027-02-01T00:00:00Z"
},
{
"id": "Stop D",
"location": [33.89351, -84.00649],
"duration": "PT10M",
"minStartTravelTime": "2027-02-01T00:00:00Z"
}
]
}
]
}
Add the itinerary to Carl’s shifts with Stop C, Stop A, Stop D and Stop B, including the stop IDs and the stop kind:
{
"id": "Carl",
"shifts": [
{
"id": "Carl-2027-02-01",
"startLocation": [33.68786, -84.18487],
"minStartTime": "2027-02-01T09:00:00Z",
"maxEndTime": "2027-02-01T11:00:00Z",
"itinerary": [
{
"id": "Stop C",
"kind": "STOP"
},
{
"id": "Stop A",
"kind": "STOP"
},
{
"id": "Stop D",
"kind": "STOP"
},
{
"id": "Stop B",
"kind": "STOP"
}
]
}
]
}
Freeze the departure times for stops that have already occurred and that drivers have begun traveling to by adding freezeTime:
{
"modelInput": {
"freezeTime": "2027-02-01T09:20:00Z"
}
}
Because Carl finished Stop C at 09:17 and is already traveling to Stop A at the freezeTime, this will keep Stop A scheduled after Stop C with a new arrival time.
|
Instead of resubmitting the full updated dataset, you can make this change with the Patch feature, which submits only the change as a JSON Patch document and generates a new revision of the plan. The Patch feature is currently available as a preview feature. If you’d like early access to it, please contact us.
To learn more about the The
Submit the full patch to modify the original input dataset to the following endpoint. Replace <ID> with the dataset ID:
When submitted, solving is triggered to generate a new revision of the plan.
|
Submit the updated input dataset to generate the new real-time plan.
-
Input
-
Output
Try this example in Timefold Platform by saving this JSON into a file called sample.json and make the following API call:
|
curl -X POST -H "Content-type: application/json" -H 'X-API-KEY: <API_KEY>' https://app.timefold.ai/api/models/pickup-delivery-routing/v1/route-plans [email protected]
{
"config": {
"run": {
"name": "Original shift plan: extended stop example"
}
},
"modelInput": {
"freezeTime": "2027-02-01T09:20:00Z",
"drivers": [
{
"id": "Carl",
"shifts": [
{
"id": "Carl Mon",
"startLocation": [33.77284, -84.42989],
"minStartTime": "2027-02-01T09:00:00Z",
"maxEndTime": "2027-02-01T11:00:00Z",
"itinerary":[
{
"id": "Stop C",
"kind": "STOP"
},
{
"id": "Stop A",
"kind": "STOP"
},
{
"id": "Stop D",
"kind": "STOP"
},
{
"id": "Stop B",
"kind": "STOP"
}
]
}
]
}
],
"jobs": [
{
"id": "job1",
"stops": [
{
"id": "Stop A",
"name": "Stop A",
"location": [33.74648, -84.46461],
"duration": "PT10M",
"minStartTravelTime": "2027-02-01T00:00:00Z"
},
{
"id": "Stop B",
"name": "Stop B",
"location": [33.65207, -84.46496],
"duration": "PT10M",
"minStartTravelTime": "2027-02-01T00:00:00Z"
}
]
},
{
"id": "job2",
"stops": [
{
"id": "Stop C",
"name": "Stop C",
"location": [33.77911, -84.49644],
"duration": "PT40M",
"minStartTravelTime": "2027-02-01T00:00:00Z"
},
{
"id": "Stop D",
"name": "Stop D",
"location": [33.65979, -84.46366],
"duration": "PT10M",
"minStartTravelTime": "2027-02-01T00:00:00Z"
}
]
}
]
}
}
To request the solution, locate the ID from the response to the post operation and append it to the following API call:
|
curl -X GET -H 'X-API-KEY: <API_KEY>' https://app.timefold.ai/api/models/pickup-delivery-routing/v1/route-plans/<ID>
{
"metadata": {
"id": "ID",
"originId": "ID",
"name": "Original shift plan: extended stop example",
"submitDateTime": "2026-04-09T16:11:41.258457+02:00",
"startDateTime": "2026-04-09T16:11:41.465113+02:00",
"activeDateTime": "2026-04-09T16:11:41.465887+02:00",
"completeDateTime": "2026-04-09T16:12:11.481571+02:00",
"shutdownDateTime": "2026-04-09T16:12:11.481574+02:00",
"solverStatus": "SOLVING_COMPLETED",
"score": "0hard/0medium/-2533soft",
"validationResult": {
"summary": "OK"
}
},
"modelOutput": {
"drivers": [
{
"id": "Carl",
"shifts": [
{
"id": "Carl Mon",
"startTime": "2027-02-01T09:00:00Z",
"itinerary": [
{
"id": "Stop C",
"arrivalTime": "2027-02-01T09:07:26Z",
"startServiceTime": "2027-02-01T09:07:26Z",
"departureTime": "2027-02-01T09:47:26Z",
"effectiveDuration": "PT40M",
"travelTimeFromPreviousStandstill": "PT7M26S",
"travelDistanceMetersFromPreviousStandstill": 6190,
"minStartTravelTime": "2027-02-01T00:00:00Z",
"load": [],
"pinned": true,
"kind": "STOP"
},
{
"id": "Stop A",
"arrivalTime": "2027-02-01T09:53:02Z",
"startServiceTime": "2027-02-01T09:53:02Z",
"departureTime": "2027-02-01T10:03:02Z",
"effectiveDuration": "PT10M",
"travelTimeFromPreviousStandstill": "PT5M36S",
"travelDistanceMetersFromPreviousStandstill": 4671,
"minStartTravelTime": "2027-02-01T09:20:00Z",
"load": [],
"kind": "STOP"
},
{
"id": "Stop D",
"arrivalTime": "2027-02-01T10:14:36Z",
"startServiceTime": "2027-02-01T10:14:36Z",
"departureTime": "2027-02-01T10:24:36Z",
"effectiveDuration": "PT10M",
"travelTimeFromPreviousStandstill": "PT11M34S",
"travelDistanceMetersFromPreviousStandstill": 9640,
"minStartTravelTime": "2027-02-01T09:20:00Z",
"load": [],
"kind": "STOP"
},
{
"id": "Stop B",
"arrivalTime": "2027-02-01T10:25:38Z",
"startServiceTime": "2027-02-01T10:25:38Z",
"departureTime": "2027-02-01T10:35:38Z",
"effectiveDuration": "PT10M",
"travelTimeFromPreviousStandstill": "PT1M2S",
"travelDistanceMetersFromPreviousStandstill": 867,
"minStartTravelTime": "2027-02-01T09:20:00Z",
"load": [],
"kind": "STOP"
}
],
"metrics": {
"totalTravelTime": "PT42M13S",
"travelTimeFromStartLocationToFirstStop": "PT7M26S",
"travelTimeBetweenStops": "PT18M12S",
"travelTimeFromLastStopToEndLocation": "PT16M35S",
"totalTravelDistanceMeters": 35183,
"travelDistanceFromStartLocationToFirstStopMeters": 6190,
"travelDistanceBetweenStopsMeters": 15178,
"travelDistanceFromLastStopToEndLocationMeters": 13815,
"endLocationArrivalTime": "2027-02-01T10:52:13Z"
}
}
]
}
],
"unassignedJobs": []
},
"inputMetrics": {
"jobs": 2,
"stops": 4,
"drivers": 1,
"driverShifts": 1,
"pinnedStops": 1
},
"kpis": {
"totalTravelTime": "PT42M13S",
"totalTravelDistanceMeters": 35183,
"totalActivatedDrivers": 1,
"totalUnassignedJobs": 0,
"totalAssignedJobs": 2,
"assignedMandatoryJobs": 2,
"totalUnassignedStops": 0,
"totalAssignedStops": 4,
"assignedMandatoryStops": 4,
"travelTimeFromStartLocationToFirstStop": "PT7M26S",
"travelTimeBetweenStops": "PT18M12S",
"travelTimeFromLastStopToEndLocation": "PT16M35S",
"travelDistanceFromStartLocationToFirstStopMeters": 6190,
"travelDistanceBetweenStopsMeters": 15178,
"travelDistanceFromLastStopToEndLocationMeters": 13815
}
}
modelOutput contains Carl’s updated shift itinerary.
Stop C took much longer than expected, so Stop A, Stop D and Stop B are all scheduled for later in the day.
Next
-
See the full API spec or try the online API.
-
Learn about real-time planning.
-
Real-time planning with pinned stops.