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
  • Job requirements and tags
  • Tags

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

Tags

While Skills can be used to model properties of drivers and requirements of jobs that can have different levels of expertise, there are also requirements and properties that are simply a yes or no question:

  • Is driver Ann certified to drive in a specific area?

  • Is driver Beth trained to handle hazardous materials?

  • Is driver Carl able to drive a vehicle with an accessibility ramp?

These properties usually refer to certifications or qualifications that a driver has and also to vehicle attributes that need to be considered.

Such a requirement can be modeled using tags.

This guide describes how to specify driver’s tags and the required tags for jobs.

Prerequisites

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 Pick-up and Delivery Routing model.

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

1. Required tags

Jobs often require drivers with specific certifcations or vehicle attributes to complete the job. For instance, if Job A is transporting hazardous material between facilities it may need the tag Hazardous Materials.

Drivers' tags are added to the shifts they’re available to work:

{
  "id": "Ann",
  "shifts": [
    {
      "id": "Ann Mon",
      "startLocation": [33.68786, -84.18487],
      "endLocation": [33.68786, -84.18487],
      "minStartTime": "2027-02-01T09:00:00Z",
      "maxEndTime": "2027-02-01T17:00:00Z",
      "tags": [
        "Hazardous Materials"
      ]
    }
  ]
}

The tags array contains the tag names as strings. This is the name of the tag and will be matched against the tags required by jobs.

Drivers can have multiple tags. Tags must be added to each shift where the tag is available.

requiredTags for jobs are declared as part of the job:

{
  "jobs": [
    {
      "id": "Job A",
      "stops": [
        {
          "id": "A1",
          "location": [33.78592, -84.46136],
          "duration": "PT20M"
        },
        {
          "id": "A2",
          "location": [33.72757, -83.96354],
          "duration": "PT20M",
          "stopDependencies": [
            {
              "id": "jobA_dep1",
              "precedingStop": "A1"
            }
          ]
        }
      ],
      "requiredTags": [
        "Hazardous Materials"
      ]
    }
  ]
}

The requiredTags contains the tags as strings that will be matched against the driver’s tags.

The Required tags hard constraint penalizes solutions with a hard score for missing tags. The hard score penalty is based on the number of missing tags.

Jobs will be left unassigned if a driver with the correct tag cannot be assigned.

In the following example, Job A requires a driver with the tag Hazardous Material. Ann and Beth are both available. Ann has the Hazardous Material tag as she has been certified to work with hazardous materials. Beth does not have the tag as she doesn’t have the relevant certification.

The job is assigned to Ann.

  • Input

  • Output

Try this example in Timefold Platform by saving this JSON into a file called required-tags-input.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": "Required tags example"
    }
  },
  "modelInput": {
    "drivers": [
      {
        "id": "Ann",
        "shifts": [
          {
            "id": "Ann Mon",
            "startLocation": [33.68786, -84.18487],
            "endLocation": [33.68786, -84.18487],
            "minStartTime": "2027-02-01T09:00:00Z",
            "maxEndTime": "2027-02-01T17:00:00Z",
            "tags": [
              "Hazardous Materials"
            ]
          }
        ]
      },
      {
        "id": "Beth",
        "shifts": [
          {
            "id": "Beth Mon",
            "startLocation": [33.68786, -84.18487],
            "endLocation": [33.68786, -84.18487],
            "minStartTime": "2027-02-01T09:00:00Z",
            "maxEndTime": "2027-02-01T17:00:00Z",
            "tags": [
            ]
          }
        ]
      }
    ],
    "jobs": [
      {
        "id": "Job A",
        "stops": [
          {
            "id": "A1",
            "location": [33.78592, -84.46136],
            "duration": "PT20M"
          },
          {
            "id": "A2",
            "location": [33.72757, -83.96354],
            "duration": "PT20M",
            "stopDependencies": [
              {
                "id": "jobA_dep1",
                "precedingStop": "A1"
              }
            ]
          }
        ],
        "requiredTags": [
          "Hazardous Materials"
        ]
      }
    ]
  }
}
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": "Required tags example",
    "submitDateTime": "2025-10-13T15:39:20.941516+02:00",
    "startDateTime": "2025-10-13T15:39:21.046345+02:00",
    "activeDateTime": "2025-10-13T15:39:21.047156+02:00",
    "completeDateTime": "2025-10-13T15:39:51.061147+02:00",
    "shutdownDateTime": "2025-10-13T15:39:51.061149+02:00",
    "solverStatus": "SOLVING_COMPLETED",
    "score": "0hard/0medium/-6855soft",
    "validationResult": {
      "summary": "OK"
    }
  },
  "modelOutput": {
    "drivers": [
      {
        "id": "Ann",
        "shifts": [
          {
            "id": "Ann 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"
            }
          }
        ]
      },
      {
        "id": "Beth",
        "shifts": [
          {
            "id": "Beth Mon",
            "startTime": "2027-02-01T09:00:00Z",
            "itinerary": [],
            "metrics": {
              "totalTravelTime": "PT0S",
              "travelTimeFromStartLocationToFirstStop": "PT0S",
              "travelTimeBetweenStops": "PT0S",
              "travelTimeFromLastStopToEndLocation": "PT0S",
              "totalTravelDistanceMeters": 0,
              "travelDistanceFromStartLocationToFirstStopMeters": 0,
              "travelDistanceBetweenStopsMeters": 0,
              "travelDistanceFromLastStopToEndLocationMeters": 0,
              "overtime": "PT0S"
            }
          }
        ]
      }
    ]
  },
  "inputMetrics": {
    "jobs": 1,
    "stops": 2,
    "drivers": 2,
    "driverShifts": 2
  },
  "kpis": {
    "totalTravelTime": "PT1H54M15S",
    "totalTravelDistanceMeters": 95216,
    "totalActivatedDrivers": 1,
    "totalUnassignedJobs": "0",
    "totalAssignedJobs": "1",
    "totalUnassignedStops": 0,
    "totalAssignedStops": 2,
    "totalOvertime": "PT0S",
    "travelTimeFromStartLocationToFirstStop": "PT33M21S",
    "travelTimeBetweenStops": "PT55M46S",
    "travelTimeFromLastStopToEndLocation": "PT25M8S",
    "travelDistanceFromStartLocationToFirstStopMeters": 27795,
    "travelDistanceBetweenStopsMeters": 46477,
    "travelDistanceFromLastStopToEndLocationMeters": 20944
  }
}

Next

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

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