Docs
  • Solver
  • Models
    • Field Service Routing
    • Employee Shift Scheduling
    • Pick-up and Delivery Routing
  • Platform
Try models
  • Pick-up and Delivery Routing
  • Driver resource constraints
  • Lunch breaks and personal appointments

Pick-up and Delivery Routing

    • Introduction
    • Getting started: Hello world
    • User guide
      • Terms
      • Planning AI concepts
      • Demo datasets
      • Validation
      • Routing with Timefold’s maps service
      • Metrics and optimization goals
    • Driver resource constraints
      • Lunch breaks and personal appointments
      • Route optimization
      • Shift hours and overtime
    • Job service constraints
      • Time windows and opening hours
      • Skills
      • Movable stops and multi-day schedules
      • Dependencies between stops
      • Priority stops and optional stops
      • Job requirements and tags
        • Job required drivers
        • Job pooling
        • Prohibit job combinations
        • Maximum time burden
        • Driver capacity
        • Tags
    • Changelog
    • Upgrading to the latest versions
    • Feature requests

Lunch breaks and personal appointments

It’s important in any pick-up and delivery routing solution to schedule breaks for drivers.

Drivers who work an 8-hour shift need a lunch break. They may also need to attend appointments that can’t be arranged outside their regular work hours.

Breaks, such as lunch breaks, can be floating breaks that fit in around the assigned stops and are taken at any location.

When drivers have appointments, the appointment needs to be included in their schedule, and the schedule must include travel time from their last stop to the appointment and from the appointment to the next stop.

Regardless of the scenario, an optimized pick-up and delivery routing plan will schedule stops, lunch breaks, appointments, and travel between them to be a feasible solution.

This guide explains how to manage breaks with the following examples:

  • Floating lunch breaks

  • Personal appointments with a location

  • Personal appointments without a location

  • Allowing and disallowing breaks during jobs

Prerequisites

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

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

  3. Create a new API key or use an existing one. Ensure the list of models for the API key contains the Pick-up and Delivery Routing model.

In the examples, replace <API_KEY> with the API Key you just copied.

1. Floating lunch breaks

Drivers' shifts run for a specific number of hours per day, ie, from 09:00 to 17:00, and must include a break for lunch. It typically doesn’t matter exactly when the lunch break occurs, as long as it’s not too early or too late.

Breaks have a set duration and are scheduled to occur after an earliest start time, for example, a lunch break might be scheduled to occur any time after 12:00.

Breaks are added to shifts by including the requiredBreaks array:

{
  "shifts": [
    {
      "requiredBreaks": [
        {
          "id": "Ann Mon Lunch",
          "type": "FLOATING",
          "minStartTime": "2027-02-01T12:00:00Z",
          "maxEndTime": "2027-02-01T14:00:00Z",
          "duration": "PT1H"
        }
      ]
    }
  ]
}

At a minimum, requiredBreaks include, id, type, minStartTime, and duration.

maxEndTime is optional, but if it is not included, it is possible the driver’s lunch break will not be scheduled until the final hour of the day.

  • id is a unique string that identifies the break, for instance: Ann Mon.

  • type can be FLOATING or FIXED. Floating breaks use type FLOATING.

  • minStartTime is a date and time (in ISO 8601 date time with offset to UTC format) for the earliest time the break can start.

  • maxEndTime is a date and time (in ISO 8601 date time with offset to UTC format) for the latest time the break can end.

  • duration represents the length of the break in ISO 8601 duration format.

The Max floating break end time (hard) hard constraint penalizes the solution with a hard score if the break ends after the maxEndTime.

Stops will be left unassigned, if assigning them would mean not assigning a lunch break and breaking this constraint.

In the following example, Ann works an eight-hour shift with a 1-hour break for lunch. There are 2 jobs (Job A and Job B) with 2 stops each. The stops in Job A and Job B are scheduled first, Ann’s lunch break is scheduled at 12:42 and ends at 13:42, and Ann is free for another job.

floating lunch break
  • Input

  • Output

Try this example in Timefold Platform by saving this JSON into a file called sample.json and make the following API call:
curl -X POST -H "Content-type: application/json" -H 'X-API-KEY: <API_KEY>' https://app.timefold.ai/api/models/pickup-delivery-routing/v1/route-plans [email protected]
{
  "config": {
    "run": {
      "name": "Floating break example"
    }
  },
  "modelInput": {
    "drivers": [
      {
        "id": "Ann",
        "shifts": [
          {
            "id": "Ann Mon",
            "startLocation": [33.77284, -84.42989],
            "endLocation": [33.77284, -84.42989
            ],
            "minStartTime": "2027-02-01T09:00:00Z",
            "maxEndTime": "2027-02-01T17:00:00Z",
            "requiredBreaks": [
              {
                "id": "Ann Mon Lunch",
                "type": "FLOATING",
                "minStartTime": "2027-02-01T12:00:00Z",
                "maxEndTime": "2027-02-01T14:00:00Z",
                "duration": "PT1H"
              }
            ]
          }
        ]
      }
    ],
    "jobs": [
      {
        "id": "Job A",
        "stops": [
          {
            "id": "A1",
            "location": [33.77911, -84.49644],
            "duration": "PT20M"
          },
          {
            "id": "A2",
            "location": [33.65979, -84.46366],
            "duration": "PT20M",
            "stopDependencies": [
              {
                "id": "A1_dep1",
                "precedingStop": "A1"
              }
            ]
          }
        ]
      },
      {
        "id": "Job B",
        "stops": [
          {
            "id": "B1",
            "location": [34.11110, -84.43002],
            "duration": "PT20M"
          },
          {
            "id": "B2",
            "location": [33.48594, -84.26560],
            "duration": "PT20M",
            "stopDependencies": [
              {
                "id": "jobB_dep1",
                "precedingStop": "B1"
              }
            ]
          }
        ]
      }
    ]
  }
}
To request the solution, locate the 'ID' from the response to the post operation and append it to the following API call:
curl -X GET -H 'X-API-KEY: <API_KEY>' https://app.timefold.ai/api/models/pickup-delivery-routing/v1/route-plans/<ID>
{
  "metadata": {
    "id": "ID",
    "parentId": null,
    "originId": "ID",
    "name": "Floating break example",
    "submitDateTime": "2025-08-05T06:01:57.647392593Z",
    "startDateTime": "2025-08-05T06:03:30.535362699Z",
    "activeDateTime": "2025-08-05T06:03:35.778590253Z",
    "completeDateTime": "2025-08-05T06:03:37.449820561Z",
    "shutdownDateTime": "2025-08-05T06:03:38.183414852Z",
    "solverStatus": "SOLVING_COMPLETED",
    "score": "0hard/0medium/-11055soft",
    "tags": [
      "system.type:from-request",
      "system.profile:default"
    ],
    "validationResult": {
      "summary": "OK"
    }
  },
  "modelOutput": {
    "drivers": [
      {
        "id": "Ann",
        "shifts": [
          {
            "id": "Ann Mon",
            "startTime": "2027-02-01T09:00:00Z",
            "itinerary": [
              {
                "id": "B1",
                "arrivalTime": "2027-02-01T09:47:38Z",
                "startServiceTime": "2027-02-01T09:47:38Z",
                "departureTime": "2027-02-01T10:07:38Z",
                "effectiveServiceDuration": "PT20M",
                "kind": "STOP",
                "travelTimeFromPreviousStandstill": "PT47M38S",
                "travelDistanceMetersFromPreviousStandstill": 55346
              },
              {
                "id": "A1",
                "arrivalTime": "2027-02-01T10:49:37Z",
                "startServiceTime": "2027-02-01T10:49:37Z",
                "departureTime": "2027-02-01T11:09:37Z",
                "effectiveServiceDuration": "PT20M",
                "kind": "STOP",
                "travelTimeFromPreviousStandstill": "PT41M59S",
                "travelDistanceMetersFromPreviousStandstill": 52623
              },
              {
                "id": "A2",
                "arrivalTime": "2027-02-01T11:26:44Z",
                "startServiceTime": "2027-02-01T11:26:44Z",
                "departureTime": "2027-02-01T11:46:44Z",
                "effectiveServiceDuration": "PT20M",
                "kind": "STOP",
                "travelTimeFromPreviousStandstill": "PT17M7S",
                "travelDistanceMetersFromPreviousStandstill": 18056
              },
              {
                "id": "B2",
                "arrivalTime": "2027-02-01T12:22:04Z",
                "startServiceTime": "2027-02-01T12:22:04Z",
                "departureTime": "2027-02-01T12:42:04Z",
                "effectiveServiceDuration": "PT20M",
                "kind": "STOP",
                "travelTimeFromPreviousStandstill": "PT35M20S",
                "travelDistanceMetersFromPreviousStandstill": 34475
              },
              {
                "id": "Ann Mon Lunch",
                "kind": "BREAK",
                "startTime": "2027-02-01T12:42:04Z",
                "endTime": "2027-02-01T13:42:04Z"
              }
            ],
            "metrics": {
              "totalTravelTime": "PT3H4M15S",
              "travelTimeFromStartLocationToFirstStop": "PT47M38S",
              "travelTimeBetweenStops": "PT1H34M26S",
              "travelTimeFromLastStopToEndLocation": "PT42M11S",
              "totalTravelDistanceMeters": 206990,
              "travelDistanceFromStartLocationToFirstStopMeters": 55346,
              "travelDistanceBetweenStopsMeters": 105154,
              "travelDistanceFromLastStopToEndLocationMeters": 46490,
              "endLocationArrivalTime": "2027-02-01T14:24:15Z",
              "overtime": "PT0S"
            }
          }
        ]
      }
    ]
  },
  "inputMetrics": {
    "stops": 4,
    "drivers": 1,
    "driverShifts": 1
  },
  "kpis": {
    "totalTravelTime": "PT3H4M15S",
    "travelTimeFromStartLocationToFirstStop": "PT47M38S",
    "travelTimeBetweenStops": "PT1H34M26S",
    "travelTimeFromLastStopToEndLocation": "PT42M11S",
    "totalTravelDistanceMeters": 206990,
    "travelDistanceFromStartLocationToFirstStopMeters": 55346,
    "travelDistanceBetweenStopsMeters": 105154,
    "travelDistanceFromLastStopToEndLocationMeters": 46490,
    "totalUnassignedStops": 0,
    "totalAssignedStops": 4,
    "totalActivatedDrivers": 1,
    "totalOvertime": "PT0S"
  }
}

2. Personal appointments with a location

Sometimes breaks are fixed and need to occur at a specific time and location, for instance, if a driver has a personal appointment, such as a visit to the dentist.

When this is the case, Timefold will schedule the break at the specified time and include travel time to the location.

Fixed breaks are defined in requiredBreaks:

{
  "shifts": [
    {
      "requiredBreaks": [
        {
          "id": "Ann Dentist",
          "type": "FIXED",
          "startTime": "2027-02-01T14:00:00Z",
          "endTime": "2027-02-01T15:00:00Z",
          "location": [33.79438, -84.13629]
        }
      ]
    }
  ]
}

At a minimum, requiredBreaks with type FIXED must include, id, startTime, and endtime, and can optionally include a location:

  • id is a unique string that identifies the break, for instance: Ann Dentist.

  • type can be FLOATING or FIXED. Fixed breaks use type, FIXED.

  • startTime is a date and time (in ISO 8601 date time with offset to UTC format) for the start time of the break.

  • endTime is a date and time (in ISO 8601 date time with offset to UTC format) for the end time of the break.

  • location is the location associated with the break.

The No conflict with fixed location break hard constraint penalizes the solution with a hard score if there is a conflict with the fixed break.

Stops will be left unassigned, if assigning them would mean breaking this constraint.

In the following example, Ann works an 8-hour shift, and she has a 1-hour dentist appointment at 11:00. There are 2 jobs (Job A and Job B) with 2 stops each. The stops in Job A are scheduled first, Ann travels to the dentist’s location, but she arrives early and must wait. The appointment ends at 12:00 and Ann travels to the next stop in her shift.

personal appointment with location
  • Input

  • Output

Try this example in Timefold Platform by saving this JSON into a file called sample.json and make the following API call:
curl -X POST -H "Content-type: application/json" -H 'X-API-KEY: <API_KEY>' https://app.timefold.ai/api/models/pickup-delivery-routing/v1/route-plans [email protected]
{
  "config": {
    "run": {
      "name": "Appointment with location example"
    }
  },
  "modelInput": {
    "drivers": [
      {
        "id": "Ann",
        "shifts": [
          {
            "id": "Ann Mon",
            "startLocation": [33.77284, -84.42989],
            "endLocation": [33.77284, -84.42989
            ],
            "minStartTime": "2027-02-01T09:00:00Z",
            "maxEndTime": "2027-02-01T17:00:00Z",
            "requiredBreaks": [
              {
                "id": "Ann Dentist",
                "type": "FIXED",
                "startTime": "2027-02-01T11:00:00Z",
                "endTime": "2027-02-01T12:00:00Z",
                "location": [33.79438, -84.13629]
              }
            ]
          }
        ]
      }
    ],
    "jobs": [
      {
        "id": "Job A",
        "stops": [
          {
            "id": "A1",
            "location": [33.77911, -84.49644],
            "duration": "PT20M"
          },
          {
            "id": "A2",
            "location": [33.65979, -84.46366],
            "duration": "PT20M",
            "stopDependencies": [
              {
                "id": "A1_dep1",
                "precedingStop": "A1"
              }
            ]
          }
        ]
      },
      {
        "id": "Job B",
        "stops": [
          {
            "id": "B1",
            "location": [34.11110, -84.43002],
            "duration": "PT20M"
          },
          {
            "id": "B2",
            "location": [33.48594, -84.26560],
            "duration": "PT20M",
            "stopDependencies": [
              {
                "id": "jobB_dep1",
                "precedingStop": "B1"
              }
            ]
          }
        ]
      }
    ]
  }
}
To request the solution, locate the 'ID' from the response to the post operation and append it to the following API call:
curl -X GET -H 'X-API-KEY: <API_KEY>' https://app.timefold.ai/api/models/pickup-delivery-routing/v1/route-plans/<ID>
{
  "metadata": {
    "id": "ID",
    "parentId": null,
    "originId": "ID",
    "name": "Appointment with location example",
    "submitDateTime": "2025-08-05T08:24:44.76921252Z",
    "startDateTime": "2025-08-05T08:24:50.226082186Z",
    "activeDateTime": "2025-08-05T08:24:54.731469264Z",
    "completeDateTime": "2025-08-05T08:29:00.207176157Z",
    "shutdownDateTime": "2025-08-05T08:29:00.683458919Z",
    "solverStatus": "SOLVING_COMPLETED",
    "score": "0hard/0medium/-12486soft",
    "tags": [
      "system.type:from-request",
      "system.profile:default"
    ],
    "validationResult": {
      "summary": "OK"
    }
  },
  "modelOutput": {
    "drivers": [
      {
        "id": "Ann",
        "shifts": [
          {
            "id": "Ann Mon",
            "startTime": "2027-02-01T09:00:00Z",
            "itinerary": [
              {
                "id": "A1",
                "arrivalTime": "2027-02-01T09:07:42Z",
                "startServiceTime": "2027-02-01T09:07:42Z",
                "departureTime": "2027-02-01T09:27:42Z",
                "effectiveServiceDuration": "PT20M",
                "kind": "STOP",
                "travelTimeFromPreviousStandstill": "PT7M42S",
                "travelDistanceMetersFromPreviousStandstill": 7661
              },
              {
                "id": "A2",
                "arrivalTime": "2027-02-01T09:44:49Z",
                "startServiceTime": "2027-02-01T09:44:49Z",
                "departureTime": "2027-02-01T10:04:49Z",
                "effectiveServiceDuration": "PT20M",
                "kind": "STOP",
                "travelTimeFromPreviousStandstill": "PT17M7S",
                "travelDistanceMetersFromPreviousStandstill": 18056
              },
              {
                "id": "Ann Dentist",
                "kind": "BREAK",
                "startTime": "2027-02-01T11:00:00Z",
                "endTime": "2027-02-01T12:00:00Z",
                "travelTimeFromPreviousStandstill": "PT44M19S",
                "travelDistanceMetersFromPreviousStandstill": 40283
              },
              {
                "id": "B1",
                "arrivalTime": "2027-02-01T13:00:26Z",
                "startServiceTime": "2027-02-01T13:00:26Z",
                "departureTime": "2027-02-01T13:20:26Z",
                "effectiveServiceDuration": "PT20M",
                "kind": "STOP",
                "travelTimeFromPreviousStandstill": "PT1H26S",
                "travelDistanceMetersFromPreviousStandstill": 55479
              },
              {
                "id": "B2",
                "arrivalTime": "2027-02-01T14:41:06Z",
                "startServiceTime": "2027-02-01T14:41:06Z",
                "departureTime": "2027-02-01T15:01:06Z",
                "effectiveServiceDuration": "PT20M",
                "kind": "STOP",
                "travelTimeFromPreviousStandstill": "PT1H20M40S",
                "travelDistanceMetersFromPreviousStandstill": 97259
              }
            ],
            "metrics": {
              "totalTravelTime": "PT4H12M25S",
              "travelTimeFromStartLocationToFirstStop": "PT7M42S",
              "travelTimeBetweenStops": "PT3H22M32S",
              "travelTimeFromLastStopToEndLocation": "PT42M11S",
              "totalTravelDistanceMeters": 265228,
              "travelDistanceFromStartLocationToFirstStopMeters": 7661,
              "travelDistanceBetweenStopsMeters": 211077,
              "travelDistanceFromLastStopToEndLocationMeters": 46490,
              "endLocationArrivalTime": "2027-02-01T15:43:17Z",
              "overtime": "PT0S"
            }
          }
        ]
      }
    ]
  },
  "inputMetrics": {
    "stops": 4,
    "drivers": 1,
    "driverShifts": 1
  },
  "kpis": {
    "totalTravelTime": "PT4H12M25S",
    "travelTimeFromStartLocationToFirstStop": "PT7M42S",
    "travelTimeBetweenStops": "PT3H22M32S",
    "travelTimeFromLastStopToEndLocation": "PT42M11S",
    "totalTravelDistanceMeters": 265228,
    "travelDistanceFromStartLocationToFirstStopMeters": 7661,
    "travelDistanceBetweenStopsMeters": 211077,
    "travelDistanceFromLastStopToEndLocationMeters": 46490,
    "totalUnassignedStops": 0,
    "totalAssignedStops": 4,
    "totalActivatedDrivers": 1,
    "totalOvertime": "PT0S"
  }
}

3. Personal appointments without a location

There are occasions when employees need to attend appointments, but the location of the appointment is sensitive information, for instance, if they are receiving medical treatment or attending a job interview, and they do not share the location with their employer.

When the location for a fixed appointment is not provided, Timefold cannot schedule travel time.

The No conflict with fixed break hard constraint penalizes the solution with a hard score if there is a conflict with the fixed break.

Stops will be left unassigned, if assigning them would mean breaking this constraint.

As with the previous example, Ann has a 1-hour appointment at 11:00, but this time she hasn’t provided the location. There are 2 jobs (Job A and Job B) with 2 stops each. The stops in Job A are scheduled first, Ann then has between 10:04 and 11:00 to travel to her appointment. Without the location information, Timefold delays travel from Stop A2 to Stop B1 until after the appointment.

However, this approach may not provide enough travel time to reach the appointment, and the travel time from the appointment to Stop B is unlikely to be accurate. In such situations, it may be necessary to pad the appointment to provide additional travel time.

personal appointment with no location
  • Input

  • Output

Try this example in Timefold Platform by saving this JSON into a file called sample.json and make the following API call:
curl -X POST -H "Content-type: application/json" -H 'X-API-KEY: <API_KEY>' https://app.timefold.ai/api/models/pickup-delivery-routing/v1/route-plans [email protected]
{
  "config": {
    "run": {
      "name": "Appointment with no location example"
    }
  },
  "modelInput": {
    "drivers": [
      {
        "id": "Ann",
        "shifts": [
          {
            "id": "Ann Mon",
            "startLocation": [33.77284, -84.42989],
            "endLocation": [33.77284, -84.42989
            ],
            "minStartTime": "2027-02-01T09:00:00Z",
            "maxEndTime": "2027-02-01T17:00:00Z",
            "requiredBreaks": [
              {
                "id": "Ann apt",
                "type": "FIXED",
                "policy": "PERMIT_JOBS",
                "startTime": "2027-02-01T11:00:00Z",
                "endTime": "2027-02-01T12:00:00Z"
              }
            ]
          }
        ]
      }
    ],
    "jobs": [
      {
        "id": "Job A",
        "stops": [
          {
            "id": "A1",
            "location": [33.77911, -84.49644],
            "duration": "PT20M"
          },
          {
            "id": "A2",
            "location": [33.65979, -84.46366],
            "duration": "PT20M",
            "stopDependencies": [
              {
                "id": "A1_dep1",
                "precedingStop": "A1"
              }
            ]
          }
        ]
      },
      {
        "id": "Job B",
        "stops": [
          {
            "id": "B1",
            "location": [34.11110, -84.43002],
            "duration": "PT20M"
          },
          {
            "id": "B2",
            "location": [33.48594, -84.26560],
            "duration": "PT20M",
            "stopDependencies": [
              {
                "id": "jobB_dep1",
                "precedingStop": "B1"
              }
            ]
          }
        ]
      }
    ]
  }
}
To request the solution, locate the 'ID' from the response to the post operation and append it to the following API call:
curl -X GET -H 'X-API-KEY: <API_KEY>' https://app.timefold.ai/api/models/pickup-delivery-routing/v1/route-plans/<ID>
{
  "metadata": {
    "id": "ID",
    "parentId": null,
    "originId": "ID",
    "name": "Appointment with no location example",
    "submitDateTime": "2025-08-05T09:26:04.038994932Z",
    "startDateTime": "2025-08-05T09:27:38.488297635Z",
    "activeDateTime": "2025-08-05T09:27:44.128400813Z",
    "shutdownDateTime": "2025-08-05T09:31:22.296055Z",
    "solverStatus": "SOLVING_COMPLETED",
    "score": "0hard/0medium/-12401soft",
    "tags": [
      "system.type:from-request",
      "system.profile:default"
    ],
    "validationResult": {
      "summary": "OK"
    }
  },
  "modelOutput": {
    "drivers": [
      {
        "id": "Ann",
        "shifts": [
          {
            "id": "Ann Mon",
            "startTime": "2027-02-01T09:00:00Z",
            "itinerary": [
              {
                "id": "A1",
                "arrivalTime": "2027-02-01T09:07:42Z",
                "startServiceTime": "2027-02-01T09:07:42Z",
                "departureTime": "2027-02-01T09:27:42Z",
                "effectiveServiceDuration": "PT20M",
                "kind": "STOP",
                "travelTimeFromPreviousStandstill": "PT7M42S",
                "travelDistanceMetersFromPreviousStandstill": 7661
              },
              {
                "id": "A2",
                "arrivalTime": "2027-02-01T09:44:49Z",
                "startServiceTime": "2027-02-01T09:44:49Z",
                "departureTime": "2027-02-01T10:04:49Z",
                "effectiveServiceDuration": "PT20M",
                "kind": "STOP",
                "travelTimeFromPreviousStandstill": "PT17M7S",
                "travelDistanceMetersFromPreviousStandstill": 18056
              },
              {
                "id": "Ann apt",
                "kind": "BREAK",
                "startTime": "2027-02-01T11:00:00Z",
                "endTime": "2027-02-01T12:00:00Z"
              },
              {
                "id": "B1",
                "arrivalTime": "2027-02-01T12:59:01Z",
                "startServiceTime": "2027-02-01T12:59:01Z",
                "departureTime": "2027-02-01T13:19:01Z",
                "effectiveServiceDuration": "PT20M",
                "kind": "STOP",
                "travelTimeFromPreviousStandstill": "PT59M1S",
                "travelDistanceMetersFromPreviousStandstill": 70725
              },
              {
                "id": "B2",
                "arrivalTime": "2027-02-01T14:39:41Z",
                "startServiceTime": "2027-02-01T14:39:41Z",
                "departureTime": "2027-02-01T14:59:41Z",
                "effectiveServiceDuration": "PT20M",
                "kind": "STOP",
                "travelTimeFromPreviousStandstill": "PT1H20M40S",
                "travelDistanceMetersFromPreviousStandstill": 97259
              }
            ],
            "metrics": {
              "totalTravelTime": "PT3H26M41S",
              "travelTimeFromStartLocationToFirstStop": "PT7M42S",
              "travelTimeBetweenStops": "PT2H36M48S",
              "travelTimeFromLastStopToEndLocation": "PT42M11S",
              "totalTravelDistanceMeters": 240191,
              "travelDistanceFromStartLocationToFirstStopMeters": 7661,
              "travelDistanceBetweenStopsMeters": 186040,
              "travelDistanceFromLastStopToEndLocationMeters": 46490,
              "endLocationArrivalTime": "2027-02-01T15:41:52Z",
              "overtime": "PT0S"
            }
          }
        ]
      }
    ]
  },
  "inputMetrics": {
    "stops": 4,
    "drivers": 1,
    "driverShifts": 1
  },
  "kpis": {
    "totalTravelTime": "PT3H26M41S",
    "travelTimeFromStartLocationToFirstStop": "PT7M42S",
    "travelTimeBetweenStops": "PT2H36M48S",
    "travelTimeFromLastStopToEndLocation": "PT42M11S",
    "totalTravelDistanceMeters": 240191,
    "travelDistanceFromStartLocationToFirstStopMeters": 7661,
    "travelDistanceBetweenStopsMeters": 186040,
    "travelDistanceFromLastStopToEndLocationMeters": 46490,
    "totalUnassignedStops": 0,
    "totalAssignedStops": 4,
    "totalActivatedDrivers": 1,
    "totalOvertime": "PT0S"
  }
}

Sharing the location of appointments with their employer is a change for many employees who might be familiar with systems where this isn’t necessary, but if employees are comfortable sharing the location of their appointments, travel time can be included, and it becomes unnecessary to add additional time, and the schedule will include less driving time and more time available for customer visits.

4. Allowing and disallowing breaks during jobs

There are different circumstances when jobs allow or do not allow drivers to take breaks. For instance:

  • Drivers can take a break during a job transporting goods.

  • Drivers cannot take a break during a job transporting passengers.

The No fixed break during job and No floating break during job hard constraints penalize solutions with a hard score if a break is assigned during a job which does not permit breaks.

Jobs can specify when breaks are or are not allowed to take place during the job by adding allowInterruption and specifying NONE or BREAKS:

{
  "id": "Job A",
  "allowInterruption": "NONE",
  "stops": [
    {
      "id": "A1",
      "location": [33.77911, -84.49644],
      "duration": "PT20M"
    },
    {
      "id": "A2",
      "location": [33.65979, -84.46366],
      "duration": "PT20M",
      "stopDependencies": [
        {
          "id": "A1_dep1",
          "precedingStop": "A1"
        }
      ]
    }
  ]
}
  • NONE prevents breaks from being assigned during the job.

  • BREAKS allows breaks to be assigned during the job.

If omitted, the default behavior is NONE.

There are also times when breaks must not be assigned during a job. For instance:

  • The driver’s vehicle must stop for maintenance and cannot have passengers or goods onboard.

requiredBreaks (both fixed and floating) can specify if the break can be taken during a job or not by adding policy and specifying PERMIT_JOBS or REJECT_JOBS:

{
  "requiredBreaks": [
    {
      "id": "Ann apt",
      "type": "FIXED",
      "policy": "PERMIT_JOBS",
      "startTime": "2027-02-01T11:00:00Z",
      "endTime": "2027-02-01T12:00:00Z"
    }
  ]
}
  • PERMIT_JOBS means that the break can be assigned during a job, provided the job permits it.

  • REJECT_JOBS means that the break cannot be assigned during a job, even when jobs permit breaks.

If omitted, the default behavior is PERMIT_JOBS.

Next

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

  • Learn about Shift hours and overtime.

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