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
A job in the Pick-up and Delivery model contains a list of stops. The stops must be listed in the order they need to be performed.
{
"id": "Job A",
"stops": [
{
"id": "A1",
"location": [33.77911, -84.49644],
"duration": "PT20M"
},
{
"id": "A2",
"location": [ 33.73613, -84.38245],
"duration": "PT20M"
},
{
"id": "A3",
"location": [33.65979, -84.46366],
"duration": "PT20M"
}
]
}
Stop A1 is the first stop in the list of stops for job A, followed by stop A2 and then stop A3.
The order of the stops in the list defines the dependencies between the stops.
Therefore, a solution for job A must provide the stops in the sequence of A1, then A2, and then A3 in order to be a valid solution.
| Reach out to Timefold if you require a more complex approach to define dependencies between stops. |
-
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 -d@sample.json
{
"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"
},
{
"id": "A3",
"location": [33.65979, -84.46366],
"duration": "PT20M"
}
]
}
]
}
}
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 four constraints that manage dependencies between stops:
-
Do not assign stops if previous stop is unassigned
-
Assign stops in sequence
-
Assign the same driver shift for dependent stops
-
No semi-assigned jobs
The Do not assign stops if previous stop is unassigned 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 Assign stops in sequence hard constraint penalizes solutions with a hard score if the sequence of stops is not respected according to their order in the job definition.
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 Assign the same driver shift for dependent stops 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.