{
  "resources": [
    {
      "id": "v1",
      "name": "Vehicle 1",
      "shifts": [
        {
          "id": "shift1",
          "from": "2024-03-15T08:00:00Z",
          "to": "2024-03-15T18:00:00Z"
        }
      ]
    }
  ],
  "jobs": [
    {
      "id": "pickup-1",
      "name": "Pickup 1",
      "location": {"lat": 52.5230, "lon": 13.4010}
    },
    {
      "id": "delivery-1",
      "name": "Delivery 1",
      "location": {"lat": 52.5170, "lon": 13.3880},
      "relations": [
        {
          "type": "SEQUENCE",
          "jobs": ["pickup-1", "delivery-1"],
          "minTimeInterval": 0,
          "maxTimeInterval": 3600
        }
      ]
    }
  ]
}

Job Relations

Job relations allow you to define dependencies and constraints between multiple jobs in your VRP optimization. These relations ensure that certain business rules are respected when the solver creates routes.

Overview

Job relations are defined using the relations array within each job. Each relation specifies:
  • A type indicating the constraint type
  • A list of jobs that are part of this relation
  • Additional parameters specific to each relation type
{
  "resources": [
    {
      "id": "v1",
      "name": "Vehicle 1",
      "shifts": [
        {
          "id": "shift1",
          "from": "2024-03-15T08:00:00Z",
          "to": "2024-03-15T18:00:00Z"
        }
      ]
    }
  ],
  "jobs": [
    {
      "id": "pickup-1",
      "name": "Pickup 1",
      "location": {"lat": 52.5230, "lon": 13.4010}
    },
    {
      "id": "delivery-1",
      "name": "Delivery 1",
      "location": {"lat": 52.5170, "lon": 13.3880},
      "relations": [
        {
          "type": "SEQUENCE",
          "jobs": ["pickup-1", "delivery-1"],
          "minTimeInterval": 0,
          "maxTimeInterval": 3600
        }
      ]
    }
  ]
}

Available Relation Types

SEQUENCE

Jobs must be visited in the specified order, but other jobs can be scheduled between them.
{
  "type": "SEQUENCE",
  "jobs": ["job-1", "job-2", "job-3"],
  "minTimeInterval": 300,
  "maxTimeInterval": 7200
}
The time intervals specify the gap between when one job ends and the next job begins. This is useful for ensuring adequate travel time or preparation between related tasks.
Use Cases:
  • Multi-stop deliveries where order matters
  • Service appointments with dependencies
  • Workflows requiring specific sequences

DIRECT_SEQUENCE

Jobs must be visited in the exact order with no other jobs scheduled between them.
{
  "type": "DIRECT_SEQUENCE",
  "jobs": ["pickup", "delivery"]
}
Use Cases:
  • Immediate transfer operations
  • Time-critical handoffs
  • Continuous processes that cannot be interrupted

SAME_TRIP

All jobs in the relation must be assigned to the same route/trip.
{
  "type": "SAME_TRIP",
  "jobs": ["pickup-1", "pickup-2", "delivery-hub"],
  "partialPlanning": false
}
Use Cases:
  • Consolidation requirements
  • Grouped deliveries
  • Ensuring related tasks stay together

SAME_TIME

Jobs must start or end within a specified time window of each other, with optional minimum time intervals. Optionally enforces resource compatibility constraints to ensure only authorized resources work together.
{
  "type": "SAME_TIME",
  "jobs": ["install-1", "install-2"],
  "minTimeInterval": 300,
  "maxTimeInterval": 600,
  "maxWaitingTime": 3600,
  "timeInterval": "FROM_ARRIVAL",
  "enforceCompatibility": true
}
When enforceCompatibility is true, the solver ensures that only compatible resources work together on SAME_TIME related jobs. Resources must define their compatible partners using the compatibleResources field.Minimum Time Interval Behavior: When minTimeInterval is specified, the last job in the jobs array must arrive/depart at least minTimeInterval seconds after all other jobs. This is useful for staggered arrivals or ensuring adequate spacing between operations.
Use Cases:
  • Synchronized operations
  • Team installations
  • Coordinated services
  • Healthcare teams requiring authorized personnel
  • Security operations with supervision requirements
  • Training scenarios with approved mentors
  • Staggered arrivals at loading docks
  • Sequential processing with minimum gaps
Example with Minimum Time Interval:
{
  "type": "SAME_TIME",
  "jobs": ["truck-1-arrival", "truck-2-arrival", "dock-preparation"],
  "minTimeInterval": 300,
  "maxTimeInterval": 600,
  "maxWaitingTime": 3600,
  "timeInterval": "FROM_ARRIVAL"
}
In this example, if “dock-preparation” is the last job in the array, it will arrive at least 300 seconds (5 minutes) after both truck arrivals, ensuring the dock is ready when preparation begins.

SAME_RESOURCE

All jobs must be assigned to the same resource/vehicle (can be on different days).
{
  "type": "SAME_RESOURCE",
  "jobs": ["service-1", "service-2", "service-3"],
  "resource": "technician-123"
}
Use Cases:
  • Customer preference for specific technician
  • Specialized equipment requirements
  • Continuity of service

PICKUP_AND_DELIVERY

Enforces that pickup happens before delivery, with both assigned to the same resource.
{
  "type": "PICKUP_AND_DELIVERY",
  "jobs": ["pickup-order-123", "delivery-order-123"]
}
Use Cases:
  • Courier services
  • Equipment rental returns
  • Any pickup/delivery pair

NEIGHBOR

Jobs should be scheduled consecutively by the same resource (flexible order).
{
  "type": "NEIGHBOR",
  "jobs": ["stop-1", "stop-2", "stop-3"]
}
Use Cases:
  • Minimizing travel between related stops
  • Clustered service areas
  • Efficiency optimization

SAME_DAY

All jobs must be completed on the same calendar day.
{
  "type": "SAME_DAY",
  "jobs": ["morning-pickup", "afternoon-delivery"]
}
Use Cases:
  • Same-day delivery services
  • Daily batch processing
  • Time-sensitive operations

GROUP_SEQUENCE

Enforces ordering between groups of jobs based on tags.
{
  "type": "GROUP_SEQUENCE",
  "tags": ["priority-high", "priority-normal", "priority-low"]
}
Use Cases:
  • Priority-based scheduling
  • Phased operations
  • Category-based ordering

FIRST_JOB

Ensures a job is scheduled as the first stop for a resource.
{
  "type": "FIRST_JOB",
  "jobs": ["warehouse-pickup"]
}
Use Cases:
  • Morning warehouse pickups
  • Mandatory first stops
  • Initial setup requirements

Complete Example

Here’s a comprehensive example showing multiple relation types working together:
{
  "resources": [
    {
      "id": "vehicle-1",
      "name": "Vehicle 1",
      "shifts": [
        {
          "id": "shift1",
          "start": {
            "location": {"lat": 52.5200, "lon": 13.4050},
            "time": "2024-03-15T08:00:00Z"
          },
          "end": {
            "location": {"lat": 52.5200, "lon": 13.4050},
            "time": "2024-03-15T18:00:00Z"
          },
          "capacity": [1000]
        }
      ]
    }
  ],
  "jobs": [
    {
      "id": "warehouse-pickup",
      "name": "Warehouse Pickup",
      "location": {"lat": 52.5200, "lon": 13.4050},
      "serviceDurationInSeconds": 1800,
      "relations": [
        {
          "type": "FIRST_JOB",
          "jobs": ["warehouse-pickup"]
        }
      ]
    },
    {
      "id": "customer-pickup-1",
      "name": "Customer Pickup 1",
      "location": {"lat": 52.5230, "lon": 13.4010},
      "serviceDurationInSeconds": 600,
      "activity": "PICKUP",
      "relations": [
        {
          "type": "PICKUP_AND_DELIVERY",
          "jobs": ["customer-pickup-1", "customer-delivery-1"]
        },
        {
          "type": "SAME_DAY",
          "jobs": ["customer-pickup-1", "customer-delivery-1", "customer-pickup-2", "customer-delivery-2"]
        }
      ]
    },
    {
      "id": "customer-delivery-1",
      "name": "Customer Delivery 1",
      "location": {"lat": 52.5170, "lon": 13.3880},
      "serviceDurationInSeconds": 600,
      "activity": "DELIVERY"
    },
    {
      "id": "customer-pickup-2",
      "name": "Customer Pickup 2",
      "location": {"lat": 52.5280, "lon": 13.4120},
      "serviceDurationInSeconds": 600,
      "activity": "PICKUP",
      "tags": [{"name": "express"}],
      "relations": [
        {
          "type": "PICKUP_AND_DELIVERY",
          "jobs": ["customer-pickup-2", "customer-delivery-2"]
        },
        {
          "type": "SEQUENCE",
          "jobs": ["customer-pickup-2", "customer-delivery-2"],
          "maxTimeInterval": 3600
        }
      ]
    },
    {
      "id": "customer-delivery-2",
      "name": "Customer Delivery 2",
      "location": {"lat": 52.5090, "lon": 13.3760},
      "serviceDurationInSeconds": 600,
      "activity": "DELIVERY",
      "tags": [{"name": "express"}]
    },
    {
      "id": "return-to-warehouse",
      "name": "Return to Warehouse",
      "location": {"lat": 52.5200, "lon": 13.4050},
      "serviceDurationInSeconds": 1800,
      "windows": [["2024-03-15T16:00:00Z", "2024-03-15T18:00:00Z"]]
    }
  ]
}

Resource Compatibility

Resource compatibility allows you to control which resources can work together on SAME_TIME related jobs. This is essential for scenarios requiring specific team compositions, authorization levels, or supervision requirements.

Configuring Resource Compatibility

Add the compatibleResources field to your resource definitions:
{
  "resources": [
    {
      "id": "Dr-Smith",
      "name": "Dr Smith",
      "compatibleResources": ["Nurse-John", "Nurse-Mary"],
      "shifts": [...]
    },
    {
      "id": "Nurse-John",
      "name": "Nurse John",
      "compatibleResources": ["Dr-Smith", "Dr-Jones"],
      "shifts": [...]
    }
  ]
}

Compatibility Rules

  1. Mutual Compatibility: For two resources to work together, both must list each other
  2. One-Way Compatibility: Useful for supervision scenarios where only one resource needs authorization
  3. Open Compatibility: Resources without compatibleResources can work with anyone

Example: Healthcare Team

{
  "resources": [
    {
      "id": "dr-smith",
      "name": "Dr. Smith",
      "compatibleResources": ["nurse-john", "nurse-mary"],
      "shifts": [
        {
          "id": "shift1",
          "from": "2024-03-15T08:00:00Z",
          "to": "2024-03-15T17:00:00Z"
        }
      ]
    },
    {
      "id": "nurse-john",
      "name": "Nurse John",
      "compatibleResources": ["dr-smith"],
      "shifts": [
        {
          "id": "shift1",
          "from": "2024-03-15T08:00:00Z",
          "to": "2024-03-15T17:00:00Z"
        }
      ]
    },
    {
      "id": "nurse-mary",
      "name": "Nurse Mary",
      "compatibleResources": ["dr-smith"],
      "shifts": [
        {
          "id": "shift1",
          "from": "2024-03-15T08:00:00Z",
          "to": "2024-03-15T17:00:00Z"
        }
      ]
    }
  ],
  "jobs": [
    {
      "id": "patient-visit-1",
      "name": "Patient Visit 1",
      "location": {"lat": 52.5230, "lon": 13.4010},
      "serviceDurationInSeconds": 1800,
      "relations": [
        {
          "type": "SAME_TIME",
          "jobs": ["patient-visit-1", "patient-visit-2"],
          "maxTimeInterval": 300,
          "enforceCompatibility": true
        }
      ]
    },
    {
      "id": "patient-visit-2",
      "name": "Patient Visit 2",
      "location": {"lat": 52.5240, "lon": 13.4020},
      "serviceDurationInSeconds": 1800
    }
  ]
}

Example: Security Team with Supervision

{
  "resources": [
    {
      "id": "senior-guard",
      "name": "Senior Guard",
      "compatibleResources": ["junior-guard-1", "junior-guard-2", "senior-guard-2"],
      "shifts": [...]
    },
    {
      "id": "junior-guard-1",
      "name": "Junior Guard 1",
      "compatibleResources": ["senior-guard"],
      "shifts": [...]
    },
    {
      "id": "trainee",
      "name": "Trainee",
      "compatibleResources": [],  // Can only work with senior guard (one-way)
      "shifts": [...]
    }
  ],
  "jobs": [
    {
      "id": "security-check-1",
      "name": "Security Check 1",
      "location": {"lat": 52.5230, "lon": 13.4010},
      "relations": [
        {
          "type": "SAME_TIME",
          "jobs": ["security-check-1", "security-check-2"],
          "enforceCompatibility": true
        }
      ]
    }
  ]
}
Important Considerations:
  • Compatibility violations result in hard constraints (infeasible solutions)
  • Only applies when enforceCompatibility: true on SAME_TIME relations
  • Overly restrictive compatibility can lead to no feasible solutions
  • Always validate your compatibility configuration with test requests

Best Practices

When using job relations, consider these best practices:
  1. Start Simple: Begin with basic relations and add complexity as needed
  2. Avoid Over-constraining: Too many relations can make the problem infeasible
  3. Use Partial Planning: Enable partialPlanning for relations that might be too restrictive
  4. Test Incrementally: Add relations one at a time to identify conflicts
  5. Monitor Performance: Complex relations can increase solve time
  6. Resource Compatibility: When using enforceCompatibility, ensure resources have properly configured compatibleResources lists
  7. Validate Compatibility: Test your compatibility configuration with sample requests before production use

Troubleshooting

Common issues when using job relations:
  • Infeasible Solutions: Too many conflicting relations
  • Long Solve Times: Complex relation networks require more computation
  • Unexpected Results: Relations might interact in non-obvious ways
Use the /v2/vrp/jobs/{id}/explanation endpoint to understand why certain relations couldn’t be satisfied in your solution.
  • Time Windows - Combine with relations for complex scheduling
  • Multi-Day Planning - Use SAME_DAY and SAME_RESOURCE across days
  • Capacities - Relations respect capacity constraints
  • Resources - Configure resource compatibility for team-based operations