Docs
  • Solver
  • Models
    • Field Service Routing
    • Employee Shift Scheduling
    • Pick-up and Delivery Routing
  • Platform
Try models
  • Pick-up and Delivery Routing
  • Job service constraints
  • Priority jobs and optional jobs

Pick-up and Delivery Routing

    • Introduction
    • Getting started: Hello world
    • User guide
      • Terms
      • Use case guide
      • Planning AI concepts
      • Integration
      • Constraints
      • Demo datasets
      • Input 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 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
    • Real-time planning
    • Changelog
    • Upgrading to the latest versions
    • Feature requests

Priority jobs and optional jobs

The urgency of jobs varies.

Some jobs are high priority while others are low. You can give individual jobs priority levels to specify the importance of assigning one job instead of another job.

When you are planning in pick-up and delivery routing, you can introduce mandatory jobs and optional jobs to help prioritize which jobs should be assigned as soon as possible and which jobs can wait until later.

Mandatory jobs are penalized with a medium-level penalty when they are left unassigned. This means assigning a mandatory job will always take precedence over satisfying soft constraints.

Optional jobs are penalized with a soft-level penalty when they are left unassigned. This means assigning an optional job will compete with all other soft constraints, so an optional job may be left unassigned in favor of satisfying other soft constraints, such as matching job or driver preferences.

This guide explains how to manage resource-limited planning with the following examples:

  • 1. Job priorities
  • 2. Mandatory and optional jobs example
  • 3. Too many mandatory jobs
  • 4. Scheduling optional jobs

1. Job priorities

There are ten built-in mandatory priorities. 1 is the highest priority, and 10 is the lowest priority.

The priority also defines if jobs are considered mandatory (default) or optional.

There are also ten built-in optional priorities. opt-1 is the highest priority for optional jobs, and opt-10 is the lowest priority.

The default built-in priority is 6. These built-in priorities use exponential penalty weights, where priority 1 is ten times more important than priority 2, priority 2 is ten times more important than priority 3 and so on.

Optionally, you can override these weights or define custom priorities in the model input priorityWeights attribute:

{
  "modelInput": {
    "priorityWeights": [
      {
        "priority": "HIGH",
        "weight": 10000
      },
      {
        "priority": "LOW",
        "weight": 10,
        "assignment": "OPTIONAL"
      }
    ]
  }
}

2. Mandatory and optional jobs example

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.

The job’s priority defines if the job is considered mandatory (default if priority not specified) or optional.

In the following example, there are two jobs:

Job A defines priority 1 which makes it a mandatory job. Mandatory jobs are penalized with a medium-level penalty when they are left unassigned.

Job B defines priority opt-1, making this an optional job. Optional jobs are penalized with a soft-level penalty when they are left unassigned.

The Require scheduling mandatory stops medium constraint adds a medium penalty to solutions that have mandatory jobs with stops that are left unassigned.

{
  "jobs": [
    {
      "id": "Job A",
      "priority": "1",
      "stops": [
        {
          "id": "A1",
          "location": [33.78592, -84.46136],
          "duration": "PT20M",
          "timeWindows": [
            {
              "minStartTime": "2027-02-01T09:00:00Z",
              "maxEndTime": "2027-02-01T17:00:00Z"
            }
          ]
        },
        {
          "id": "A2",
          "location": [33.72757, -83.96354],
          "duration": "PT20M",
          "timeWindows": [
            {
              "minStartTime": "2027-02-01T09:00:00Z",
              "maxEndTime": "2027-02-01T17:00:00Z"
            }
          ],
          "stopDependencies": [
            {
              "id": "jobA_dep1",
              "precedingStop": "A1"
            }
          ]
        }
      ]
    },
    {
      "id": "Job B",
      "priority": "opt-1",
      "stops": [
        {
          "id": "B1",
          "location": [34.11110, -84.43002],
          "duration": "PT20M",
          "timeWindows": [
            {
              "minStartTime": "2027-02-01T09:00:00Z",
              "maxEndTime": "2027-02-02T17:00:00Z"
            }
          ]
        },
        {
          "id": "B2",
          "location": [33.48594, -84.26560],
          "duration": "PT20M",
          "timeWindows": [
            {
              "minStartTime": "2027-02-01T09:00:00Z",
              "maxEndTime": "2027-02-02T17:00:00Z"
            }
          ],
          "stopDependencies": [
            {
              "id": "jobB_dep1",
              "precedingStop": "B1"
            }
          ]
        }
      ]
    }
  ]
}

Carl is working between 09:00 and 13:00 and only has time to complete one job.

Job A is a mandatory job and is assigned to Carl. Because Job B is an optional job and is left unassigned.

mandatory jobs

  • 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": "Assign mandatory and optional jobs example"
    }
  },
  "modelInput": {
    "drivers": [
      {
        "id": "Carl",
        "shifts": [
          {
            "id": "Carl Mon",
            "startLocation": [33.68786, -84.18487],
            "endLocation": [33.68786, -84.18487],
            "minStartTime": "2027-02-01T09:00:00Z",
            "maxEndTime": "2027-02-01T13:00:00Z"
          }
        ]
      }
    ],
    "jobs": [
      {
        "id": "Job A",
        "priority": "1",
        "stops": [
          {
            "id": "A1",
            "location": [33.78592, -84.46136],
            "duration": "PT20M",
            "timeWindows": [
              {
                "minStartTime": "2027-02-01T09:00:00Z",
                "maxEndTime": "2027-02-01T17:00:00Z"
              }
            ]
          },
          {
            "id": "A2",
            "location": [33.72757, -83.96354],
            "duration": "PT20M",
            "timeWindows": [
              {
                "minStartTime": "2027-02-01T09:00:00Z",
                "maxEndTime": "2027-02-01T17:00:00Z"
              }
            ],
            "stopDependencies": [
              {
                "id": "jobA_dep1",
                "precedingStop": "A1"
              }
            ]
          }
        ]
      },
      {
        "id": "Job B",
        "priority": "opt-1",
        "stops": [
          {
            "id": "B1",
            "location": [34.11110, -84.43002],
            "duration": "PT20M",
            "timeWindows": [
              {
                "minStartTime": "2027-02-01T09:00:00Z",
                "maxEndTime": "2027-02-01T17:00:00Z"
              }
            ]
          },
          {
            "id": "B2",
            "location": [33.48594, -84.26560],
            "duration": "PT20M",
            "timeWindows": [
              {
                "minStartTime": "2027-02-01T09:00:00Z",
                "maxEndTime": "2027-02-01T17:00:00Z"
              }
            ],
            "stopDependencies": [
              {
                "id": "jobB_dep1",
                "precedingStop": "B1"
              }
            ]
          }
        ]
      }
    ],
    "planningWindow": {
      "startDate": "2027-02-01T00:00:00Z",
      "endDate": "2027-02-02T00:00:00Z"
    }
  }
}
To request the solution, locate the ID from the response to the post operation and append it to the following API call:
curl -X GET -H 'X-API-KEY: <API_KEY>' https://app.timefold.ai/api/models/pickup-delivery-routing/v1/route-plans/<ID>
{
  "metadata": {
    "id": "ID",
    "originId": "ID",
    "name": "Assign mandatory and optional jobs example",
    "submitDateTime": "2025-11-07T21:34:01.088550366+01:00",
    "startDateTime": "2025-11-07T21:34:01.325779948+01:00",
    "activeDateTime": "2025-11-07T21:34:01.328985773+01:00",
    "completeDateTime": "2025-11-07T21:34:31.451704108+01:00",
    "shutdownDateTime": "2025-11-07T21:34:31.451715727+01:00",
    "solverStatus": "SOLVING_COMPLETED",
    "score": "0hard/0medium/-2000006855soft",
    "validationResult": {
      "summary": "OK"
    }
  },
  "modelOutput": {
    "drivers": [
      {
        "id": "Carl",
        "shifts": [
          {
            "id": "Carl Mon",
            "startTime": "2027-02-01T09:00:00Z",
            "itinerary": [
              {
                "id": "A1",
                "arrivalTime": "2027-02-01T09:33:21Z",
                "startServiceTime": "2027-02-01T09:33:21Z",
                "departureTime": "2027-02-01T09:53:21Z",
                "effectiveServiceDuration": "PT20M",
                "kind": "STOP",
                "travelTimeFromPreviousStandstill": "PT33M21S",
                "travelDistanceMetersFromPreviousStandstill": 27795
              },
              {
                "id": "A2",
                "arrivalTime": "2027-02-01T10:49:07Z",
                "startServiceTime": "2027-02-01T10:49:07Z",
                "departureTime": "2027-02-01T11:09:07Z",
                "effectiveServiceDuration": "PT20M",
                "kind": "STOP",
                "travelTimeFromPreviousStandstill": "PT55M46S",
                "travelDistanceMetersFromPreviousStandstill": 46477
              }
            ],
            "metrics": {
              "totalTravelTime": "PT1H54M15S",
              "travelTimeFromStartLocationToFirstStop": "PT33M21S",
              "travelTimeBetweenStops": "PT55M46S",
              "travelTimeFromLastStopToEndLocation": "PT25M8S",
              "totalTravelDistanceMeters": 95216,
              "travelDistanceFromStartLocationToFirstStopMeters": 27795,
              "travelDistanceBetweenStopsMeters": 46477,
              "travelDistanceFromLastStopToEndLocationMeters": 20944,
              "endLocationArrivalTime": "2027-02-01T11:34:15Z",
              "overtime": "PT0S"
            }
          }
        ]
      }
    ]
  },
  "inputMetrics": {
    "jobs": 2,
    "stops": 4,
    "drivers": 1,
    "driverShifts": 1
  },
  "kpis": {
    "totalTravelTime": "PT1H54M15S",
    "totalTravelDistanceMeters": 95216,
    "totalActivatedDrivers": 1,
    "totalUnassignedJobs": 1,
    "totalAssignedJobs": 1,
    "assignedMandatoryJobs": 1,
    "assignedOptionalJobs": 0,
    "totalUnassignedStops": 2,
    "totalAssignedStops": 2,
    "assignedMandatoryStops": 2,
    "assignedOptionalStops": 0,
    "totalOvertime": "PT0S",
    "travelTimeFromStartLocationToFirstStop": "PT33M21S",
    "travelTimeBetweenStops": "PT55M46S",
    "travelTimeFromLastStopToEndLocation": "PT25M8S",
    "travelDistanceFromStartLocationToFirstStopMeters": 27795,
    "travelDistanceBetweenStopsMeters": 46477,
    "travelDistanceFromLastStopToEndLocationMeters": 20944
  }
}

3. Too many mandatory jobs

When there are too many mandatory jobs to assign during the planning window, mandatory jobs will be left unassigned, and a medium penalty will be applied to the dataset score.

From the original example, Job B’s priority has been changed from opt-1 to 1, making it a mandatory job.

{
  "planningWindow": {
    "startDate": "2027-02-01T00:00:00Z",
    "endDate": "2027-02-02T00:00:00Z"
  },
  "jobs": [
    {
      "id": "Job B",
      "priority": "1",
      "stops": [
        {
          "id": "B1",
          "location": [34.11110, -84.43002],
          "duration": "PT20M",
          "timeWindows": [
            {
              "minStartTime": "2027-02-01T09:00:00Z",
              "maxEndTime": "2027-02-01T17:00:00Z"
            }
          ]
        },
        {
          "id": "B2",
          "location": [33.48594, -84.26560],
          "duration": "PT20M",
          "timeWindows": [
            {
              "minStartTime": "2027-02-01T09:00:00Z",
              "maxEndTime": "2027-02-01T17:00:00Z"
            }
          ],
          "stopDependencies": [
            {
              "id": "jobB_dep1",
              "precedingStop": "B1"
            }
          ]
        }
      ]
    }
  ]
}

Mandatory Job A is assigned while mandatory Job B is unassigned. This time, the medium score includes the penalty (-2000medium) for leaving one mandatory job (with two stops) unassigned.

  • 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": "Assign too many mandatory jobs example"
    }
  },
  "modelInput": {
    "drivers": [
      {
        "id": "Carl",
        "shifts": [
          {
            "id": "Carl Mon",
            "startLocation": [33.68786, -84.18487],
            "endLocation": [33.68786, -84.18487],
            "minStartTime": "2027-02-01T09:00:00Z",
            "maxEndTime": "2027-02-01T13:00:00Z"
          }
        ]
      }
    ],
    "jobs": [
      {
        "id": "Job A",
        "priority": "1",
        "stops": [
          {
            "id": "A1",
            "location": [33.78592, -84.46136],
            "duration": "PT20M",
            "timeWindows": [
              {
                "minStartTime": "2027-02-01T09:00:00Z",
                "maxEndTime": "2027-02-01T17:00:00Z"
              }
            ]
          },
          {
            "id": "A2",
            "location": [33.72757, -83.96354],
            "duration": "PT20M",
            "timeWindows": [
              {
                "minStartTime": "2027-02-01T09:00:00Z",
                "maxEndTime": "2027-02-01T17:00:00Z"
              }
            ],
            "stopDependencies": [
              {
                "id": "jobA_dep1",
                "precedingStop": "A1"
              }
            ]
          }
        ]
      },
      {
        "id": "Job B",
        "priority": "1",
        "stops": [
          {
            "id": "B1",
            "location": [34.11110, -84.43002],
            "duration": "PT20M",
            "timeWindows": [
              {
                "minStartTime": "2027-02-01T09:00:00Z",
                "maxEndTime": "2027-02-01T17:00:00Z"
              }
            ]
          },
          {
            "id": "B2",
            "location": [33.48594, -84.26560],
            "duration": "PT20M",
            "timeWindows": [
              {
                "minStartTime": "2027-02-01T09:00:00Z",
                "maxEndTime": "2027-02-01T17:00:00Z"
              }
            ],
            "stopDependencies": [
              {
                "id": "jobB_dep1",
                "precedingStop": "B1"
              }
            ]
          }
        ]
      }
    ],
    "planningWindow": {
      "startDate": "2027-02-01T00:00:00Z",
      "endDate": "2027-02-02T00:00:00Z"
    }
  }
}
To request the solution, locate the ID from the response to the post operation and append it to the following API call:
curl -X GET -H 'X-API-KEY: <API_KEY>' https://app.timefold.ai/api/models/pickup-delivery-routing/v1/route-plans/<ID>
{
  "metadata": {
    "id": "ID",
    "originId": "ID",
    "name": "Assign too many mandatory jobs example",
    "submitDateTime": "2025-11-07T21:39:55.717524976+01:00",
    "startDateTime": "2025-11-07T21:39:55.740454259+01:00",
    "activeDateTime": "2025-11-07T21:39:55.741069272+01:00",
    "completeDateTime": "2025-11-07T21:40:25.765007594+01:00",
    "shutdownDateTime": "2025-11-07T21:40:25.765013306+01:00",
    "solverStatus": "SOLVING_COMPLETED",
    "score": "0hard/-2000000000medium/-6855soft",
    "validationResult": {
      "summary": "OK"
    }
  },
  "modelOutput": {
    "drivers": [
      {
        "id": "Carl",
        "shifts": [
          {
            "id": "Carl Mon",
            "startTime": "2027-02-01T09:00:00Z",
            "itinerary": [
              {
                "id": "A1",
                "arrivalTime": "2027-02-01T09:33:21Z",
                "startServiceTime": "2027-02-01T09:33:21Z",
                "departureTime": "2027-02-01T09:53:21Z",
                "effectiveServiceDuration": "PT20M",
                "kind": "STOP",
                "travelTimeFromPreviousStandstill": "PT33M21S",
                "travelDistanceMetersFromPreviousStandstill": 27795
              },
              {
                "id": "A2",
                "arrivalTime": "2027-02-01T10:49:07Z",
                "startServiceTime": "2027-02-01T10:49:07Z",
                "departureTime": "2027-02-01T11:09:07Z",
                "effectiveServiceDuration": "PT20M",
                "kind": "STOP",
                "travelTimeFromPreviousStandstill": "PT55M46S",
                "travelDistanceMetersFromPreviousStandstill": 46477
              }
            ],
            "metrics": {
              "totalTravelTime": "PT1H54M15S",
              "travelTimeFromStartLocationToFirstStop": "PT33M21S",
              "travelTimeBetweenStops": "PT55M46S",
              "travelTimeFromLastStopToEndLocation": "PT25M8S",
              "totalTravelDistanceMeters": 95216,
              "travelDistanceFromStartLocationToFirstStopMeters": 27795,
              "travelDistanceBetweenStopsMeters": 46477,
              "travelDistanceFromLastStopToEndLocationMeters": 20944,
              "endLocationArrivalTime": "2027-02-01T11:34:15Z",
              "overtime": "PT0S"
            }
          }
        ]
      }
    ]
  },
  "inputMetrics": {
    "jobs": 2,
    "stops": 4,
    "drivers": 1,
    "driverShifts": 1
  },
  "kpis": {
    "totalTravelTime": "PT1H54M15S",
    "totalTravelDistanceMeters": 95216,
    "totalActivatedDrivers": 1,
    "totalUnassignedJobs": 1,
    "totalAssignedJobs": 1,
    "assignedMandatoryJobs": 1,
    "totalUnassignedStops": 2,
    "totalAssignedStops": 2,
    "assignedMandatoryStops": 2,
    "totalOvertime": "PT0S",
    "travelTimeFromStartLocationToFirstStop": "PT33M21S",
    "travelTimeBetweenStops": "PT55M46S",
    "travelTimeFromLastStopToEndLocation": "PT25M8S",
    "travelDistanceFromStartLocationToFirstStopMeters": 27795,
    "travelDistanceBetweenStopsMeters": 46477,
    "travelDistanceFromLastStopToEndLocationMeters": 20944
  }
}

4. Scheduling optional jobs

The Prefer scheduling optional stops soft constraint applies a soft score penalty to the dataset score for every stop that is not assigned, incentivizing time to schedule as many optional jobs as it can.

Soft constraints compete with each other as the solver optimizes the final solution. That means this constraint may leave jobs unassigned in favor of jobs with different soft constraints.

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

Learn about constraint weights.

4.1. Scheduling optional jobs example

In the following example, there are five jobs. One job (Job A) is mandatory, and the other four jobs are optional.

The mandatory job and three of the optional jobs are assigned. One optional job is left unassigned and a soft score penalty is applied to the data set score.

optional jobs
  • 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": "Assign optional jobs example"
    }
  },
  "modelInput": {
    "drivers": [
      {
        "id": "Carl",
        "shifts": [
          {
            "id": "Carl Mon",
            "startLocation": [33.68786, -84.18487],
            "endLocation": [33.68786, -84.18487],
            "minStartTime": "2027-02-01T09:00:00Z",
            "maxEndTime": "2027-02-01T17:00:00Z"
          }
        ]
      }
    ],
    "jobs": [
      {
        "id": "Job A",
        "priority": "5",
        "prohibitJobPooling": true,
        "stops": [
          {
            "id": "A1",
            "location": [33.70592, -84.26136],
            "duration": "PT20M",
            "timeWindows": [
              {
                "minStartTime": "2027-02-01T09:00:00Z",
                "maxEndTime": "2027-02-01T17:00:00Z"
              }
            ]
          },
          {
            "id": "A2",
            "location": [33.72757, -83.96354],
            "duration": "PT20M",
            "timeWindows": [
              {
                "minStartTime": "2027-02-01T09:00:00Z",
                "maxEndTime": "2027-02-01T17:00:00Z"
              }
            ],
            "stopDependencies": [
              {
                "id": "jobA_dep1",
                "precedingStop": "A1"
              }
            ]
          }
        ]
      },
      {
        "id": "Job B",
        "priority": "opt-2",
        "prohibitJobPooling": true,
        "stops": [
          {
            "id": "B1",
            "location": [33.75757, -83.76354],
            "duration": "PT20M",
            "timeWindows": [
              {
                "minStartTime": "2027-02-01T09:00:00Z",
                "maxEndTime": "2027-02-01T17:00:00Z"
              }
            ]
          },
          {
            "id": "B2",
            "location": [33.95594, -83.26560],
            "duration": "PT20M",
            "timeWindows": [
              {
                "minStartTime": "2027-02-01T09:00:00Z",
                "maxEndTime": "2027-02-01T17:00:00Z"
              }
            ],
            "stopDependencies": [
              {
                "id": "jobB_dep1",
                "precedingStop": "B1"
              }
            ]
          }
        ]
      },
      {
        "id": "Job C",
        "priority": "opt-2",
        "prohibitJobPooling": true,
        "stops": [
          {
            "id": "C1",
            "location": [34.01, -83.563],
            "duration": "PT20M",
            "timeWindows": [
              {
                "minStartTime": "2027-02-01T09:00:00Z",
                "maxEndTime": "2027-02-01T17:00:00Z"
              }
            ]
          },
          {
            "id": "C2",
            "location": [33.978, -83.860],
            "duration": "PT20M",
            "timeWindows": [
              {
                "minStartTime": "2027-02-01T09:00:00Z",
                "maxEndTime": "2027-02-01T17:00:00Z"
              }
            ],
            "stopDependencies": [
              {
                "id": "jobC_dep1",
                "precedingStop": "C1"
              }
            ]
          }
        ]
      },
      {
        "id": "Job D",
        "priority": "opt-2",
        "prohibitJobPooling": true,
        "stops": [
          {
            "id": "D1",
            "location": [33.901, -83.990],
            "duration": "PT20M",
            "timeWindows": [
              {
                "minStartTime": "2027-02-01T09:00:00Z",
                "maxEndTime": "2027-02-01T17:00:00Z"
              }
            ]
          },
          {
            "id": "D2",
            "location": [33.955, -83.650],
            "duration": "PT20M",
            "timeWindows": [
              {
                "minStartTime": "2027-02-01T09:00:00Z",
                "maxEndTime": "2027-02-01T17:00:00Z"
              }
            ],
            "stopDependencies": [
              {
                "id": "jobD_dep1",
                "precedingStop": "D1"
              }
            ]
          }
        ]
      },
      {
        "id": "Job E",
        "priority": "opt-2",
        "prohibitJobPooling": true,
        "stops": [
          {
            "id": "E1",
            "location": [33.695, -83.950],
            "duration": "PT20M",
            "timeWindows": [
              {
                "minStartTime": "2027-02-01T09:00:00Z",
                "maxEndTime": "2027-02-01T17:00:00Z"
              }
            ]
          },
          {
            "id": "E2",
            "location": [33.698, -83.650],
            "duration": "PT20M",
            "timeWindows": [
              {
                "minStartTime": "2027-02-01T09:00:00Z",
                "maxEndTime": "2027-02-01T17:00:00Z"
              }
            ],
            "stopDependencies": [
              {
                "id": "jobE_dep1",
                "precedingStop": "E1"
              }
            ]
          }
        ]
      }
    ]
  }
}
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": "Assign optional jobs example",
    "submitDateTime": "2025-12-15T08:22:12.393845749Z",
    "startDateTime": "2025-12-15T08:22:30.51558286Z",
    "activeDateTime": "2025-12-15T08:22:30.592648357Z",
    "completeDateTime": "2025-12-15T08:23:01.180386428Z",
    "shutdownDateTime": "2025-12-15T08:23:01.180390857Z",
    "solverStatus": "SOLVING_COMPLETED",
    "score": "0hard/0medium/-200017662soft",
    "tags": [
      "system.type:from-request",
      "system.profile:default"
    ],
    "validationResult": {
      "summary": "OK"
    }
  },
  "modelOutput": {
    "drivers": [
      {
        "id": "Carl",
        "shifts": [
          {
            "id": "Carl Mon",
            "startTime": "2027-02-01T09:00:00Z",
            "itinerary": [
              {
                "id": "A1",
                "arrivalTime": "2027-02-01T09:13:31Z",
                "startServiceTime": "2027-02-01T09:13:31Z",
                "departureTime": "2027-02-01T09:33:31Z",
                "effectiveServiceDuration": "PT20M",
                "kind": "STOP",
                "travelTimeFromPreviousStandstill": "PT13M31S",
                "travelDistanceMetersFromPreviousStandstill": 8699
              },
              {
                "id": "A2",
                "arrivalTime": "2027-02-01T10:11:08Z",
                "startServiceTime": "2027-02-01T10:11:08Z",
                "departureTime": "2027-02-01T10:31:08Z",
                "effectiveServiceDuration": "PT20M",
                "kind": "STOP",
                "travelTimeFromPreviousStandstill": "PT37M37S",
                "travelDistanceMetersFromPreviousStandstill": 38485
              },
              {
                "id": "E1",
                "arrivalTime": "2027-02-01T10:38:24Z",
                "startServiceTime": "2027-02-01T10:38:24Z",
                "departureTime": "2027-02-01T10:58:24Z",
                "effectiveServiceDuration": "PT20M",
                "kind": "STOP",
                "travelTimeFromPreviousStandstill": "PT7M16S",
                "travelDistanceMetersFromPreviousStandstill": 4443
              },
              {
                "id": "E2",
                "arrivalTime": "2027-02-01T11:41:17Z",
                "startServiceTime": "2027-02-01T11:41:17Z",
                "departureTime": "2027-02-01T12:01:17Z",
                "effectiveServiceDuration": "PT20M",
                "kind": "STOP",
                "travelTimeFromPreviousStandstill": "PT42M53S",
                "travelDistanceMetersFromPreviousStandstill": 41069
              },
              {
                "id": "D1",
                "arrivalTime": "2027-02-01T12:54:03Z",
                "startServiceTime": "2027-02-01T12:54:03Z",
                "departureTime": "2027-02-01T13:14:03Z",
                "effectiveServiceDuration": "PT20M",
                "kind": "STOP",
                "travelTimeFromPreviousStandstill": "PT52M46S",
                "travelDistanceMetersFromPreviousStandstill": 47720
              },
              {
                "id": "D2",
                "arrivalTime": "2027-02-01T13:48:26Z",
                "startServiceTime": "2027-02-01T13:48:26Z",
                "departureTime": "2027-02-01T14:08:26Z",
                "effectiveServiceDuration": "PT20M",
                "kind": "STOP",
                "travelTimeFromPreviousStandstill": "PT34M23S",
                "travelDistanceMetersFromPreviousStandstill": 38523
              },
              {
                "id": "C1",
                "arrivalTime": "2027-02-01T14:25:19Z",
                "startServiceTime": "2027-02-01T14:25:19Z",
                "departureTime": "2027-02-01T14:45:19Z",
                "effectiveServiceDuration": "PT20M",
                "kind": "STOP",
                "travelTimeFromPreviousStandstill": "PT16M53S",
                "travelDistanceMetersFromPreviousStandstill": 16686
              },
              {
                "id": "C2",
                "arrivalTime": "2027-02-01T15:16:33Z",
                "startServiceTime": "2027-02-01T15:16:33Z",
                "departureTime": "2027-02-01T15:36:33Z",
                "effectiveServiceDuration": "PT20M",
                "kind": "STOP",
                "travelTimeFromPreviousStandstill": "PT31M14S",
                "travelDistanceMetersFromPreviousStandstill": 35417
              }
            ],
            "metrics": {
              "totalTravelTime": "PT4H54M22S",
              "travelTimeFromStartLocationToFirstStop": "PT13M31S",
              "travelTimeBetweenStops": "PT3H43M2S",
              "travelTimeFromLastStopToEndLocation": "PT57M49S",
              "totalTravelDistanceMeters": 299904,
              "travelDistanceFromStartLocationToFirstStopMeters": 8699,
              "travelDistanceBetweenStopsMeters": 222343,
              "travelDistanceFromLastStopToEndLocationMeters": 68862,
              "endLocationArrivalTime": "2027-02-01T16:34:22Z"
            }
          }
        ]
      }
    ]
  },
  "inputMetrics": {
    "jobs": 5,
    "stops": 10,
    "drivers": 1,
    "driverShifts": 1
  },
  "kpis": {
    "totalTravelTime": "PT4H54M22S",
    "totalTravelDistanceMeters": 299904,
    "totalActivatedDrivers": 1,
    "totalUnassignedJobs": 1,
    "totalAssignedJobs": 4,
    "assignedMandatoryJobs": 1,
    "assignedOptionalJobs": 3,
    "totalUnassignedStops": 2,
    "totalAssignedStops": 8,
    "assignedMandatoryStops": 2,
    "assignedOptionalStops": 6,
    "travelTimeFromStartLocationToFirstStop": "PT13M31S",
    "travelTimeBetweenStops": "PT3H43M2S",
    "travelTimeFromLastStopToEndLocation": "PT57M49S",
    "travelDistanceFromStartLocationToFirstStopMeters": 8699,
    "travelDistanceBetweenStopsMeters": 222343,
    "travelDistanceFromLastStopToEndLocationMeters": 68862
  },
  "run": {
    "id": "ID",
    "originId": "ID",
    "name": "Assign optional jobs example",
    "submitDateTime": "2025-12-15T08:22:12.393845749Z",
    "startDateTime": "2025-12-15T08:22:30.51558286Z",
    "activeDateTime": "2025-12-15T08:22:30.592648357Z",
    "completeDateTime": "2025-12-15T08:23:01.180386428Z",
    "shutdownDateTime": "2025-12-15T08:23:01.180390857Z",
    "solverStatus": "SOLVING_COMPLETED",
    "score": "0hard/0medium/-200017662soft",
    "tags": [
      "system.type:from-request",
      "system.profile:default"
    ],
    "validationResult": {
      "summary": "OK"
    }
  }
}

Next

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

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