Docs
  • Solver
  • Models
    • Field Service Routing
    • Employee Shift Scheduling
  • Platform
Try models
  • Employee Shift Scheduling
  • Employee resource constraints
  • Shift rotations and patterns
  • Multi-day shift sequence patterns

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
      • Work limits
        • Work limits
        • Minutes worked per period
        • Minutes worked in a rolling window
        • Minutes logged per period
        • Days worked per period
        • Days worked in a rolling window
        • Consecutive days worked
        • Shifts worked per period
        • Shifts worked in a rolling window
        • Weekend minutes worked per period
        • Weekends worked per period
        • Weekends worked in a rolling window
        • Consecutive weekends worked
      • Time off
        • Time off
        • Days off per period
        • Consecutive days off per period
        • Consecutive days off in a rolling window
        • Consecutive minutes off in a rolling window
        • Shifts to avoid close to day off requests
      • Shift rotations and patterns
        • Shift rotations and patterns
        • Shift rotations
        • Single day shift sequence patterns
        • Multi-day shift sequence patterns
        • Daily shift pairings
        • Overlapping shifts
        • Shift start times differences
        • Minutes between shifts
      • Shift type diversity
        • Shift type diversity
        • Shift types worked per period
        • Unique tags per period
      • Fairness
        • Fairness
        • Balance time worked
        • Balance shift count
      • Pairing employees
      • Shift travel and locations
    • Shift service constraints
      • Alternative shifts
      • Cost management
      • Demand-based scheduling
      • Mandatory and optional shifts
      • Shift assignments
      • Skills and risk factors
    • Recommendations
    • Real-time planning
    • Time zones and Daylight Saving Time (DST)
    • Changelog
    • Upgrading to the latest versions
    • Feature requests

Multi-day shift sequence patterns

Employees often work across multiple days that must be scheduled in specific patterns.

Multi-day shift patterns set rules about which combinations of shifts are preferred and which ones should be avoided or are prohibited altogether.

For instance, it might be preferable to have employees work a specific number of shifts in a row, followed by a specific number of days off or employees might work a specific number of shifts types followed by a specific number of shifts of a different type.

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

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

1. Define multi-day shift sequences

Multi-day shift sequence pattern rules are configured in contracts:

{
  "contracts": [
    {
      "id": "shiftContract",
      "multiDayShiftSequencePatternRules": [
        {
          "id": "2DaysOn2DaysOff",
          "pattern": [
            {
              "type": "ON",
              "includeShiftTags": [
                "Day"
              ],
              "shiftTagMatches": "ALL"
            },
            {
              "type": "ON",
              "includeShiftTags": [
                "Day"
              ],
              "shiftTagMatches": "ALL"
            },
            {
              "type": "OFF"
            },
            {
              "type": "OFF"
            }
          ],
          "satisfiability": "PREFERRED",
          "weight": 1
        }
      ]
    }
  ]
}

multiDayShiftSequencePatternRules must include an ID.

pattern specifies the shifts that are included or excluded in the pattern based on the shift’s tags and the order in which the shifts occur.

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

Further information about including or excluding 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": ["Part-time"]
    }
  ]
}

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.

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

{
  "includeShiftTags": ["Part-time", "Weekend"],
  "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.

{
  "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, will 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, will exclude any shift that includes the tags Part-time or Weekend, whether they occur together or not.

weight can be used to make the pattern more important than other patterns. For example, setting the weight to 2 will make the reward/penalty of this pattern’s elements count twice when matched.

shiftMatches is used when there are multiple shifts on a single day, you can configure the pattern to match ALL the shifts, or ANY of the shifts using the shiftMatches attribute.

{
  "pattern": [
    {
      "type": "ON",
      "includeShiftTags": [
        "Day"
      ],
      "shiftTagMatches": "ALL",
      "shiftMatches": "ANY"
    }
  ]
}

When a pattern starts or ends with OFF, you must specify the planning window interval to ensure the pattern matches properly throughout the period you are planning for.

{
  "planningWindow": {
    "start": "2027-02-01T00:00:00Z",
    "end": "2027-02-09T00:00:00Z"
  }
}

Each employee must specify which contracts apply to them:

{
  "employees": [
    {
      "id": "Ann",
      "contracts": [
        "partTimeContract"
      ]
    }
  ]
}

Multi-day patterns can be defined without using tags, however, if the pattern uses tags to match specific shift types, shifts must also include tags:

{
  "id": "Mon 0300",
  "start": "2027-02-01T03:00:00Z",
  "end": "2027-02-01T07:00:00Z",
  "tags": ["Early"]
}

The satisfiability of the pattern can be PREFERRED, PROHIBITED, or UNPREFERRED.

2. Preferred multi-day shift sequence

When the satisfiability of the rule is PREFERRED, the Employee works preferred multi day shift sequence pattern soft constraint is invoked, which adds a soft reward to the run score when employees are assigned shifts that match the pattern. Timefold is incentivized to use solutions with the best score.

Shifts that do not match the pattern, will still be assigned if there is no alternative.

In the following example, there are 8 shifts and 2 employees. There is one multiDayShiftSequencePatternRules:

  1. Employees work 4 consecutive days on and 4 consecutive days off.

Ann is assigned the first 4 consecutive shifts and Beth is assigned the next 4 consecutive shifts.

multi day shift patterns preferred
  • 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": "Preferred multi-day shift pattern example"
    }
  },
  "modelInput": {
    "contracts": [
      {
        "id": "shiftContract",
        "multiDayShiftSequencePatternRules": [
          {
            "id": "4DaysOn4DaysOff",
            "pattern": [
              {
                "type": "ON",
                "includeShiftTags": [
                  "Day"
                ],
                "shiftTagMatches": "ALL"
              },
              {
                "type": "ON",
                "includeShiftTags": [
                  "Day"
                ],
                "shiftTagMatches": "ALL"
              },
              {
                "type": "ON",
                "includeShiftTags": [
                  "Day"
                ],
                "shiftTagMatches": "ALL"
              },
              {
                "type": "ON",
                "includeShiftTags": [
                  "Day"
                ],
                "shiftTagMatches": "ALL"
              },
              {
                "type": "OFF"
              },
              {
                "type": "OFF"
              },
              {
                "type": "OFF"
              },
              {
                "type": "OFF"
              }
            ],
            "satisfiability": "PREFERRED",
            "weight": 1
          }
        ]
      }
    ],
    "employees": [
      {
        "id": "Ann",
        "contracts": [
          "shiftContract"
        ]
      },
      {
        "id": "Beth",
        "contracts": [
          "shiftContract"
        ]
      }
    ],
    "shifts": [
      {
        "id": "Mon Day",
        "start": "2027-02-01T06:00:00Z",
        "end": "2027-02-01T18:00:00Z",
        "tags": ["Day"]
      },
      {
        "id": "Tue Day",
        "start": "2027-02-02T06:00:00Z",
        "end": "2027-02-02T18:00:00Z",
        "tags": ["Day"]
      },
      {
        "id": "Wed Day",
        "start": "2027-02-03T06:00:00Z",
        "end": "2027-02-03T18:00:00Z",
        "tags": ["Day"]
      },
      {
        "id": "Thu Day",
        "start": "2027-02-04T06:00:00Z",
        "end": "2027-02-04T18:00:00Z",
        "tags": ["Day"]
      },
      {
        "id": "Fri Day",
        "start": "2027-02-05T06:00:00Z",
        "end": "2027-02-05T18:00:00Z",
        "tags": ["Day"]
      },
      {
        "id": "Sat Day",
        "start": "2027-02-06T06:00:00Z",
        "end": "2027-02-06T18:00:00Z",
        "tags": ["Day"]
      },
      {
        "id": "Sun Day",
        "start": "2027-02-07T06:00:00Z",
        "end": "2027-02-07T18:00:00Z",
        "tags": ["Day"]
      },
      {
        "id": "Mon Day 2",
        "start": "2027-02-08T06:00:00Z",
        "end": "2027-02-08T18:00:00Z",
        "tags": ["Day"]
      }
    ],
    "planningWindow": {
      "start": "2027-02-01T00:00:00Z",
      "end": "2027-02-09T00:00:00Z"
    }
  }
}
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": "Preferred multi-day shift pattern example",
    "submitDateTime": "2025-04-17T05:42:12.053757236Z",
    "startDateTime": "2025-04-17T05:42:28.93501674Z",
    "activeDateTime": "2025-04-17T05:42:29.057374899Z",
    "completeDateTime": "2025-04-17T05:42:59.881667907Z",
    "shutdownDateTime": "2025-04-17T05:43:00.075651383Z",
    "solverStatus": "SOLVING_COMPLETED",
    "score": "0hard/0medium/61440soft",
    "tags": [
      "system.profile:default"
    ],
    "validationResult": {
      "summary": "OK"
    }
  },
  "modelOutput": {
    "shifts": [
      {
        "id": "Mon Day",
        "employee": "Ann"
      },
      {
        "id": "Tue Day",
        "employee": "Ann"
      },
      {
        "id": "Wed Day",
        "employee": "Ann"
      },
      {
        "id": "Thu Day",
        "employee": "Ann"
      },
      {
        "id": "Fri Day",
        "employee": "Beth"
      },
      {
        "id": "Sat Day",
        "employee": "Beth"
      },
      {
        "id": "Sun Day",
        "employee": "Beth"
      },
      {
        "id": "Mon Day 2",
        "employee": "Beth"
      }
    ]
  },
  "inputMetrics": {
    "employees": 2,
    "shifts": 8,
    "pinnedShifts": 0
  },
  "kpis": {
    "assignedShifts": 8,
    "unassignedShifts": 0,
    "assignedShiftGroups": 0,
    "unassignedShiftGroups": 0,
    "workingTimeFairnessPercentage": null,
    "disruptionPercentage": 0.0,
    "averageDurationOfEmployeesPreferencesMet": null,
    "minimumDurationOfPreferencesMetAcrossEmployees": null,
    "averageDurationOfEmployeesUnpreferencesViolated": null,
    "maximumDurationOfUnpreferencesViolatedAcrossEmployees": null,
    "activatedEmployees": 2,
    "assignedMandatoryShifts": 8,
    "assignedOptionalShifts": 0,
    "travelDistance": 0
  }
}

modelOutput contains the schedule with Ann assigned to 4 shifts on then 4 days off, and Beth assigned to 4 days off then 4 shifts on.

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

KPIs provides the KPIs for the output including:

{
  "assignedShifts": 8,
  "activatedEmployees": 2,
  "assignedMandatoryShifts": 8
}

3. Unpreferred multi-day shift sequence

When the satisfiability of the rule is UNPREFERRED, the Employee works unpreferred multi day shift sequence pattern soft constraint is invoked.

{
  "contracts": [
    {
      "id": "shiftContract",
      "multiDayShiftSequencePatternRules": [
        {
          "id": "1DayOn1DayOff1DayOn1DayOff",
          "pattern": [
            {
              "type": "ON",
              "includeShiftTags": [
                "Day"
              ],
              "shiftTagMatches": "ALL"
            },
            {
              "type": "OFF"
            },
            {
              "type": "ON",
              "includeShiftTags": [
                "Day"
              ],
              "shiftTagMatches": "ALL"
            },
            {
              "type": "OFF"
            }
          ],
          "satisfiability": "UNPREFERRED",
          "weight": 1
        }
      ]
    }
  ]
}

This constraint adds a soft penalty to the run score when an employee is assigned shifts in a pattern that is UNPREFERRED, incentivizing Timefold to find an alternative solution.

In the following example, there is one multiDayShiftSequencePatternRules:

  • Employees should not work 1 day on, then 1 off, then 1 day on, then another day off.

There are only 2 shifts, and 1 employee. The shifts are on Monday and Wednesday. Assigning the shifts to Ann breaks the pattern and a soft penalty is applied to the run score.

multi day shift patterns unpreferred
  • 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": "Unpreferred multi-day shift pattern example"
    }
  },
  "modelInput": {
    "contracts": [
      {
        "id": "shiftContract",
        "multiDayShiftSequencePatternRules": [
          {
            "id": "1DayOn1DayOff1DayOn1DayOff",
            "pattern": [
              {
                "type": "ON",
                "includeShiftTags": [
                  "Day"
                ],
                "shiftTagMatches": "ALL"
              },
              {
                "type": "OFF"
              },
              {
                "type": "ON",
                "includeShiftTags": [
                  "Day"
                ],
                "shiftTagMatches": "ALL"
              },
              {
                "type": "OFF"
              }
            ],
            "satisfiability": "UNPREFERRED",
            "weight": 1
          }
        ]
      }
    ],
    "employees": [
      {
        "id": "Ann",
        "contracts": [
          "shiftContract"
        ]
      }
    ],
    "shifts": [
      {
        "id": "Mon Day",
        "start": "2027-02-01T06:00:00Z",
        "end": "2027-02-01T18:00:00Z",
        "tags": ["Day"]
      },
      {
        "id": "Wed Day",
        "start": "2027-02-03T06:00:00Z",
        "end": "2027-02-03T18:00:00Z",
        "tags": ["Day"]
      }
    ],
    "planningWindow": {
      "start": "2027-02-01T00:00:00Z",
      "end": "2027-02-05T00:00:00Z"
    }
  }
}
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": "Unpreferred multi-day shift pattern example",
    "submitDateTime": "2025-04-17T07:04:27.137312142Z",
    "startDateTime": "2025-04-17T07:04:38.65529439Z",
    "activeDateTime": "2025-04-17T07:04:38.845195643Z",
    "completeDateTime": "2025-04-17T07:05:09.666946748Z",
    "shutdownDateTime": "2025-04-17T07:05:09.962379646Z",
    "solverStatus": "SOLVING_COMPLETED",
    "score": "0hard/0medium/-3840soft",
    "tags": [
      "system.profile:default"
    ],
    "validationResult": {
      "summary": "OK"
    }
  },
  "modelOutput": {
    "shifts": [
      {
        "id": "Mon Day",
        "employee": "Ann"
      },
      {
        "id": "Wed Day",
        "employee": "Ann"
      }
    ]
  },
  "inputMetrics": {
    "employees": 1,
    "shifts": 2,
    "pinnedShifts": 0
  },
  "kpis": {
    "assignedShifts": 2,
    "unassignedShifts": 0,
    "assignedShiftGroups": 0,
    "unassignedShiftGroups": 0,
    "workingTimeFairnessPercentage": null,
    "disruptionPercentage": 0.0,
    "averageDurationOfEmployeesPreferencesMet": null,
    "minimumDurationOfPreferencesMetAcrossEmployees": null,
    "averageDurationOfEmployeesUnpreferencesViolated": null,
    "maximumDurationOfUnpreferencesViolatedAcrossEmployees": null,
    "activatedEmployees": 1,
    "assignedMandatoryShifts": 2,
    "assignedOptionalShifts": 0,
    "travelDistance": 0
  }
}

modelOutput contains the schedule with Ann assigned to 2 shifts that break the multi-day shift pattern.

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

KPIs provides the KPIs for the output including:

{
  "assignedShifts": 2,
  "activatedEmployees": 1,
  "assignedMandatoryShifts": 2
}

4. Prohibited satisfiability

When the satisfiability of the rule is PROHIBITED, the Employee works prohibited multi day shift sequence pattern hard constraint is invoked.

{
  "contracts": [
    {
      "id": "shiftContract",
      "multiDayShiftSequencePatternRules": [
        {
          "id": "noLateFollowedByEarly",
          "pattern": [
            {
              "type": "ON",
              "includeShiftTags": [
                "Late"
              ],
              "shiftTagMatches": "ALL"
            },
            {
              "type": "ON",
              "includeShiftTags": [
                "Early"
              ],
              "shiftTagMatches": "ALL"
            }
          ],
          "satisfiability": "PROHIBITED",
          "weight": 1
        }
      ]
    }
  ]
}

Shifts will be left unassigned if assigning them would break the Employee works prohibited single day shift sequence pattern constraint.

In the following example, there is 1 late shift followed by 1 early shift the next day. There is only 1 employee.

There is 1 multiDayShiftSequencePatternRules:

  • Employees cannot work a "Late" shift followed by an "Early" shift the next day.

multi day shift patterns prohbited
  • 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": "Prohibited multi-day shift pattern example"
    }
  },
  "modelInput": {
    "contracts": [
      {
        "id": "shiftContract",
        "multiDayShiftSequencePatternRules": [
          {
            "id": "noLateFollowedByEarly",
            "pattern": [
              {
                "type": "ON",
                "includeShiftTags": [
                  "Late"
                ],
                "shiftTagMatches": "ALL"
              },
              {
                "type": "ON",
                "includeShiftTags": [
                  "Early"
                ],
                "shiftTagMatches": "ALL"
              }
            ],
            "satisfiability": "PROHIBITED",
            "weight": 1
          }
        ]
      }
    ],
    "employees": [
      {
        "id": "Ann",
        "contracts": [
          "shiftContract"
        ]
      }
    ],
    "shifts": [
      {
        "id": "Mon",
        "start": "2027-02-01T16:00:00Z",
        "end": "2027-02-02T00:00:00Z",
        "tags": ["Late"]
      },
      {
        "id": "Tue",
        "start": "2027-02-02T00:00:00Z",
        "end": "2027-02-02T08:00:00Z",
        "tags": ["Early"]
      }
    ]
  }
}
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": "Prohibited multi-day shift pattern example",
    "submitDateTime": "2025-04-17T08:40:50.397686975Z",
    "startDateTime": "2025-04-17T08:41:03.819058992Z",
    "activeDateTime": "2025-04-17T08:41:03.990649293Z",
    "completeDateTime": "2025-04-17T08:41:34.845305126Z",
    "shutdownDateTime": "2025-04-17T08:41:35.037087833Z",
    "solverStatus": "SOLVING_COMPLETED",
    "score": "0hard/-1medium/0soft",
    "tags": [
      "system.profile:default"
    ],
    "validationResult": {
      "summary": "OK"
    }
  },
  "modelOutput": {
    "shifts": [
      {
        "id": "Mon",
        "employee": "Ann"
      },
      {
        "id": "Tue",
        "employee": null
      }
    ]
  },
  "inputMetrics": {
    "employees": 1,
    "shifts": 2,
    "pinnedShifts": 0
  },
  "kpis": {
    "assignedShifts": 1,
    "unassignedShifts": 1,
    "assignedShiftGroups": 0,
    "unassignedShiftGroups": 0,
    "workingTimeFairnessPercentage": null,
    "disruptionPercentage": 0.0,
    "averageDurationOfEmployeesPreferencesMet": null,
    "minimumDurationOfPreferencesMetAcrossEmployees": null,
    "averageDurationOfEmployeesUnpreferencesViolated": null,
    "maximumDurationOfUnpreferencesViolatedAcrossEmployees": null,
    "activatedEmployees": 1,
    "assignedMandatoryShifts": 1,
    "assignedOptionalShifts": 0,
    "travelDistance": 0
  }
}

modelOutput contains the schedule with Ann scheduled a "Late" shift, but with the "Early" shift the next day 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,
    "activatedEmployees": 1,
    "assignedMandatoryShifts": 1
}

5. Mandatory and optional shifts with multi-day shift patterns

When it is preferable to assign shifts that match the multi-day shift pattern at the expense of leaving some shifts unassigned, custom priorities can be defined so that shifts are considered optional and will not be assigned if assigning them would break the multi-day pattern.

See mandatory and optional shifts for more information.

Next

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

  • Learn more about employee shift scheduling from our YouTube playlist.

  • Working with Employee availability.

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