Docs
  • Solver
  • Models
    • Field Service Routing
    • Employee Shift Scheduling
  • Platform
Try models
  • Employee Shift Scheduling
  • Employee resource constraints
  • Shift travel and locations

Employee Shift Scheduling

    • Introduction
    • Planning AI concepts
    • Metrics and optimization goals
    • Getting started with employee shift scheduling
    • Understanding the API
    • Employee shift scheduling user guide
    • Employee resource constraints
      • Employee availability
      • Employee contracts
      • Employee contracts: period rules
      • Employee contracts: shift rules
      • Fairness
      • Pairing employees
      • Shift travel and locations
    • Shift service constraints
      • Alternative shifts
      • Demand and supply
      • Mandatory and optional visits
      • Shift assignments
      • Shift sequence patterns: single day shifts
      • Shift sequence patterns: multi-day shifts
      • Shift sequence patterns: daily shift pairings
      • Skills and risk factors
    • Recommendations
    • Real-time planning
    • Time zones and Daylight Saving Time (DST)
    • New and noteworthy
    • Upgrading to the latest versions
    • Feature requests

Shift travel and locations

When employees work in multiple locations there are additional factors to take into account when assigning them to shifts.

These include:

  • How far must employees travel from their home location to the location of the shift?

  • If multiple shifts are assigned close together, how much time do employees need to travel between shifts in different locations?

  • Can travel be minimized by assigning employees to shifts closer to their home location?

  • How many different locations should an employee work at in a given period?

Employee contracts can provide limits on how far employees travel to shifts and how many locations an employee can be expected to work at during specific periods of time.

This guide explains managing shift travel and locations with the following examples:

  • Maximum travel distance for employees to their shifts

  • Minimum time between employees' shifts including travel

  • Minimize employees travel distance

  • Control when different travel configuration rules apply

  • Maximum number of locations employees can work

  • Control when different period rules apply

Prerequisites

To run the examples in this guide, you need to authenticate with a valid API key for the Employee Shift Scheduling model:

  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 Employee Shift Scheduling model.

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

1. Maximum travel distance for employees to their shifts

Ann is a full time employee who can be sent to several different locations for work. Ann’s contract stipulates the maximum distance she can travel to a shift is 25,000 meters (or 25 kilometers).

This travel configuration is defined as part of the contract that applies to Ann (and other full-time employees):

{
  "contracts": [
    {
      "id": "fullTimeContract",
      "travelConfigurations": [
        {
          "id": "maxTravel25000meters",
          "maxEmployeeToShiftTravelDistanceInMeters": 25000
        }
      ]
    }
  ]
}

travelConfigurations must include an ID.

maxEmployeeToShiftTravelDistanceInMeters defines, in meters, the maximum distance Ann can travel to a shift.

Employee’s contract and location must be provided:

{
  "employees": [
    {
      "id": "Ann",
      "contracts": [
        "fullTimeContract"
      ],
      "location": [33.73905, -84.45473]
    }
  ]
}

The location of the shifts must also be provided:

{
  "shifts": [
    {
      "id": "Mon",
      "start": "2027-02-01T08:00:00Z",
      "end": "2027-02-01T16:00:00Z",
      "location": [34.00898, -84.33827]
    }
  ]
}

The Maximum employee to shift travel distance exceeded hard constraint makes sure the maximum travel distance between the employee location and shift location does not exceed the maxEmployeeToShiftTravelDistanceInMeters limit defined in the travel configuration. If there is no available employee close enough to the shift, the shift will be left unassigned.

In the following example, there are three shifts, Ann is close enough to be assigned two of the shifts, however, the third shift is too far away and so it is left unassigned.

shift travel
  • 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/employee-scheduling/v1/schedules [email protected]
{
  "config": {
    "run": {
      "name": "Maximum travel distance for employee to their shift"
    }
  },
  "modelInput": {
    "contracts": [
      {
        "id": "fullTimeContract",
        "travelConfigurations": [
          {
            "id": "maxTravel25000meters",
            "maxEmployeeToShiftTravelDistanceInMeters": 25000
          }
        ]
      }
    ],
    "employees": [
      {
        "id": "Ann",
        "contracts": [
          "fullTimeContract"
        ],
        "location": [33.73905, -84.45473]
      }
    ],
    "shifts": [
      {
        "id": "Mon",
        "start": "2027-02-01T08:00:00Z",
        "end": "2027-02-01T16:00:00Z",
        "location": [34.00898, -84.33827]
      },
      {
        "id": "Tue",
        "start": "2027-02-02T08:00:00Z",
        "end": "2027-02-02T16:00:00Z",
        "location": [33.73225, -84.41196]
      },
      {
        "id": "Wed",
        "start": "2027-02-03T08:00:00Z",
        "end": "2027-02-03T16:00:00Z",
        "location": [33.72581, -84.41243]
      }
    ]
  }
}
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/employee-scheduling/v1/schedules/<ID>
{
  "run": {
    "id": "ID",
    "name": "Maximum travel distance for employee to their shift",
    "submitDateTime": "2025-03-11T06:44:33.798764913Z",
    "startDateTime": "2025-03-11T06:44:46.486281208Z",
    "activeDateTime": "2025-03-11T06:44:46.66916114Z",
    "completeDateTime": "2025-03-11T06:49:47.499965127Z",
    "shutdownDateTime": "2025-03-11T06:49:47.751467201Z",
    "solverStatus": "SOLVING_COMPLETED",
    "score": "0hard/-1medium/0soft",
    "tags": [
      "system.profile:default"
    ],
    "validationResult": {
      "summary": "OK"
    }
  },
  "modelOutput": {
    "shifts": [
      {
        "id": "Mon",
        "employee": null
      },
      {
        "id": "Tue",
        "employee": "Ann"
      },
      {
        "id": "Wed",
        "employee": "Ann"
      }
    ]
  },
  "inputMetrics": {
    "employees": 1,
    "shifts": 3,
    "pinnedShifts": 0
  },
  "kpis": {
    "assignedShifts": 2,
    "unassignedShifts": 1,
    "workingTimeFairnessPercentage": null,
    "disruptionPercentage": 0.0,
    "averageDurationOfEmployeesPreferencesMet": null,
    "minimumDurationOfPreferencesMetAcrossEmployees": null,
    "averageDurationOfEmployeesUnpreferencesViolated": null,
    "maximumDurationOfUnpreferencesViolatedAcrossEmployees": null,
    "activatedEmployees": 1,
    "assignedMandatoryShifts": 2,
    "assignedOptionalShifts": 0,
    "travelDistance": 8207
  }
}

modelOutput contains the schedule with Ann assigned to the shifts within 25,000 meters from her location. The shift that is further than 25,000 meters from Ann is unassigned.

inputMetrics provides a breakdown of the inputs in the input dataset.

KPIs provides the KPIs for the output including:

{
  "assignedShifts": 2,
  "unassignedShifts": 1
}

2. Minimum time between employees' shifts including travel

When employees can be assigned multiple shifts on the same day or close to each other, it’s important to give them enough time to travel between shifts.

minMinutesBetweenShiftsInDifferentLocations sets a limit in minutes for the minimum time between shifts in different locations and is defined in travel configurations:

{
  "contracts": [
    {
      "id": "fullTimeContract",
      "travelConfigurations": [
        {
          "id": "maxTravel25000meters",
          "maxEmployeeToShiftTravelDistanceInMeters": 25000,
          "minMinutesBetweenShiftsInDifferentLocations": 60
        }
      ]
    }
  ]
}

Employee contract and location must be provided:

{
  "employees": [
    {
      "id": "Ann",
      "contracts": [
        "fullTimeContract"
      ],
      "location": [33.73905, -84.45473]
    }
  ]
}

The location of the shifts must also be provided:

{
  "shifts": [
    {
      "id": "Mon",
      "start": "2027-02-01T08:00:00Z",
      "end": "2027-02-01T16:00:00Z",
      "location": [34.00898, -84.33827]
    }
  ]
}

The Minimum time between shifts including travel not met hard constraint makes sure the minimum time between shifts at different locations is not less than the minMinutesBetweenShiftsInDifferentLocations limit defined in the travel configuration. If there is no employee to cover the shift without breaking this constraint, the shift will be left unassigned.

In the following example, there are two shifts, Ann could be assigned both of the shifts, however, the second shift occurs 30 minutes after the first shift ends.

Even though the shift is within the 25,000 meter travel limit, the minMinutesBetweenShiftsInDifferentLocations is 60 (minutes), and assigning the shift to Ann would break the constraint, so the shift is left unassigned.

  • 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/employee-scheduling/v1/schedules [email protected]
{
  "config": {
    "run": {
      "name": "Minimum time between shifts in different locations"
    }
  },
  "modelInput": {
    "contracts": [
      {
        "id": "fullTimeContract",
        "travelConfigurations": [
          {
            "id": "maxDistanceAndMinutesBetweenLocations",
            "maxEmployeeToShiftTravelDistanceInMeters": 25000,
            "minMinutesBetweenShiftsInDifferentLocations": 60
          }
        ]
      }
    ],
    "employees": [
      {
        "id": "Ann",
        "contracts": [
          "fullTimeContract"
        ],
        "location": [33.73905, -84.45473]
      }
    ],
    "shifts": [
      {
        "id": "Mon AM",
        "start": "2027-02-01T08:00:00Z",
        "end": "2027-02-01T12:00:00Z",
        "location": [33.87565, -84.39817]
      },
      {
        "id": "Mon PM",
        "start": "2027-02-01T12:30:00Z",
        "end": "2027-02-01T16:30:00Z",
        "location": [33.73225, -84.41196]
      }
    ]
  }
}
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/employee-scheduling/v1/schedules/<ID>
{
  "run": {
    "id": "ID",
    "name": "Minimum time between shifts in different locations",
    "submitDateTime": "2025-03-07T08:07:02.265825001Z",
    "startDateTime": "2025-03-07T08:07:14.381708957Z",
    "activeDateTime": "2025-03-07T08:07:14.583894825Z",
    "completeDateTime": "2025-03-07T08:12:15.096154763Z",
    "shutdownDateTime": "2025-03-07T08:12:15.375136522Z",
    "solverStatus": "SOLVING_COMPLETED",
    "score": "0hard/-1medium/0soft",
    "tags": [
      "system.profile:default"
    ],
    "validationResult": {
      "summary": "OK"
    }
  },
  "modelOutput": {
    "shifts": [
      {
        "id": "Mon AM",
        "employee": "Ann"
      },
      {
        "id": "Mon PM",
        "employee": null
      }
    ]
  },
  "inputMetrics": {
    "employees": 1,
    "shifts": 2,
    "pinnedShifts": 0
  },
  "kpis": {
    "assignedShifts": 1,
    "unassignedShifts": 1,
    "workingTimeFairnessPercentage": null,
    "disruptionPercentage": 0.0,
    "averageDurationOfEmployeesPreferencesMet": null,
    "minimumDurationOfPreferencesMetAcrossEmployees": null,
    "averageDurationOfEmployeesUnpreferencesViolated": null,
    "maximumDurationOfUnpreferencesViolatedAcrossEmployees": null,
    "activatedEmployees": 1,
    "assignedMandatoryShifts": 1,
    "assignedOptionalShifts": 0,
    "travelDistance": 16063
  }
}

modelOutput contains the schedule with Ann assigned to the "mon am". The "mon pm" shift is left unassigned.

inputMetrics provides a breakdown of the inputs in the input dataset.

KPIs provides the KPIs for the output including:

{
  "assignedShifts": 1,
  "unassignedShifts": 1
}

3. Minimize employees travel distance

When either maxEmployeeToShiftTravelDistanceInMeters or minMinutesBetweenShiftsInDifferentLocations are defined, the Minimize travel distance soft constraint will be invoked to minimize travel distance.

In the following example, there is one shift, both Ann and Beth could be assigned the shift, however, Beth’s location is closer to the shift’s location, and so Beth is assigned the shift to minimize the travel distance.

  • 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/employee-scheduling/v1/schedules [email protected]
{
  "config": {
    "run": {
      "name": "Minimize travel distance"
    }
  },
  "modelInput": {
    "contracts": [
      {
        "id": "fullTimeContract",
        "travelConfigurations": [
          {
            "id": "maxDistanceAndMinutesBetweenLocations",
            "maxEmployeeToShiftTravelDistanceInMeters": 25000,
            "minMinutesBetweenShiftsInDifferentLocations": 60
          }
        ]
      }
    ],
    "employees": [
      {
        "id": "Ann",
        "contracts": [
          "fullTimeContract"
        ],
        "location": [33.73905, -84.45473]
      },
      {
        "id": "Beth",
        "contracts": [
          "fullTimeContract"
        ],
        "location": [34.00316, -84.34836]
      }
    ],
    "shifts": [
      {
        "id": "Mon",
        "start": "2027-02-01T08:00:00Z",
        "end": "2027-02-01T16:00:00Z",
        "location": [34.00898, -84.33827]
      }
    ]
  }
}
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/employee-scheduling/v1/schedules/<ID>
{
  "run": {
    "id": "1a7acfcf-e4de-4b88-ae3c-dccc9bba8331",
    "name": "Minimize travel distance",
    "submitDateTime": "2025-03-07T08:50:18.588960282Z",
    "startDateTime": "2025-03-07T08:50:30.381030612Z",
    "activeDateTime": "2025-03-07T08:50:30.505351937Z",
    "completeDateTime": "2025-03-07T08:50:38.184960096Z",
    "shutdownDateTime": "2025-03-07T08:50:38.550701781Z",
    "solverStatus": "SOLVING_COMPLETED",
    "score": "0hard/0medium/0soft",
    "tags": [
      "system.profile:default"
    ],
    "validationResult": {
      "summary": "OK"
    }
  },
  "modelOutput": {
    "shifts": [
      {
        "id": "Mon",
        "employee": "Beth"
      }
    ]
  },
  "inputMetrics": {
    "employees": 2,
    "shifts": 1,
    "pinnedShifts": 0
  },
  "kpis": {
    "assignedShifts": 1,
    "unassignedShifts": 0,
    "workingTimeFairnessPercentage": null,
    "disruptionPercentage": 0.0,
    "averageDurationOfEmployeesPreferencesMet": null,
    "minimumDurationOfPreferencesMetAcrossEmployees": null,
    "averageDurationOfEmployeesUnpreferencesViolated": null,
    "maximumDurationOfUnpreferencesViolatedAcrossEmployees": null,
    "activatedEmployees": 1,
    "assignedMandatoryShifts": 1,
    "assignedOptionalShifts": 0,
    "travelDistance": 1133
  }
}

modelOutput contains the schedule with Beth assigned to the shift because her location is closer.

inputMetrics provides a breakdown of the inputs in the input dataset.

KPIs provides the KPIs for the output including:

{
  "assignedShifts": 1,
  "travelDistance": 1133
}

4. Control when different travel configuration rules apply

Travel configuration rules can be limited in the following ways:

  1. Validity time spans can limit when the rules apply.

  2. Shifts with specific tags can be included or excluded from the rules.

4.1. Rule Validity Date Time Span

To define a time span when the rule is applied, add ruleValidityDateTimeSpan with start and end times.

If not provided, the rule is always valid in the period configured.

{
  "ruleValidityDateTimeSpan": {
    "start": "2027-02-01T00:00:00Z",
    "end": "2027-02-08T00:00:00Z"
  }
}

4.1.1. Example rule validity date time span

{
  "travelConfigurations": [
    {
      "id": "maxTravel25000meters",
      "maxEmployeeToShiftTravelDistanceInMeters": 25000,
      "ruleValidityDateTimeSpan": {
        "start": "2027-02-01T00:00",
        "end": "2027-02-08T00:00"
      }
    }
  ]
}

4.2. Include or exclude shifts with shift tags

Shifts with specific tags can be included or excluded by the rule. Tags are defined in shifts:

{
  "shifts": [
    {
      "id": "2027-02-01",
      "start": "2027-02-01T09:00:00Z",
      "end": "2027-02-01T17:00:00Z",
      "tags": ["ICU"]
    }
  ]
}

Use includeShiftTags to include shifts with specific tags or excludeShiftTags to exclude shifts with specific tags.

shiftTagMatches can be set to ALL or ANY. The default behavior for shiftTagMatches is ALL, and if omitted, the default ALL will be used.

4.2.1. Include shift tags

{
  "includeShiftTags": ["ICU", "Cardiology"],
  "shiftTagMatches": "ALL"
}

With shiftTagMatches set to ALL, all tags defined by the rule’s includeShiftTags attribute must be present in the shift. With shiftTagMatches set to ANY, at least one tag defined by the rule’s includeShiftTags attribute must be present in the shift.

4.2.2. Exclude shift tags

{
  "excludeShiftTags": ["Part-time", "Weekend"],
  "shiftTagMatches": "ALL"
}

With shiftTagMatches set to ALL, all tags defined by the rule’s excludeShiftTags attribute cannot be present in the shift. This is useful when you want to exclude things in combination with each other. For instance, excluding the shift tags Part-time and Weekend with shiftTagMatches set to All, would exclude shifts that include the tags Part-time and Weekend from the rule. Shifts tagged only Part-time or only Weekend will not be excluded.

With shiftTagMatches set to ANY, any of the tags defined by the rule’s excludeShiftTags attribute cannot be present in the shift. This is useful when you need to exclude tags regardless of their relationship to other tags. For instance, excluding the shift tags Part-time and Weekend with shiftTagMatches set to ANY, would exclude any shift that includes the tags Part-time or Weekend, whether they occur together or not.

The rule can define either includeShiftTags or excludeShiftTags, but not both.

4.2.3. Example include shift tags

{
  "travelConfigurations": [
    {
      "id": "maxTravel25000meters",
      "maxEmployeeToShiftTravelDistanceInMeters": 25000,
      "includeShiftTags": ["ICU"],
      "shiftTagMatches": "ALL"
    }
  ]
}

4.3. Exclude shift tag types

Shift tags of a certain type can be excluded with excludeMatchingShiftTagTypes.

{
  "travelConfigurations": [
    {
      "id": "maxTravel25000meters",
      "maxEmployeeToShiftTravelDistanceInMeters": 25000,
      "excludeMatchingShiftTagTypes": ["department"]
    }
  ]
}

In the following example, the tags department a and department b are of tag type department.

{
  "tagTypes": [
    {
        "id": "department"
    }
  ],
  "tags": [
    {
        "id": "department a",
        "tagType": "department"
    },
    {
        "id": "department b",
        "tagType": "department"
    }
  ]
}

If the tag type department is declared in excludeMatchingShiftTagTypes, any shift with a tag that has a tag type department will be excluded.

5. Maximum number of locations employees can work

Period rules can stipulate how many different locations employees work at in a given period.

Locations must be added for each shift:

{
  "shifts": [
    {
      "id": "Mon",
      "start": "2027-02-01T08:00:00Z",
      "end": "2027-02-01T16:00:00Z",
      "location": [33.73857, -84.37162]
    }
  ]
}

Ann is a full-time employee who works at various locations. The following contract includes a period rule that states Ann can work at a maximum of 2 locations per week.

Period rules are defined in contracts. Multiple periodRules can be defined.

{
  "contracts": [
    {
      "id": "fullTimeContract",
      "periodRules": [
        {
          "id": "Max2Locations",
          "period": "WEEK",
          "locationsWorkedMax": 2,
          "satisfiability": "REQUIRED"
        }
      ]
    }
  ]
}

A periodRule must include an id.

period sets the period the rule applies to, for instance, DAY, WEEK, MONTH, SCHEDULE.

In this case, the Max2Locations rule specifies locationsWorkedMax is 2 per WEEK period.

The satisfiability of this rule is REQUIRED, which invokes the hard constraint Locations worked per period not in required range for employee.

With the satisfiability set to REQUIRED, Ann will not be assigned shifts in more than 2 locations over a period of 1 week. If there are no other employees available who could be assigned the shift in the third location, the shift will be left unassigned.

multiple locations

The satisfiability can also be set to PREFERRED, which invokes the soft constraint Locations worked per period not in preferred range for employee. Ann might be assigned to shifts in more than 2 locations over a period of 1 week, but this constraint adds a soft penalty to the run score, incentivizing Timefold to assign Ann shifts in only 2 locations, and to assign other shifts to other employees if possible.

multiple locations 2

In the following example, Ann has a contract with a period rule that permits a maximum of 2 locations per week. The satisfiability is REQUIRED. There are 3 shifts, each at a different location. Ann is the only employee. She is assigned 2 of the shifts and the third shift is left unassigned.

  • 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/employee-scheduling/v1/schedules [email protected]
{
  "config": {
    "run": {
      "name": "Multiple shift locations example - required"
    }
  },
  "modelInput": {
    "contracts": [
      {
        "id": "fullTimeContract",
        "periodRules": [
          {
            "id": "Max2Locations",
            "period": "WEEK",
            "locationsWorkedMax": 2,
            "satisfiability": "REQUIRED"
          }
        ]
      }
    ],
    "employees": [
      {
        "id": "Ann",
        "contracts": [
          "fullTimeContract"
        ]
      }
    ],
    "shifts": [
      {
        "id": "Mon",
        "start": "2027-02-01T08:00:00Z",
        "end": "2027-02-01T16:00:00Z",
        "location": [33.73857, -84.37162]
      },
      {
        "id": "Tue",
        "start": "2027-02-02T08:00:00Z",
        "end": "2027-02-02T16:00:00Z",
        "location": [33.77922, -84.44547]
      },
      {
        "id": "Wed",
        "start": "2027-02-03T08:00:00Z",
        "end": "2027-02-03T16:00:00Z",
        "location": [33.70746, -84.45918]
      }
    ]
  }
}
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/employee-scheduling/v1/schedules/<ID>
{
  "run": {
    "id": "ID",
    "name": "Multiple shift locations example - required",
    "submitDateTime": "2025-03-03T04:58:04.281420236Z",
    "startDateTime": "2025-03-03T04:58:17.925453698Z",
    "activeDateTime": "2025-03-03T04:58:18.012120227Z",
    "completeDateTime": "2025-03-03T05:03:18.984062782Z",
    "shutdownDateTime": "2025-03-03T05:03:19.203818807Z",
    "solverStatus": "SOLVING_COMPLETED",
    "score": "0hard/-1medium/0soft",
    "tags": [
      "system.profile:default"
    ],
    "validationResult": {
      "summary": "OK"
    }
  },
  "modelOutput": {
    "shifts": [
      {
        "id": "Mon",
        "employee": "Ann"
      },
      {
        "id": "Tue",
        "employee": "Ann"
      },
      {
        "id": "Wed",
        "employee": null
      }
    ]
  },
  "inputMetrics": {
    "employees": 1,
    "shifts": 3,
    "pinnedShifts": 0
  },
  "kpis": {
    "assignedShifts": 2,
    "unassignedShifts": 1,
    "workingTimeFairnessPercentage": null,
    "disruptionPercentage": 0.0,
    "averageDurationOfEmployeesPreferencesMet": null,
    "minimumDurationOfPreferencesMetAcrossEmployees": null,
    "averageDurationOfEmployeesUnpreferencesViolated": null,
    "maximumDurationOfUnpreferencesViolatedAcrossEmployees": null
  }
}

modelOutput contains the schedule with Ann assigned to 2 shifts in 2 locations. The third shift is left unassigned.

inputMetrics provides a breakdown of the inputs in the input dataset.

KPIs provides the KPIs for the output including:

{
  "assignedShifts": 2,
  "unassignedShifts": 1
}

6. Control when different period rules apply

Period rules can be limited in the following ways:

  1. Validity Date time spans can limit when the rules apply.

  2. Shifts with specific tags can be included or excluded from the rules.

6.1. Rule Validity Date Time Span

To define a time span when the rule is applied, add ruleValidityDateTimeSpan with start and end times.

If not provided, the rule is always valid in the period configured.

{
  "ruleValidityDateTimeSpan": {
    "start": "2027-02-01T00:00:00Z",
    "end": "2027-02-08T00:00:00Z"
  }
}

6.1.1. Example rule validity date time span

{
  "periodRules": [
      {
        "id": "Max2Locations",
        "period": "WEEK",
        "locationsWorkedMax": 2,
        "satisfiability": "REQUIRED",
        "ruleValidityDateTimeSpan": {
          "start": "2027-02-01T00:00",
          "end": "2027-02-08T00:00"
      }
    }
  ]
}

6.2. Include or exclude shifts with shift tags

Shifts with specific tags can be included or excluded by the rule. Tags are defined in shifts:

{
  "shifts": [
    {
      "id": "2027-02-01",
      "start": "2027-02-01T09:00:00Z",
      "end": "2027-02-01T17:00:00Z",
      "tags": ["ICU"]
    }
  ]
}

Use includeShiftTags to include shifts with specific tags or excludeShiftTags to exclude shifts with specific tags.

shiftTagMatches can be set to ALL or ANY. The default behavior for shiftTagMatches is ALL, and if omitted, the default ALL will be used.

6.2.1. Include shift tags

{
  "includeShiftTags": ["ICU", "Cardiology"],
  "shiftTagMatches": "ALL"
}

With shiftTagMatches set to ALL, all tags defined by the rule’s includeShiftTags attribute must be present in the shift. With shiftTagMatches set to ANY, at least one tag defined by the rule’s includeShiftTags attribute must be present in the shift.

6.2.2. Exclude shift tags

{
  "excludeShiftTags": ["Part-time", "Weekend"],
  "shiftTagMatches": "ALL"
}

With shiftTagMatches set to ALL, all tags defined by the rule’s excludeShiftTags attribute cannot be present in the shift. This is useful when you want to exclude things in combination with each other. For instance, excluding the shift tags Part-time and Weekend with shiftTagMatches set to All, would exclude shifts that include the tags Part-time and Weekend from the rule. Shifts tagged only Part-time or only Weekend will not be excluded.

With shiftTagMatches set to ANY, any of the tags defined by the rule’s excludeShiftTags attribute cannot be present in the shift. This is useful when you need to exclude tags regardless of their relationship to other tags. For instance, excluding the shift tags Part-time and Weekend with shiftTagMatches set to ANY, would exclude any shift that includes the tags Part-time or Weekend, whether they occur together or not.

The rule can define either includeShiftTags or excludeShiftTags, but not both.

6.2.3. Example include shift tags

{
  "periodRules": [
      {
        "id": "Max2Locations",
        "period": "WEEK",
        "locationsWorkedMax": 2,
        "satisfiability": "REQUIRED",
        "excludeShiftTags": ["ICU"],
        "shiftTagMatches": "ALL"
      }
    }
  ]
}

Next

  • See the Employee shift scheduling user guide

  • Understand the constraints of the Employee Shift Scheduling model.

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

  • Manage schedules with Time zones and Daylight Saving Time (DST) changes.

  • Manage Employee availability.

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