Stop service level agreement (SLA)
As described in the time windows guide, stops can have hard requirements for when they can be serviced. This can refer to business hours of the service location or describe the general availability of the customer on site. In addition to these hard constraints about site availability, there are often requirements regarding the latest time the stop can be completed. This requirement is called a service level agreement (SLA). This is often determined by the urgency of the incident that has been reported, and how quickly the service must be performed.
This guide describes the SLA with the following example:
1. Stop SLAs
Learn how to configure an API Key to run the examples in this guide:
In the examples, replace |
To define an SLA, add the property latestSlaEndTime to the stop.
{
"jobs": [
{
"id": "Job A",
"stops": [
{
"id": "A1",
"location": [33.78592, -84.46136],
"duration": "PT20M",
"latestSlaEndTime": "2027-02-01T15:00:00Z"
}
]
}
]
}
latestSlaEndTime uses the ISO 8601 date and time format with offset to UTC format.
The Latest SLA end time soft constraint is invoked for any stop with a latestSlaEndTime, where the latestSlaEndTime is broken.
To optimize for SLAs, the constraint adds a soft penalty to the dataset score if the stop ends after the time specified by latestSlaEndTime.
The penality is derived from the time between latestSlaEndTime and when the stop ends.
If a stop with a latestSlaEndTime remains unassigned and the latestSlaEndTime is within the planning window, the soft penalty will also be applied.
In this case, the penalty is derived from the time between latestSlaEndTime and the end of the planning window.
Stops can still be scheduled even if doing so breaks this constraint, but Timefold is incentivized to use the route plan with the best score.
1.1. Stop SLAs example
Below is an example dataset with two drivers and one job with two stops, where one stop has a latestSlaEndTime defined:
-
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": "Latest SLA end time example"
}
},
"modelInput": {
"drivers": [
{
"id": "Ann",
"shifts": [
{
"id": "Ann Mon",
"startLocation": [33.68786, -84.18487],
"endLocation": [33.68786, -84.18487],
"minStartTime": "2027-02-01T08:00:00Z",
"maxEndTime": "2027-02-01T16:00:00Z"
}
]
},
{
"id": "Carl",
"shifts": [
{
"id": "Carl Mon",
"startLocation": [33.7859, -84.4613],
"endLocation": [33.7859, -84.4613],
"minStartTime": "2027-02-01T12:00:00Z",
"maxEndTime": "2027-02-01T20:00:00Z"
}
]
}
],
"jobs": [
{
"id": "Job A",
"stops": [
{
"id": "A1",
"location": [33.78592, -84.46136],
"duration": "PT20M"
},
{
"id": "A2",
"location": [33.72757, -83.96354],
"duration": "PT20M",
"latestSlaEndTime": "2027-02-01T12:00:00Z",
"stopDependencies": [
{
"id": "jobA_dep1",
"precedingStop": "A1"
}
]
}
]
}
]
}
}
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": "Latest SLA end time example",
"submitDateTime": "2025-12-17T10:30:21.323246+01:00",
"startDateTime": "2025-12-17T10:30:21.345487+01:00",
"activeDateTime": "2025-12-17T10:30:21.346208+01:00",
"completeDateTime": "2025-12-17T10:30:51.356161+01:00",
"shutdownDateTime": "2025-12-17T10:30:51.356163+01:00",
"solverStatus": "SOLVING_COMPLETED",
"score": "0hard/0medium/-6855soft",
"validationResult": {
"summary": "OK"
}
},
"modelOutput": {
"drivers": [
{
"id": "Ann",
"shifts": [
{
"id": "Ann Mon",
"startTime": "2027-02-01T08:00:00Z",
"itinerary": [
{
"id": "A1",
"kind": "STOP",
"arrivalTime": "2027-02-01T08:33:21Z",
"startServiceTime": "2027-02-01T08:33:21Z",
"departureTime": "2027-02-01T08:53:21Z",
"effectiveServiceDuration": "PT20M",
"travelTimeFromPreviousStandstill": "PT33M21S",
"travelDistanceMetersFromPreviousStandstill": 27795,
"load": []
},
{
"id": "A2",
"kind": "STOP",
"arrivalTime": "2027-02-01T09:49:07Z",
"startServiceTime": "2027-02-01T09:49:07Z",
"departureTime": "2027-02-01T10:09:07Z",
"effectiveServiceDuration": "PT20M",
"travelTimeFromPreviousStandstill": "PT55M46S",
"travelDistanceMetersFromPreviousStandstill": 46477,
"load": []
}
],
"metrics": {
"totalTravelTime": "PT1H54M15S",
"travelTimeFromStartLocationToFirstStop": "PT33M21S",
"travelTimeBetweenStops": "PT55M46S",
"travelTimeFromLastStopToEndLocation": "PT25M8S",
"totalTravelDistanceMeters": 95216,
"travelDistanceFromStartLocationToFirstStopMeters": 27795,
"travelDistanceBetweenStopsMeters": 46477,
"travelDistanceFromLastStopToEndLocationMeters": 20944,
"endLocationArrivalTime": "2027-02-01T10:34:15Z"
}
}
]
},
{
"id": "Carl",
"shifts": [
{
"id": "Carl Mon",
"startTime": "2027-02-01T12:00:00Z",
"itinerary": [],
"metrics": {
"totalTravelTime": "PT0S",
"travelTimeFromStartLocationToFirstStop": "PT0S",
"travelTimeBetweenStops": "PT0S",
"travelTimeFromLastStopToEndLocation": "PT0S",
"totalTravelDistanceMeters": 0,
"travelDistanceFromStartLocationToFirstStopMeters": 0,
"travelDistanceBetweenStopsMeters": 0,
"travelDistanceFromLastStopToEndLocationMeters": 0
}
}
]
}
],
"unassignedJobs": []
},
"inputMetrics": {
"jobs": 1,
"stops": 2,
"drivers": 2,
"driverShifts": 2
},
"kpis": {
"totalTravelTime": "PT1H54M15S",
"totalTravelDistanceMeters": 95216,
"totalActivatedDrivers": 1,
"totalUnassignedJobs": 0,
"totalAssignedJobs": 1,
"assignedMandatoryJobs": 1,
"totalUnassignedStops": 0,
"totalAssignedStops": 2,
"assignedMandatoryStops": 2,
"travelTimeFromStartLocationToFirstStop": "PT33M21S",
"travelTimeBetweenStops": "PT55M46S",
"travelTimeFromLastStopToEndLocation": "PT25M8S",
"travelDistanceFromStartLocationToFirstStopMeters": 27795,
"travelDistanceBetweenStopsMeters": 46477,
"travelDistanceFromLastStopToEndLocationMeters": 20944
}
}
In this example, there are two drivers: Ann and Carl.
Anne’s shift begins at 08:00 and Carl’s shift begins at 12:00.
There is one job with two stops: stop A1 and stop A2.
Stop A2 has a latestSlaEndTime of 2027-02-01T12:00:00Z.
Although Carl’s location is very close to stop A1, the job gets assigned to Ann.
If it had been assigned to Carl, the SLA for stop A2 would have been broken.
Next
-
See the full API spec or try the online API.