Docs
  • Solver
  • Models
    • Field Service Routing
    • Employee Shift Scheduling
    • Pick-up and Delivery Routing
  • Platform
Try models
  • Pick-up and Delivery Routing
  • Getting started: Hello world

Pick-up and Delivery Routing

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

Getting started: Hello world

The Pick-up and Delivery Routing model assigns pick-ups and deliveries to drivers so that multiple pick-ups and deliveries can be made on the same trip while minimizing driving time and customer wait times.

Please note that this model is currently in Preview status (i.e., mostly developed but still being refined and may still introduce backward-incompatible changes). If you’re interested in using this model, please contact us.

This guide introduces Timefold’s Pick-up and Delivery Routing model and walks you through the steps to use Timefold Platform to create an optimized pick-up and delivery routing solution.

To follow the steps in this guide, you need an active Timefold Platform account.

Request a trial
  1. Navigate to https://app.timefold.ai.

  2. Click Log in.

  3. Click Sign up.

    Either enter the email address and password you want to register with and click continue or select one of the federated log-in options and follow the prompts.

  4. Verify your email address.

  5. Click Request a trial.

A member of our team will contact you to arrange a trial.

The steps in this guide can be completed in under 10 minutes:

  1. Create a problem dataset

  2. Post the dataset

  3. Request the solution

This hello world example uses an example with 1 driver and 1 job to demonstrate the process of requesting and retrieving a solution from Timefold’s Pick-up and Delivery Routing model.

At the end of this guide, you will have a solution for the pick-up and delivery routing hello world problem.

getting started

This hello world uses the POST and GET methods and the API endpoint: https://app.timefold.ai/api/models/pickup-delivery-routing/v1/route-plans/

1. Create a problem dataset

A dataset for the Pick-up and Delivery Routing model must include information about your jobs and your drivers' availability.

The following is an example pick-up and delivery routing input dataset:

{
  "config": {
    "run": {
      "name": "Hello world example"
    }
  },
  "modelInput": {
    "drivers": [
      {
        "id": "Ann",
        "shifts": [
          {
            "id": "Ann Mon",
            "startLocation": [33.77284, -84.42989],
            "minStartTime": "2027-02-01T09:00:00Z",
            "maxEndTime": "2027-02-01T17:00:00Z"
          }
        ]
      }
    ],
    "jobs": [
      {
        "id": "A1",
        "stops": [
          {
            "id": "A1-pickup",
            "name": "A1-pickup",
            "location": [33.77911, -84.49644],
            "duration": "PT10M"
          },
          {
            "id": "A1-delivery",
            "name": "A1-delivery",
            "location": [33.65979, -84.46366],
            "duration": "PT5M",
            "stopDependencies": [
              {
                "id": "A1_dep1",
                "precedingStop": "A1-pickup"
              }
            ]
          }
        ]
      }
    ]
  }
}
Copy this example dataset into a file called sample.json to use in this hello world example.

The modelInput object of the dataset contains the data to be optimized.

At a minimum, modelInput must include drivers and jobs:

1.1. Drivers

In this example, there is 1 driver. drivers includes an id and shifts

  • id is a unique identifier that identifies the driver.

  • shifts represents a period of time the driver works, typically a single day shift, for example, February 1st, 2027 from 09:00 to 17:00. A driver can have multiple shifts.

{
  "drivers": [
    {
      "id": "Ann",
      "shifts": [
        {
          "id": "Ann Mon",
          "startLocation": [33.77284, -84.42989],
          "minStartTime": "2027-02-01T09:00:00Z",
          "maxEndTime": "2027-02-01T17:00:00Z"
        }
      ]
    }
  ]
}

shifts includes an id, startLocation, minStartTime, and maxEndTime.

  • id is a unique identifier that identifies the shift.

  • startLocation provides the driver’s location at the start of the shift.

  • minStartTime sets the earliest time the driver can start their shift in ISO 8601 date and time format.

  • maxEndTime sets the latest time the driver can end their shift in ISO 8601 date and time format.

Learn about additional options that can be included for drivers in the Driver resource constraints guides.

1.2. Jobs

In this example, there is 1 job. jobs includes an id and stops.

  • id is a unique identifier that identifies the job.

  • stops represents the stops that need to be made as part of the job.

{
  "jobs": [
    {
      "id": "A1",
      "stops": [
        {
          "id": "A1-pickup",
          "name": "A1-pickup",
          "location": [33.77911, -84.49644],
          "duration": "PT10M"
        },
        {
          "id": "A1-delivery",
          "name": "A1-delivery",
          "location": [33.65979, -84.46366],
          "duration": "PT5M",
          "stopDependencies": [
            {
              "id": "A1_dep1",
              "precedingStop": "A1-pickup"
            }
          ]
        }
      ]
    }
  ]
}

stops includes an id, name, location, duration, and if applicable stopDependencies.

  • id is a unique identifier that identifies the stop.

  • name is a unique name that identifies the stop.

  • location provides the location of the stop.

  • duration is the estimated time the stop will take in ISO 8601 duration format. The value "PT10M" indicates the estimated duration is 10 minutes.

  • stopDependencies includes an id and precedingStop.

    • id is a unique identifier that identifies the stopDependencies of the stop.

    • precedingStop specifies the stop that must occur before this stop.

Learn about additional options that can be included for jobs in the Job service constraints guides.

When the dataset is posted to Timefold, Timefold will attempt to assign drivers to jobs, while considering the constraints of the domain.

2. Post the dataset

You have two options to post the dataset.

  1. Post the dataset in the Timefold Platform UI

  2. Post the dataset to the Timefold API

2.1. Post the dataset in the Timefold Platform UI

Typically, you post the dataset to the API. However, for testing purposes, you can upload your sample.json file directly in the Timefold Platform UI:

  1. Log into the Timefold Platform dashboard: https://app.timefold.ai

  2. Select the Pick-up and Delivery Routing tile.

  3. Click New dataset.

  4. Select Custom and click Next

  5. Upload the sample.json file you saved earlier.

  6. Click Next, then click Run.

2.2. Post the dataset to the Timefold API

You need an API key to access the API.

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

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

  3. Create a new API key or use an existing one. Ensure the list of models for the API key contains the current model.

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

POST the dataset contained in the sample.json file for solving to the API endpoint: https://app.timefold.ai/api/models/pickup-delivery-routing/v1/route-plans/

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]

The dataset will be validated. If the dataset is valid you will receive a response similar to:

{
  "id": "ID",
  "parentId": null,
  "originId": "ID",
  "name": "Hello world example",
  "submitDateTime": "2025-09-17T09:48:35.370120579Z",
  "startDateTime": null,
  "activeDateTime": null,
  "completeDateTime": null,
  "shutdownDateTime": null,
  "solverStatus": "SOLVING_SCHEDULED",
  "score": null,
  "tags": [
    "system.type:from-request",
    "system.profile:default"
  ],
  "validationResult": null
}

The output includes an ID that has been assigned to the dataset. You’ll use this ID to retrieve the solution for your dataset.

solverStatus confirms the dataset has been scheduled for solving.

3. Request the solution

Append the <ID> from the output you received to the endpoint https://app.timefold.ai/api/models/pickup-delivery-routing/v1/route-plans/ and create a GET request to retrieve the solution:

curl -X GET -H 'X-API-KEY: <API_KEY>' https://app.timefold.ai/api/models/pickup-delivery-routing/v1/route-plans/<ID>
{
  "metadata": {
    "id": "97e1d38d-3e6a-4675-b5cd-a0d79bdcd1d0",
    "parentId": null,
    "originId": "97e1d38d-3e6a-4675-b5cd-a0d79bdcd1d0",
    "name": "Hello world example",
    "submitDateTime": "2025-09-17T09:48:35.370120579Z",
    "startDateTime": "2025-09-17T09:48:46.339269139Z",
    "activeDateTime": "2025-09-17T09:48:47.503330119Z",
    "completeDateTime": "2025-09-17T09:48:48.252818322Z",
    "shutdownDateTime": "2025-09-17T09:48:48.5747474Z",
    "solverStatus": "SOLVING_COMPLETED",
    "score": "0hard/0medium/-2701soft",
    "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-pickup",
                "arrivalTime": "2027-02-01T09:07:42Z",
                "startServiceTime": "2027-02-01T09:07:42Z",
                "departureTime": "2027-02-01T09:17:42Z",
                "effectiveServiceDuration": "PT10M",
                "kind": "STOP",
                "travelTimeFromPreviousStandstill": "PT7M42S",
                "travelDistanceMetersFromPreviousStandstill": 7661
              },
              {
                "id": "A1-delivery",
                "arrivalTime": "2027-02-01T09:34:49Z",
                "startServiceTime": "2027-02-01T09:34:49Z",
                "departureTime": "2027-02-01T09:39:49Z",
                "effectiveServiceDuration": "PT5M",
                "kind": "STOP",
                "travelTimeFromPreviousStandstill": "PT17M7S",
                "travelDistanceMetersFromPreviousStandstill": 18056
              }
            ],
            "metrics": {
              "totalTravelTime": "PT45M1S",
              "travelTimeFromStartLocationToFirstStop": "PT7M42S",
              "travelTimeBetweenStops": "PT17M7S",
              "travelTimeFromLastStopToEndLocation": "PT20M12S",
              "totalTravelDistanceMeters": 42080,
              "travelDistanceFromStartLocationToFirstStopMeters": 7661,
              "travelDistanceBetweenStopsMeters": 18056,
              "travelDistanceFromLastStopToEndLocationMeters": 16363,
              "endLocationArrivalTime": "2027-02-01T10:00:01Z",
              "overtime": "PT0S"
            }
          }
        ]
      }
    ]
  },
  "inputMetrics": {
    "stops": 2,
    "drivers": 1,
    "driverShifts": 1
  },
  "kpis": {
    "totalTravelTime": "PT45M1S",
    "travelTimeFromStartLocationToFirstStop": "PT7M42S",
    "travelTimeBetweenStops": "PT17M7S",
    "travelTimeFromLastStopToEndLocation": "PT20M12S",
    "totalTravelDistanceMeters": 42080,
    "travelDistanceFromStartLocationToFirstStopMeters": 7661,
    "travelDistanceBetweenStopsMeters": 18056,
    "travelDistanceFromLastStopToEndLocationMeters": 16363,
    "totalUnassignedStops": 0,
    "totalAssignedStops": 2,
    "totalActivatedDrivers": 1,
    "totalOvertime": "PT0S"
  },
  "run": {
    "id": "97e1d38d-3e6a-4675-b5cd-a0d79bdcd1d0",
    "parentId": null,
    "originId": "97e1d38d-3e6a-4675-b5cd-a0d79bdcd1d0",
    "name": "Hello world example",
    "submitDateTime": "2025-09-17T09:48:35.370120579Z",
    "startDateTime": "2025-09-17T09:48:46.339269139Z",
    "activeDateTime": "2025-09-17T09:48:47.503330119Z",
    "completeDateTime": "2025-09-17T09:48:48.252818322Z",
    "shutdownDateTime": "2025-09-17T09:48:48.5747474Z",
    "solverStatus": "SOLVING_COMPLETED",
    "score": "0hard/0medium/-2701soft",
    "tags": [
      "system.type:from-request",
      "system.profile:default"
    ],
    "validationResult": {
      "summary": "OK"
    }
  }
}

The output shows the solverStatus is SOLVING COMPLETED.

modelOutput contains the solution for the dataset.

{
  "modelOutput": {
    "drivers": [
      {
        "id": "Ann",
        "shifts": [
          {
            "id": "Ann Mon",
            "startTime": "2027-02-01T09:00:00Z",
            "itinerary": [
              {
                "id": "A1-pickup",
                "arrivalTime": "2027-02-01T09:07:42Z",
                "startServiceTime": "2027-02-01T09:07:42Z",
                "departureTime": "2027-02-01T09:17:42Z",
                "effectiveServiceDuration": "PT10M",
                "kind": "STOP",
                "travelTimeFromPreviousStandstill": "PT7M42S",
                "travelDistanceMetersFromPreviousStandstill": 7661
              },
              {
                "id": "A1-delivery",
                "arrivalTime": "2027-02-01T09:34:49Z",
                "startServiceTime": "2027-02-01T09:34:49Z",
                "departureTime": "2027-02-01T09:39:49Z",
                "effectiveServiceDuration": "PT5M",
                "kind": "STOP",
                "travelTimeFromPreviousStandstill": "PT17M7S",
                "travelDistanceMetersFromPreviousStandstill": 18056
              }
            ],
            "metrics": {
              "totalTravelTime": "PT45M1S",
              "travelTimeFromStartLocationToFirstStop": "PT7M42S",
              "travelTimeBetweenStops": "PT17M7S",
              "travelTimeFromLastStopToEndLocation": "PT20M12S",
              "totalTravelDistanceMeters": 42080,
              "travelDistanceFromStartLocationToFirstStopMeters": 7661,
              "travelDistanceBetweenStopsMeters": 18056,
              "travelDistanceFromLastStopToEndLocationMeters": 16363,
              "endLocationArrivalTime": "2027-02-01T10:00:01Z",
              "overtime": "PT0S"
            }
          }
        ]
      }
    ]
  }
}

Ann is assigned to this job. She leaves her start location at 09:00 and arrives at the pick-up location at 09:07. She leaves this location at 09:17 and arrives at the delivery location at 09:34. Ann arrives at her end location at 10:00.

getting started

Next

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

  • Configure a webhook.

  • Try the Demo datasets.

  • Learn about Driver resource constraints and Job service constraints constraints.

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