GuidesAPI Reference
Guides

Get multiple unassigned job reasons

By default, when the HERE Tour Planning API cannot assign a job to any vehicle, it returns a single reason code that identifies the most frequent constraint violation across all vehicles in the fleet. However, a job might fail to be assigned for different reasons across different vehicles, making it more challenging to diagnose the root cause when only one reason is provided.

Understand single vs. multiple unassigned reasons

By default, the unassigned job response contains only a single reason—typically the most frequent constraint violation across all vehicles. For example:

{
    "jobId": "9e76e576-5038-4f55-9b6a-4ac9fbfd440b",
    "reasons": [
        {
            "code": "TERRITORY_CONSTRAINT",
            "description": "cannot be assigned due to territory constraint"
        }
    ]
}

With the multipleUnassignedReasons experimental feature enabled, you receive multiple applicable reason codes for each unassigned job, grouped by vehicle shift, for example:

{
    "jobId": "9e76e576-5038-4f55-9b6a-4ac9fbfd440b",
    "reasons": [
        {
            "code": "TERRITORY_CONSTRAINT",
            "description": "cannot be assigned due to territory constraint",
            "details": [
                {
                    "shiftIndex": 0,
                    "vehicleIds": [
                        "218_1_1",
                        "655_1_1",
                        "695_1_1",
                        "712_1_1"
                    ]
                }
            ]
        },
        {
            "code": "TIME_WINDOW_CONSTRAINT",
            "description": "cannot be assigned due to violation of time window",
            "details": [
                {
                    "shiftIndex": 0,
                    "vehicleIds": [
                        "166_1_1",
                        "180_1_1",
                        "217_1_1"
                    ]
                }
            ]
        }
    ]
}
📘

Note

The same vehicle can appear in multiple reason codes if it has multiple shifts and the job fails different constraints in different shifts, for example:

{
 "jobId": "9e76e576-5038-4f55-9b6a-4ac9fbfd440b",
 "reasons": [
   {
     "code": "CAPACITY_CONSTRAINT",
     "description": "cannot be assigned due to capacity of vehicle",
     "details": [
       {
         "shiftIndex": 0,
         "vehicleIds": [
           "218_1_1"
         ]
       }
     ]
   },
   {
     "code": "TIME_WINDOW_CONSTRAINT",
     "description": "cannot be assigned due to violation of time window",
     "details": [
       {
         "shiftIndex": 1,
         "vehicleIds": [
           "218_1_1"
         ]
       }
     ]
   }
 ]
}

This feature provides a comprehensive view of why a job could not be assigned, enabling you to make more informed decisions when adjusting your problem definition.

Enable multiple unassigned reasons

📘

Note

This is an ALPHA feature, which means it is new or experimental and under active development. Alpha features are provided for testing and feedback purposes. They may change significantly or might not become generally available.

For more information, see Explore experimental features.

To receive multiple unassigned reasons in your solution, add the multipleUnassignedReasons feature to the experimentalFeatures array in your problem configuration:

{
  "configuration": {
    "experimentalFeatures": [
      "multipleUnassignedReasons"
    ]
  },
  "fleet": {
    ...
  },
  "plan": {
    ...
  }
}

When the multipleUnassignedReasons feature is enabled, the solution includes a reasons array for each unassigned job. Unlike the default behavior which returns only a single reason, this array can contain multiple reason codes for the same job, allowing you to see all the different constraint violations across your fleet. Each reason contains:

  • code: The constraint violation reason code (for example, SKILL_CONSTRAINT, CAPACITY_CONSTRAINT)
  • description: A human-readable explanation of the constraint violation
  • details: An array of objects specifying which vehicles and shifts are affected by this particular constraint
📘

Important

Multiple unassigned reasons are only reported based on vehicles that are actually used in the solution.

For an unassigned job, the service checks all created tours and aggregates reasons why the job could not be added to any of those tours. Vehicles for which no tour was created are ignored. Consider a problem that includes Vehicle_1 and Vehicle_2 as the available vehicle fleet:

  • If only one vehicle is used in the solution, you see only the constraint associated with that vehicle (for example, CAPACITY_CONSTRAINT or SKILL_CONSTRAINT).
  • Multiple unassigned reasons (for example, both CAPACITY_CONSTRAINT and SKILL_CONSTRAINT, or CAPACITY_CONSTRAINT and REACHABLE_CONSTRAINT) are only shown when tours exist for multiple vehicles that were considered for the job.

Example: Diagnose complex constraint violations

This example demonstrates how multiple unassigned reasons help diagnose why a job cannot be assigned when different vehicles fail for different reasons.

Problem overview

The fleet consists of:

  • One car (car_1): Has 4-unit capacity, has the bulky_items skill for transporting large packages
  • Two scooters (scooter_1): Each has 2-unit capacity, has the express_delivery skill for time-sensitive deliveries

The jobs are distributed as follows:

  • Jobs 1-3: Require the bulky_items skill (1 unit each)
  • Jobs 4-8: Require the express_delivery skill (1 unit each)

The tight capacity and skill constraints create a scenario where Job_8 cannot be assigned to any vehicle.

Problem

The following section contains the complete problem JSON that demonstrates multiple unassigned reasons:

Click to expand/collapse the sample JSON
{ "configuration": {
  "experimentalFeatures": [
    "multipleUnassignedReasons"
  ]
},
  "fleet": {
    "types": [
      {
        "id": "car_1",
        "profile": "car",
        "costs": {
          "fixed": 10.0,
          "distance": 0.001,
          "time": 0.002
        },
        "shifts": [
          {
            "start": {
              "time": "2024-06-24T06:00:00Z",
              "location": {
                "lat": 52.53097,
                "lng": 13.38504
              }
            },
            "end": {
              "time": "2024-06-24T06:45:00Z",
              "location": {
                "lat": 52.53097,
                "lng": 13.38504
              }
            }
          }
        ],
        "capacity": [4],
        "skills": ["bulky_items"],
        "amount": 1
      },
      {
        "id": "scooter_1",
        "profile": "scooter",
        "costs": {
          "fixed": 10.0,
          "distance": 0.001,
          "time": 0.002
        },
        "shifts": [
          {
            "start": {
              "time": "2024-06-24T06:00:00Z",
              "location": {
                "lat": 52.53097,
                "lng": 13.38504
              }
            },
            "end": {
              "time": "2024-06-24T06:45:00Z",
              "location": {
                "lat": 52.53097,
                "lng": 13.38504
              }
            }
          }
        ],
        "capacity": [2],
        "skills": ["express_delivery"],
        "amount": 2
      }
    ],
    "profiles": [
      {
        "type": "car",
        "name": "car"
      },
      {
        "type": "scooter",
        "name": "scooter"
      }
    ]
  },
  "plan": {
    "jobs": [
      {
        "id": "Job_1",
        "skills": ["bulky_items"],
        "tasks": {
          "deliveries": [
            {
              "places": [
                {
                  "location": {
                    "lat": 52.531757,
                    "lng": 13.384426
                  },
                  "duration": 300
                }
              ],
              "demand": [1]
            }
          ]
        }
      },
      {
        "id": "Job_2",
        "skills": ["bulky_items"],
        "tasks": {
          "deliveries": [
            {
              "places": [
                {
                  "location": {
                    "lat": 52.532983,
                    "lng": 13.38787
                  },
                  "duration": 300
                }
              ],
              "demand": [1]
            }
          ]
        }
      },
      {
        "id": "Job_3",
        "skills": ["bulky_items"],
        "tasks": {
          "deliveries": [
            {
              "places": [
                {
                  "location": {
                    "lat": 52.532065,
                    "lng": 13.389091
                  },
                  "duration": 300
                }
              ],
              "demand": [1]
            }
          ]
        }
      },
      {
        "id": "Job_4",
        "skills": ["express_delivery"],
        "tasks": {
          "deliveries": [
            {
              "places": [
                {
                  "location": {
                    "lat": 52.530384,
                    "lng": 13.391224
                  },
                  "duration": 300
                }
              ],
              "demand": [1]
            }
          ]
        }
      },
      {
        "id": "Job_5",
        "skills": ["express_delivery"],
        "tasks": {
          "deliveries": [
            {
              "places": [
                {
                  "location": {
                    "lat": 52.530008,
                    "lng": 13.389562
                  },
                  "duration": 300
                }
              ],
              "demand": [1]
            }
          ]
        }
      },
      {
        "id": "Job_6",
        "skills": ["express_delivery"],
        "tasks": {
          "deliveries": [
            {
              "places": [
                {
                  "location": {
                    "lat": 52.529496,
                    "lng": 13.385547
                  },
                  "duration": 300
                }
              ],
              "demand": [1]
            }
          ]
        }
      },
      {
        "id": "Job_7",
        "skills": ["express_delivery"],
        "tasks": {
          "deliveries": [
            {
              "places": [
                {
                  "location": {
                    "lat": 52.528909,
                    "lng": 13.385381
                  },
                  "duration": 300
                }
              ],
              "demand": [1]
            }
          ]
        }
      },
      {
        "id": "Job_8",
        "skills": ["express_delivery"],
        "tasks": {
          "deliveries": [
            {
              "places": [
                {
                  "location": {
                    "lat": 52.530533,
                    "lng": 13.383505
                  },
                  "duration": 300
                }
              ],
              "demand": [1]
            }
          ]
        }
      }
    ]
  }
}

Solution analysis

When you submit this problem with the multipleUnassignedReasons feature enabled, the optimization algorithm assigns Jobs 1-3 to the car and Jobs 4-7 to the two scooters. However, Job_8 remains unassigned. The solution reveals why this job cannot be assigned to any vehicle:

{
  "unassigned": [
    {
      "jobId": "Job_8",
      "reasons": [
        {
          "code": "CAPACITY_CONSTRAINT",
          "description": "cannot be assigned due to capacity of vehicle",
          "details": [
            {
              "shiftIndex": 0,
              "vehicleIds": [
                "scooter_1_1",
                "scooter_1_2"
              ]
            }
          ]
        },
        {
          "code": "SKILL_CONSTRAINT",
          "description": "cannot be assigned due to required skill",
          "details": [
            {
              "shiftIndex": 0,
              "vehicleIds": [
                "car_1_1"
              ]
            }
          ]
        }
      ]
    }
  ]
}

The response shows that:

  • Scooter vehicles (scooter_1_1 and scooter_1_2) cannot take Job_8 due to CAPACITY_CONSTRAINT as both scooters have already reached their 2-unit capacity by serving Jobs 4-7
  • Car vehicle (car_1_1) cannot take Job_8 due to SKILL_CONSTRAINT because the car has the bulky_items skill, but Job_8 requires the express_delivery skill

Comparison with the default behavior

By contrast, if you submit the same problem without the multipleUnassignedReasons feature enabled, the service returns only the most frequent constraint violation:

{
  "unassigned": [
    {
      "jobId": "Job_8",
      "reasons": [
        {
          "code": "CAPACITY_CONSTRAINT",
          "description": "cannot be assigned due to capacity of vehicle"
        }
      ]
    }
  ]
}

This default response shows only the CAPACITY_CONSTRAINT, which might lead you to believe that extending capacity for a vehicle type is the only solution. However, the complete picture reveals another issue: the car vehicle cannot take this job due to the SKILL_CONSTRAINT. Without the multipleUnassignedReasons feature, you could miss that there are multiple viable paths to resolve this issue.

Resolution strategies

With the complete picture from multiple unassigned reasons, you can choose from a broader range of approaches to resolve the issue, for example:

  • Add another scooter to the fleet to handle the remaining express_delivery jobs
  • If operationally feasible, increase the capacity of existing scooters to accommodate more jobs
  • Give the car the express_delivery skill so it can serve Job_8 alongside its other jobs
  • If Job_8 can be served by any vehicle type, remove or adjust the skill requirement to allow the car to handle it
  • Allow scooters to unload cargo mid-shift and continue serving additional jobs

The key advantage of multiple unassigned reasons is that you can see more constraint violations at once, giving you more options to choose the solution that is more feasoble, given your business objectives and fleet status. You could resolve the issue by addressing either the capacity constraint on the scooters or the skill constraint on the car.

Next steps