Dependencies between stops
In pick-up and delivery routing it is important to make sure stops are scheduled in the correct order and that stops in the same job are assigned to the same driver.
An item cannot be delivered before it has been collected, and both the pick-up and delivery must be made by the same driver.
1. Defining stop dependencies
Stops specify which stop (if any) they come after in the sequence:
{
"id": "Job A",
"stops": [
{
"id": "A1",
"location": [33.77911, -84.49644],
"duration": "PT20M"
},
{
"id": "A2",
"location": [ 33.73613, -84.38245],
"duration": "PT20M",
"stopDependencies": [
{
"id": "A_dep1",
"precedingStop": "A1"
}
]
},
{
"id": "A3",
"location": [33.65979, -84.46366],
"duration": "PT20M",
"stopDependencies": [
{
"id": "A_dep2",
"precedingStop": "A2"
}
]
}
]
}
Stop A1 does not include a stop dependency because it is the first stop in the job.
Stop A2 lists stop A1 as a procedingStop.
Stop A2 cannot occur until after stop A1.
{
"id": "A2",
"location": [ 33.73613, -84.38245],
"duration": "PT20M",
"stopDependencies": [
{
"id": "A_dep1",
"precedingStop": "A1"
}
]
}
Stop A3 lists stop A2 as a procedingStop.
Stop A3 cannot occur until after stop A2.
{
"id": "A3",
"location": [33.65979, -84.46366],
"duration": "PT20M",
"stopDependencies": [
{
"id": "A_dep2",
"precedingStop": "A2"
}
]
}
| Stops can define multiple preceding stops in advanced scenarios; however, it is important in this situation not to create a loop. For basic scenarios we recommend defining a single preceding stops per stop. |
Stop A1 comes first and is followed by stop A2, which is followed by stop A3.
-
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": "Dependencies between stops example"
}
},
"modelInput": {
"drivers": [
{
"id": "Ann",
"shifts": [
{
"id": "Ann Mon",
"startLocation": [33.75522, -84.32040],
"endLocation": [33.75522, -84.32040],
"minStartTime": "2027-02-01T09:00:00Z",
"maxEndTime": "2027-02-01T17:00:00Z"
}
]
}
],
"jobs": [
{
"id": "Job A",
"stops": [
{
"id": "A1",
"location": [33.77911, -84.49644],
"duration": "PT20M"
},
{
"id": "A2",
"location": [33.79656, -84.34159],
"duration": "PT20M",
"stopDependencies": [
{
"id": "A_dep1",
"precedingStop": "A1"
}
]
},
{
"id": "A3",
"location": [33.65979, -84.46366],
"duration": "PT20M",
"stopDependencies": [
{
"id": "A_dep2",
"precedingStop": "A2"
}
]
}
]
}
]
}
}
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",
"parentId": null,
"originId": "ID",
"name": "Dependencies between stops example",
"submitDateTime": "2025-08-14T07:46:32.053666902Z",
"startDateTime": "2025-08-14T07:46:37.739466519Z",
"activeDateTime": "2025-08-14T07:46:38.278549981Z",
"completeDateTime": "2025-08-14T07:46:39.734340868Z",
"shutdownDateTime": "2025-08-14T07:46:40.472425825Z",
"solverStatus": "SOLVING_COMPLETED",
"score": "0hard/0medium/-5817soft",
"tags": [
"system.type:from-request",
"system.profile:default"
],
"validationResult": {
"summary": "OK"
}
},
"modelOutput": {
"drivers": [
{
"id": "Ann",
"shifts": [
{
"id": "Ann Mon",
"startTime": "2027-02-01T09:00:00Z",
"itinerary": [
{
"id": "A1",
"arrivalTime": "2027-02-01T09:20:47Z",
"startServiceTime": "2027-02-01T09:20:47Z",
"departureTime": "2027-02-01T09:40:47Z",
"effectiveServiceDuration": "PT20M",
"kind": "STOP",
"travelTimeFromPreviousStandstill": "PT20M47S",
"travelDistanceMetersFromPreviousStandstill": 21621
},
{
"id": "A2",
"arrivalTime": "2027-02-01T10:03:47Z",
"startServiceTime": "2027-02-01T10:03:47Z",
"departureTime": "2027-02-01T10:23:47Z",
"effectiveServiceDuration": "PT20M",
"kind": "STOP",
"travelTimeFromPreviousStandstill": "PT23M",
"travelDistanceMetersFromPreviousStandstill": 21406
},
{
"id": "A3",
"arrivalTime": "2027-02-01T10:51:56Z",
"startServiceTime": "2027-02-01T10:51:56Z",
"departureTime": "2027-02-01T11:11:56Z",
"effectiveServiceDuration": "PT20M",
"kind": "STOP",
"travelTimeFromPreviousStandstill": "PT28M9S",
"travelDistanceMetersFromPreviousStandstill": 23763
}
],
"metrics": {
"totalTravelTime": "PT1H36M57S",
"travelTimeFromStartLocationToFirstStop": "PT20M47S",
"travelTimeBetweenStops": "PT51M9S",
"travelTimeFromLastStopToEndLocation": "PT25M1S",
"totalTravelDistanceMeters": 88552,
"travelDistanceFromStartLocationToFirstStopMeters": 21621,
"travelDistanceBetweenStopsMeters": 45169,
"travelDistanceFromLastStopToEndLocationMeters": 21762,
"endLocationArrivalTime": "2027-02-01T11:36:57Z",
"overtime": "PT0S"
}
}
]
}
]
},
"inputMetrics": {
"stops": 3,
"drivers": 1,
"driverShifts": 1
},
"kpis": {
"totalTravelTime": "PT1H36M57S",
"travelTimeFromStartLocationToFirstStop": "PT20M47S",
"travelTimeBetweenStops": "PT51M9S",
"travelTimeFromLastStopToEndLocation": "PT25M1S",
"totalTravelDistanceMeters": 88552,
"travelDistanceFromStartLocationToFirstStopMeters": 21621,
"travelDistanceBetweenStopsMeters": 45169,
"travelDistanceFromLastStopToEndLocationMeters": 21762,
"totalUnassignedStops": 0,
"totalAssignedStops": 3,
"totalActivatedDrivers": 1,
"totalOvertime": "PT0S"
}
}
There are 4 constraints that manage dependencies between stops:
-
Require stop dependency prerequisite
-
Require stop dependency sequence
-
Require the same driver shift for dependent stop
-
No semi-assigned jobs
The Require stop dependency prerequisite hard constraint penalizes solutions with a hard score if the preceding stop is not assigned when the following stop is assigned.
The score is based on the sum of the effective service durations of the preceding and following stops.
Stops will not be assigned if they break this constraint.
The Require stop dependency sequence hard constraint penalize solutions with a hard score if the sequence of stops is not respected according to their dependency.
The score is based on the time between the preceding stop’s departure time and the following stop’s start service time.
In case the preceding stop is unassigned, the effective service duration of the preceding stop is used.
Stops will not be assigned if they break this constraint.
The Require the same driver shift for dependent stop hard constraint penalizes solutions with a hard score if the dependent stop is not assigned to the same driver shift as the preceding stop.
The score is based on the effective service duration of the following stop.
Stops will not be assigned if they break this constraint.
The No semi-assigned jobs hard constraint penalizes solutions with a hard score if a job is semi-assigned (all of its stops must be either assigned or unassigned).
Stops will not be assigned if they break this constraint.
Next
-
See the full API spec or try the online API.