Visit time window recommendations
Recommendations are a useful way of populating a draft plan.
This guide explains recommendations with the following examples:
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. Recommendations for unsolved route plans
Continuous planning includes four stages:
-
Historic
The historic stage includes schedules that have already been executed and cannot be changed. -
Published
The published stage includes schedules that have been shared with your customers and technicians and should only be changed in special circumstances. -
Draft
The draft stage includes schedules that can change without impacting customers or technicians. At some point, draft schedules will be solved and moved to the published stage. -
Unplanned
The unplanned stage is too far away to start planning schedules.
When customers request work during a time period that is still in the draft phase, you can use recommendations to provide the customer with time windows to choose from. The customer’s chosen time window can be added to the draft schedule, which will be optimized before being published.
1.1. The recommendation input dataset
A recommendation input dataset includes the following:
1.1.1. Max number of recommendations per time window
The maximum number of recommendations to make per time window specified in the input dataset.
The recommendation will consider the technicians whose vehicle shifts overlap the specified time window and make the number of recommendations specified in maxNumberOfRecommendationsPerTimeWindow
.
Each recommendation relates to a technician’s vehicle shift.
If there are 5 technicians and the maxNumberOfRecommendationsPerTimeWindow
is set to 2, only 2 technicians will be included in the recommendations per time window.
{
"maxNumberOfRecommendationsPerTimeWindow": 2
}
1.1.2. Fit visit ID
The ID of the visit to make the recommendations for. The provided fitVisitID
must match an ID from a visit in the modelInput
.
{
"fitVisitId": "Visit A"
}
1.1.3. Time windows
The time windows to be considered for the recommendation. For instance, 09:00 to 13:00 on February 1, 2027 and 13:00 to 17:00 on February 1, 2027.
Time windows can cover an entire day or even longer, but as time windows get longer, you reduce the number of recommendations and the customer’s options.
{
"timeWindows": [
{
"startTime": "2027-02-01T09:00:00Z",
"endTime": "2027-02-01T13:00:00Z"
},
{
"startTime": "2027-02-01T13:00:00Z",
"endTime": "2027-02-01T17:00:00Z"
}
]
}
1.1.4. Model input
The model input must include the technician’s vehicles and shifts and the visit the recommendations are for.
To learn more about vehicle shifts and visits, see Shift hours and overtime.
{
"modelInput": {
"vehicles": [
{
"id": "Ann",
"shifts": [
{
"id": "Ann-2027-02-01",
"startLocation": [33.68786, -84.18487],
"minStartTime": "2027-02-01T09:00:00Z",
"maxEndTime": "2027-02-01T17:00:00Z"
}
]
},
{
"id": "Beth",
"shifts": [
{
"id": "Beth-2027-02-01",
"startLocation": [33.70474, -84.06508],
"minStartTime": "2027-02-01T09:00:00Z",
"maxEndTime": "2027-02-01T17:00:00Z"
}
]
}
],
"visits": [
{
"id": "Visit A",
"location": [33.84475, -84.63649],
"serviceDuration": "PT2H"
}
]
}
}
1.2. Submit the input dataset
Submit the recommendations input dataset to the API endpoint: /v1/route-plans/recommendations/visit-recommend-time-windows
.
The HTTP response provides the recommendations.
-
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/recommendations/visit-recommend-time-windows [email protected]
{
"maxNumberOfRecommendationsPerTimeWindow": 2,
"fitVisitId": "Visit A",
"timeWindows": [
{
"startTime": "2027-02-01T09:00:00Z",
"endTime": "2027-02-01T13:00:00Z"
},
{
"startTime": "2027-02-01T13:00:00Z",
"endTime": "2027-02-01T17:00:00Z"
}
],
"modelInput": {
"vehicles": [
{
"id": "Ann",
"shifts": [
{
"id": "Ann-2027-02-01",
"startLocation": [33.68786, -84.18487],
"minStartTime": "2027-02-01T09:00:00Z",
"maxEndTime": "2027-02-01T17:00:00Z"
}
]
},
{
"id": "Beth",
"shifts": [
{
"id": "Beth-2027-02-01",
"startLocation": [33.70474, -84.06508],
"minStartTime": "2027-02-01T09:00:00Z",
"maxEndTime": "2027-02-01T17:00:00Z"
}
]
}
],
"visits": [
{
"id": "Visit A",
"location": [33.84475, -84.63649],
"serviceDuration": "PT2H"
}
]
}
}
{
"recommendations": [
{
"scoreDiff": "0hard/10000medium/-117593soft",
"constraintDiffs": [
{
"score": "0hard/0medium/0soft",
"constraintName": "Balance movable and non-movable visits"
},
{
"score": "0hard/0medium/-322soft",
"constraintName": "Balance time utilization"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Max last visit departure time (hard)"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Max last visit departure time (soft)"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Max shift end time (hard)"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Max shift end time (soft)"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Max travel time per visit (hard)"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Minimize scheduling vehicles with unnecessary skill levels"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Minimize the visit completion risk"
},
{
"score": "0hard/0medium/-111336soft",
"constraintName": "Minimize travel distance"
},
{
"score": "0hard/0medium/-5935soft",
"constraintName": "Minimize travel time"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "No conflict with fixed break"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "No conflict with fixed location break"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "No loops allowed"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "No semi-assigned visit groups"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Prefer scheduling optional visits"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Prefer visit vehicle match preferred vehicles"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Prefer visits scheduled to the earliest day"
},
{
"score": "0hard/10000medium/0soft",
"constraintName": "Require scheduling mandatory visits"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require service max end time"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require service max start time"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require skills"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require tags"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require the same vehicle for dependent visit"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require visit dependency delay"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require visit dependency prerequisite assigned"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require visit vehicle match required vehicles"
}
],
"timeWindow": {
"minStartTime": "2027-02-01T09:00:00Z",
"maxStartTime": null,
"maxEndTime": "2027-02-01T13:00:00Z"
},
"vehicleShift": {
"id": "Ann-2027-02-01",
"startTime": "2027-02-01T09:00:00Z",
"itinerary": [
{
"id": "Visit A",
"kind": "VISIT",
"arrivalTime": "2027-02-01T09:48:57Z",
"startServiceTime": "2027-02-01T09:48:57Z",
"departureTime": "2027-02-01T11:48:57Z",
"effectiveServiceDuration": "PT2H",
"travelTimeFromPreviousStandstill": "PT48M57S",
"travelDistanceMetersFromPreviousStandstill": 54141,
"minStartTravelTime": "2027-02-01T00:00:00Z"
}
],
"metrics": {
"totalTravelTime": "PT1H38M55S",
"travelTimeFromStartLocationToFirstVisit": "PT48M57S",
"travelTimeBetweenVisits": "PT0S",
"travelTimeFromLastVisitToEndLocation": "PT49M58S",
"totalTravelDistanceMeters": 111336,
"travelDistanceFromStartLocationToFirstVisitMeters": 54141,
"travelDistanceBetweenVisitsMeters": 0,
"travelDistanceFromLastVisitToEndLocationMeters": 57195,
"endLocationArrivalTime": "2027-02-01T12:38:55Z"
}
},
"dependentVehicleShifts": [],
"kpis": {
"totalTravelTime": "PT1H38M55S",
"travelTimeFromStartLocationToFirstVisit": "PT48M57S",
"travelTimeBetweenVisits": "PT0S",
"travelTimeFromLastVisitToEndLocation": "PT49M58S",
"totalTravelDistanceMeters": 111336,
"travelDistanceFromStartLocationToFirstVisitMeters": 54141,
"travelDistanceBetweenVisitsMeters": 0,
"travelDistanceFromLastVisitToEndLocationMeters": 57195,
"totalUnassignedVisits": 0,
"workingTimeFairnessPercentage": 0.0
}
},
{
"scoreDiff": "0hard/10000medium/-117875soft",
"constraintDiffs": [
{
"score": "0hard/0medium/0soft",
"constraintName": "Balance movable and non-movable visits"
},
{
"score": "0hard/0medium/-604soft",
"constraintName": "Balance time utilization"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Max last visit departure time (hard)"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Max last visit departure time (soft)"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Max shift end time (hard)"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Max shift end time (soft)"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Max travel time per visit (hard)"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Minimize scheduling vehicles with unnecessary skill levels"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Minimize the visit completion risk"
},
{
"score": "0hard/0medium/-111336soft",
"constraintName": "Minimize travel distance"
},
{
"score": "0hard/0medium/-5935soft",
"constraintName": "Minimize travel time"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "No conflict with fixed break"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "No conflict with fixed location break"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "No loops allowed"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "No semi-assigned visit groups"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Prefer scheduling optional visits"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Prefer visit vehicle match preferred vehicles"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Prefer visits scheduled to the earliest day"
},
{
"score": "0hard/10000medium/0soft",
"constraintName": "Require scheduling mandatory visits"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require service max end time"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require service max start time"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require skills"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require tags"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require the same vehicle for dependent visit"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require visit dependency delay"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require visit dependency prerequisite assigned"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require visit vehicle match required vehicles"
}
],
"timeWindow": {
"minStartTime": "2027-02-01T13:00:00Z",
"maxStartTime": null,
"maxEndTime": "2027-02-01T17:00:00Z"
},
"vehicleShift": {
"id": "Ann-2027-02-01",
"startTime": "2027-02-01T09:00:00Z",
"itinerary": [
{
"id": "Visit A",
"kind": "VISIT",
"arrivalTime": "2027-02-01T09:48:57Z",
"startServiceTime": "2027-02-01T13:00:00Z",
"departureTime": "2027-02-01T15:00:00Z",
"effectiveServiceDuration": "PT2H",
"travelTimeFromPreviousStandstill": "PT48M57S",
"travelDistanceMetersFromPreviousStandstill": 54141,
"minStartTravelTime": "2027-02-01T00:00:00Z"
}
],
"metrics": {
"totalTravelTime": "PT1H38M55S",
"travelTimeFromStartLocationToFirstVisit": "PT48M57S",
"travelTimeBetweenVisits": "PT0S",
"travelTimeFromLastVisitToEndLocation": "PT49M58S",
"totalTravelDistanceMeters": 111336,
"travelDistanceFromStartLocationToFirstVisitMeters": 54141,
"travelDistanceBetweenVisitsMeters": 0,
"travelDistanceFromLastVisitToEndLocationMeters": 57195,
"endLocationArrivalTime": "2027-02-01T15:49:58Z"
}
},
"dependentVehicleShifts": [],
"kpis": {
"totalTravelTime": "PT1H38M55S",
"travelTimeFromStartLocationToFirstVisit": "PT48M57S",
"travelTimeBetweenVisits": "PT0S",
"travelTimeFromLastVisitToEndLocation": "PT49M58S",
"totalTravelDistanceMeters": 111336,
"travelDistanceFromStartLocationToFirstVisitMeters": 54141,
"travelDistanceBetweenVisitsMeters": 0,
"travelDistanceFromLastVisitToEndLocationMeters": 57195,
"totalUnassignedVisits": 0,
"workingTimeFairnessPercentage": 0.0
}
},
{
"scoreDiff": "0hard/10000medium/-137632soft",
"constraintDiffs": [
{
"score": "0hard/0medium/0soft",
"constraintName": "Balance movable and non-movable visits"
},
{
"score": "0hard/0medium/-343soft",
"constraintName": "Balance time utilization"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Max last visit departure time (hard)"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Max last visit departure time (soft)"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Max shift end time (hard)"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Max shift end time (soft)"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Max travel time per visit (hard)"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Minimize scheduling vehicles with unnecessary skill levels"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Minimize the visit completion risk"
},
{
"score": "0hard/0medium/-130532soft",
"constraintName": "Minimize travel distance"
},
{
"score": "0hard/0medium/-6757soft",
"constraintName": "Minimize travel time"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "No conflict with fixed break"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "No conflict with fixed location break"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "No loops allowed"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "No semi-assigned visit groups"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Prefer scheduling optional visits"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Prefer visit vehicle match preferred vehicles"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Prefer visits scheduled to the earliest day"
},
{
"score": "0hard/10000medium/0soft",
"constraintName": "Require scheduling mandatory visits"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require service max end time"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require service max start time"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require skills"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require tags"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require the same vehicle for dependent visit"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require visit dependency delay"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require visit dependency prerequisite assigned"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require visit vehicle match required vehicles"
}
],
"timeWindow": {
"minStartTime": "2027-02-01T09:00:00Z",
"maxStartTime": null,
"maxEndTime": "2027-02-01T13:00:00Z"
},
"vehicleShift": {
"id": "Beth-2027-02-01",
"startTime": "2027-02-01T09:00:00Z",
"itinerary": [
{
"id": "Visit A",
"kind": "VISIT",
"arrivalTime": "2027-02-01T09:55:56Z",
"startServiceTime": "2027-02-01T09:55:56Z",
"departureTime": "2027-02-01T11:55:56Z",
"effectiveServiceDuration": "PT2H",
"travelTimeFromPreviousStandstill": "PT55M56S",
"travelDistanceMetersFromPreviousStandstill": 64909,
"minStartTravelTime": "2027-02-01T00:00:00Z"
}
],
"metrics": {
"totalTravelTime": "PT1H52M37S",
"travelTimeFromStartLocationToFirstVisit": "PT55M56S",
"travelTimeBetweenVisits": "PT0S",
"travelTimeFromLastVisitToEndLocation": "PT56M41S",
"totalTravelDistanceMeters": 130532,
"travelDistanceFromStartLocationToFirstVisitMeters": 64909,
"travelDistanceBetweenVisitsMeters": 0,
"travelDistanceFromLastVisitToEndLocationMeters": 65623,
"endLocationArrivalTime": "2027-02-01T12:52:37Z"
}
},
"dependentVehicleShifts": [],
"kpis": {
"totalTravelTime": "PT1H52M37S",
"travelTimeFromStartLocationToFirstVisit": "PT55M56S",
"travelTimeBetweenVisits": "PT0S",
"travelTimeFromLastVisitToEndLocation": "PT56M41S",
"totalTravelDistanceMeters": 130532,
"travelDistanceFromStartLocationToFirstVisitMeters": 64909,
"travelDistanceBetweenVisitsMeters": 0,
"travelDistanceFromLastVisitToEndLocationMeters": 65623,
"totalUnassignedVisits": 0,
"workingTimeFairnessPercentage": 0.0
}
},
{
"scoreDiff": "0hard/10000medium/-137903soft",
"constraintDiffs": [
{
"score": "0hard/0medium/0soft",
"constraintName": "Balance movable and non-movable visits"
},
{
"score": "0hard/0medium/-614soft",
"constraintName": "Balance time utilization"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Max last visit departure time (hard)"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Max last visit departure time (soft)"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Max shift end time (hard)"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Max shift end time (soft)"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Max travel time per visit (hard)"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Minimize scheduling vehicles with unnecessary skill levels"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Minimize the visit completion risk"
},
{
"score": "0hard/0medium/-130532soft",
"constraintName": "Minimize travel distance"
},
{
"score": "0hard/0medium/-6757soft",
"constraintName": "Minimize travel time"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "No conflict with fixed break"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "No conflict with fixed location break"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "No loops allowed"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "No semi-assigned visit groups"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Prefer scheduling optional visits"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Prefer visit vehicle match preferred vehicles"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Prefer visits scheduled to the earliest day"
},
{
"score": "0hard/10000medium/0soft",
"constraintName": "Require scheduling mandatory visits"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require service max end time"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require service max start time"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require skills"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require tags"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require the same vehicle for dependent visit"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require visit dependency delay"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require visit dependency prerequisite assigned"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require visit vehicle match required vehicles"
}
],
"timeWindow": {
"minStartTime": "2027-02-01T13:00:00Z",
"maxStartTime": null,
"maxEndTime": "2027-02-01T17:00:00Z"
},
"vehicleShift": {
"id": "Beth-2027-02-01",
"startTime": "2027-02-01T09:00:00Z",
"itinerary": [
{
"id": "Visit A",
"kind": "VISIT",
"arrivalTime": "2027-02-01T09:55:56Z",
"startServiceTime": "2027-02-01T13:00:00Z",
"departureTime": "2027-02-01T15:00:00Z",
"effectiveServiceDuration": "PT2H",
"travelTimeFromPreviousStandstill": "PT55M56S",
"travelDistanceMetersFromPreviousStandstill": 64909,
"minStartTravelTime": "2027-02-01T00:00:00Z"
}
],
"metrics": {
"totalTravelTime": "PT1H52M37S",
"travelTimeFromStartLocationToFirstVisit": "PT55M56S",
"travelTimeBetweenVisits": "PT0S",
"travelTimeFromLastVisitToEndLocation": "PT56M41S",
"totalTravelDistanceMeters": 130532,
"travelDistanceFromStartLocationToFirstVisitMeters": 64909,
"travelDistanceBetweenVisitsMeters": 0,
"travelDistanceFromLastVisitToEndLocationMeters": 65623,
"endLocationArrivalTime": "2027-02-01T15:56:41Z"
}
},
"dependentVehicleShifts": [],
"kpis": {
"totalTravelTime": "PT1H52M37S",
"travelTimeFromStartLocationToFirstVisit": "PT55M56S",
"travelTimeBetweenVisits": "PT0S",
"travelTimeFromLastVisitToEndLocation": "PT56M41S",
"totalTravelDistanceMeters": 130532,
"travelDistanceFromStartLocationToFirstVisitMeters": 64909,
"travelDistanceBetweenVisitsMeters": 0,
"travelDistanceFromLastVisitToEndLocationMeters": 65623,
"totalUnassignedVisits": 0,
"workingTimeFairnessPercentage": 0.0
}
}
]
}
1.3. The recommendations
In this example, there are a maximum of 2 recommendations per time window. Each recommendation relates to a technician’s vehicle shift. We have 2 time windows and 2 technicians for a total of 4 recommendations for the visit.
Each recommendation includes the following:
1.3.1. ScoreDiff
The difference implementing the recommendation will make to the final score of the optimized solution:
{
"scoreDiff": "0hard/10000medium/-117593soft"
}
1.3.2. Constraint scores
The score for each constraint, for instance:
{
"score": "0hard/0medium/-111336soft",
"constraintName": "Minimize travel distance"
}
1.3.3. The time window
The time window the recommendation is for:
{
"timeWindow": {
"minStartTime": "2027-02-01T09:00:00Z",
"maxStartTime": null,
"maxEndTime": "2027-02-01T13:00:00Z"
}
}
1.3.4. The vehicle shift
The vehicle shift the recommendation applies to and the shift itinerary, metrics, and KPIs.
{
"vehicleShift": {
"id": "Ann-2027-02-01",
"startTime": "2027-02-01T09:00:00Z",
"itinerary": [
{
"id": "Visit A",
"kind": "VISIT",
"arrivalTime": "2027-02-01T09:48:57Z",
"startServiceTime": "2027-02-01T09:48:57Z",
"departureTime": "2027-02-01T11:48:57Z",
"effectiveServiceDuration": "PT2H",
"travelTimeFromPreviousStandstill": "PT48M57S",
"travelDistanceMetersFromPreviousStandstill": 54141,
"minStartTravelTime": "2027-02-01T00:00:00Z"
}
],
"metrics": {
"totalTravelTime": "PT1H38M55S",
"travelTimeFromStartLocationToFirstVisit": "PT48M57S",
"travelTimeBetweenVisits": "PT0S",
"travelTimeFromLastVisitToEndLocation": "PT49M58S",
"totalTravelDistanceMeters": 111336,
"travelDistanceFromStartLocationToFirstVisitMeters": 54141,
"travelDistanceBetweenVisitsMeters": 0,
"travelDistanceFromLastVisitToEndLocationMeters": 57195,
"endLocationArrivalTime": "2027-02-01T12:38:55Z"
}
},
"kpis": {
"totalTravelTime": "PT1H38M55S",
"travelTimeFromStartLocationToFirstVisit": "PT48M57S",
"travelTimeBetweenVisits": "PT0S",
"travelTimeFromLastVisitToEndLocation": "PT49M58S",
"totalTravelDistanceMeters": 111336,
"travelDistanceFromStartLocationToFirstVisitMeters": 54141,
"travelDistanceBetweenVisitsMeters": 0,
"travelDistanceFromLastVisitToEndLocationMeters": 57195,
"totalUnassignedVisits": 0,
"workingTimeFairnessPercentage": 0.0
}
}
1.4. Implementing the recommendation
The recommendation output for this example includes four recommendations:
-
Between 09:00 and 13:00 on February 1st with technician Ann.
-
Between 13:00 and 17:00 on February 1st with technician Ann.
-
Between 09:00 and 13:00 on February 1st with technician Beth.
-
Between 13:00 and 17:00 on February 1st with technician Beth.
The recommendations include specific times, however, the plan is still a draft and those times will likely change as additional visits are added to the plan. |
If the customer agrees to one of the time windows, it must be added to the recommendation input dataset (the draft plan).
In this instance, the customer accepted the second recommendation:
-
Between 13:00 and 17:00 on February 1st with technician Ann.
First, add the visit to Ann’s itinerary:
{
"id": "Ann",
"shifts": [
{
"id": "Ann-2027-02-01",
"startLocation": [33.68786, -84.18487],
"minStartTime": "2027-02-01T09:00:00Z",
"maxEndTime": "2027-02-01T17:00:00Z",
"itinerary": [
{
"id": "Visit A",
"kind": "VISIT"
}
]
}
]
}
Next, add the time window to the visit:
{
"id": "Visit A",
"location": [33.84475, -84.63649],
"serviceDuration": "PT2H",
"timeWindows": [
{
"minStartTime": "2027-02-01T13:00:00Z",
"maxEndTime": "2027-02-01T17:00:00Z"
}
]
}
It is important to decide how to manage concurrent requests. If multiple requests are made for recommendations, which recommendation is added to the input dataset and what happens to other recommendations? |
2. Recommendations for unsolved route plans with existing visits
When additional customers call to request technicians to complete work at their premises, their requests can be added to the recommendation input dataset.
In the following example, a customer has called and needs a time window for Visit B:
-
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/recommendations/visit-recommend-time-windows [email protected]
{
"maxNumberOfRecommendationsPerTimeWindow": 2,
"fitVisitId": "Visit B",
"timeWindows": [
{
"startTime": "2027-02-01T09:00:00Z",
"endTime": "2027-02-01T13:00:00Z"
},
{
"startTime": "2027-02-01T13:00:00Z",
"endTime": "2027-02-01T17:00:00Z"
}
],
"modelInput": {
"vehicles": [
{
"id": "Ann",
"shifts": [
{
"id": "Ann-2027-02-01",
"startLocation": [33.68786, -84.18487],
"minStartTime": "2027-02-01T09:00:00Z",
"maxEndTime": "2027-02-01T17:00:00Z",
"itinerary": [
{
"id": "Visit A",
"kind": "VISIT"
}
]
}
]
},
{
"id": "Beth",
"shifts": [
{
"id": "Beth-2027-02-01",
"startLocation": [33.70474, -84.06508],
"minStartTime": "2027-02-01T09:00:00Z",
"maxEndTime": "2027-02-01T17:00:00Z"
}
]
}
],
"visits": [
{
"id": "Visit A",
"location": [33.84475, -84.63649],
"serviceDuration": "PT2H",
"timeWindows": [
{
"minStartTime": "2027-02-01T13:00:00Z",
"maxEndTime": "2027-02-01T17:00:00Z"
}
]
},
{
"id": "Visit B",
"location": [33.67590, -84.11845],
"serviceDuration": "PT2H"
}
]
}
}
{
"recommendations": [
{
"scoreDiff": "0hard/10000medium/-18406soft",
"constraintDiffs": [
{
"score": "0hard/0medium/0soft",
"constraintName": "Balance movable and non-movable visits"
},
{
"score": "0hard/0medium/546soft",
"constraintName": "Balance time utilization"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Max last visit departure time (hard)"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Max last visit departure time (soft)"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Max shift end time (hard)"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Max shift end time (soft)"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Max travel time per visit (hard)"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Minimize scheduling vehicles with unnecessary skill levels"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Minimize the visit completion risk"
},
{
"score": "0hard/0medium/-17677soft",
"constraintName": "Minimize travel distance"
},
{
"score": "0hard/0medium/-1275soft",
"constraintName": "Minimize travel time"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "No conflict with fixed break"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "No conflict with fixed location break"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "No loops allowed"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "No semi-assigned visit groups"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Prefer scheduling optional visits"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Prefer visit vehicle match preferred vehicles"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Prefer visits scheduled to the earliest day"
},
{
"score": "0hard/10000medium/0soft",
"constraintName": "Require scheduling mandatory visits"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require service max end time"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require service max start time"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require skills"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require tags"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require the same vehicle for dependent visit"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require visit dependency delay"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require visit dependency prerequisite assigned"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require visit vehicle match required vehicles"
}
],
"timeWindow": {
"minStartTime": "2027-02-01T13:00:00Z",
"maxStartTime": null,
"maxEndTime": "2027-02-01T17:00:00Z"
},
"vehicleShift": {
"id": "Beth-2027-02-01",
"startTime": "2027-02-01T09:00:00Z",
"itinerary": [
{
"id": "Visit B",
"kind": "VISIT",
"arrivalTime": "2027-02-01T09:10:37Z",
"startServiceTime": "2027-02-01T13:00:00Z",
"departureTime": "2027-02-01T15:00:00Z",
"effectiveServiceDuration": "PT2H",
"travelTimeFromPreviousStandstill": "PT10M37S",
"travelDistanceMetersFromPreviousStandstill": 8843,
"minStartTravelTime": "2027-02-01T00:00:00Z"
}
],
"metrics": {
"totalTravelTime": "PT21M15S",
"travelTimeFromStartLocationToFirstVisit": "PT10M37S",
"travelTimeBetweenVisits": "PT0S",
"travelTimeFromLastVisitToEndLocation": "PT10M38S",
"totalTravelDistanceMeters": 17677,
"travelDistanceFromStartLocationToFirstVisitMeters": 8843,
"travelDistanceBetweenVisitsMeters": 0,
"travelDistanceFromLastVisitToEndLocationMeters": 8834,
"endLocationArrivalTime": "2027-02-01T15:10:38Z"
}
},
"dependentVehicleShifts": [],
"kpis": {
"totalTravelTime": "PT2H10S",
"travelTimeFromStartLocationToFirstVisit": "PT59M34S",
"travelTimeBetweenVisits": "PT0S",
"travelTimeFromLastVisitToEndLocation": "PT1H36S",
"totalTravelDistanceMeters": 129013,
"travelDistanceFromStartLocationToFirstVisitMeters": 62984,
"travelDistanceBetweenVisitsMeters": 0,
"travelDistanceFromLastVisitToEndLocationMeters": 66029,
"totalUnassignedVisits": 0,
"workingTimeFairnessPercentage": 94.96
}
},
{
"scoreDiff": "0hard/10000medium/-18507soft",
"constraintDiffs": [
{
"score": "0hard/0medium/0soft",
"constraintName": "Balance movable and non-movable visits"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Balance time utilization"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Max last visit departure time (hard)"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Max last visit departure time (soft)"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Max shift end time (hard)"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Max shift end time (soft)"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Max travel time per visit (hard)"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Minimize scheduling vehicles with unnecessary skill levels"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Minimize the visit completion risk"
},
{
"score": "0hard/0medium/-17413soft",
"constraintName": "Minimize travel distance"
},
{
"score": "0hard/0medium/-1094soft",
"constraintName": "Minimize travel time"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "No conflict with fixed break"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "No conflict with fixed location break"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "No loops allowed"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "No semi-assigned visit groups"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Prefer scheduling optional visits"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Prefer visit vehicle match preferred vehicles"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Prefer visits scheduled to the earliest day"
},
{
"score": "0hard/10000medium/0soft",
"constraintName": "Require scheduling mandatory visits"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require service max end time"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require service max start time"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require skills"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require tags"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require the same vehicle for dependent visit"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require visit dependency delay"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require visit dependency prerequisite assigned"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require visit vehicle match required vehicles"
}
],
"timeWindow": {
"minStartTime": "2027-02-01T09:00:00Z",
"maxStartTime": null,
"maxEndTime": "2027-02-01T13:00:00Z"
},
"vehicleShift": {
"id": "Ann-2027-02-01",
"startTime": "2027-02-01T09:00:00Z",
"itinerary": [
{
"id": "Visit B",
"kind": "VISIT",
"arrivalTime": "2027-02-01T09:12:00Z",
"startServiceTime": "2027-02-01T09:12:00Z",
"departureTime": "2027-02-01T11:12:00Z",
"effectiveServiceDuration": "PT2H",
"travelTimeFromPreviousStandstill": "PT12M",
"travelDistanceMetersFromPreviousStandstill": 9019,
"minStartTravelTime": "2027-02-01T00:00:00Z"
},
{
"id": "Visit A",
"kind": "VISIT",
"arrivalTime": "2027-02-01T12:07:11Z",
"startServiceTime": "2027-02-01T13:00:00Z",
"departureTime": "2027-02-01T15:00:00Z",
"effectiveServiceDuration": "PT2H",
"travelTimeFromPreviousStandstill": "PT55M11S",
"travelDistanceMetersFromPreviousStandstill": 62535,
"minStartTravelTime": "2027-02-01T00:00:00Z"
}
],
"metrics": {
"totalTravelTime": "PT1H57M9S",
"travelTimeFromStartLocationToFirstVisit": "PT12M",
"travelTimeBetweenVisits": "PT55M11S",
"travelTimeFromLastVisitToEndLocation": "PT49M58S",
"totalTravelDistanceMeters": 128749,
"travelDistanceFromStartLocationToFirstVisitMeters": 9019,
"travelDistanceBetweenVisitsMeters": 62535,
"travelDistanceFromLastVisitToEndLocationMeters": 57195,
"endLocationArrivalTime": "2027-02-01T15:49:58Z"
}
},
"dependentVehicleShifts": [],
"kpis": {
"totalTravelTime": "PT1H57M9S",
"travelTimeFromStartLocationToFirstVisit": "PT12M",
"travelTimeBetweenVisits": "PT55M11S",
"travelTimeFromLastVisitToEndLocation": "PT49M58S",
"totalTravelDistanceMeters": 128749,
"travelDistanceFromStartLocationToFirstVisitMeters": 9019,
"travelDistanceBetweenVisitsMeters": 62535,
"travelDistanceFromLastVisitToEndLocationMeters": 57195,
"totalUnassignedVisits": 0,
"workingTimeFairnessPercentage": 0.0
}
},
{
"scoreDiff": "0hard/10000medium/-18744soft",
"constraintDiffs": [
{
"score": "0hard/0medium/0soft",
"constraintName": "Balance movable and non-movable visits"
},
{
"score": "0hard/0medium/208soft",
"constraintName": "Balance time utilization"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Max last visit departure time (hard)"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Max last visit departure time (soft)"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Max shift end time (hard)"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Max shift end time (soft)"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Max travel time per visit (hard)"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Minimize scheduling vehicles with unnecessary skill levels"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Minimize the visit completion risk"
},
{
"score": "0hard/0medium/-17677soft",
"constraintName": "Minimize travel distance"
},
{
"score": "0hard/0medium/-1275soft",
"constraintName": "Minimize travel time"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "No conflict with fixed break"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "No conflict with fixed location break"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "No loops allowed"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "No semi-assigned visit groups"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Prefer scheduling optional visits"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Prefer visit vehicle match preferred vehicles"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Prefer visits scheduled to the earliest day"
},
{
"score": "0hard/10000medium/0soft",
"constraintName": "Require scheduling mandatory visits"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require service max end time"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require service max start time"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require skills"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require tags"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require the same vehicle for dependent visit"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require visit dependency delay"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require visit dependency prerequisite assigned"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require visit vehicle match required vehicles"
}
],
"timeWindow": {
"minStartTime": "2027-02-01T09:00:00Z",
"maxStartTime": null,
"maxEndTime": "2027-02-01T13:00:00Z"
},
"vehicleShift": {
"id": "Beth-2027-02-01",
"startTime": "2027-02-01T09:00:00Z",
"itinerary": [
{
"id": "Visit B",
"kind": "VISIT",
"arrivalTime": "2027-02-01T09:10:37Z",
"startServiceTime": "2027-02-01T09:10:37Z",
"departureTime": "2027-02-01T11:10:37Z",
"effectiveServiceDuration": "PT2H",
"travelTimeFromPreviousStandstill": "PT10M37S",
"travelDistanceMetersFromPreviousStandstill": 8843,
"minStartTravelTime": "2027-02-01T00:00:00Z"
}
],
"metrics": {
"totalTravelTime": "PT21M15S",
"travelTimeFromStartLocationToFirstVisit": "PT10M37S",
"travelTimeBetweenVisits": "PT0S",
"travelTimeFromLastVisitToEndLocation": "PT10M38S",
"totalTravelDistanceMeters": 17677,
"travelDistanceFromStartLocationToFirstVisitMeters": 8843,
"travelDistanceBetweenVisitsMeters": 0,
"travelDistanceFromLastVisitToEndLocationMeters": 8834,
"endLocationArrivalTime": "2027-02-01T11:21:15Z"
}
},
"dependentVehicleShifts": [],
"kpis": {
"totalTravelTime": "PT2H10S",
"travelTimeFromStartLocationToFirstVisit": "PT59M34S",
"travelTimeBetweenVisits": "PT0S",
"travelTimeFromLastVisitToEndLocation": "PT1H36S",
"totalTravelDistanceMeters": 129013,
"travelDistanceFromStartLocationToFirstVisitMeters": 62984,
"travelDistanceBetweenVisitsMeters": 0,
"travelDistanceFromLastVisitToEndLocationMeters": 66029,
"totalUnassignedVisits": 0,
"workingTimeFairnessPercentage": 51.25
}
},
{
"scoreDiff": "-7303hard/10000medium/-33075446soft",
"constraintDiffs": [
{
"score": "0hard/0medium/0soft",
"constraintName": "Balance movable and non-movable visits"
},
{
"score": "0hard/0medium/-201soft",
"constraintName": "Balance time utilization"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Max last visit departure time (hard)"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Max last visit departure time (soft)"
},
{
"score": "-3997hard/0medium/0soft",
"constraintName": "Max shift end time (hard)"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Max shift end time (soft)"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Max travel time per visit (hard)"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Minimize scheduling vehicles with unnecessary skill levels"
},
{
"score": "0hard/0medium/-33060000soft",
"constraintName": "Minimize the visit completion risk"
},
{
"score": "0hard/0medium/-14246soft",
"constraintName": "Minimize travel distance"
},
{
"score": "0hard/0medium/-999soft",
"constraintName": "Minimize travel time"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "No conflict with fixed break"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "No conflict with fixed location break"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "No loops allowed"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "No semi-assigned visit groups"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Prefer scheduling optional visits"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Prefer visit vehicle match preferred vehicles"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Prefer visits scheduled to the earliest day"
},
{
"score": "0hard/10000medium/0soft",
"constraintName": "Require scheduling mandatory visits"
},
{
"score": "-3306hard/0medium/0soft",
"constraintName": "Require service max end time"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require service max start time"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require skills"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require tags"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require the same vehicle for dependent visit"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require visit dependency delay"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require visit dependency prerequisite assigned"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require visit vehicle match required vehicles"
}
],
"timeWindow": {
"minStartTime": "2027-02-01T13:00:00Z",
"maxStartTime": null,
"maxEndTime": "2027-02-01T17:00:00Z"
},
"vehicleShift": {
"id": "Ann-2027-02-01",
"startTime": "2027-02-01T09:00:00Z",
"itinerary": [
{
"id": "Visit A",
"kind": "VISIT",
"arrivalTime": "2027-02-01T09:48:57Z",
"startServiceTime": "2027-02-01T13:00:00Z",
"departureTime": "2027-02-01T15:00:00Z",
"effectiveServiceDuration": "PT2H",
"travelTimeFromPreviousStandstill": "PT48M57S",
"travelDistanceMetersFromPreviousStandstill": 54141,
"minStartTravelTime": "2027-02-01T00:00:00Z"
},
{
"id": "Visit B",
"kind": "VISIT",
"arrivalTime": "2027-02-01T15:55:06Z",
"startServiceTime": "2027-02-01T15:55:06Z",
"departureTime": "2027-02-01T17:55:06Z",
"effectiveServiceDuration": "PT2H",
"travelTimeFromPreviousStandstill": "PT55M6S",
"travelDistanceMetersFromPreviousStandstill": 62744,
"minStartTravelTime": "2027-02-01T00:00:00Z"
}
],
"metrics": {
"totalTravelTime": "PT1H55M34S",
"travelTimeFromStartLocationToFirstVisit": "PT48M57S",
"travelTimeBetweenVisits": "PT55M6S",
"travelTimeFromLastVisitToEndLocation": "PT11M31S",
"totalTravelDistanceMeters": 125582,
"travelDistanceFromStartLocationToFirstVisitMeters": 54141,
"travelDistanceBetweenVisitsMeters": 62744,
"travelDistanceFromLastVisitToEndLocationMeters": 8697,
"endLocationArrivalTime": "2027-02-01T18:06:37Z"
}
},
"dependentVehicleShifts": [],
"kpis": {
"totalTravelTime": "PT1H55M34S",
"travelTimeFromStartLocationToFirstVisit": "PT48M57S",
"travelTimeBetweenVisits": "PT55M6S",
"travelTimeFromLastVisitToEndLocation": "PT11M31S",
"totalTravelDistanceMeters": 125582,
"travelDistanceFromStartLocationToFirstVisitMeters": 54141,
"travelDistanceBetweenVisitsMeters": 62744,
"travelDistanceFromLastVisitToEndLocationMeters": 8697,
"totalUnassignedVisits": 0,
"workingTimeFairnessPercentage": 0.0
}
}
]
}
The recommendation output for this example includes four recommendations:
-
Between 13:00 and 17:00 on February 1st with technician Beth.
-
Between 09:00 and 13:00 on February 1st with technician Ann.
-
Between 09:00 and 13:00 on February 1st with technician Beth.
-
Between 13:00 and 17:00 on February 1st with technician Ann.
However, because Ann already has a visit on the draft plan for the fourth recommendation, scheduling Visit B during this time window with Ann breaks the following hard constraints and is not a feasible schedule:
-
Max shift end time
-
Require service max end time
{
"score": "-3997hard/0medium/0soft",
"constraintName": "Max shift end time (hard)"
},
{
"score": "-3306hard/0medium/0soft",
"constraintName": "Require service max end time"
}
Depending on the urgency of the situation, it might be possible to reschedule the other visit during that time window in Ann’s shift, so that Visit B can take this time window. |
3. Recommendations for solved route plans
If a customer visit needs to be added to a published schedule, the process for requesting recommendations is the same as above.
The following input dataset generated the schedule that is currently being executed:
-
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": "Recommendations sample schedule"
}
},
"modelInput": {
"vehicles": [
{
"id": "Ann",
"shifts": [
{
"id": "Ann-2027-02-01",
"startLocation": [33.68786, -84.18487],
"minStartTime": "2027-02-01T09:00:00Z",
"maxEndTime": "2027-02-01T17:00:00Z",
"itinerary": [
{
"id": "Visit D",
"kind": "VISIT"
},
{
"id": "Visit A",
"kind": "VISIT"
}
]
}
]
},
{
"id": "Beth",
"shifts": [
{
"id": "Beth-2027-02-01",
"startLocation": [33.70474, -84.06508],
"minStartTime": "2027-02-01T09:00:00Z",
"maxEndTime": "2027-02-01T17:00:00Z",
"itinerary": [
{
"id": "Visit B",
"kind": "VISIT"
}
]
}
]
}
],
"visits": [
{
"id": "Visit A",
"location": [33.84475, -84.63649],
"serviceDuration": "PT2H",
"timeWindows": [
{
"minStartTime": "2027-02-01T13:00:00Z",
"maxEndTime": "2027-02-01T17:00:00Z"
}
]
},
{
"id": "Visit B",
"location": [33.67590, -84.11845],
"serviceDuration": "PT2H",
"timeWindows": [
{
"minStartTime": "2027-02-01T09:00:00Z",
"maxEndTime": "2027-02-01T13:00:00Z"
}
]
},
{
"id": "Visit C",
"location": [33.51744, -84.12329],
"serviceDuration": "PT2H"
},
{
"id": "Visit D",
"location": [33.84954, -84.52085],
"serviceDuration": "PT1H",
"timeWindows": [
{
"minStartTime": "2027-02-01T09:00:00Z",
"maxEndTime": "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": "Recommendations sample schedule",
"submitDateTime": "2025-01-17T06:35:26.852340505Z",
"startDateTime": "2025-01-17T06:35:33.653699216Z",
"activeDateTime": "2025-01-17T06:35:33.972843058Z",
"completeDateTime": "2025-01-17T06:40:34.032295161Z",
"shutdownDateTime": "2025-01-17T06:40:34.363235655Z",
"solverStatus": "SOLVING_COMPLETED",
"score": "0hard/0medium/-186900soft",
"tags": [],
"validationResult": {
"summary": "OK"
}
},
"modelOutput": {
"vehicles": [
{
"id": "Ann",
"shifts": [
{
"id": "Ann-2027-02-01",
"startTime": "2027-02-01T09:00:00Z",
"itinerary": [
{
"id": "Visit D",
"kind": "VISIT",
"arrivalTime": "2027-02-01T09:41:09Z",
"startServiceTime": "2027-02-01T09:41:09Z",
"departureTime": "2027-02-01T10:41:09Z",
"effectiveServiceDuration": "PT1H",
"travelTimeFromPreviousStandstill": "PT41M9S",
"travelDistanceMetersFromPreviousStandstill": 45639,
"minStartTravelTime": "2027-02-01T00:00:00Z"
},
{
"id": "Visit A",
"kind": "VISIT",
"arrivalTime": "2027-02-01T10:59:06Z",
"startServiceTime": "2027-02-01T13:00:00Z",
"departureTime": "2027-02-01T15:00:00Z",
"effectiveServiceDuration": "PT2H",
"travelTimeFromPreviousStandstill": "PT17M57S",
"travelDistanceMetersFromPreviousStandstill": 13866,
"minStartTravelTime": "2027-02-01T00:00:00Z"
}
],
"metrics": {
"totalTravelTime": "PT1H49M4S",
"travelTimeFromStartLocationToFirstVisit": "PT41M9S",
"travelTimeBetweenVisits": "PT17M57S",
"travelTimeFromLastVisitToEndLocation": "PT49M58S",
"totalTravelDistanceMeters": 116700,
"travelDistanceFromStartLocationToFirstVisitMeters": 45639,
"travelDistanceBetweenVisitsMeters": 13866,
"travelDistanceFromLastVisitToEndLocationMeters": 57195,
"endLocationArrivalTime": "2027-02-01T15:49:58Z"
}
}
]
},
{
"id": "Beth",
"shifts": [
{
"id": "Beth-2027-02-01",
"startTime": "2027-02-01T09:00:00Z",
"itinerary": [
{
"id": "Visit B",
"kind": "VISIT",
"arrivalTime": "2027-02-01T09:10:37Z",
"startServiceTime": "2027-02-01T09:10:37Z",
"departureTime": "2027-02-01T11:10:37Z",
"effectiveServiceDuration": "PT2H",
"travelTimeFromPreviousStandstill": "PT10M37S",
"travelDistanceMetersFromPreviousStandstill": 8843,
"minStartTravelTime": "2027-02-01T00:00:00Z"
},
{
"id": "Visit C",
"kind": "VISIT",
"arrivalTime": "2027-02-01T11:34:00Z",
"startServiceTime": "2027-02-01T11:34:00Z",
"departureTime": "2027-02-01T13:34:00Z",
"effectiveServiceDuration": "PT2H",
"travelTimeFromPreviousStandstill": "PT23M23S",
"travelDistanceMetersFromPreviousStandstill": 21217,
"minStartTravelTime": "2027-02-01T00:00:00Z"
}
],
"metrics": {
"totalTravelTime": "PT1H7M13S",
"travelTimeFromStartLocationToFirstVisit": "PT10M37S",
"travelTimeBetweenVisits": "PT23M23S",
"travelTimeFromLastVisitToEndLocation": "PT33M13S",
"totalTravelDistanceMeters": 59472,
"travelDistanceFromStartLocationToFirstVisitMeters": 8843,
"travelDistanceBetweenVisitsMeters": 21217,
"travelDistanceFromLastVisitToEndLocationMeters": 29412,
"endLocationArrivalTime": "2027-02-01T14:07:13Z"
}
}
]
}
]
},
"kpis": {
"totalTravelTime": "PT2H56M17S",
"travelTimeFromStartLocationToFirstVisit": "PT51M46S",
"travelTimeBetweenVisits": "PT41M20S",
"travelTimeFromLastVisitToEndLocation": "PT1H23M11S",
"totalTravelDistanceMeters": 176172,
"travelDistanceFromStartLocationToFirstVisitMeters": 54482,
"travelDistanceBetweenVisitsMeters": 35083,
"travelDistanceFromLastVisitToEndLocationMeters": 86607,
"totalUnassignedVisits": 0,
"workingTimeFairnessPercentage": 85.67
}
}
modelOutput
contains Ann’s and Beth’s itineraries.
If a customer calls to request a technician on the same day, the following recommendations input dataset will return time window recommendations for the request (Visit E):
-
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/recommendations/visit-recommend-time-windows [email protected]
{
"maxNumberOfRecommendationsPerTimeWindow": 2,
"fitVisitId": "Visit E",
"timeWindows": [
{
"startTime": "2027-02-01T09:00:00Z",
"endTime": "2027-02-01T13:00:00Z"
},
{
"startTime": "2027-02-01T13:00:00Z",
"endTime": "2027-02-01T17:00:00Z"
}
],
"modelInput": {
"vehicles": [
{
"id": "Ann",
"shifts": [
{
"id": "Ann-2027-02-01",
"startLocation": [33.68786, -84.18487],
"minStartTime": "2027-02-01T09:00:00Z",
"maxEndTime": "2027-02-01T17:00:00Z",
"itinerary": [
{
"id": "Visit D",
"kind": "VISIT"
},
{
"id": "Visit A",
"kind": "VISIT"
}
]
}
]
},
{
"id": "Beth",
"shifts": [
{
"id": "Beth-2027-02-01",
"startLocation": [33.70474, -84.06508],
"minStartTime": "2027-02-01T09:00:00Z",
"maxEndTime": "2027-02-01T17:00:00Z",
"itinerary": [
{
"id": "Visit B",
"kind": "VISIT"
},
{
"id": "Visit C",
"kind": "VISIT"
}
]
}
]
}
],
"visits": [
{
"id": "Visit A",
"location": [33.84475, -84.63649],
"serviceDuration": "PT2H",
"timeWindows": [
{
"minStartTime": "2027-02-01T13:00:00Z",
"maxEndTime": "2027-02-01T17:00:00Z"
}
]
},
{
"id": "Visit B",
"location": [33.67590, -84.11845],
"serviceDuration": "PT2H",
"timeWindows": [
{
"minStartTime": "2027-02-01T09:00:00Z",
"maxEndTime": "2027-02-01T13:00:00Z"
}
]
},
{
"id": "Visit C",
"location": [33.51744, -84.12329],
"serviceDuration": "PT2H"
},
{
"id": "Visit D",
"location": [33.84954, -84.52085],
"serviceDuration": "PT1H",
"timeWindows": [
{
"minStartTime": "2027-02-01T09:00:00Z",
"maxEndTime": "2027-02-01T13:00:00Z"
}
]
},
{
"id": "Visit E",
"location": [33.36302, -84.34906],
"serviceDuration": "PT1H"
}
]
}
}
{
"recommendations": [
{
"scoreDiff": "0hard/10000medium/-69563soft",
"constraintDiffs": [
{
"score": "0hard/0medium/0soft",
"constraintName": "Balance movable and non-movable visits"
},
{
"score": "0hard/0medium/112soft",
"constraintName": "Balance time utilization"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Max last visit departure time (hard)"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Max last visit departure time (soft)"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Max shift end time (hard)"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Max shift end time (soft)"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Max travel time per visit (hard)"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Minimize scheduling vehicles with unnecessary skill levels"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Minimize the visit completion risk"
},
{
"score": "0hard/0medium/-65510soft",
"constraintName": "Minimize travel distance"
},
{
"score": "0hard/0medium/-4165soft",
"constraintName": "Minimize travel time"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "No conflict with fixed break"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "No conflict with fixed location break"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "No loops allowed"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "No semi-assigned visit groups"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Prefer scheduling optional visits"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Prefer visit vehicle match preferred vehicles"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Prefer visits scheduled to the earliest day"
},
{
"score": "0hard/10000medium/0soft",
"constraintName": "Require scheduling mandatory visits"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require service max end time"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require service max start time"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require skills"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require tags"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require the same vehicle for dependent visit"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require visit dependency delay"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require visit dependency prerequisite assigned"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require visit vehicle match required vehicles"
}
],
"timeWindow": {
"minStartTime": "2027-02-01T13:00:00Z",
"maxStartTime": null,
"maxEndTime": "2027-02-01T17:00:00Z"
},
"vehicleShift": {
"id": "Beth-2027-02-01",
"startTime": "2027-02-01T09:00:00Z",
"itinerary": [
{
"id": "Visit B",
"kind": "VISIT",
"arrivalTime": "2027-02-01T09:10:37Z",
"startServiceTime": "2027-02-01T09:10:37Z",
"departureTime": "2027-02-01T11:10:37Z",
"effectiveServiceDuration": "PT2H",
"travelTimeFromPreviousStandstill": "PT10M37S",
"travelDistanceMetersFromPreviousStandstill": 8843,
"minStartTravelTime": "2027-02-01T00:00:00Z"
},
{
"id": "Visit C",
"kind": "VISIT",
"arrivalTime": "2027-02-01T11:34:00Z",
"startServiceTime": "2027-02-01T11:34:00Z",
"departureTime": "2027-02-01T13:34:00Z",
"effectiveServiceDuration": "PT2H",
"travelTimeFromPreviousStandstill": "PT23M23S",
"travelDistanceMetersFromPreviousStandstill": 21217,
"minStartTravelTime": "2027-02-01T00:00:00Z"
},
{
"id": "Visit E",
"kind": "VISIT",
"arrivalTime": "2027-02-01T14:12:11Z",
"startServiceTime": "2027-02-01T14:12:11Z",
"departureTime": "2027-02-01T15:12:11Z",
"effectiveServiceDuration": "PT1H",
"travelTimeFromPreviousStandstill": "PT38M11S",
"travelDistanceMetersFromPreviousStandstill": 34234,
"minStartTravelTime": "2027-02-01T00:00:00Z"
}
],
"metrics": {
"totalTravelTime": "PT2H16M38S",
"travelTimeFromStartLocationToFirstVisit": "PT10M37S",
"travelTimeBetweenVisits": "PT1H1M34S",
"travelTimeFromLastVisitToEndLocation": "PT1H4M27S",
"totalTravelDistanceMeters": 124982,
"travelDistanceFromStartLocationToFirstVisitMeters": 8843,
"travelDistanceBetweenVisitsMeters": 55451,
"travelDistanceFromLastVisitToEndLocationMeters": 60688,
"endLocationArrivalTime": "2027-02-01T16:16:38Z"
}
},
"dependentVehicleShifts": [],
"kpis": {
"totalTravelTime": "PT4H5M42S",
"travelTimeFromStartLocationToFirstVisit": "PT51M46S",
"travelTimeBetweenVisits": "PT1H19M31S",
"travelTimeFromLastVisitToEndLocation": "PT1H54M25S",
"totalTravelDistanceMeters": 241682,
"travelDistanceFromStartLocationToFirstVisitMeters": 54482,
"travelDistanceBetweenVisitsMeters": 69317,
"travelDistanceFromLastVisitToEndLocationMeters": 117883,
"totalUnassignedVisits": 0,
"workingTimeFairnessPercentage": 96.85
}
},
{
"scoreDiff": "0hard/10000medium/-145149soft",
"constraintDiffs": [
{
"score": "0hard/0medium/0soft",
"constraintName": "Balance movable and non-movable visits"
},
{
"score": "0hard/0medium/-97soft",
"constraintName": "Balance time utilization"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Max last visit departure time (hard)"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Max last visit departure time (soft)"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Max shift end time (hard)"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Max shift end time (soft)"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Max travel time per visit (hard)"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Minimize scheduling vehicles with unnecessary skill levels"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Minimize the visit completion risk"
},
{
"score": "0hard/0medium/-137448soft",
"constraintName": "Minimize travel distance"
},
{
"score": "0hard/0medium/-7604soft",
"constraintName": "Minimize travel time"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "No conflict with fixed break"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "No conflict with fixed location break"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "No loops allowed"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "No semi-assigned visit groups"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Prefer scheduling optional visits"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Prefer visit vehicle match preferred vehicles"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Prefer visits scheduled to the earliest day"
},
{
"score": "0hard/10000medium/0soft",
"constraintName": "Require scheduling mandatory visits"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require service max end time"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require service max start time"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require skills"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require tags"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require the same vehicle for dependent visit"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require visit dependency delay"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require visit dependency prerequisite assigned"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require visit vehicle match required vehicles"
}
],
"timeWindow": {
"minStartTime": "2027-02-01T09:00:00Z",
"maxStartTime": null,
"maxEndTime": "2027-02-01T13:00:00Z"
},
"vehicleShift": {
"id": "Ann-2027-02-01",
"startTime": "2027-02-01T09:00:00Z",
"itinerary": [
{
"id": "Visit D",
"kind": "VISIT",
"arrivalTime": "2027-02-01T09:41:09Z",
"startServiceTime": "2027-02-01T09:41:09Z",
"departureTime": "2027-02-01T10:41:09Z",
"effectiveServiceDuration": "PT1H",
"travelTimeFromPreviousStandstill": "PT41M9S",
"travelDistanceMetersFromPreviousStandstill": 45639,
"minStartTravelTime": "2027-02-01T00:00:00Z"
},
{
"id": "Visit E",
"kind": "VISIT",
"arrivalTime": "2027-02-01T11:48:39Z",
"startServiceTime": "2027-02-01T11:48:39Z",
"departureTime": "2027-02-01T12:48:39Z",
"effectiveServiceDuration": "PT1H",
"travelTimeFromPreviousStandstill": "PT1H7M30S",
"travelDistanceMetersFromPreviousStandstill": 70970,
"minStartTravelTime": "2027-02-01T00:00:00Z"
},
{
"id": "Visit A",
"kind": "VISIT",
"arrivalTime": "2027-02-01T14:05:50Z",
"startServiceTime": "2027-02-01T14:05:50Z",
"departureTime": "2027-02-01T16:05:50Z",
"effectiveServiceDuration": "PT2H",
"travelTimeFromPreviousStandstill": "PT1H17M11S",
"travelDistanceMetersFromPreviousStandstill": 80344,
"minStartTravelTime": "2027-02-01T00:00:00Z"
}
],
"metrics": {
"totalTravelTime": "PT3H55M48S",
"travelTimeFromStartLocationToFirstVisit": "PT41M9S",
"travelTimeBetweenVisits": "PT2H24M41S",
"travelTimeFromLastVisitToEndLocation": "PT49M58S",
"totalTravelDistanceMeters": 254148,
"travelDistanceFromStartLocationToFirstVisitMeters": 45639,
"travelDistanceBetweenVisitsMeters": 151314,
"travelDistanceFromLastVisitToEndLocationMeters": 57195,
"endLocationArrivalTime": "2027-02-01T16:55:48Z"
}
},
"dependentVehicleShifts": [],
"kpis": {
"totalTravelTime": "PT5H3M1S",
"travelTimeFromStartLocationToFirstVisit": "PT51M46S",
"travelTimeBetweenVisits": "PT2H48M4S",
"travelTimeFromLastVisitToEndLocation": "PT1H23M11S",
"totalTravelDistanceMeters": 313620,
"travelDistanceFromStartLocationToFirstVisitMeters": 54482,
"travelDistanceBetweenVisitsMeters": 172531,
"travelDistanceFromLastVisitToEndLocationMeters": 86607,
"totalUnassignedVisits": 0,
"workingTimeFairnessPercentage": 78.47
}
},
{
"scoreDiff": "-310hard/10000medium/-85893soft",
"constraintDiffs": [
{
"score": "0hard/0medium/0soft",
"constraintName": "Balance movable and non-movable visits"
},
{
"score": "0hard/0medium/-34soft",
"constraintName": "Balance time utilization"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Max last visit departure time (hard)"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Max last visit departure time (soft)"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Max shift end time (hard)"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Max shift end time (soft)"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Max travel time per visit (hard)"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Minimize scheduling vehicles with unnecessary skill levels"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Minimize the visit completion risk"
},
{
"score": "0hard/0medium/-80818soft",
"constraintName": "Minimize travel distance"
},
{
"score": "0hard/0medium/-5041soft",
"constraintName": "Minimize travel time"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "No conflict with fixed break"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "No conflict with fixed location break"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "No loops allowed"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "No semi-assigned visit groups"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Prefer scheduling optional visits"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Prefer visit vehicle match preferred vehicles"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Prefer visits scheduled to the earliest day"
},
{
"score": "0hard/10000medium/0soft",
"constraintName": "Require scheduling mandatory visits"
},
{
"score": "-310hard/0medium/0soft",
"constraintName": "Require service max end time"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require service max start time"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require skills"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require tags"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require the same vehicle for dependent visit"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require visit dependency delay"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require visit dependency prerequisite assigned"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require visit vehicle match required vehicles"
}
],
"timeWindow": {
"minStartTime": "2027-02-01T09:00:00Z",
"maxStartTime": null,
"maxEndTime": "2027-02-01T13:00:00Z"
},
"vehicleShift": {
"id": "Ann-2027-02-01",
"startTime": "2027-02-01T09:00:00Z",
"itinerary": [
{
"id": "Visit E",
"kind": "VISIT",
"arrivalTime": "2027-02-01T09:56:46Z",
"startServiceTime": "2027-02-01T09:56:46Z",
"departureTime": "2027-02-01T10:56:46Z",
"effectiveServiceDuration": "PT1H",
"travelTimeFromPreviousStandstill": "PT56M46S",
"travelDistanceMetersFromPreviousStandstill": 54628,
"minStartTravelTime": "2027-02-01T00:00:00Z"
},
{
"id": "Visit D",
"kind": "VISIT",
"arrivalTime": "2027-02-01T12:05:10Z",
"startServiceTime": "2027-02-01T12:05:10Z",
"departureTime": "2027-02-01T13:05:10Z",
"effectiveServiceDuration": "PT1H",
"travelTimeFromPreviousStandstill": "PT1H8M24S",
"travelDistanceMetersFromPreviousStandstill": 71829,
"minStartTravelTime": "2027-02-01T00:00:00Z"
},
{
"id": "Visit A",
"kind": "VISIT",
"arrivalTime": "2027-02-01T13:23:07Z",
"startServiceTime": "2027-02-01T13:23:07Z",
"departureTime": "2027-02-01T15:23:07Z",
"effectiveServiceDuration": "PT2H",
"travelTimeFromPreviousStandstill": "PT17M57S",
"travelDistanceMetersFromPreviousStandstill": 13866,
"minStartTravelTime": "2027-02-01T00:00:00Z"
}
],
"metrics": {
"totalTravelTime": "PT3H13M5S",
"travelTimeFromStartLocationToFirstVisit": "PT56M46S",
"travelTimeBetweenVisits": "PT1H26M21S",
"travelTimeFromLastVisitToEndLocation": "PT49M58S",
"totalTravelDistanceMeters": 197518,
"travelDistanceFromStartLocationToFirstVisitMeters": 54628,
"travelDistanceBetweenVisitsMeters": 85695,
"travelDistanceFromLastVisitToEndLocationMeters": 57195,
"endLocationArrivalTime": "2027-02-01T16:13:05Z"
}
},
"dependentVehicleShifts": [],
"kpis": {
"totalTravelTime": "PT4H20M18S",
"travelTimeFromStartLocationToFirstVisit": "PT1H7M23S",
"travelTimeBetweenVisits": "PT1H49M44S",
"travelTimeFromLastVisitToEndLocation": "PT1H23M11S",
"totalTravelDistanceMeters": 256990,
"travelDistanceFromStartLocationToFirstVisitMeters": 63471,
"travelDistanceBetweenVisitsMeters": 106912,
"travelDistanceFromLastVisitToEndLocationMeters": 86607,
"totalUnassignedVisits": 0,
"workingTimeFairnessPercentage": 82.99
}
},
{
"scoreDiff": "-668hard/10000medium/-69640soft",
"constraintDiffs": [
{
"score": "0hard/0medium/0soft",
"constraintName": "Balance movable and non-movable visits"
},
{
"score": "0hard/0medium/31soft",
"constraintName": "Balance time utilization"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Max last visit departure time (hard)"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Max last visit departure time (soft)"
},
{
"score": "-668hard/0medium/0soft",
"constraintName": "Max shift end time (hard)"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Max shift end time (soft)"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Max travel time per visit (hard)"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Minimize scheduling vehicles with unnecessary skill levels"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Minimize the visit completion risk"
},
{
"score": "0hard/0medium/-65512soft",
"constraintName": "Minimize travel distance"
},
{
"score": "0hard/0medium/-4159soft",
"constraintName": "Minimize travel time"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "No conflict with fixed break"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "No conflict with fixed location break"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "No loops allowed"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "No semi-assigned visit groups"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Prefer scheduling optional visits"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Prefer visit vehicle match preferred vehicles"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Prefer visits scheduled to the earliest day"
},
{
"score": "0hard/10000medium/0soft",
"constraintName": "Require scheduling mandatory visits"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require service max end time"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require service max start time"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require skills"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require tags"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require the same vehicle for dependent visit"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require visit dependency delay"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require visit dependency prerequisite assigned"
},
{
"score": "0hard/0medium/0soft",
"constraintName": "Require visit vehicle match required vehicles"
}
],
"timeWindow": {
"minStartTime": "2027-02-01T13:00:00Z",
"maxStartTime": null,
"maxEndTime": "2027-02-01T17:00:00Z"
},
"vehicleShift": {
"id": "Beth-2027-02-01",
"startTime": "2027-02-01T09:00:00Z",
"itinerary": [
{
"id": "Visit B",
"kind": "VISIT",
"arrivalTime": "2027-02-01T09:10:37Z",
"startServiceTime": "2027-02-01T09:10:37Z",
"departureTime": "2027-02-01T11:10:37Z",
"effectiveServiceDuration": "PT2H",
"travelTimeFromPreviousStandstill": "PT10M37S",
"travelDistanceMetersFromPreviousStandstill": 8843,
"minStartTravelTime": "2027-02-01T00:00:00Z"
},
{
"id": "Visit E",
"kind": "VISIT",
"arrivalTime": "2027-02-01T12:05:24Z",
"startServiceTime": "2027-02-01T13:00:00Z",
"departureTime": "2027-02-01T14:00:00Z",
"effectiveServiceDuration": "PT1H",
"travelTimeFromPreviousStandstill": "PT54M47S",
"travelDistanceMetersFromPreviousStandstill": 52521,
"minStartTravelTime": "2027-02-01T00:00:00Z"
},
{
"id": "Visit C",
"kind": "VISIT",
"arrivalTime": "2027-02-01T14:37:55Z",
"startServiceTime": "2027-02-01T14:37:55Z",
"departureTime": "2027-02-01T16:37:55Z",
"effectiveServiceDuration": "PT2H",
"travelTimeFromPreviousStandstill": "PT37M55S",
"travelDistanceMetersFromPreviousStandstill": 34208,
"minStartTravelTime": "2027-02-01T00:00:00Z"
}
],
"metrics": {
"totalTravelTime": "PT2H16M32S",
"travelTimeFromStartLocationToFirstVisit": "PT10M37S",
"travelTimeBetweenVisits": "PT1H32M42S",
"travelTimeFromLastVisitToEndLocation": "PT33M13S",
"totalTravelDistanceMeters": 124984,
"travelDistanceFromStartLocationToFirstVisitMeters": 8843,
"travelDistanceBetweenVisitsMeters": 86729,
"travelDistanceFromLastVisitToEndLocationMeters": 29412,
"endLocationArrivalTime": "2027-02-01T17:11:08Z"
}
},
"dependentVehicleShifts": [],
"kpis": {
"totalTravelTime": "PT4H5M36S",
"travelTimeFromStartLocationToFirstVisit": "PT51M46S",
"travelTimeBetweenVisits": "PT1H50M39S",
"travelTimeFromLastVisitToEndLocation": "PT1H23M11S",
"totalTravelDistanceMeters": 241684,
"travelDistanceFromStartLocationToFirstVisitMeters": 54482,
"travelDistanceBetweenVisitsMeters": 100595,
"travelDistanceFromLastVisitToEndLocationMeters": 86607,
"totalUnassignedVisits": 0,
"workingTimeFairnessPercentage": 90.99
}
}
]
}
The recommendation output for this example includes four recommendations:
-
Between 13:00 and 17:00 on February 1st with technician Beth.
-
Between 09:00 and 13:00 on February 1st with technician Ann.
-
Between 09:00 and 13:00 on February 1st with technician Ann.
-
Between 13:00 and 17:00 on February 1st with technician Beth.
The third and fourth recommendations both break hard constraints and are not feasible solutions.
The customer can accept one of the recommendations, either:
-
Between 13:00 and 17:00 on February 1st with technician Beth.
-
Between 09:00 and 13:00 on February 1st with technician Ann.
After the customer has accepted one of the time windows, create a real-time plan to include the visit in the optimized plan for the day. Failure to update the real-time plan will lead to errors with Visit E not being included if there are further updates to the plan throughout the day.
Next
-
Understand the constraints of the Field Service Routing model.
-
See the full API spec or try the online API.
-
Manage shift times with Time zones and daylight-saving time (DST) changes.