Docs
  • Solver
  • Models
    • Field Service Routing
    • Employee Shift Scheduling
    • Pick-up and Delivery Routing
  • Platform
Try models
  • Pick-up and Delivery Routing
  • Recommendations
  • Stop time window recommendations

Pick-up and Delivery Routing

    • Introduction
    • Getting started: Hello world
    • User guide
      • Terminology
      • Use case guide
      • Planning AI concepts
      • Integration
      • Constraints
      • Understanding the API
      • Demo datasets
      • Input datasets
        • Model configuration
        • Model input
        • Planning window
      • Input validation
      • Output datasets
        • Metadata
        • Model output
        • Input metrics
        • Key performance indicators (KPIs)
      • 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 jobs and optional jobs
      • Stop service level agreement (SLA)
      • Job requirements and tags
        • Job required drivers
        • Job pooling
        • Prohibit job combinations
        • Maximum time burden
        • Driver capacity
        • Tags
    • Recommendations
      • Job time window recommendations
      • Stop time window recommendations
    • Real-time planning
      • Real-time planning: pinning stops
    • Changelog
    • Upgrading to the latest versions
    • Feature requests

Stop time window recommendations

When requesting recommendations for a pick-up and deliver job, it is sometimes required to provide more precise information about time windows for the individual stops than just one large time window for the whole job.

Recommendations for stop time windows provide a range of recommendations for a single job where one or more stops have individual time windows to consider.

This guide explains stops recommendations with the following examples:

  • 1. Stop recommendations for unsolved route plans
  • 2. Stop recommendations for unsolved route plans with existing jobs
  • 3. Stop recommendations for published route plans

1. Stop recommendations for unsolved route plans

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

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

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

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

Continuous planning includes four stages:

  1. Historic
    The historic stage includes schedules that have already been executed and cannot be changed.

  2. Published
    The published stage includes schedules that have been shared with your customers and drivers and should only be changed in special circumstances.

  3. Draft
    The draft stage includes schedules that can change without impacting customers or drivers. At some point, draft schedules will be solved and moved to the published stage.

  4. 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 schedules to choose from.

The customer’s chosen recommendation 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

The maximum number of recommendations to make for this request.

The recommendation will consider driver shifts that overlap with the specified stop time windows.

{
  "maxNumberOfRecommendations": 2
}

1.1.2. Fit job ID

This recommendation request will try to fit the whole job with all its stops into the schedule. Therefore, the ID of the job to make the recommendations for is part of the request. The provided fitJobId must match an ID from a job in the modelInput.

{
  "fitJobId": "Job A"
}

1.1.3. Stop time windows

While creating recommendations for the given job, these time windows for specific stops will be considered.

For every stop of a job, time windows can be provided to indicate the potential periods in which the stop can be scheduled.

For instance, 09:00 to 13:00 on February 1, 2027, and 13:00 to 17:00 on February 1, 2027.

{
  "fitStops": [
    {
      "fitStopId": "stop-1",
      "timeWindows": [
        {
            "minStartTime": "2027-02-01T11:00:00Z",
            "maxEndTime": "2027-02-01T14:00:00Z"
        }
      ]
    }
  ]
}

1.1.4. Model input

The model input must include the drivers and shifts and the job the recommendations are for.

To learn more about driver shifts and stops, see Shift hours and overtime.

{
  "modelInput": {
    "drivers": [
      {
        "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"
          }
        ]
      }
    ],
    "jobs": [
      {
        "id": "Job A",
        "stops": [
            {
                "id": "stop-1",
                "location": [33.84475, -84.63649],
                "serviceDuration": "PT10M"
            },
            {
                "id": "stop-2",
                "location": [33.57448, -84.94636],
                "serviceDuration": "PT10M",
                "stopDependencies": [
                  {
                    "id": "prev-stop-2",
                    "precedingStop": "stop-1"

                  }
                ]
            }
        ]
      }
    ]
  }
}

1.2. Submit the input dataset

Submit the recommendations input dataset to the API endpoint: /v1/route-plans/recommendations/stops-recommend-time-windows.

If you want individual constraint match justifications included in the response, you can specify an optional boolean includeJustifications query parameter: /v1/route-plans/recommendations/stops-recommend-time-windows?includeJustifications=true.

The HTTP response provides the recommendations.

  • Input

  • Output (without justifications)

  • Output (with justifications)

Try this example in Timefold Platform by saving this JSON into a file called sample.json and make the following API call:

To get recommendations without constraint match justifications (default):

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/recommendations/stops-recommend-time-windows [email protected]

To get recommendations with constraint match justifications:

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/recommendations/stops-recommend-time-windows?includeJustifications=true [email protected]
{
  "maxNumberOfRecommendations": 2,
  "fitJobId": "Job A",
  "fitStops": [
    {
      "fitStopId": "stop-1",
      "timeWindows": [
        {
          "minStartTime": "2027-02-01T11:00:00Z",
          "maxEndTime": "2027-02-01T14:00:00Z"
        }
      ]
    }
  ],
  "modelInput": {
    "drivers": [
      {
        "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"
          }
        ]
      }
    ],
    "jobs": [
      {
        "id": "Job A",
        "stops": [
          {
            "id": "stop-1",
            "location": [
              33.84475,
              -84.63649
            ],
            "duration": "PT10M"
          },
          {
            "id": "stop-2",
            "location": [
              33.57448,
              -84.94636
            ],
            "duration": "PT10M",
            "stopDependencies": [
              {
                "id": "prev-stop-2",
                "precedingStop": "stop-1"
              }
            ]
          }
        ]
      }
    ]
  }
}
{
  "recommendations": [
    {
      "scoreDiff": "0hard/20000medium/-11405soft",
      "constraintDiffs": [
        {
          "score": "0hard/0medium/-11405soft",
          "constraintName": "Minimize travel time",
          "matchCountDiff": 2
        },
        {
          "score": "0hard/20000medium/0soft",
          "constraintName": "Require scheduling mandatory stops",
          "matchCountDiff": -2
        }
      ],
      "driverShift": {
        "id": "Ann-2027-02-01",
        "startTime": "2027-02-01T09:00:00Z",
        "itinerary": [
          {
            "id": "stop-1",
            "kind": "STOP",
            "arrivalTime": "2027-02-01T09:54:18Z",
            "startServiceTime": "2027-02-01T11:00:00Z",
            "departureTime": "2027-02-01T11:10:00Z",
            "effectiveServiceDuration": "PT10M",
            "travelTimeFromPreviousStandstill": "PT54M18S",
            "travelDistanceMetersFromPreviousStandstill": 45245,
            "load": []
          },
          {
            "id": "stop-2",
            "kind": "STOP",
            "arrivalTime": "2027-02-01T11:59:50Z",
            "startServiceTime": "2027-02-01T11:59:50Z",
            "departureTime": "2027-02-01T12:09:50Z",
            "effectiveServiceDuration": "PT10M",
            "travelTimeFromPreviousStandstill": "PT49M50S",
            "travelDistanceMetersFromPreviousStandstill": 41530,
            "load": []
          }
        ],
        "metrics": {
          "totalTravelTime": "PT3H10M5S",
          "travelTimeFromStartLocationToFirstStop": "PT54M18S",
          "travelTimeBetweenStops": "PT49M50S",
          "travelTimeFromLastStopToEndLocation": "PT1H25M57S",
          "totalTravelDistanceMeters": 158394,
          "travelDistanceFromStartLocationToFirstStopMeters": 45245,
          "travelDistanceBetweenStopsMeters": 41530,
          "travelDistanceFromLastStopToEndLocationMeters": 71619,
          "endLocationArrivalTime": "2027-02-01T13:35:47Z"
        }
      },
      "kpis": {
        "totalTravelTime": "PT3H10M5S",
        "totalTravelDistanceMeters": 158394,
        "totalActivatedDrivers": 1,
        "totalUnassignedJobs": 0,
        "totalAssignedJobs": 1,
        "assignedMandatoryJobs": 1,
        "totalUnassignedStops": 0,
        "totalAssignedStops": 2,
        "assignedMandatoryStops": 2,
        "travelTimeFromStartLocationToFirstStop": "PT54M18S",
        "travelTimeBetweenStops": "PT49M50S",
        "travelTimeFromLastStopToEndLocation": "PT1H25M57S",
        "travelDistanceFromStartLocationToFirstStopMeters": 45245,
        "travelDistanceBetweenStopsMeters": 41530,
        "travelDistanceFromLastStopToEndLocationMeters": 71619
      }
    },
    {
      "scoreDiff": "0hard/20000medium/-12920soft",
      "constraintDiffs": [
        {
          "score": "0hard/0medium/-12920soft",
          "constraintName": "Minimize travel time",
          "matchCountDiff": 2
        },
        {
          "score": "0hard/20000medium/0soft",
          "constraintName": "Require scheduling mandatory stops",
          "matchCountDiff": -2
        }
      ],
      "driverShift": {
        "id": "Beth-2027-02-01",
        "startTime": "2027-02-01T09:00:00Z",
        "itinerary": [
          {
            "id": "stop-1",
            "kind": "STOP",
            "arrivalTime": "2027-02-01T10:06:04Z",
            "startServiceTime": "2027-02-01T11:00:00Z",
            "departureTime": "2027-02-01T11:10:00Z",
            "effectiveServiceDuration": "PT10M",
            "travelTimeFromPreviousStandstill": "PT1H6M4S",
            "travelDistanceMetersFromPreviousStandstill": 55061,
            "load": []
          },
          {
            "id": "stop-2",
            "kind": "STOP",
            "arrivalTime": "2027-02-01T11:59:50Z",
            "startServiceTime": "2027-02-01T11:59:50Z",
            "departureTime": "2027-02-01T12:09:50Z",
            "effectiveServiceDuration": "PT10M",
            "travelTimeFromPreviousStandstill": "PT49M50S",
            "travelDistanceMetersFromPreviousStandstill": 41530,
            "load": []
          }
        ],
        "metrics": {
          "totalTravelTime": "PT3H35M20S",
          "travelTimeFromStartLocationToFirstStop": "PT1H6M4S",
          "travelTimeBetweenStops": "PT49M50S",
          "travelTimeFromLastStopToEndLocation": "PT1H39M26S",
          "totalTravelDistanceMeters": 179450,
          "travelDistanceFromStartLocationToFirstStopMeters": 55061,
          "travelDistanceBetweenStopsMeters": 41530,
          "travelDistanceFromLastStopToEndLocationMeters": 82859,
          "endLocationArrivalTime": "2027-02-01T13:49:16Z"
        }
      },
      "kpis": {
        "totalTravelTime": "PT3H35M20S",
        "totalTravelDistanceMeters": 179450,
        "totalActivatedDrivers": 1,
        "totalUnassignedJobs": 0,
        "totalAssignedJobs": 1,
        "assignedMandatoryJobs": 1,
        "totalUnassignedStops": 0,
        "totalAssignedStops": 2,
        "assignedMandatoryStops": 2,
        "travelTimeFromStartLocationToFirstStop": "PT1H6M4S",
        "travelTimeBetweenStops": "PT49M50S",
        "travelTimeFromLastStopToEndLocation": "PT1H39M26S",
        "travelDistanceFromStartLocationToFirstStopMeters": 55061,
        "travelDistanceBetweenStopsMeters": 41530,
        "travelDistanceFromLastStopToEndLocationMeters": 82859
      }
    }
  ]
}
{
  "recommendations": [
    {
      "scoreDiff": "0hard/20000medium/-11405soft",
      "constraintDiffs": [
        {
          "score": "0hard/0medium/-11405soft",
          "constraintName": "Minimize travel time",
          "matchesDiff": [
            {
              "score": "0hard/0medium/-8147soft",
              "justification": {
                "stopId": "stop-2",
                "travelTime": "PT2H15M47S",
                "description": "Stop (stop-2) has travel time of 'PT2H15M47S'."
              }
            },
            {
              "score": "0hard/0medium/-3258soft",
              "justification": {
                "stopId": "stop-1",
                "travelTime": "PT54M18S",
                "description": "Stop (stop-1) has travel time of 'PT54M18S'."
              }
            }
          ],
          "matchCountDiff": 2
        },
        {
          "score": "0hard/20000medium/0soft",
          "constraintName": "Require scheduling mandatory stops",
          "matchesDiff": [
            {
              "score": "0hard/10000medium/0soft",
              "justification": {
                "stopId": "stop-1",
                "description": "Mandatory stop (stop-1) has been left unassigned."
              }
            },
            {
              "score": "0hard/10000medium/0soft",
              "justification": {
                "stopId": "stop-2",
                "description": "Mandatory stop (stop-2) has been left unassigned."
              }
            }
          ],
          "matchCountDiff": -2
        }
      ],
      "driverShift": {
        "id": "Ann-2027-02-01",
        "startTime": "2027-02-01T09:00:00Z",
        "itinerary": [
          {
            "id": "stop-1",
            "kind": "STOP",
            "arrivalTime": "2027-02-01T09:54:18Z",
            "startServiceTime": "2027-02-01T11:00:00Z",
            "departureTime": "2027-02-01T11:10:00Z",
            "effectiveServiceDuration": "PT10M",
            "travelTimeFromPreviousStandstill": "PT54M18S",
            "travelDistanceMetersFromPreviousStandstill": 45245,
            "load": []
          },
          {
            "id": "stop-2",
            "kind": "STOP",
            "arrivalTime": "2027-02-01T11:59:50Z",
            "startServiceTime": "2027-02-01T11:59:50Z",
            "departureTime": "2027-02-01T12:09:50Z",
            "effectiveServiceDuration": "PT10M",
            "travelTimeFromPreviousStandstill": "PT49M50S",
            "travelDistanceMetersFromPreviousStandstill": 41530,
            "load": []
          }
        ],
        "metrics": {
          "totalTravelTime": "PT3H10M5S",
          "travelTimeFromStartLocationToFirstStop": "PT54M18S",
          "travelTimeBetweenStops": "PT49M50S",
          "travelTimeFromLastStopToEndLocation": "PT1H25M57S",
          "totalTravelDistanceMeters": 158394,
          "travelDistanceFromStartLocationToFirstStopMeters": 45245,
          "travelDistanceBetweenStopsMeters": 41530,
          "travelDistanceFromLastStopToEndLocationMeters": 71619,
          "endLocationArrivalTime": "2027-02-01T13:35:47Z"
        }
      },
      "kpis": {
        "totalTravelTime": "PT3H10M5S",
        "totalTravelDistanceMeters": 158394,
        "totalActivatedDrivers": 1,
        "totalUnassignedJobs": 0,
        "totalAssignedJobs": 1,
        "assignedMandatoryJobs": 1,
        "totalUnassignedStops": 0,
        "totalAssignedStops": 2,
        "assignedMandatoryStops": 2,
        "travelTimeFromStartLocationToFirstStop": "PT54M18S",
        "travelTimeBetweenStops": "PT49M50S",
        "travelTimeFromLastStopToEndLocation": "PT1H25M57S",
        "travelDistanceFromStartLocationToFirstStopMeters": 45245,
        "travelDistanceBetweenStopsMeters": 41530,
        "travelDistanceFromLastStopToEndLocationMeters": 71619
      }
    },
    {
      "scoreDiff": "0hard/20000medium/-12920soft",
      "constraintDiffs": [
        {
          "score": "0hard/0medium/-12920soft",
          "constraintName": "Minimize travel time",
          "matchesDiff": [
            {
              "score": "0hard/0medium/-3964soft",
              "justification": {
                "stopId": "stop-1",
                "travelTime": "PT1H6M4S",
                "description": "Stop (stop-1) has travel time of 'PT1H6M4S'."
              }
            },
            {
              "score": "0hard/0medium/-8956soft",
              "justification": {
                "stopId": "stop-2",
                "travelTime": "PT2H29M16S",
                "description": "Stop (stop-2) has travel time of 'PT2H29M16S'."
              }
            }
          ],
          "matchCountDiff": 2
        },
        {
          "score": "0hard/20000medium/0soft",
          "constraintName": "Require scheduling mandatory stops",
          "matchesDiff": [
            {
              "score": "0hard/10000medium/0soft",
              "justification": {
                "stopId": "stop-1",
                "description": "Mandatory stop (stop-1) has been left unassigned."
              }
            },
            {
              "score": "0hard/10000medium/0soft",
              "justification": {
                "stopId": "stop-2",
                "description": "Mandatory stop (stop-2) has been left unassigned."
              }
            }
          ],
          "matchCountDiff": -2
        }
      ],
      "driverShift": {
        "id": "Beth-2027-02-01",
        "startTime": "2027-02-01T09:00:00Z",
        "itinerary": [
          {
            "id": "stop-1",
            "kind": "STOP",
            "arrivalTime": "2027-02-01T10:06:04Z",
            "startServiceTime": "2027-02-01T11:00:00Z",
            "departureTime": "2027-02-01T11:10:00Z",
            "effectiveServiceDuration": "PT10M",
            "travelTimeFromPreviousStandstill": "PT1H6M4S",
            "travelDistanceMetersFromPreviousStandstill": 55061,
            "load": []
          },
          {
            "id": "stop-2",
            "kind": "STOP",
            "arrivalTime": "2027-02-01T11:59:50Z",
            "startServiceTime": "2027-02-01T11:59:50Z",
            "departureTime": "2027-02-01T12:09:50Z",
            "effectiveServiceDuration": "PT10M",
            "travelTimeFromPreviousStandstill": "PT49M50S",
            "travelDistanceMetersFromPreviousStandstill": 41530,
            "load": []
          }
        ],
        "metrics": {
          "totalTravelTime": "PT3H35M20S",
          "travelTimeFromStartLocationToFirstStop": "PT1H6M4S",
          "travelTimeBetweenStops": "PT49M50S",
          "travelTimeFromLastStopToEndLocation": "PT1H39M26S",
          "totalTravelDistanceMeters": 179450,
          "travelDistanceFromStartLocationToFirstStopMeters": 55061,
          "travelDistanceBetweenStopsMeters": 41530,
          "travelDistanceFromLastStopToEndLocationMeters": 82859,
          "endLocationArrivalTime": "2027-02-01T13:49:16Z"
        }
      },
      "kpis": {
        "totalTravelTime": "PT3H35M20S",
        "totalTravelDistanceMeters": 179450,
        "totalActivatedDrivers": 1,
        "totalUnassignedJobs": 0,
        "totalAssignedJobs": 1,
        "assignedMandatoryJobs": 1,
        "totalUnassignedStops": 0,
        "totalAssignedStops": 2,
        "assignedMandatoryStops": 2,
        "travelTimeFromStartLocationToFirstStop": "PT1H6M4S",
        "travelTimeBetweenStops": "PT49M50S",
        "travelTimeFromLastStopToEndLocation": "PT1H39M26S",
        "travelDistanceFromStartLocationToFirstStopMeters": 55061,
        "travelDistanceBetweenStopsMeters": 41530,
        "travelDistanceFromLastStopToEndLocationMeters": 82859
      }
    }
  ]
}

1.3. The recommendations

In this example, there are two recommendations.

  1. Assign Job A to driver Ann such that stop-1 starts at 11:00.

  2. Assign Job A to driver Beth such that stop-1 starts at 11:00.

recommendations stop time window unsolved

Each recommendation includes the following:

1.3.1. ScoreDiff

The score diff is based on the difference before the new job has been scheduled and after the new job has been scheduled.

{
  "scoreDiff": "0hard/20000medium/-11405soft"
}

1.3.2. Constraint scores without justifications

The score for each constraint that has changed, for instance:

{
    "score": "0hard/0medium/-11405soft",
    "constraintName": "Minimize travel time",
    "matchCountDiff": 2
}

1.3.3. Constraint scores with justifications

The score and justifications for each constraint that has changed, for instance:

{
  "score": "0hard/0medium/-11405soft",
  "constraintName": "Minimize travel time",
  "matchesDiff": [
    {
      "score": "0hard/0medium/-8147soft",
      "justification": {
        "stopId": "stop-2",
        "travelTime": "PT2H15M47S",
        "description": "Stop (stop-2) has travel time of 'PT2H15M47S'."
      }
    },
    {
      "score": "0hard/0medium/-3258soft",
      "justification": {
        "stopId": "stop-1",
        "travelTime": "PT54M18S",
        "description": "Stop (stop-1) has travel time of 'PT54M18S'."
      }
    }
  ],
  "matchCountDiff": 2
}

1.3.4. The driver shifts

The driver shift the recommendation applies to and the shift itinerary and metrics.

{
  "driverShift": {
    "id": "Ann-2027-02-01",
    "startTime": "2027-02-01T09:00:00Z",
    "itinerary": [
      {
        "id": "stop-1",
        "kind": "STOP",
        "arrivalTime": "2027-02-01T09:54:18Z",
        "startServiceTime": "2027-02-01T11:00:00Z",
        "departureTime": "2027-02-01T11:10:00Z",
        "effectiveServiceDuration": "PT10M",
        "travelTimeFromPreviousStandstill": "PT54M18S",
        "travelDistanceMetersFromPreviousStandstill": 45245,
        "load": []
      },
      {
        "id": "stop-2",
        "kind": "STOP",
        "arrivalTime": "2027-02-01T11:59:50Z",
        "startServiceTime": "2027-02-01T11:59:50Z",
        "departureTime": "2027-02-01T12:09:50Z",
        "effectiveServiceDuration": "PT10M",
        "travelTimeFromPreviousStandstill": "PT49M50S",
        "travelDistanceMetersFromPreviousStandstill": 41530,
        "load": []
      }
    ],
    "metrics": {
      "totalTravelTime": "PT3H10M5S",
      "travelTimeFromStartLocationToFirstStop": "PT54M18S",
      "travelTimeBetweenStops": "PT49M50S",
      "travelTimeFromLastStopToEndLocation": "PT1H25M57S",
      "totalTravelDistanceMeters": 158394,
      "travelDistanceFromStartLocationToFirstStopMeters": 45245,
      "travelDistanceBetweenStopsMeters": 41530,
      "travelDistanceFromLastStopToEndLocationMeters": 71619,
      "endLocationArrivalTime": "2027-02-01T13:35:47Z"
    }
  }
}

1.4. Implementing the recommendation

The recommendation output for this example includes two recommendations:

  1. Assign Job A to driver Ann such that stop-1 starts at 11:00.

  2. Assign Job A to driver Beth such that stop-1 starts at 11:00.

The recommendations include specific times, however, the plan is still a draft and those times will likely change as additional jobs are added to the plan.

If the customer agrees to one of the recommendations, it must be added to the recommendation input dataset (the draft plan).

In this instance, the customer accepted the second recommendation:

  • Assign Job A to driver Beth such that stop-1 starts at 11:00.

First, add the stops of Job A to Beth’s itineraries:

Where there are multiple stops to include in the itinerary, they must be included in the order they occur in the recommendation response.
{
  "id": "Beth",
  "shifts": [
    {
      "id": "Beth-2027-02-01",
      "startLocation": [33.68786, -84.18487],
      "minStartTime": "2027-02-01T09:00:00Z",
      "maxEndTime": "2027-02-01T17:00:00Z",
      "itinerary": [
        {
          "id": "stop-1",
          "kind": "STOP"
        },
        {
          "id": "stop-2",
          "kind": "STOP"
        }
      ]
    }
  ]
}

Next, add the matching time windows to the stops of Job A:

{
  "jobs": [
    {
      "id": "Job A",
      "stops": [
        {
          "id": "stop-1",
          "location": [
            33.84475,
            -84.63649
          ],
          "duration": "PT10M",
          "timeWindows": [
            {
              "minStartTime": "2027-02-01T11:00:00Z",
              "maxEndTime": "2027-02-01T14:00:00Z"
            }
          ]
        },
        {
          "id": "stop-2",
          "location": [
            33.57448,
            -84.94636
          ],
          "duration": "PT10M",
          "stopDependencies": [
            {
              "id": "prev-stop-2",
              "precedingStop": "stop-1"
            }
          ],
          "timeWindows": [
            {
              "minStartTime": "2027-02-01T11:00:00Z",
              "maxEndTime": "2027-02-01T14: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. Stop recommendations for unsolved route plans with existing jobs

When additional customers call to request drivers to complete a pick-up and delivery job, their requests can be added to the recommendation input dataset.

In the following example, a customer has called and needs a driver for a pick-up and delivery job (Job B) with two stops (stop-3 and stop-4), where stop-4 has a specific time window to consider.

  • 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/recommendations/stops-recommend-time-windows [email protected]
{
  "maxNumberOfRecommendations": 2,
  "fitJobId": "Job B",
  "fitStops": [
    {
      "fitStopId": "stop-4",
      "timeWindows": [
        {
          "minStartTime": "2027-02-01T13:30:00Z",
          "maxEndTime": "2027-02-01T15:00:00Z"
        }
      ]
    }
  ],
  "modelInput": {
    "drivers": [
      {
        "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",
            "itinerary": [
              {
                "id": "stop-1",
                "kind": "STOP"
              },
              {
                "id": "stop-2",
                "kind": "STOP"
              }
            ]
          }
        ]
      }
    ],
    "jobs": [
      {
        "id": "Job A",
        "stops": [
          {
            "id": "stop-1",
            "location": [
              33.84475,
              -84.63649
            ],
            "duration": "PT10M"
          },
          {
            "id": "stop-2",
            "location": [
              33.57448,
              -84.94636
            ],
            "duration": "PT10M",
            "stopDependencies": [
              {
                "id": "prev-stop-2",
                "precedingStop": "stop-1"
              }
            ]
          }
        ]
      },
      {
        "id": "Job B",
        "stops": [
          {
            "id": "stop-3",
            "location": [
              33.48574,
              -84.36969
            ],
            "duration": "PT10M"
          },
          {
            "id": "stop-4",
            "location": [
              33.78463,
              -84.35816
            ],
            "duration": "PT10M",
            "stopDependencies": [
              {
                "id": "prev-stop-4",
                "precedingStop": "stop-3"
              }
            ]
          }
        ]
      }
    ]
  }
}
{
  "recommendations": [
    {
      "scoreDiff": "0hard/20000medium/-2395soft",
      "constraintDiffs": [
        {
          "score": "0hard/0medium/-2395soft",
          "constraintName": "Minimize travel time",
          "matchCountDiff": 2
        },
        {
          "score": "0hard/20000medium/0soft",
          "constraintName": "Require scheduling mandatory stops",
          "matchCountDiff": -2
        }
      ],
      "driverShift": {
        "id": "Beth-2027-02-01",
        "startTime": "2027-02-01T09:00:00Z",
        "itinerary": [
          {
            "id": "stop-1",
            "kind": "STOP",
            "arrivalTime": "2027-02-01T10:06:04Z",
            "startServiceTime": "2027-02-01T10:06:04Z",
            "departureTime": "2027-02-01T10:16:04Z",
            "effectiveServiceDuration": "PT10M",
            "travelTimeFromPreviousStandstill": "PT1H6M4S",
            "travelDistanceMetersFromPreviousStandstill": 55061,
            "load": []
          },
          {
            "id": "stop-2",
            "kind": "STOP",
            "arrivalTime": "2027-02-01T11:05:54Z",
            "startServiceTime": "2027-02-01T11:05:54Z",
            "departureTime": "2027-02-01T11:15:54Z",
            "effectiveServiceDuration": "PT10M",
            "travelTimeFromPreviousStandstill": "PT49M50S",
            "travelDistanceMetersFromPreviousStandstill": 41530,
            "load": []
          },
          {
            "id": "stop-3",
            "kind": "STOP",
            "arrivalTime": "2027-02-01T12:21:08Z",
            "startServiceTime": "2027-02-01T12:21:08Z",
            "departureTime": "2027-02-01T12:31:08Z",
            "effectiveServiceDuration": "PT10M",
            "travelTimeFromPreviousStandstill": "PT1H5M14S",
            "travelDistanceMetersFromPreviousStandstill": 54356,
            "load": []
          },
          {
            "id": "stop-4",
            "kind": "STOP",
            "arrivalTime": "2027-02-01T13:11:02Z",
            "startServiceTime": "2027-02-01T13:30:00Z",
            "departureTime": "2027-02-01T13:40:00Z",
            "effectiveServiceDuration": "PT10M",
            "travelTimeFromPreviousStandstill": "PT39M54S",
            "travelDistanceMetersFromPreviousStandstill": 33252,
            "load": []
          }
        ],
        "metrics": {
          "totalTravelTime": "PT4H15M15S",
          "travelTimeFromStartLocationToFirstStop": "PT1H6M4S",
          "travelTimeBetweenStops": "PT2H34M58S",
          "travelTimeFromLastStopToEndLocation": "PT34M13S",
          "totalTravelDistanceMeters": 212716,
          "travelDistanceFromStartLocationToFirstStopMeters": 55061,
          "travelDistanceBetweenStopsMeters": 129138,
          "travelDistanceFromLastStopToEndLocationMeters": 28517,
          "endLocationArrivalTime": "2027-02-01T14:14:13Z"
        }
      },
      "kpis": {
        "totalTravelTime": "PT4H15M15S",
        "totalTravelDistanceMeters": 212716,
        "totalActivatedDrivers": 1,
        "totalUnassignedJobs": 0,
        "totalAssignedJobs": 2,
        "assignedMandatoryJobs": 2,
        "totalUnassignedStops": 0,
        "totalAssignedStops": 4,
        "assignedMandatoryStops": 4,
        "travelTimeFromStartLocationToFirstStop": "PT1H6M4S",
        "travelTimeBetweenStops": "PT2H34M58S",
        "travelTimeFromLastStopToEndLocation": "PT34M13S",
        "travelDistanceFromStartLocationToFirstStopMeters": 55061,
        "travelDistanceBetweenStopsMeters": 129138,
        "travelDistanceFromLastStopToEndLocationMeters": 28517
      }
    },
    {
      "scoreDiff": "0hard/20000medium/-5818soft",
      "constraintDiffs": [
        {
          "score": "0hard/0medium/-5818soft",
          "constraintName": "Minimize travel time",
          "matchCountDiff": 2
        },
        {
          "score": "0hard/20000medium/0soft",
          "constraintName": "Require scheduling mandatory stops",
          "matchCountDiff": -2
        }
      ],
      "driverShift": {
        "id": "Ann-2027-02-01",
        "startTime": "2027-02-01T09:00:00Z",
        "itinerary": [
          {
            "id": "stop-3",
            "kind": "STOP",
            "arrivalTime": "2027-02-01T09:33:54Z",
            "startServiceTime": "2027-02-01T09:33:54Z",
            "departureTime": "2027-02-01T09:43:54Z",
            "effectiveServiceDuration": "PT10M",
            "travelTimeFromPreviousStandstill": "PT33M54S",
            "travelDistanceMetersFromPreviousStandstill": 28253,
            "load": []
          },
          {
            "id": "stop-4",
            "kind": "STOP",
            "arrivalTime": "2027-02-01T10:23:48Z",
            "startServiceTime": "2027-02-01T13:30:00Z",
            "departureTime": "2027-02-01T13:40:00Z",
            "effectiveServiceDuration": "PT10M",
            "travelTimeFromPreviousStandstill": "PT39M54S",
            "travelDistanceMetersFromPreviousStandstill": 33252,
            "load": []
          }
        ],
        "metrics": {
          "totalTravelTime": "PT1H36M58S",
          "travelTimeFromStartLocationToFirstStop": "PT33M54S",
          "travelTimeBetweenStops": "PT39M54S",
          "travelTimeFromLastStopToEndLocation": "PT23M10S",
          "totalTravelDistanceMeters": 80807,
          "travelDistanceFromStartLocationToFirstStopMeters": 28253,
          "travelDistanceBetweenStopsMeters": 33252,
          "travelDistanceFromLastStopToEndLocationMeters": 19302,
          "endLocationArrivalTime": "2027-02-01T14:03:10Z"
        }
      },
      "kpis": {
        "totalTravelTime": "PT5H12M18S",
        "totalTravelDistanceMeters": 260257,
        "totalActivatedDrivers": 2,
        "totalUnassignedJobs": 0,
        "totalAssignedJobs": 2,
        "assignedMandatoryJobs": 2,
        "totalUnassignedStops": 0,
        "totalAssignedStops": 4,
        "assignedMandatoryStops": 4,
        "travelTimeFromStartLocationToFirstStop": "PT1H39M58S",
        "travelTimeBetweenStops": "PT1H29M44S",
        "travelTimeFromLastStopToEndLocation": "PT2H2M36S",
        "travelDistanceFromStartLocationToFirstStopMeters": 83314,
        "travelDistanceBetweenStopsMeters": 74782,
        "travelDistanceFromLastStopToEndLocationMeters": 102161
      }
    }
  ]
}

The recommendation output for this example includes two recommendations:

  1. Assign Job B to driver Beth such that stop-4 starts at 13:30.

  2. Assign Job B to driver Ann such that stop-4 starts at 13:30.

recommendations stop time window unsolved existing jobs

3. Stop recommendations for published route plans

If a customer’s job 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/pickup-delivery-routing/v1/route-plans [email protected]
{
  "config": {
    "run": {
      "name": "Recommendations sample schedule"
    }
  },
  "modelInput": {
    "drivers": [
      {
        "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"
          }
        ]
      }
    ],
    "jobs": [
      {
        "id": "Job A",
        "stops": [
          {
            "id": "jobA-pickup",
            "name": "jobA-pickup",
            "location": [
              33.70701327565276,
              -84.32533338611672
            ],
            "duration": "PT10M"
          },
          {
            "id": "jobA-delivery",
            "name": "jobA-delivery",
            "location": [
              33.71731006962202,
              -84.37791873718393
            ],
            "duration": "PT5M",
            "stopDependencies": [
              {
                "id": "jobA-delivery_dep1",
                "precedingStop": "jobA-pickup"
              }
            ]
          }
        ]
      },
      {
        "id": "Job B",
        "stops": [
          {
            "id": "jobB-pickup",
            "name": "jobB-pickup",
            "location": [
              33.76284855890686,
              -84.30035807391094
            ],
            "duration": "PT10M"
          },
          {
            "id": "jobB-delivery",
            "name": "jobB-delivery",
            "location": [
              33.66913690724926,
              -84.40131814080493
            ],
            "duration": "PT5M",
            "stopDependencies": [
              {
                "id": "jobB-delivery_dep1",
                "precedingStop": "jobB-pickup"
              }
            ]
          }
        ]
      },
      {
        "id": "Job C",
        "stops": [
          {
            "id": "jobC-pickup",
            "name": "jobC-pickup",
            "location": [
              33.710952545550136,
              -84.32995136580453
            ],
            "duration": "PT10M"
          },
          {
            "id": "jobC-delivery",
            "name": "jobC-delivery",
            "location": [
              33.75503881265245,
              -84.41532534997714
            ],
            "duration": "PT5M",
            "stopDependencies": [
              {
                "id": "jobC-delivery_dep1",
                "precedingStop": "jobC-pickup"
              }
            ]
          }
        ]
      },
      {
        "id": "Job D",
        "stops": [
          {
            "id": "jobD-pickup",
            "name": "jobD-pickup",
            "location": [
              33.67152243761529,
              -84.25467147287567
            ],
            "duration": "PT10M"
          },
          {
            "id": "jobD-delivery",
            "name": "jobD-delivery",
            "location": [
              33.71070292968887,
              -84.42189409559566
            ],
            "duration": "PT5M",
            "stopDependencies": [
              {
                "id": "jobD-delivery_dep1",
                "precedingStop": "jobD-pickup"
              }
            ]
          }
        ]
      }
    ]
  }
}
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",
    "originId": "ID",
    "name": "Recommendations sample schedule",
    "submitDateTime": "2026-02-03T14:37:18.647402+01:00",
    "startDateTime": "2026-02-03T14:37:18.717498+01:00",
    "activeDateTime": "2026-02-03T14:37:18.718545+01:00",
    "completeDateTime": "2026-02-03T14:37:48.737812+01:00",
    "shutdownDateTime": "2026-02-03T14:37:48.737817+01:00",
    "solverStatus": "SOLVING_COMPLETED",
    "score": "0hard/0medium/-4678soft",
    "validationResult": {
      "summary": "OK"
    }
  },
  "modelOutput": {
    "drivers": [
      {
        "id": "Ann",
        "shifts": [
          {
            "id": "Ann-2027-02-01",
            "startTime": "2027-02-01T09:00:00Z",
            "itinerary": [
              {
                "id": "jobD-pickup",
                "kind": "STOP",
                "arrivalTime": "2027-02-01T09:08:03Z",
                "startServiceTime": "2027-02-01T09:08:03Z",
                "departureTime": "2027-02-01T09:18:03Z",
                "effectiveServiceDuration": "PT10M",
                "travelTimeFromPreviousStandstill": "PT8M3S",
                "travelDistanceMetersFromPreviousStandstill": 6709,
                "load": []
              },
              {
                "id": "jobB-pickup",
                "kind": "STOP",
                "arrivalTime": "2027-02-01T09:31:15Z",
                "startServiceTime": "2027-02-01T09:31:15Z",
                "departureTime": "2027-02-01T09:41:15Z",
                "effectiveServiceDuration": "PT10M",
                "travelTimeFromPreviousStandstill": "PT13M12S",
                "travelDistanceMetersFromPreviousStandstill": 10999,
                "load": []
              },
              {
                "id": "jobA-pickup",
                "kind": "STOP",
                "arrivalTime": "2027-02-01T09:49:12Z",
                "startServiceTime": "2027-02-01T09:49:12Z",
                "departureTime": "2027-02-01T09:59:12Z",
                "effectiveServiceDuration": "PT10M",
                "travelTimeFromPreviousStandstill": "PT7M57S",
                "travelDistanceMetersFromPreviousStandstill": 6624,
                "load": []
              },
              {
                "id": "jobC-pickup",
                "kind": "STOP",
                "arrivalTime": "2027-02-01T09:59:56Z",
                "startServiceTime": "2027-02-01T09:59:56Z",
                "departureTime": "2027-02-01T10:09:56Z",
                "effectiveServiceDuration": "PT10M",
                "travelTimeFromPreviousStandstill": "PT44S",
                "travelDistanceMetersFromPreviousStandstill": 612,
                "load": []
              },
              {
                "id": "jobA-delivery",
                "kind": "STOP",
                "arrivalTime": "2027-02-01T10:15:19Z",
                "startServiceTime": "2027-02-01T10:15:19Z",
                "departureTime": "2027-02-01T10:20:19Z",
                "effectiveServiceDuration": "PT5M",
                "travelTimeFromPreviousStandstill": "PT5M23S",
                "travelDistanceMetersFromPreviousStandstill": 4493,
                "load": []
              },
              {
                "id": "jobC-delivery",
                "kind": "STOP",
                "arrivalTime": "2027-02-01T10:26:50Z",
                "startServiceTime": "2027-02-01T10:26:50Z",
                "departureTime": "2027-02-01T10:31:50Z",
                "effectiveServiceDuration": "PT5M",
                "travelTimeFromPreviousStandstill": "PT6M31S",
                "travelDistanceMetersFromPreviousStandstill": 5437,
                "load": []
              },
              {
                "id": "jobD-delivery",
                "kind": "STOP",
                "arrivalTime": "2027-02-01T10:37:48Z",
                "startServiceTime": "2027-02-01T10:37:48Z",
                "departureTime": "2027-02-01T10:42:48Z",
                "effectiveServiceDuration": "PT5M",
                "travelTimeFromPreviousStandstill": "PT5M58S",
                "travelDistanceMetersFromPreviousStandstill": 4967,
                "load": []
              },
              {
                "id": "jobB-delivery",
                "kind": "STOP",
                "arrivalTime": "2027-02-01T10:48:48Z",
                "startServiceTime": "2027-02-01T10:48:48Z",
                "departureTime": "2027-02-01T10:53:48Z",
                "effectiveServiceDuration": "PT5M",
                "travelTimeFromPreviousStandstill": "PT6M",
                "travelDistanceMetersFromPreviousStandstill": 4999,
                "load": []
              }
            ],
            "metrics": {
              "totalTravelTime": "PT1H17M58S",
              "travelTimeFromStartLocationToFirstStop": "PT8M3S",
              "travelTimeBetweenStops": "PT45M45S",
              "travelTimeFromLastStopToEndLocation": "PT24M10S",
              "totalTravelDistanceMeters": 64976,
              "travelDistanceFromStartLocationToFirstStopMeters": 6709,
              "travelDistanceBetweenStopsMeters": 38131,
              "travelDistanceFromLastStopToEndLocationMeters": 20136,
              "endLocationArrivalTime": "2027-02-01T11:17:58Z"
            }
          }
        ]
      },
      {
        "id": "Beth",
        "shifts": [
          {
            "id": "Beth-2027-02-01",
            "startTime": "2027-02-01T09:00:00Z",
            "itinerary": [],
            "metrics": {
              "totalTravelTime": "PT0S",
              "travelTimeFromStartLocationToFirstStop": "PT0S",
              "travelTimeBetweenStops": "PT0S",
              "travelTimeFromLastStopToEndLocation": "PT0S",
              "totalTravelDistanceMeters": 0,
              "travelDistanceFromStartLocationToFirstStopMeters": 0,
              "travelDistanceBetweenStopsMeters": 0,
              "travelDistanceFromLastStopToEndLocationMeters": 0
            }
          }
        ]
      }
    ],
    "unassignedJobs": []
  },
  "inputMetrics": {
    "jobs": 4,
    "stops": 8,
    "drivers": 2,
    "driverShifts": 2
  },
  "kpis": {
    "totalTravelTime": "PT1H17M58S",
    "totalTravelDistanceMeters": 64976,
    "totalActivatedDrivers": 1,
    "totalUnassignedJobs": 0,
    "totalAssignedJobs": 4,
    "assignedMandatoryJobs": 4,
    "totalUnassignedStops": 0,
    "totalAssignedStops": 8,
    "assignedMandatoryStops": 8,
    "travelTimeFromStartLocationToFirstStop": "PT8M3S",
    "travelTimeBetweenStops": "PT45M45S",
    "travelTimeFromLastStopToEndLocation": "PT24M10S",
    "travelDistanceFromStartLocationToFirstStopMeters": 6709,
    "travelDistanceBetweenStopsMeters": 38131,
    "travelDistanceFromLastStopToEndLocationMeters": 20136
  }
}

modelOutput contains Ann’s and Beth’s itineraries.

If a customer calls to request a driver on the same day, the following recommendations input dataset will return recommendations for the request (Job 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/pickup-delivery-routing/v1/route-plans/recommendations/stops-recommend-time-windows [email protected]
{
  "maxNumberOfRecommendations": 2,
  "fitJobId": "Job E",
  "fitStops": [
    {
      "fitStopId": "jobE-pickup",
      "timeWindows": [
        {
          "minStartTime": "2027-02-01T11:00:00Z",
          "maxEndTime": "2027-02-01T14:00:00Z"
        }
      ]
    },
    {
      "fitStopId": "jobE-delivery",
      "timeWindows": [
        {
          "minStartTime": "2027-02-01T15:00:00Z",
          "maxEndTime": "2027-02-01T17:00:00Z"
        }
      ]
    }
  ],
  "modelInput": {
    "drivers": [
      {
        "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": "jobD-pickup",
                "kind": "STOP"
              },
              {
                "id": "jobB-pickup",
                "kind": "STOP"
              },
              {
                "id": "jobA-pickup",
                "kind": "STOP"
              },
              {
                "id": "jobC-pickup",
                "kind": "STOP"
              },
              {
                "id": "jobA-delivery",
                "kind": "STOP"
              },
              {
                "id": "jobC-delivery",
                "kind": "STOP"
              },
              {
                "id": "jobD-delivery",
                "kind": "STOP"
              },
              {
                "id": "jobB-delivery",
                "kind": "STOP"
              }
            ]
          }
        ]
      },
      {
        "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"
          }
        ]
      }
    ],
    "jobs": [
      {
        "id": "Job A",
        "stops": [
          {
            "id": "jobA-pickup",
            "name": "jobA-pickup",
            "location": [
              33.70701327565276,
              -84.32533338611672
            ],
            "duration": "PT10M"
          },
          {
            "id": "jobA-delivery",
            "name": "jobA-delivery",
            "location": [
              33.71731006962202,
              -84.37791873718393
            ],
            "duration": "PT5M",
            "stopDependencies": [
              {
                "id": "jobA-delivery_dep1",
                "precedingStop": "jobA-pickup"
              }
            ]
          }
        ]
      },
      {
        "id": "Job B",
        "stops": [
          {
            "id": "jobB-pickup",
            "name": "jobB-pickup",
            "location": [
              33.76284855890686,
              -84.30035807391094
            ],
            "duration": "PT10M"
          },
          {
            "id": "jobB-delivery",
            "name": "jobB-delivery",
            "location": [
              33.66913690724926,
              -84.40131814080493
            ],
            "duration": "PT5M",
            "stopDependencies": [
              {
                "id": "jobB-delivery_dep1",
                "precedingStop": "jobB-pickup"
              }
            ]
          }
        ]
      },
      {
        "id": "Job C",
        "stops": [
          {
            "id": "jobC-pickup",
            "name": "jobC-pickup",
            "location": [
              33.710952545550136,
              -84.32995136580453
            ],
            "duration": "PT10M"
          },
          {
            "id": "jobC-delivery",
            "name": "jobC-delivery",
            "location": [
              33.75503881265245,
              -84.41532534997714
            ],
            "duration": "PT5M",
            "stopDependencies": [
              {
                "id": "jobC-delivery_dep1",
                "precedingStop": "jobC-pickup"
              }
            ]
          }
        ]
      },
      {
        "id": "Job D",
        "stops": [
          {
            "id": "jobD-pickup",
            "name": "jobD-pickup",
            "location": [
              33.67152243761529,
              -84.25467147287567
            ],
            "duration": "PT10M"
          },
          {
            "id": "jobD-delivery",
            "name": "jobD-delivery",
            "location": [
              33.71070292968887,
              -84.42189409559566
            ],
            "duration": "PT5M",
            "stopDependencies": [
              {
                "id": "jobD-delivery_dep1",
                "precedingStop": "jobD-pickup"
              }
            ]
          }
        ]
      },
      {
        "id": "Job E",
        "stops": [
          {
            "id": "jobE-pickup",
            "name": "jobE-pickup",
            "location": [
              33.76320212102979,
              -84.44574867672038
            ],
            "duration": "PT10M"
          },
          {
            "id": "jobE-delivery",
            "name": "jobE-delivery",
            "location": [
              33.68909752717026,
              -84.27707020988122
            ],
            "duration": "PT5M",
            "stopDependencies": [
              {
                "id": "jobE-delivery_dep1",
                "precedingStop": "jobE-pickup"
              }
            ]
          }
        ]
      }
    ]
  }
}
{
  "recommendations": [
    {
      "scoreDiff": "0hard/20000medium/-1243soft",
      "constraintDiffs": [
        {
          "score": "0hard/0medium/-1243soft",
          "constraintName": "Minimize travel time",
          "matchCountDiff": 2
        },
        {
          "score": "0hard/20000medium/0soft",
          "constraintName": "Require scheduling mandatory stops",
          "matchCountDiff": -2
        }
      ],
      "driverShift": {
        "id": "Ann-2027-02-01",
        "startTime": "2027-02-01T09:00:00Z",
        "itinerary": [
          {
            "id": "jobD-pickup",
            "kind": "STOP",
            "arrivalTime": "2027-02-01T09:08:03Z",
            "startServiceTime": "2027-02-01T09:08:03Z",
            "departureTime": "2027-02-01T09:18:03Z",
            "effectiveServiceDuration": "PT10M",
            "travelTimeFromPreviousStandstill": "PT8M3S",
            "travelDistanceMetersFromPreviousStandstill": 6709,
            "load": []
          },
          {
            "id": "jobB-pickup",
            "kind": "STOP",
            "arrivalTime": "2027-02-01T09:31:15Z",
            "startServiceTime": "2027-02-01T09:31:15Z",
            "departureTime": "2027-02-01T09:41:15Z",
            "effectiveServiceDuration": "PT10M",
            "travelTimeFromPreviousStandstill": "PT13M12S",
            "travelDistanceMetersFromPreviousStandstill": 10999,
            "load": []
          },
          {
            "id": "jobA-pickup",
            "kind": "STOP",
            "arrivalTime": "2027-02-01T09:49:12Z",
            "startServiceTime": "2027-02-01T09:49:12Z",
            "departureTime": "2027-02-01T09:59:12Z",
            "effectiveServiceDuration": "PT10M",
            "travelTimeFromPreviousStandstill": "PT7M57S",
            "travelDistanceMetersFromPreviousStandstill": 6624,
            "load": []
          },
          {
            "id": "jobC-pickup",
            "kind": "STOP",
            "arrivalTime": "2027-02-01T09:59:56Z",
            "startServiceTime": "2027-02-01T09:59:56Z",
            "departureTime": "2027-02-01T10:09:56Z",
            "effectiveServiceDuration": "PT10M",
            "travelTimeFromPreviousStandstill": "PT44S",
            "travelDistanceMetersFromPreviousStandstill": 612,
            "load": []
          },
          {
            "id": "jobA-delivery",
            "kind": "STOP",
            "arrivalTime": "2027-02-01T10:15:19Z",
            "startServiceTime": "2027-02-01T10:15:19Z",
            "departureTime": "2027-02-01T10:20:19Z",
            "effectiveServiceDuration": "PT5M",
            "travelTimeFromPreviousStandstill": "PT5M23S",
            "travelDistanceMetersFromPreviousStandstill": 4493,
            "load": []
          },
          {
            "id": "jobC-delivery",
            "kind": "STOP",
            "arrivalTime": "2027-02-01T10:26:50Z",
            "startServiceTime": "2027-02-01T10:26:50Z",
            "departureTime": "2027-02-01T10:31:50Z",
            "effectiveServiceDuration": "PT5M",
            "travelTimeFromPreviousStandstill": "PT6M31S",
            "travelDistanceMetersFromPreviousStandstill": 5437,
            "load": []
          },
          {
            "id": "jobD-delivery",
            "kind": "STOP",
            "arrivalTime": "2027-02-01T10:37:48Z",
            "startServiceTime": "2027-02-01T10:37:48Z",
            "departureTime": "2027-02-01T10:42:48Z",
            "effectiveServiceDuration": "PT5M",
            "travelTimeFromPreviousStandstill": "PT5M58S",
            "travelDistanceMetersFromPreviousStandstill": 4967,
            "load": []
          },
          {
            "id": "jobB-delivery",
            "kind": "STOP",
            "arrivalTime": "2027-02-01T10:48:48Z",
            "startServiceTime": "2027-02-01T10:48:48Z",
            "departureTime": "2027-02-01T10:53:48Z",
            "effectiveServiceDuration": "PT5M",
            "travelTimeFromPreviousStandstill": "PT6M",
            "travelDistanceMetersFromPreviousStandstill": 4999,
            "load": []
          },
          {
            "id": "jobE-pickup",
            "kind": "STOP",
            "arrivalTime": "2027-02-01T11:07:17Z",
            "startServiceTime": "2027-02-01T11:07:17Z",
            "departureTime": "2027-02-01T11:17:17Z",
            "effectiveServiceDuration": "PT10M",
            "travelTimeFromPreviousStandstill": "PT13M29S",
            "travelDistanceMetersFromPreviousStandstill": 11238,
            "load": []
          },
          {
            "id": "jobE-delivery",
            "kind": "STOP",
            "arrivalTime": "2027-02-01T11:38:27Z",
            "startServiceTime": "2027-02-01T15:00:00Z",
            "departureTime": "2027-02-01T15:05:00Z",
            "effectiveServiceDuration": "PT5M",
            "travelTimeFromPreviousStandstill": "PT21M10S",
            "travelDistanceMetersFromPreviousStandstill": 17642,
            "load": []
          }
        ],
        "metrics": {
          "totalTravelTime": "PT1H38M41S",
          "travelTimeFromStartLocationToFirstStop": "PT8M3S",
          "travelTimeBetweenStops": "PT1H20M24S",
          "travelTimeFromLastStopToEndLocation": "PT10M14S",
          "totalTravelDistanceMeters": 82252,
          "travelDistanceFromStartLocationToFirstStopMeters": 6709,
          "travelDistanceBetweenStopsMeters": 67011,
          "travelDistanceFromLastStopToEndLocationMeters": 8532,
          "endLocationArrivalTime": "2027-02-01T15:15:14Z"
        }
      },
      "kpis": {
        "totalTravelTime": "PT1H38M41S",
        "totalTravelDistanceMeters": 82252,
        "totalActivatedDrivers": 1,
        "totalUnassignedJobs": 0,
        "totalAssignedJobs": 5,
        "assignedMandatoryJobs": 5,
        "totalUnassignedStops": 0,
        "totalAssignedStops": 10,
        "assignedMandatoryStops": 10,
        "travelTimeFromStartLocationToFirstStop": "PT8M3S",
        "travelTimeBetweenStops": "PT1H20M24S",
        "travelTimeFromLastStopToEndLocation": "PT10M14S",
        "travelDistanceFromStartLocationToFirstStopMeters": 6709,
        "travelDistanceBetweenStopsMeters": 67011,
        "travelDistanceFromLastStopToEndLocationMeters": 8532
      }
    },
    {
      "scoreDiff": "0hard/20000medium/-2105soft",
      "constraintDiffs": [
        {
          "score": "0hard/0medium/-2105soft",
          "constraintName": "Minimize travel time",
          "matchCountDiff": 2
        },
        {
          "score": "0hard/20000medium/0soft",
          "constraintName": "Require scheduling mandatory stops",
          "matchCountDiff": -2
        }
      ],
      "driverShift": {
        "id": "Ann-2027-02-01",
        "startTime": "2027-02-01T09:00:00Z",
        "itinerary": [
          {
            "id": "jobD-pickup",
            "kind": "STOP",
            "arrivalTime": "2027-02-01T09:08:03Z",
            "startServiceTime": "2027-02-01T09:08:03Z",
            "departureTime": "2027-02-01T09:18:03Z",
            "effectiveServiceDuration": "PT10M",
            "travelTimeFromPreviousStandstill": "PT8M3S",
            "travelDistanceMetersFromPreviousStandstill": 6709,
            "load": []
          },
          {
            "id": "jobB-pickup",
            "kind": "STOP",
            "arrivalTime": "2027-02-01T09:31:15Z",
            "startServiceTime": "2027-02-01T09:31:15Z",
            "departureTime": "2027-02-01T09:41:15Z",
            "effectiveServiceDuration": "PT10M",
            "travelTimeFromPreviousStandstill": "PT13M12S",
            "travelDistanceMetersFromPreviousStandstill": 10999,
            "load": []
          },
          {
            "id": "jobA-pickup",
            "kind": "STOP",
            "arrivalTime": "2027-02-01T09:49:12Z",
            "startServiceTime": "2027-02-01T09:49:12Z",
            "departureTime": "2027-02-01T09:59:12Z",
            "effectiveServiceDuration": "PT10M",
            "travelTimeFromPreviousStandstill": "PT7M57S",
            "travelDistanceMetersFromPreviousStandstill": 6624,
            "load": []
          },
          {
            "id": "jobC-pickup",
            "kind": "STOP",
            "arrivalTime": "2027-02-01T09:59:56Z",
            "startServiceTime": "2027-02-01T09:59:56Z",
            "departureTime": "2027-02-01T10:09:56Z",
            "effectiveServiceDuration": "PT10M",
            "travelTimeFromPreviousStandstill": "PT44S",
            "travelDistanceMetersFromPreviousStandstill": 612,
            "load": []
          },
          {
            "id": "jobA-delivery",
            "kind": "STOP",
            "arrivalTime": "2027-02-01T10:15:19Z",
            "startServiceTime": "2027-02-01T10:15:19Z",
            "departureTime": "2027-02-01T10:20:19Z",
            "effectiveServiceDuration": "PT5M",
            "travelTimeFromPreviousStandstill": "PT5M23S",
            "travelDistanceMetersFromPreviousStandstill": 4493,
            "load": []
          },
          {
            "id": "jobC-delivery",
            "kind": "STOP",
            "arrivalTime": "2027-02-01T10:26:50Z",
            "startServiceTime": "2027-02-01T10:26:50Z",
            "departureTime": "2027-02-01T10:31:50Z",
            "effectiveServiceDuration": "PT5M",
            "travelTimeFromPreviousStandstill": "PT6M31S",
            "travelDistanceMetersFromPreviousStandstill": 5437,
            "load": []
          },
          {
            "id": "jobE-pickup",
            "kind": "STOP",
            "arrivalTime": "2027-02-01T10:35:23Z",
            "startServiceTime": "2027-02-01T11:00:00Z",
            "departureTime": "2027-02-01T11:10:00Z",
            "effectiveServiceDuration": "PT10M",
            "travelTimeFromPreviousStandstill": "PT3M33S",
            "travelDistanceMetersFromPreviousStandstill": 2955,
            "load": []
          },
          {
            "id": "jobE-delivery",
            "kind": "STOP",
            "arrivalTime": "2027-02-01T11:31:10Z",
            "startServiceTime": "2027-02-01T15:00:00Z",
            "departureTime": "2027-02-01T15:05:00Z",
            "effectiveServiceDuration": "PT5M",
            "travelTimeFromPreviousStandstill": "PT21M10S",
            "travelDistanceMetersFromPreviousStandstill": 17642,
            "load": []
          },
          {
            "id": "jobD-delivery",
            "kind": "STOP",
            "arrivalTime": "2027-02-01T15:21:20Z",
            "startServiceTime": "2027-02-01T15:21:20Z",
            "departureTime": "2027-02-01T15:26:20Z",
            "effectiveServiceDuration": "PT5M",
            "travelTimeFromPreviousStandstill": "PT16M20S",
            "travelDistanceMetersFromPreviousStandstill": 13611,
            "load": []
          },
          {
            "id": "jobB-delivery",
            "kind": "STOP",
            "arrivalTime": "2027-02-01T15:32:20Z",
            "startServiceTime": "2027-02-01T15:32:20Z",
            "departureTime": "2027-02-01T15:37:20Z",
            "effectiveServiceDuration": "PT5M",
            "travelTimeFromPreviousStandstill": "PT6M",
            "travelDistanceMetersFromPreviousStandstill": 4999,
            "load": []
          }
        ],
        "metrics": {
          "totalTravelTime": "PT1H53M3S",
          "travelTimeFromStartLocationToFirstStop": "PT8M3S",
          "travelTimeBetweenStops": "PT1H20M50S",
          "travelTimeFromLastStopToEndLocation": "PT24M10S",
          "totalTravelDistanceMeters": 94217,
          "travelDistanceFromStartLocationToFirstStopMeters": 6709,
          "travelDistanceBetweenStopsMeters": 67372,
          "travelDistanceFromLastStopToEndLocationMeters": 20136,
          "endLocationArrivalTime": "2027-02-01T16:01:30Z"
        }
      },
      "kpis": {
        "totalTravelTime": "PT1H53M3S",
        "totalTravelDistanceMeters": 94217,
        "totalActivatedDrivers": 1,
        "totalUnassignedJobs": 0,
        "totalAssignedJobs": 5,
        "assignedMandatoryJobs": 5,
        "totalUnassignedStops": 0,
        "totalAssignedStops": 10,
        "assignedMandatoryStops": 10,
        "travelTimeFromStartLocationToFirstStop": "PT8M3S",
        "travelTimeBetweenStops": "PT1H20M50S",
        "travelTimeFromLastStopToEndLocation": "PT24M10S",
        "travelDistanceFromStartLocationToFirstStopMeters": 6709,
        "travelDistanceBetweenStopsMeters": 67372,
        "travelDistanceFromLastStopToEndLocationMeters": 20136
      }
    }
  ]
}

The recommendation output for this example includes two recommendations:

  1. Job E is assigned to driver Ann at the end of the current schedule.

  2. Job E is assigned to driver Ann, where Job E is pooled with other jobs.

After the customer has accepted a recommendation, update the dataset to include the job in the optimized plan for the day. Failure to update the real-time plan will lead to errors with Job E not being included if there are further updates to the plan throughout the day.

Next

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

  • Learn about Job time window recommendations for recommendations based on time windows for the whole job.

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