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
-
Navigate to https://app.timefold.ai.
-
Click Log in.
-
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.
-
Verify your email address.
-
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:
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.
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
-
idis a unique identifier that identifies the driver. -
shiftsrepresents 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.
-
idis a unique identifier that identifies the shift. -
startLocationprovides the driver’s location at the start of the shift. -
minStartTimesets the earliest time the driver can start their shift in ISO 8601 date and time format. -
maxEndTimesets 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.
-
idis a unique identifier that identifies the job. -
stopsrepresents 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.
-
idis a unique identifier that identifies the stop. -
nameis a unique name that identifies the stop. -
locationprovides the location of the stop. -
durationis the estimated time the stop will take in ISO 8601 duration format. The value"PT10M"indicates the estimated duration is 10 minutes. -
stopDependenciesincludes anidandprecedingStop.-
idis a unique identifier that identifies thestopDependenciesof the stop. -
precedingStopspecifies 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.
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:
-
Log into the Timefold Platform dashboard: https://app.timefold.ai
-
Select the Pick-up and Delivery Routing tile.
-
Click New dataset.
-
Select Custom and click Next
-
Upload the
sample.jsonfile you saved earlier. -
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:
-
Log in to Timefold Platform: app.timefold.ai.
-
From the Dashboard, click your tenant, and from the drop-down menu select Tenant Settings, then choose API Keys.
-
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.
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.