Visit service level agreement (SLA)
As described in the guide about time windows, visits 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 is often requirements about when the visit should be completed. This requirement is called a service level agreement (SLA) and describes the maximum time allowed to complete the visit. 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:
Prerequisites
To run the examples in this guide, you need to authenticate with a valid API key for this model:
-
Log in to Timefold Platform: app.timefold.ai.
-
From the Dashboard, click your tenant, and from the drop-down menu select Tenant Settings, then choose API Keys.
-
Create a new API key or use an existing one. Ensure the list of models for the API key contains the current model.
In the examples, replace <API_KEY>
with the API Key you just copied.
1. Visit SLAs
In order to define an SLA, the property latestSlaEndTime
needs to be added to the visit.
{
"visits": [
{
"id": "Visit A",
"location": [34.31785, -83.82816],
"serviceDuration": "PT1H30M",
"latestSlaEndTime": "2027-02-01T13:00:00Z"
}
]
}
latestSlaEndTime
uses the ISO 8601 date and time format with offset to UTC format.
Below is an example dataset with 1 vehicle and 2 visits, where 1 visit has a latestSlaEndTime
defined:
-
Input
-
Output
Try this example in Timefold Platform by saving the JSON into a file called sla-example-1.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/field-service-routing/v1/route-plans [email protected]
{
"config": {
"run": {
"name": "SLA example"
}
},
"modelInput": {
"vehicles": [
{
"id": "Carl",
"shifts": [
{
"id": "Carl-2027-02-01",
"startLocation": [33.68786, -84.18487],
"minStartTime": "2027-02-01T09:00:00Z",
"maxEndTime": "2027-02-01T17:00:00Z"
}
]
}
],
"visits": [
{
"id": "Visit A",
"location": [33.31785, -83.82816],
"serviceDuration": "PT2H30M"
},
{
"id": "Visit B",
"location": [33.32468, -84.127456],
"serviceDuration": "PT3H",
"latestSlaEndTime": "2027-02-01T13: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/field-service-routing/v1/route-plans/<ID>
{
"run": {
"id": "ID",
"name": "SLA example",
"submitDateTime": "2025-05-16T12:01:05.974541+02:00",
"startDateTime": "2025-05-16T12:01:05.987945+02:00",
"activeDateTime": "2025-05-16T12:01:05.993695+02:00",
"completeDateTime": "2025-05-16T12:01:36.002054+02:00",
"shutdownDateTime": "2025-05-16T12:01:36.004414+02:00",
"solverStatus": "SOLVING_COMPLETED",
"score": "0hard/0medium/-130078soft",
"tags": null,
"validationResult": {
"summary": "OK"
}
},
"modelOutput": {
"vehicles": [
{
"id": "Carl",
"shifts": [
{
"id": "Carl-2027-02-01",
"startTime": "2027-02-01T09:00:00Z",
"itinerary": [
{
"id": "Visit B",
"kind": "VISIT",
"arrivalTime": "2027-02-01T09:48:53Z",
"startServiceTime": "2027-02-01T09:48:53Z",
"departureTime": "2027-02-01T12:48:53Z",
"effectiveServiceDuration": "PT3H",
"travelTimeFromPreviousStandstill": "PT48M53S",
"travelDistanceMetersFromPreviousStandstill": 40733,
"minStartTravelTime": "2027-02-01T00:00:00Z"
},
{
"id": "Visit A",
"kind": "VISIT",
"arrivalTime": "2027-02-01T13:22:16Z",
"startServiceTime": "2027-02-01T13:22:16Z",
"departureTime": "2027-02-01T15:52:16Z",
"effectiveServiceDuration": "PT2H30M",
"travelTimeFromPreviousStandstill": "PT33M23S",
"travelDistanceMetersFromPreviousStandstill": 27819,
"minStartTravelTime": "2027-02-01T00:00:00Z"
}
],
"metrics": {
"totalTravelTime": "PT2H25M37S",
"travelTimeFromStartLocationToFirstVisit": "PT48M53S",
"travelTimeBetweenVisits": "PT33M23S",
"travelTimeFromLastVisitToEndLocation": "PT1H3M21S",
"totalTravelDistanceMeters": 121341,
"travelDistanceFromStartLocationToFirstVisitMeters": 40733,
"travelDistanceBetweenVisitsMeters": 27819,
"travelDistanceFromLastVisitToEndLocationMeters": 52789,
"endLocationArrivalTime": "2027-02-01T16:55:37Z",
"technicianCosts": null,
"overtime": null
}
}
]
}
]
},
"inputMetrics": {
"visits": 2,
"visitGroups": 0,
"vehicles": 1,
"mandatoryVisits": 2,
"optionalVisits": 0,
"vehicleShifts": 1,
"visitsWithSla": 1
},
"kpis": {
"totalTravelTime": "PT2H25M37S",
"travelTimeFromStartLocationToFirstVisit": "PT48M53S",
"travelTimeBetweenVisits": "PT33M23S",
"travelTimeFromLastVisitToEndLocation": "PT1H3M21S",
"totalTravelDistanceMeters": 121341,
"travelDistanceFromStartLocationToFirstVisitMeters": 40733,
"travelDistanceBetweenVisitsMeters": 27819,
"travelDistanceFromLastVisitToEndLocationMeters": 52789,
"totalUnassignedVisits": 0,
"totalAssignedVisits": 2,
"assignedMandatoryVisits": 2,
"assignedOptionalVisits": 0,
"totalActivatedVehicles": 1,
"workingTimeFairnessPercentage": 100.0,
"totalTechnicianCosts": null,
"totalOvertime": null,
"percentageVisitsInSla": "100.0",
"absoluteVisitsInSla": "1"
}
}
modelOutput
contains the itinerary for Carl’s shift with Visit A and Visit B.
In this example, Carl’s shift begins at 09:00.
There are 2 visits, Visit A and Visit B, that need to be scheduled.
Visit B has a latestSlaEndTime
of 2027-02-01T13:00:00Z
, which means that it must be completed by 13:00.
In order to accomplish that, Visit B needs to be handled first, otherwise it won’t be possible for Carl to be in the SLA for Visit B. Luckily, Visit A has more freedom and no SLA defined, so it can be scheduled after Visit B.
Next
-
See the full API spec or try the online API.
-
Learn more about field service routing from our YouTube playlist.