Docs
  • Solver
  • Models
    • Field Service Routing
    • Employee Shift Scheduling
    • Pick-up and Delivery Routing
  • Platform
Try models
  • Field Service Routing
  • Visit service constraints
  • Visit service level agreement (SLA)

Field Service Routing

    • Introduction
    • Getting started: Hello world
    • User guide
      • Terms
      • Planning AI concepts
      • Constraints
      • Understanding the API
      • Demo datasets
      • Planning window
      • Model configuration
      • Configuration overrides
      • Time zones and daylight-saving time (DST)
      • Routing with Timefold’s maps service
      • Validation
      • Model response
      • Key performance indicators (KPIs)
      • Metrics and optimization goals
    • Vehicle resource constraints
      • Shift hours and overtime
      • Lunch breaks and personal appointments
      • Fairness
      • Route optimization
      • Technician costs
      • Technician ratings
    • Visit service constraints
      • Time windows and opening hours
      • Skills
      • Visit dependencies
      • Visit requirements, area affinity, and tags
      • Multi-vehicle visits
      • Movable visits and multi-day schedules
      • Priority visits and optional visits
      • Visit service level agreement (SLA)
    • Recommendations
      • Visit time window recommendations
      • Visit group time window recommendations
    • Real-time planning
      • Real-time planning: extended visit
      • Real-time planning: reassignment
      • Real-time planning: emergency visit
      • Real-time planning: no show
      • Real-time planning: technician ill
      • Real-time planning: pinning visits
    • Real-time planning (preview)
      • Real-time planning: extended visit (preview)
      • Real-time planning: reassignment (preview)
      • Real-time planning: emergency visit (preview)
      • Real-time planning: no show (Preview)
      • Real-time planning: technician ill (Preview)
      • Real-time planning: pinning visits (preview)
    • Scenarios
      • Long-running visits
    • Changelog
    • Upgrade to the latest version
    • Feature requests

Visit service level agreement (SLA)

Visits can have hard and soft requirements for the time windows when visits can occur. These time windows can represent the business hours of the service location, the customer’s availability, or even the customer’s preferred times.

In addition to these constraints about site availability, there are often requirements (including contractual requirements) regarding the latest time visits can be completed. For instance, if the company promises service within a specific time period of a service being requested.

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:

  • Visit SLAs

Prerequisites

Learn how to configure an API Key to run the examples in this guide:
  1. Log in to Timefold Platform: app.timefold.ai.

  2. From the Dashboard, click your tenant, and from the drop-down menu select Tenant Settings, then choose API Keys.

  3. 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

SLAs are defined in visits:

{
  "visits": [
    {
      "id": "Visit A",
      "location": [34.31785, -83.82816],
      "serviceDuration": "PT1H30M",
      "latestSlaEndTime": "2027-02-01T13:00:00Z"
    }
  ]
}

latestSlaEndTime defines the latest time the visit can end to satisfy the SLA. latestSlaEndTime uses ISO 8601 date and time format with offset to UTC format.

The Latest SLA end time soft constraint is invoked for any visit with a latestSlaEndTime. To optimize for SLAs, the constraint adds a soft score penalty to the dataset if the visit ends after the time specified by latestSlaEndTime. The penalty is derived from the time between latestSlaEndTime and when the visit ends.

Visits can still be scheduled even if doing so breaks this constraint, but Timefold is incentivized to use the route plan with the best score.

Every soft constraint has a weight that can be configured to change the relative importance of the constraint compared to other constraints.

Learn about constraint weights.

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.

Visit A does not include a latestSlaEndTime.

Visit B is scheduled first so the visit can be completed by the latestSlaEndTime.

visit slas
  • 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>
{
  "metadata": {
    "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"
  }
}

Next

  • See the full API spec or try the online API.

  • Learn more about field service routing from our YouTube playlist.

  • © 2025 Timefold BV
  • Timefold.ai
  • Documentation
  • Changelog
  • Send feedback
  • Privacy
  • Legal
    • Light mode
    • Dark mode
    • System default