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
  • Dependencies between stops

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
      • Driver capacity
    • Job service constraints
      • Time windows and opening hours
      • Skills
      • Multi-day schedules and movable stops
      • 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
        • 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

Dependencies between stops

In pick-up and delivery routing it is important to make sure stops are scheduled in the correct order and that stops in the same job are assigned to the same driver.

An item cannot be delivered before it has been collected, and both the pick-up and delivery must be made by the same driver.

1. Defining stop dependencies

A job in the Pick-up and Delivery model contains a list of stops. The stops must be listed in the order they need to be performed.

{
  "id": "Job A",
  "stops": [
    {
      "id": "A1",
      "location": [33.77911, -84.49644],
      "duration": "PT20M"
    },
    {
      "id": "A2",
      "location": [ 33.73613, -84.38245],
      "duration": "PT20M"
    },
    {
      "id": "A3",
      "location": [33.65979, -84.46366],
      "duration": "PT20M"
    }
  ]
}

Stop A1 is the first stop in the list of stops for job A, followed by stop A2 and then stop A3. The order of the stops in the list defines the dependencies between the stops. Therefore, a solution for job A must provide the stops in the sequence of A1, then A2, and then A3 in order to be a valid solution.

Reach out to Timefold if you require a more complex approach to define dependencies between stops.
dependencies between stops
  • 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 -d@sample.json
{
  "config": {
    "run": {
      "name": "Dependencies between stops example"
    }
  },
  "modelInput": {
    "drivers": [
      {
        "id": "Ann",
        "shifts": [
          {
            "id": "Ann Mon",
            "startLocation": [33.75522, -84.32040],
            "endLocation": [33.75522, -84.32040],
            "minStartTime": "2027-02-01T09:00:00Z",
            "maxEndTime": "2027-02-01T17:00:00Z"
          }
        ]
      }
    ],
    "jobs": [
      {
        "id": "Job A",
        "stops": [
          {
            "id": "A1",
            "location": [33.77911, -84.49644],
            "duration": "PT20M"
          },
          {
            "id": "A2",
            "location": [33.79656, -84.34159],
            "duration": "PT20M"
          },
          {
            "id": "A3",
            "location": [33.65979, -84.46366],
            "duration": "PT20M"
          }
        ]
      }
    ]
  }
}
To request the solution, locate the ID from the response to the post operation and append it to the following API call:
curl -X GET -H 'X-API-KEY: <API_KEY>' https://app.timefold.ai/api/models/pickup-delivery-routing/v1/route-plans/<ID>
{
  "metadata": {
    "id": "ID",
    "parentId": null,
    "originId": "ID",
    "name": "Dependencies between stops example",
    "submitDateTime": "2025-08-14T07:46:32.053666902Z",
    "startDateTime": "2025-08-14T07:46:37.739466519Z",
    "activeDateTime": "2025-08-14T07:46:38.278549981Z",
    "completeDateTime": "2025-08-14T07:46:39.734340868Z",
    "shutdownDateTime": "2025-08-14T07:46:40.472425825Z",
    "solverStatus": "SOLVING_COMPLETED",
    "score": "0hard/0medium/-5817soft",
    "tags": [
      "system.type:from-request",
      "system.profile:default"
    ],
    "validationResult": {
      "summary": "OK"
    }
  },
  "modelOutput": {
    "drivers": [
      {
        "id": "Ann",
        "shifts": [
          {
            "id": "Ann Mon",
            "startTime": "2027-02-01T09:00:00Z",
            "itinerary": [
              {
                "id": "A1",
                "arrivalTime": "2027-02-01T09:20:47Z",
                "startServiceTime": "2027-02-01T09:20:47Z",
                "departureTime": "2027-02-01T09:40:47Z",
                "effectiveServiceDuration": "PT20M",
                "kind": "STOP",
                "travelTimeFromPreviousStandstill": "PT20M47S",
                "travelDistanceMetersFromPreviousStandstill": 21621
              },
              {
                "id": "A2",
                "arrivalTime": "2027-02-01T10:03:47Z",
                "startServiceTime": "2027-02-01T10:03:47Z",
                "departureTime": "2027-02-01T10:23:47Z",
                "effectiveServiceDuration": "PT20M",
                "kind": "STOP",
                "travelTimeFromPreviousStandstill": "PT23M",
                "travelDistanceMetersFromPreviousStandstill": 21406
              },
              {
                "id": "A3",
                "arrivalTime": "2027-02-01T10:51:56Z",
                "startServiceTime": "2027-02-01T10:51:56Z",
                "departureTime": "2027-02-01T11:11:56Z",
                "effectiveServiceDuration": "PT20M",
                "kind": "STOP",
                "travelTimeFromPreviousStandstill": "PT28M9S",
                "travelDistanceMetersFromPreviousStandstill": 23763
              }
            ],
            "metrics": {
              "totalTravelTime": "PT1H36M57S",
              "travelTimeFromStartLocationToFirstStop": "PT20M47S",
              "travelTimeBetweenStops": "PT51M9S",
              "travelTimeFromLastStopToEndLocation": "PT25M1S",
              "totalTravelDistanceMeters": 88552,
              "travelDistanceFromStartLocationToFirstStopMeters": 21621,
              "travelDistanceBetweenStopsMeters": 45169,
              "travelDistanceFromLastStopToEndLocationMeters": 21762,
              "endLocationArrivalTime": "2027-02-01T11:36:57Z",
              "overtime": "PT0S"
            }
          }
        ]
      }
    ]
  },
  "inputMetrics": {
    "stops": 3,
    "drivers": 1,
    "driverShifts": 1
  },
  "kpis": {
    "totalTravelTime": "PT1H36M57S",
    "travelTimeFromStartLocationToFirstStop": "PT20M47S",
    "travelTimeBetweenStops": "PT51M9S",
    "travelTimeFromLastStopToEndLocation": "PT25M1S",
    "totalTravelDistanceMeters": 88552,
    "travelDistanceFromStartLocationToFirstStopMeters": 21621,
    "travelDistanceBetweenStopsMeters": 45169,
    "travelDistanceFromLastStopToEndLocationMeters": 21762,
    "totalUnassignedStops": 0,
    "totalAssignedStops": 3,
    "totalActivatedDrivers": 1,
    "totalOvertime": "PT0S"
  }
}

There are four constraints that manage dependencies between stops:

  • Do not assign stops if previous stop is unassigned

  • Assign stops in sequence

  • Assign the same driver shift for dependent stops

  • No semi-assigned jobs

The Do not assign stops if previous stop is unassigned hard constraint penalizes solutions with a hard score if the preceding stop is not assigned when the following stop is assigned. The score is based on the sum of the effective service durations of the preceding and following stops.

Stops will not be assigned if they break this constraint.

The Assign stops in sequence hard constraint penalizes solutions with a hard score if the sequence of stops is not respected according to their order in the job definition. The score is based on the time between the preceding stop’s departure time and the following stop’s start service time. In case the preceding stop is unassigned, the effective service duration of the preceding stop is used.

Stops will not be assigned if they break this constraint.

The Assign the same driver shift for dependent stops hard constraint penalizes solutions with a hard score if the dependent stop is not assigned to the same driver shift as the preceding stop. The score is based on the effective service duration of the following stop.

Stops will not be assigned if they break this constraint.

The No semi-assigned jobs hard constraint penalizes solutions with a hard score if a job is semi-assigned (all of its stops must be either assigned or unassigned).

Stops will not be assigned if they break this constraint.

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