Shift sequence patterns: multi-day shifts
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 preferrable 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.
This guide explains how to manage multi-day shift sequence patterns with the following examples:
Prerequisites
To run the examples in this guide, you need to authenticate with a valid API key for the Employee Shift Scheduling model:
-
Log in to Timefold Platform: app.timefold.ai
-
From the Dashboard, click your tenant, and from the drop-down menu select Tenant Settings, then choose API Keys.
-
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. Multi-day shift sequence
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.
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
, not both.
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"]
}
satisfiability
of the pattern can be PREFERRED
, PROHIBITED
, or UNPREFERRED
.
1.1. Preferred satisfiability
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
:
-
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.

-
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": "Multi-day shift pattern rules - preferred"
}
},
"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": "Multi-day shift pattern rules - preferred",
"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
}
1.2. Unpreferred satisfiability
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.

-
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": "Multi-day shift pattern rules - unpreferred"
}
},
"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": "Multi-day shift pattern rules - unpreferred",
"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
}
1.3. 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.

-
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": "Multi-day shift pattern rules - prohibited"
}
},
"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": "Multi-day shift pattern rules - prohibited",
"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
}
1.4. 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
-
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.
-
Working with Employee availability.