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
  • Prohibit job combinations

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

Prohibit job combinations

In pick-up and delivery routing, Job pooling adds efficiencies that can result in all jobs being completed quicker and with fewer resources. However, there are times when two jobs should not be combined, for instance, if a customer had a bad experience traveling with another customer, they can request not to travel with that customer again.

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. Prohibited jobs

When a job must not overlap with another job for the same driver, add a prohibitedJobs array to the job with the job IDs listed:

{
  "jobs": [
    {
      "id": "Job C",
      "prohibitedJobs": ["Job B"],
      "stops": [
        {
          "id": "C1",
          "location": [33.78592, -84.46136],
          "duration": "PT20M"
        },
        {
          "id": "C2",
          "location": [33.72757, -83.96354],
          "duration": "PT20M",
          "stopDependencies": [
            {
              "id": "jobC_dep1",
              "precedingStop": "C1"
            }
          ]
        }
      ]
    }
  ]
}

If the prohibitedJobs array is empty or not specified, the job doesn’t restrict how it can be assigned.

The Prohibit job combinations hard constraint penalizes solutions with a hard score if jobs overlap for the same driver and at least one of the jobs prohibits such a combination.

Jobs will be left unassigned if assigning them would break this constraint.

In the following example, Job C lists Job B as a prohibited job.

Although Job B and Job C share the same starting location, they are assigned sequentially.

  • Input

  • Output

Try this example in Timefold Platform by saving the JSON into a file called prohibited-jobs-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": "Prohibited jobs 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"
          }
        ]
      }
    ],
    "jobs": [
      {
        "id": "Job C",
        "prohibitedJobs": ["Job B"],
        "stops": [
          {
            "id": "C1",
            "location": [33.78592, -84.46136],
            "duration": "PT20M"
          },
          {
            "id": "C2",
            "location": [33.72757, -83.96354],
            "duration": "PT20M",
            "stopDependencies": [
              {
                "id": "jobC_dep1",
                "precedingStop": "C1"
              }
            ]
          }
        ]
      },
      {
        "id": "Job B",
        "prohibitedJobs": [],
        "stops": [
          {
            "id": "B1",
            "location": [33.78592, -84.46136],
            "duration": "PT20M"
          },
          {
            "id": "B2",
            "location": [33.72757, -83.96354],
            "duration": "PT20M",
            "stopDependencies": [
              {
                "id": "jobB_dep1",
                "precedingStop": "B1"
              }
            ]
          }
        ]
      }
    ]
  }
}
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": "Prohibited jobs example",
    "submitDateTime": "2025-09-23T11:40:55.994757+02:00",
    "startDateTime": "2025-09-23T11:40:56.054933+02:00",
    "activeDateTime": "2025-09-23T11:40:56.056582+02:00",
    "completeDateTime": "2025-09-23T11:40:56.532302+02:00",
    "shutdownDateTime": "2025-09-23T11:40:56.536063+02:00",
    "solverStatus": "SOLVING_COMPLETED",
    "score": "0hard/0medium/-13547soft",
    "validationResult": {
      "summary": "OK"
    }
  },
  "modelOutput": {
    "drivers": [
      {
        "id": "Ann",
        "shifts": [
          {
            "id": "Ann Mon",
            "startTime": "2027-02-01T09:00:00Z",
            "itinerary": [
              {
                "id": "B1",
                "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": "B2",
                "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
              },
              {
                "id": "C1",
                "arrivalTime": "2027-02-01T12:04:53Z",
                "startServiceTime": "2027-02-01T12:04:53Z",
                "departureTime": "2027-02-01T12:24:53Z",
                "effectiveServiceDuration": "PT20M",
                "kind": "STOP",
                "travelTimeFromPreviousStandstill": "PT55M46S",
                "travelDistanceMetersFromPreviousStandstill": 46477
              },
              {
                "id": "C2",
                "arrivalTime": "2027-02-01T13:20:39Z",
                "startServiceTime": "2027-02-01T13:20:39Z",
                "departureTime": "2027-02-01T13:40:39Z",
                "effectiveServiceDuration": "PT20M",
                "kind": "STOP",
                "travelTimeFromPreviousStandstill": "PT55M46S",
                "travelDistanceMetersFromPreviousStandstill": 46477
              }
            ],
            "metrics": {
              "totalTravelTime": "PT3H45M47S",
              "travelTimeFromStartLocationToFirstStop": "PT33M21S",
              "travelTimeBetweenStops": "PT2H47M18S",
              "travelTimeFromLastStopToEndLocation": "PT25M8S",
              "totalTravelDistanceMeters": 188170,
              "travelDistanceFromStartLocationToFirstStopMeters": 27795,
              "travelDistanceBetweenStopsMeters": 139431,
              "travelDistanceFromLastStopToEndLocationMeters": 20944,
              "endLocationArrivalTime": "2027-02-01T14:05:47Z",
              "overtime": "PT0S"
            }
          }
        ]
      }
    ]
  },
  "inputMetrics": {
    "stops": 4,
    "drivers": 1,
    "driverShifts": 1
  },
  "kpis": {
    "totalTravelTime": "PT3H45M47S",
    "travelTimeFromStartLocationToFirstStop": "PT33M21S",
    "travelTimeBetweenStops": "PT2H47M18S",
    "travelTimeFromLastStopToEndLocation": "PT25M8S",
    "totalTravelDistanceMeters": 188170,
    "travelDistanceFromStartLocationToFirstStopMeters": 27795,
    "travelDistanceBetweenStopsMeters": 139431,
    "travelDistanceFromLastStopToEndLocationMeters": 20944,
    "totalUnassignedStops": 0,
    "totalAssignedStops": 4,
    "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