Docs
  • Solver
  • Models
    • Field Service Routing
    • Employee Shift Scheduling
    • Pick-up and Delivery Routing
  • Platform
Try models
  • Field Service Routing
  • Visit service constraints
  • Visit requirements, area affinity, and tags
  • Technician coverage area

Field Service Routing

    • Introduction
    • Getting started: Hello world
    • User guide
      • Terms
      • Use case guide
      • Planning AI concepts
      • Integration
      • 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
      • Input 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
      • Multi-vehicle visits
      • Movable visits and multi-day schedules
      • Priority visits and optional visits
      • Visit service level agreement (SLA)
      • Visit requirements, area affinity, and tags
        • Visit requirements
        • Technician coverage area
        • Tags
    • 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
      • Configuring labor law compliance
    • Changelog
    • Upgrade to the latest version
    • Feature requests

Technician coverage area

Customer sites are in locations that technicians must travel to in order to perform the required work.

Technicians can cover specific areas during their shifts, but they might also have a preference for which areas to work in (or not). For instance, one technician might only be able to cover a specific city or region, while another technician prefers to work locations close to where their children go to school but will work in other areas as required.

A covered (or required) area is enforced as a hard constraint, while the preferred area is a soft constraint. Both required and preferred areas can be defined independently of each other, however, when both are defined the required area and preferred area need to overlap (at least partly) for the preferred area to be considered.

In Timefold, a technician’s required or preferred area can be defined using either a single polygon or multiple polygons. The area definition uses a subset of the GeoJSON format, namely Polygon and MultiPolygon geometry types.

This guide explains how to define technician coverage areas with the following examples:

  • 1. Required area coverage with a single polygon
  • 2. Required area coverage defined by multiple polygons
  • 3. Area preference defined by a polygon

1. Required area coverage with a single polygon

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 Manage tenant, 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.

A vehicle’s requiredArea property can be used to define the area that a technician can cover during their shift. If the property is not set, the technician can cover any visit location.

The following example shows a technician with a coverage area defined by a single polygon:

{
  "requiredArea": {
    "type": "Polygon",
    "coordinates": [
        [
          [ -85.0, 34.0 ],
          [ -84.2, 34.0 ],
          [ -84.2, 33.0 ],
          [ -85.0, 33.0 ],
          [ -85.0, 34.0 ]
        ]
      ]
    }
}
  • The coordinates array must contain at least one array of coordinates describing the polygon’s outer boundary.

  • Any additional arrays of coordinates would describe holes in the polygon (not used in this example).

  • Coordinates are expressed as [longitude, latitude] pairs.

    Compatibility with the GeoJSON specification means that the order of coordinates is different from the common [latitude, longitude] format used when defining locations throughout the rest of the model input.
  • Each array of coordinates must contain at least four coordinate pairs.

  • The first and the last points in the coordinates array must be the same to close the polygon.

Please see GeoJSON specification for more details about the Polygon type.

1.1. Required area coverage with a single polygon example

In the following example, Carl and Beth have shifts. Both Beth and Carl have their coverage area defined which covers visits A and B, but not visit C.

Timefold assigns the visits A and B and leaves C unassigned.

area coverage
  • 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/field-service-routing/v1/route-plans [email protected]
{
  "config": {
    "run": {
      "name": "Technician area coverage - Polygon example"
    }
  },
  "modelInput": {
    "vehicles": [
      {
        "id": "Beth",
        "requiredArea": {
          "type": "Polygon",
          "coordinates": [
            [
              [ -85.0, 34.0 ],
              [ -84.2, 34.0 ],
              [ -84.2, 33.0 ],
              [ -85.0, 33.0 ],
              [ -85.0, 34.0 ]
            ]
          ]
        },
        "shifts": [
          {
            "id": "Beth-2027-02-01",
            "startLocation": [33.72470, -84.37847],
            "minStartTime": "2027-02-01T09:00:00Z",
            "maxEndTime": "2027-02-01T17:00:00Z"
          }
        ]
      },
      {
        "id": "Carl",
        "requiredArea": {
          "type": "Polygon",
          "coordinates": [
            [
              [ -84.2, 34.0 ],
              [ -83.0, 34.0 ],
              [ -83.0, 33.0 ],
              [ -84.2, 33.0 ],
              [ -84.2, 34.0 ]
            ]
          ]
        },
        "shifts": [
          {
            "id": "Carl-2027-02-01",
            "startLocation": [33.68786, -84.18487],
            "minStartTime": "2027-02-01T13:00:00Z",
            "maxEndTime": "2027-02-01T17:00:00Z"
          }
        ]
      }
    ],
    "visits": [
      {
        "id": "Visit A",
        "location": [33.77301, -84.43838],
        "serviceDuration": "PT2H"
      },
      {
        "id": "Visit B",
        "location": [33.74699, -84.02504],
        "serviceDuration": "PT2H"
      },
      {
        "id": "Visit C",
        "location": [34.11155, -84.57443],
        "serviceDuration": "PT1H"
      }
    ]
  }
}
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",
    "originId": "ID",
    "name": "Technician area coverage - Polygon example",
    "submitDateTime": "2025-11-21T06:14:07.446286553Z",
    "startDateTime": "2025-11-21T06:14:26.865763195Z",
    "activeDateTime": "2025-11-21T06:14:26.905084455Z",
    "completeDateTime": "2025-11-21T06:14:57.41871743Z",
    "shutdownDateTime": "2025-11-21T06:14:57.418727721Z",
    "solverStatus": "SOLVING_COMPLETED",
    "score": "0hard/-10000medium/-4562soft",
    "tags": [
      "system.type:from-request",
      "system.profile:default"
    ],
    "validationResult": {
      "summary": "OK"
    }
  },
  "modelOutput": {
    "vehicles": [
      {
        "id": "Beth",
        "shifts": [
          {
            "id": "Beth-2027-02-01",
            "startTime": "2027-02-01T09:00:00Z",
            "itinerary": [
              {
                "id": "Visit A",
                "kind": "VISIT",
                "arrivalTime": "2027-02-01T09:14:38Z",
                "startServiceTime": "2027-02-01T09:14:38Z",
                "departureTime": "2027-02-01T11:14:38Z",
                "effectiveServiceDuration": "PT2H",
                "travelTimeFromPreviousStandstill": "PT14M38S",
                "travelDistanceMetersFromPreviousStandstill": 11045,
                "minStartTravelTime": "2027-02-01T00:00:00Z"
              }
            ],
            "metrics": {
              "totalTravelTime": "PT28M23S",
              "travelTimeFromStartLocationToFirstVisit": "PT14M38S",
              "travelTimeBetweenVisits": "PT0S",
              "travelTimeFromLastVisitToEndLocation": "PT13M45S",
              "totalTravelDistanceMeters": 21824,
              "travelDistanceFromStartLocationToFirstVisitMeters": 11045,
              "travelDistanceBetweenVisitsMeters": 0,
              "travelDistanceFromLastVisitToEndLocationMeters": 10779,
              "endLocationArrivalTime": "2027-02-01T11:28:23Z"
            }
          }
        ]
      },
      {
        "id": "Carl",
        "shifts": [
          {
            "id": "Carl-2027-02-01",
            "startTime": "2027-02-01T13:00:00Z",
            "itinerary": [
              {
                "id": "Visit B",
                "kind": "VISIT",
                "arrivalTime": "2027-02-01T13:21:37Z",
                "startServiceTime": "2027-02-01T13:21:37Z",
                "departureTime": "2027-02-01T15:21:37Z",
                "effectiveServiceDuration": "PT2H",
                "travelTimeFromPreviousStandstill": "PT21M37S",
                "travelDistanceMetersFromPreviousStandstill": 21413,
                "minStartTravelTime": "2027-02-01T00:00:00Z"
              }
            ],
            "metrics": {
              "totalTravelTime": "PT43M17S",
              "travelTimeFromStartLocationToFirstVisit": "PT21M37S",
              "travelTimeBetweenVisits": "PT0S",
              "travelTimeFromLastVisitToEndLocation": "PT21M40S",
              "totalTravelDistanceMeters": 42635,
              "travelDistanceFromStartLocationToFirstVisitMeters": 21413,
              "travelDistanceBetweenVisitsMeters": 0,
              "travelDistanceFromLastVisitToEndLocationMeters": 21222,
              "endLocationArrivalTime": "2027-02-01T15:43:17Z"
            }
          }
        ]
      }
    ],
    "unassignedVisits": [
      "Visit C"
    ]
  },
  "inputMetrics": {
    "visits": 3,
    "visitGroups": 0,
    "visitDependencies": 0,
    "mandatoryVisits": 3,
    "optionalVisits": 0,
    "vehicles": 2,
    "vehicleShifts": 2,
    "visitsWithSla": 0,
    "movableVisits": 0,
    "pinnedVisits": 0
  },
  "kpis": {
    "totalTravelTime": "PT1H11M40S",
    "travelTimeFromStartLocationToFirstVisit": "PT36M15S",
    "travelTimeBetweenVisits": "PT0S",
    "travelTimeFromLastVisitToEndLocation": "PT35M25S",
    "totalTravelDistanceMeters": 64459,
    "travelDistanceFromStartLocationToFirstVisitMeters": 32458,
    "travelDistanceBetweenVisitsMeters": 0,
    "travelDistanceFromLastVisitToEndLocationMeters": 32001,
    "totalUnassignedVisits": 1,
    "totalAssignedVisits": 2,
    "assignedMandatoryVisits": 2,
    "assignedOptionalVisits": 0,
    "totalActivatedVehicles": 2,
    "workingTimeFairnessPercentage": 62.48
  },
  "run": {
    "id": "e5b058fa-bef0-4fd8-b9ef-5e6bf477282e",
    "originId": "e5b058fa-bef0-4fd8-b9ef-5e6bf477282e",
    "name": "Technician area coverage - Polygon example",
    "submitDateTime": "2025-11-21T06:14:07.446286553Z",
    "startDateTime": "2025-11-21T06:14:26.865763195Z",
    "activeDateTime": "2025-11-21T06:14:26.905084455Z",
    "completeDateTime": "2025-11-21T06:14:57.41871743Z",
    "shutdownDateTime": "2025-11-21T06:14:57.418727721Z",
    "solverStatus": "SOLVING_COMPLETED",
    "score": "0hard/-10000medium/-4562soft",
    "tags": [
      "system.type:from-request",
      "system.profile:default"
    ],
    "validationResult": {
      "summary": "OK"
    }
  }
}

2. Required area coverage defined by multiple polygons

The following shows a technician with a coverage area defined by multiple polygons, or a MultiPolygon:

{
  "requiredArea": {
    "type": "MultiPolygon",
    "coordinates": [
        [
          [
            [ -85.0, 34.0 ],
            [ -84.2, 34.0 ],
            [ -84.2, 33.0 ],
            [ -85.0, 33.0 ],
            [ -85.0, 34.0 ]
          ]
        ],
        [
          [
            [ -85.1, 35.0 ],
            [ -84.3, 35.0 ],
            [ -84.3, 34.0 ],
            [ -85.1, 34.0 ],
            [ -85.1, 35.0 ]
          ]
        ]
      ]
    }
}
  • The coordinates array contains multiple arrays, each representing a polygon.

  • Each of the polygon arrays must follow the same rules as described in the Required area coverage with a single polygon section above.

  • Coordinates are expressed as [longitude, latitude] pairs.

    Compatibility with the GeoJSON specification means that the order of coordinates is different from the common [latitude, longitude] format used when defining locations throughout the rest of the model input.

Please see GeoJSON specification for more details about the MultiPolygon type.

2.1. Required area coverage defined by multiple polygons example

In the following example, Carl and Beth have shifts. Carl’s area covers visit B. Beth’s area is defined using multiple polygons, the first of them covering visit A and the second polygon covering visit C.

Timefold assigns all visits A, B and C because they are located within a technician’s coverage area.

area coverage multi
  • 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/field-service-routing/v1/route-plans [email protected]
{
  "config": {
    "run": {
      "name": "Technician area coverage - MultiPolygon example"
    }
  },
  "modelInput": {
    "vehicles": [
      {
        "id": "Beth",
        "requiredArea": {
          "type": "MultiPolygon",
          "coordinates": [
            [
              [
                [ -85.0, 34.0 ],
                [ -84.2, 34.0 ],
                [ -84.2, 33.0 ],
                [ -85.0, 33.0 ],
                [ -85.0, 34.0 ]
              ]
            ],
            [
              [
                [ -85.1, 35.0 ],
                [ -84.3, 35.0 ],
                [ -84.3, 34.0 ],
                [ -85.1, 34.0 ],
                [ -85.1, 35.0 ]
              ]
            ]
          ]
        },
        "shifts": [
          {
            "id": "Beth-2027-02-01",
            "startLocation": [33.72470, -84.37847],
            "minStartTime": "2027-02-01T09:00:00Z",
            "maxEndTime": "2027-02-01T17:00:00Z"
          }
        ]
      },
      {
        "id": "Carl",
        "requiredArea": {
          "type": "Polygon",
          "coordinates": [
            [
              [ -84.2, 34.0 ],
              [ -83.0, 34.0 ],
              [ -83.0, 33.0 ],
              [ -84.2, 33.0 ],
              [ -84.2, 34.0 ]
            ]
          ]
        },
        "shifts": [
          {
            "id": "Carl-2027-02-01",
            "startLocation": [33.68786, -84.18487],
            "minStartTime": "2027-02-01T13:00:00Z",
            "maxEndTime": "2027-02-01T17:00:00Z"
          }
        ]
      }
    ],
    "visits": [
      {
        "id": "Visit A",
        "location": [33.77301, -84.43838],
        "serviceDuration": "PT2H"
      },
      {
        "id": "Visit B",
        "location": [33.74699, -84.02504],
        "serviceDuration": "PT2H"
      },
      {
        "id": "Visit C",
        "location": [34.11155, -84.57443],
        "serviceDuration": "PT2H"
      }
    ]
  }
}
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",
    "originId": "ID",
    "name": "Technician area coverage - MultiPolygon example",
    "submitDateTime": "2025-11-21T06:42:06.087613129Z",
    "startDateTime": "2025-11-21T06:42:18.856904447Z",
    "activeDateTime": "2025-11-21T06:42:18.930121181Z",
    "completeDateTime": "2025-11-21T06:42:49.519119395Z",
    "shutdownDateTime": "2025-11-21T06:42:49.519129695Z",
    "solverStatus": "SOLVING_COMPLETED",
    "score": "0hard/0medium/-8717soft",
    "tags": [
      "system.type:from-request",
      "system.profile:default"
    ],
    "validationResult": {
      "summary": "OK"
    }
  },
  "modelOutput": {
    "vehicles": [
      {
        "id": "Beth",
        "shifts": [
          {
            "id": "Beth-2027-02-01",
            "startTime": "2027-02-01T09:00:00Z",
            "itinerary": [
              {
                "id": "Visit C",
                "kind": "VISIT",
                "arrivalTime": "2027-02-01T09:45:47Z",
                "startServiceTime": "2027-02-01T09:45:47Z",
                "departureTime": "2027-02-01T11:45:47Z",
                "effectiveServiceDuration": "PT2H",
                "travelTimeFromPreviousStandstill": "PT45M47S",
                "travelDistanceMetersFromPreviousStandstill": 53061,
                "minStartTravelTime": "2027-02-01T00:00:00Z"
              },
              {
                "id": "Visit A",
                "kind": "VISIT",
                "arrivalTime": "2027-02-01T12:27:53Z",
                "startServiceTime": "2027-02-01T12:27:53Z",
                "departureTime": "2027-02-01T14:27:53Z",
                "effectiveServiceDuration": "PT2H",
                "travelTimeFromPreviousStandstill": "PT42M6S",
                "travelDistanceMetersFromPreviousStandstill": 49213,
                "minStartTravelTime": "2027-02-01T00:00:00Z"
              }
            ],
            "metrics": {
              "totalTravelTime": "PT1H41M38S",
              "travelTimeFromStartLocationToFirstVisit": "PT45M47S",
              "travelTimeBetweenVisits": "PT42M6S",
              "travelTimeFromLastVisitToEndLocation": "PT13M45S",
              "totalTravelDistanceMeters": 113053,
              "travelDistanceFromStartLocationToFirstVisitMeters": 53061,
              "travelDistanceBetweenVisitsMeters": 49213,
              "travelDistanceFromLastVisitToEndLocationMeters": 10779,
              "endLocationArrivalTime": "2027-02-01T14:41:38Z"
            }
          }
        ]
      },
      {
        "id": "Carl",
        "shifts": [
          {
            "id": "Carl-2027-02-01",
            "startTime": "2027-02-01T13:00:00Z",
            "itinerary": [
              {
                "id": "Visit B",
                "kind": "VISIT",
                "arrivalTime": "2027-02-01T13:21:37Z",
                "startServiceTime": "2027-02-01T13:21:37Z",
                "departureTime": "2027-02-01T15:21:37Z",
                "effectiveServiceDuration": "PT2H",
                "travelTimeFromPreviousStandstill": "PT21M37S",
                "travelDistanceMetersFromPreviousStandstill": 21413,
                "minStartTravelTime": "2027-02-01T00:00:00Z"
              }
            ],
            "metrics": {
              "totalTravelTime": "PT43M17S",
              "travelTimeFromStartLocationToFirstVisit": "PT21M37S",
              "travelTimeBetweenVisits": "PT0S",
              "travelTimeFromLastVisitToEndLocation": "PT21M40S",
              "totalTravelDistanceMeters": 42635,
              "travelDistanceFromStartLocationToFirstVisitMeters": 21413,
              "travelDistanceBetweenVisitsMeters": 0,
              "travelDistanceFromLastVisitToEndLocationMeters": 21222,
              "endLocationArrivalTime": "2027-02-01T15:43:17Z"
            }
          }
        ]
      }
    ],
    "unassignedVisits": []
  },
  "inputMetrics": {
    "visits": 3,
    "visitGroups": 0,
    "visitDependencies": 0,
    "mandatoryVisits": 3,
    "optionalVisits": 0,
    "vehicles": 2,
    "vehicleShifts": 2,
    "visitsWithSla": 0,
    "movableVisits": 0,
    "pinnedVisits": 0
  },
  "kpis": {
    "totalTravelTime": "PT2H24M55S",
    "travelTimeFromStartLocationToFirstVisit": "PT1H7M24S",
    "travelTimeBetweenVisits": "PT42M6S",
    "travelTimeFromLastVisitToEndLocation": "PT35M25S",
    "totalTravelDistanceMeters": 155688,
    "travelDistanceFromStartLocationToFirstVisitMeters": 74474,
    "travelDistanceBetweenVisitsMeters": 49213,
    "travelDistanceFromLastVisitToEndLocationMeters": 32001,
    "totalUnassignedVisits": 0,
    "totalAssignedVisits": 3,
    "assignedMandatoryVisits": 3,
    "assignedOptionalVisits": 0,
    "totalActivatedVehicles": 2,
    "workingTimeFairnessPercentage": 97.74
  },
  "run": {
    "id": "ID",
    "originId": "ID",
    "name": "Technician area coverage - MultiPolygon example",
    "submitDateTime": "2025-11-21T06:42:06.087613129Z",
    "startDateTime": "2025-11-21T06:42:18.856904447Z",
    "activeDateTime": "2025-11-21T06:42:18.930121181Z",
    "completeDateTime": "2025-11-21T06:42:49.519119395Z",
    "shutdownDateTime": "2025-11-21T06:42:49.519129695Z",
    "solverStatus": "SOLVING_COMPLETED",
    "score": "0hard/0medium/-8717soft",
    "tags": [
      "system.type:from-request",
      "system.profile:default"
    ],
    "validationResult": {
      "summary": "OK"
    }
  }
}

3. Area preference defined by a polygon

The following example shows a preferred area for a technician defined by a polygon:

{
  "preferredArea": {
    "type": "Polygon",
    "coordinates": [
        [
          [ -85.0, 34.0 ],
          [ -84.2, 34.0 ],
          [ -84.2, 33.0 ],
          [ -85.0, 33.0 ],
          [ -85.0, 34.0 ]
        ]
      ]
    }
}
  • The coordinates array must contain at least one array of coordinates describing the polygon’s outer boundary.

  • Any additional arrays of coordinates would describe holes in the polygon (not used in this example).

  • Coordinates are expressed as [longitude, latitude] pairs.

    Compatibility with the GeoJSON specification means that the order of coordinates is different from the common [latitude, longitude] format used when defining locations throughout the rest of the model input.
  • Each array of coordinates must contain at least four coordinate pairs.

  • The first and the last points in the coordinates array must be the same to close the polygon.

Please see GeoJSON specification for more details about the Polygon type.

  • 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/field-service-routing/v1/route-plans [email protected]
{
  "config": {
    "run": {
      "name": "Technician preferred area - Polygon example"
    }
  },
  "modelInput": {
    "vehicles": [
      {
        "id": "Beth",
        "preferredArea": {
          "type": "Polygon",
          "coordinates": [
            [
              [ -85.0, 34.0 ],
              [ -84.2, 34.0 ],
              [ -84.2, 33.0 ],
              [ -85.0, 33.0 ],
              [ -85.0, 34.0 ]
            ]
          ]
        },
        "shifts": [
          {
            "id": "Beth-2027-02-01",
            "startLocation": [33.72470, -84.37847],
            "minStartTime": "2027-02-01T09:00:00Z",
            "maxEndTime": "2027-02-01T17:00:00Z"
          }
        ]
      },
      {
        "id": "Carl",
        "shifts": [
          {
            "id": "Carl-2027-02-01",
            "startLocation": [33.7247, -84.37847],
            "minStartTime": "2027-02-01T13:00:00Z",
            "maxEndTime": "2027-02-01T17:00:00Z"
          }
        ]
      }
    ],
    "visits": [
      {
        "id": "Visit A",
        "location": [33.77301, -84.43838],
        "serviceDuration": "PT2H"
      },
      {
        "id": "Visit B",
        "location": [33.74699, -84.31504],
        "serviceDuration": "PT2H"
      },
      {
        "id": "Visit C",
        "location": [34.35155, -84.57443],
        "serviceDuration": "PT1H"
      }
    ]
  }
}
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",
    "originId": "ID",
    "name": "Technician area coverage - MultiPolygon example",
    "submitDateTime": "2025-11-21T06:42:06.087613129Z",
    "startDateTime": "2025-11-21T06:42:18.856904447Z",
    "activeDateTime": "2025-11-21T06:42:18.930121181Z",
    "completeDateTime": "2025-11-21T06:42:49.519119395Z",
    "shutdownDateTime": "2025-11-21T06:42:49.519129695Z",
    "solverStatus": "SOLVING_COMPLETED",
    "score": "0hard/0medium/-8717soft",
    "tags": [
      "system.type:from-request",
      "system.profile:default"
    ],
    "validationResult": {
      "summary": "OK"
    }
  },
  "modelOutput": {
    "vehicles": [
      {
        "id": "Beth",
        "shifts": [
          {
            "id": "Beth-2027-02-01",
            "startTime": "2027-02-01T09:00:00Z",
            "itinerary": [
              {
                "id": "Visit C",
                "kind": "VISIT",
                "arrivalTime": "2027-02-01T09:45:47Z",
                "startServiceTime": "2027-02-01T09:45:47Z",
                "departureTime": "2027-02-01T11:45:47Z",
                "effectiveServiceDuration": "PT2H",
                "travelTimeFromPreviousStandstill": "PT45M47S",
                "travelDistanceMetersFromPreviousStandstill": 53061,
                "minStartTravelTime": "2027-02-01T00:00:00Z"
              },
              {
                "id": "Visit A",
                "kind": "VISIT",
                "arrivalTime": "2027-02-01T12:27:53Z",
                "startServiceTime": "2027-02-01T12:27:53Z",
                "departureTime": "2027-02-01T14:27:53Z",
                "effectiveServiceDuration": "PT2H",
                "travelTimeFromPreviousStandstill": "PT42M6S",
                "travelDistanceMetersFromPreviousStandstill": 49213,
                "minStartTravelTime": "2027-02-01T00:00:00Z"
              }
            ],
            "metrics": {
              "totalTravelTime": "PT1H41M38S",
              "travelTimeFromStartLocationToFirstVisit": "PT45M47S",
              "travelTimeBetweenVisits": "PT42M6S",
              "travelTimeFromLastVisitToEndLocation": "PT13M45S",
              "totalTravelDistanceMeters": 113053,
              "travelDistanceFromStartLocationToFirstVisitMeters": 53061,
              "travelDistanceBetweenVisitsMeters": 49213,
              "travelDistanceFromLastVisitToEndLocationMeters": 10779,
              "endLocationArrivalTime": "2027-02-01T14:41:38Z"
            }
          }
        ]
      },
      {
        "id": "Carl",
        "shifts": [
          {
            "id": "Carl-2027-02-01",
            "startTime": "2027-02-01T13:00:00Z",
            "itinerary": [
              {
                "id": "Visit B",
                "kind": "VISIT",
                "arrivalTime": "2027-02-01T13:21:37Z",
                "startServiceTime": "2027-02-01T13:21:37Z",
                "departureTime": "2027-02-01T15:21:37Z",
                "effectiveServiceDuration": "PT2H",
                "travelTimeFromPreviousStandstill": "PT21M37S",
                "travelDistanceMetersFromPreviousStandstill": 21413,
                "minStartTravelTime": "2027-02-01T00:00:00Z"
              }
            ],
            "metrics": {
              "totalTravelTime": "PT43M17S",
              "travelTimeFromStartLocationToFirstVisit": "PT21M37S",
              "travelTimeBetweenVisits": "PT0S",
              "travelTimeFromLastVisitToEndLocation": "PT21M40S",
              "totalTravelDistanceMeters": 42635,
              "travelDistanceFromStartLocationToFirstVisitMeters": 21413,
              "travelDistanceBetweenVisitsMeters": 0,
              "travelDistanceFromLastVisitToEndLocationMeters": 21222,
              "endLocationArrivalTime": "2027-02-01T15:43:17Z"
            }
          }
        ]
      }
    ],
    "unassignedVisits": []
  },
  "inputMetrics": {
    "visits": 3,
    "visitGroups": 0,
    "visitDependencies": 0,
    "mandatoryVisits": 3,
    "optionalVisits": 0,
    "vehicles": 2,
    "vehicleShifts": 2,
    "visitsWithSla": 0,
    "movableVisits": 0,
    "pinnedVisits": 0
  },
  "kpis": {
    "totalTravelTime": "PT2H24M55S",
    "travelTimeFromStartLocationToFirstVisit": "PT1H7M24S",
    "travelTimeBetweenVisits": "PT42M6S",
    "travelTimeFromLastVisitToEndLocation": "PT35M25S",
    "totalTravelDistanceMeters": 155688,
    "travelDistanceFromStartLocationToFirstVisitMeters": 74474,
    "travelDistanceBetweenVisitsMeters": 49213,
    "travelDistanceFromLastVisitToEndLocationMeters": 32001,
    "totalUnassignedVisits": 0,
    "totalAssignedVisits": 3,
    "assignedMandatoryVisits": 3,
    "assignedOptionalVisits": 0,
    "totalActivatedVehicles": 2,
    "workingTimeFairnessPercentage": 97.74
  },
  "run": {
    "id": "ID",
    "originId": "ID",
    "name": "Technician area coverage - MultiPolygon example",
    "submitDateTime": "2025-11-21T06:42:06.087613129Z",
    "startDateTime": "2025-11-21T06:42:18.856904447Z",
    "activeDateTime": "2025-11-21T06:42:18.930121181Z",
    "completeDateTime": "2025-11-21T06:42:49.519119395Z",
    "shutdownDateTime": "2025-11-21T06:42:49.519129695Z",
    "solverStatus": "SOLVING_COMPLETED",
    "score": "0hard/0medium/-8717soft",
    "tags": [
      "system.type:from-request",
      "system.profile:default"
    ],
    "validationResult": {
      "summary": "OK"
    }
  }
}

Next

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

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

  • Read Skills to learn how to avoid scheduling overqualified technicians for a job.

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