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
  • Skills

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

Skills

Drivers have different skills and levels of experience which has an impact on which jobs they can be assigned.

  • Spanish speaker.

  • First-aid.

  • Working with minors.

Skills can also include levels.

Drivers might have first-aid training, but some jobs might only require basic first-aid and others might require advanced first-aid training.

Jobs can request specific skills with a minimum skill level. This ensures that jobs that require higher skill levels are assigned to experienced drivers with the correct experience, while jobs that require a lower minimum level can be assigned to less experienced drivers.

This guide describes how to specify the skill and skill level of the drivers and the required skills 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 skills and skill levels

Jobs often require drivers with specific skills to complete the job. For instance, if Job A is transporting a patient between medical facilities it may need the skill First-aid.

Drivers' skills 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",
      "skills": [
        {
          "name": "First-aid",
          "level": 2
        }
      ]
    }
  ]
}

The skills array must include a name. This is the name of the skill and will be matched against the skills required by jobs.

skills can optionally include a level. Jobs can optionally require a minLevel for skills, which will exclude any driver with a level lower than the minLevel.

Drivers can have multiple skills. Skills must be added to each shift where the skill is available.

requiredSkills 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"
            }
          ]
        }
      ],
      "requiredSkills": [
        {
          "name": "First-aid",
          "minLevel": 2
        }
      ]
    }
  ]
}

The requiredSkill must include a name. This is the name of the skill and will be matched against the driver’s skills.

requiredSkills can optionally include a minLevel. Skills have different levels, minLevel specifies the minimum level required for the job.

The Required skills hard constraint penalizes solutions with a hard score for missing skills or skill levels. The hard score penalty is based on the number of missing skills and skill levels.

Jobs will be left unassigned if a driver with the correct skill and skill level cannot be assigned.

The Minimize scheduling drivers with unnecessary skill levels soft constraint penalizes solutions with a soft score when they assign overqualified drivers to a job. For instance, if a job has a required skill of level 2 and a driver with a skill level 4 is assigned.

This incentivizes Timefold to assign a driver with at least the minimum required skill level and as close to the minimum as possible.

In the following example, Job A requires a driver with the skill First-aid with a minLevel of 2. Ann and Beth are both available. Ann has the skill First-aid at level 2, Beth has the skill First-aid at level 1.

The job is assigned to Ann.

required skill and skill level
  • 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": "Required skills 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",
            "skills": [
              {
                "name": "First-aid",
                "level": 2
              }
            ]
          }
        ]
      },
      {
        "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",
            "skills": [
              {
                "name": "First-aid",
                "level": 1
              }
            ]
          }
        ]
      }
    ],
    "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"
              }
            ]
          }
        ],
        "requiredSkills": [
          {
            "name": "First-aid",
            "minLevel": 2
          }
        ]
      }
    ]
  }
}
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": "Required skills example",
    "submitDateTime": "2025-08-07T05:18:28.75798922Z",
    "startDateTime": "2025-08-07T05:18:34.105880599Z",
    "activeDateTime": "2025-08-07T05:18:34.692823818Z",
    "completeDateTime": "2025-08-07T05:18:35.849272889Z",
    "shutdownDateTime": "2025-08-07T05:18:36.348992719Z",
    "solverStatus": "SOLVING_COMPLETED",
    "score": "0hard/0medium/-6852soft",
    "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:31:38Z",
                "startServiceTime": "2027-02-01T09:31:38Z",
                "departureTime": "2027-02-01T09:51:38Z",
                "effectiveServiceDuration": "PT20M",
                "kind": "STOP",
                "travelTimeFromPreviousStandstill": "PT31M38S",
                "travelDistanceMetersFromPreviousStandstill": 33381
              },
              {
                "id": "A2",
                "arrivalTime": "2027-02-01T10:45:22Z",
                "startServiceTime": "2027-02-01T10:45:22Z",
                "departureTime": "2027-02-01T11:05:22Z",
                "effectiveServiceDuration": "PT20M",
                "kind": "STOP",
                "travelTimeFromPreviousStandstill": "PT53M44S",
                "travelDistanceMetersFromPreviousStandstill": 62124
              }
            ],
            "metrics": {
              "totalTravelTime": "PT1H54M12S",
              "travelTimeFromStartLocationToFirstStop": "PT31M38S",
              "travelTimeBetweenStops": "PT53M44S",
              "travelTimeFromLastStopToEndLocation": "PT28M50S",
              "totalTravelDistanceMeters": 127024,
              "travelDistanceFromStartLocationToFirstStopMeters": 33381,
              "travelDistanceBetweenStopsMeters": 62124,
              "travelDistanceFromLastStopToEndLocationMeters": 31519,
              "endLocationArrivalTime": "2027-02-01T11:34:12Z",
              "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": {
    "stops": 2,
    "drivers": 2,
    "driverShifts": 2
  },
  "kpis": {
    "totalTravelTime": "PT1H54M12S",
    "travelTimeFromStartLocationToFirstStop": "PT31M38S",
    "travelTimeBetweenStops": "PT53M44S",
    "travelTimeFromLastStopToEndLocation": "PT28M50S",
    "totalTravelDistanceMeters": 127024,
    "travelDistanceFromStartLocationToFirstStopMeters": 33381,
    "travelDistanceBetweenStopsMeters": 62124,
    "travelDistanceFromLastStopToEndLocationMeters": 31519,
    "totalUnassignedStops": 0,
    "totalAssignedStops": 2,
    "totalActivatedDrivers": 1,
    "totalOvertime": "PT0S"
  }
}

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