Docs
  • Solver
  • Models
    • Field Service Routing
    • Employee Shift Scheduling
    • Pick-up and Delivery Routing
  • Platform
Try models
  • Employee Shift Scheduling
  • Consecutive days worked and shift sequences
  • latest
    • latest
    • 1.16.x

Employee Shift Scheduling

    • Introduction
    • Getting started: Hello world
    • User guide
      • Terms
      • Use case guide
      • Planning AI concepts
      • Constraints
      • Understanding the API
      • Demo datasets
      • Planning window
      • Time zones and Daylight Saving Time (DST)
      • Tags and tag types
      • Input validation
      • Metrics and optimization goals
      • Score analysis
    • Employee resource constraints
      • Employee contracts
      • Employee availability
      • Pairing employees
      • Shift travel and locations
      • Employee activation
      • 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
        • 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
        • Consecutive weekends off per period
      • Shift rotations and patterns
        • Shift rotations
        • Single day shift sequence patterns
        • Minimize gaps between shifts
        • Multi-day shift sequence patterns
        • Daily shift pairings
        • Overlapping shifts
        • Shift start times differences
        • Minutes between shifts
      • Shift type diversity
        • Shift types worked per period
        • Unique tags per period
      • Fairness
        • Balance time worked
        • Balance shift count
    • Shift service constraints
      • Alternative shifts
      • Cost management
      • Demand-based scheduling
      • Mandatory and optional shifts
      • Shift assignments
      • Skills and risk factors
    • Manual intervention
    • Recommendations
    • Real-time planning
    • Real-time planning (preview)
    • Scenarios
      • Configuring labor law compliance
    • Changelog
    • Upgrade to the latest version
    • Feature requests

Consecutive days worked and shift sequences

There are different techniques for managing employees' working hours.

For different scenarios see Work limits.

When working with consecutive days worked you can define shift sequences, for instance, a sequence of morning shifts or a sequence of afternoon shifts.

With sequences defined you can specify minimum times between employees being assigned different shift sequences. This can prevent an employee finishing a sequence of afternoon shifts 1 day and being assigned the start of a sequence of morning shifts the following day.

You can also define the start day for shift sequences.

This guide shows you how to manage consecutive day shift sequences with the following examples:

  • 1. Defining shift sequences
  • 2. Required minimum time between different sequences
  • 3. Preferred minimum time between different sequences
  • 4. Required sequence start day
  • 5. Preferred sequence start day
  • 6. Sequence ends on allowed sequence start day
  • 7. Employee works compact sequence

1. Defining shift sequences

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.

One or multiple shifts of the same type form a sequence. Sequences can have days off in between the shifts.

Use shiftTypeTagCategories of consecutiveDaysWorkedRules to define the sequences.

{
  "consecutiveDaysWorkedRules": [
    {
      "id": "sequenceExample",
      "shiftTypeTagCategories": ["AM", "PM"]
    }
  ]
}

Shifts must have at most 1 tag from the shiftTypeTagCategories defined in the consecutiveDaysWorkedRules. The following shift matches the sequence category "AM":

{
  "shifts": [
  {
      "id": "shift-1",
      "start": "2027-02-01T00:00:00Z",
      "end": "2027-02-01T12:00:00Z",
      "tags": [
        "AM", "ICU"
      ]
    }
  ]
}

In the following example there are 2 sequences.

  • AM shift sequence between 1st Feb 2027 and 5th Feb 2027.

  • PM shift sequence between 8th Feb 2027 and 13th Feb 2027.

sequences

2. Required minimum time between different sequences

When the satisfiability of the rule is REQUIRED, the Required minimum time between different sequences not met for employee hard constraint is invoked, which makes sure there is enough time between 2 different sequences, for example AM and PM.

There are 2 ways to define the required time interval between the sequences:

2.1. Absolute duration

minDurationBetweenDifferentSequences specifies the absolute duration (ISO 8601 format), for example 24 hours (PT24H).

In that case, the following sequence is required to start no sooner than 24 hours after the previous sequence ended. If the prior sequence ended on 1st Feb 2027, 12:00, the next sequence can start no sooner than 2nd Feb 2027, 12:00.

{
  "consecutiveDaysWorkedRules": [
    {
      "id": "sequenceExample",
      "shiftTypeTagCategories": ["AM", "PM"],
      "minDurationBetweenDifferentSequences": "PT24H"
    }
  ]
}

In the following example, there are 5 shifts and 1 employee. A consecutiveDaysWorkedRules that specifies the employee can work 3 consecutive shifts in a sequence and there must 24 hours between different shift sequences.

The shifts on Monday and Tuesday are tagged PM, the shifts on Wednesday, Thursday, and Friday are tagged AM Beth is assigned the Monday and Tuesday PM shifts, there is not 24 hours between the Tuesday shift and the Wednesday shift, so the Wednesday shift is left unassigned. Beth is assigned the Thursday and Friday 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": "Required consecutive days and shift sequences example"
    }
  },
  "modelInput": {
    "contracts": [
      {
        "id": "fullTimeContract",
        "consecutiveDaysWorkedRules": [
          {
            "id": "Max3Consecutive12HourShifts",
            "maximum": 3,
            "minDurationBetweenDifferentSequences": "PT24H",
            "shiftTypeTagCategories": ["AM", "PM"],
            "satisfiability": "REQUIRED"
          }
        ]
      }
    ],
    "employees": [
      {
        "id": "Beth",
        "contracts": [
          "fullTimeContract"
        ]
      }
    ],
    "shifts": [
      {
        "id": "Mon",
        "start": "2027-02-01T12:00:00Z",
        "end": "2027-02-02T00:00:00Z",
        "tags": ["PM"]
      },
      {
        "id": "Tue",
        "start": "2027-02-02T12:00:00Z",
        "end": "2027-02-03T00:00:00Z",
        "tags": ["PM"]
      },
      {
        "id": "Wed",
        "start": "2027-02-03T00:00:00Z",
        "end": "2027-02-03T12:00:00Z",
        "tags": ["AM"]
      },
      {
        "id": "Thu",
        "start": "2027-02-04T00:00:00Z",
        "end": "2027-02-04T12:00:00Z",
        "tags": ["AM"]
      },
      {
        "id": "Fri",
        "start": "2027-02-05T00:00:00Z",
        "end": "2027-02-05T12:00:00Z",
        "tags": ["AM"]
      }
    ]
  }
}
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>
{
  "metadata": {
    "id": "ID",
    "originId": "ID",
    "name": "Required consecutive days and shift sequences example",
    "submitDateTime": "2025-12-09T06:28:06.099733892Z",
    "startDateTime": "2025-12-09T06:28:13.039909151Z",
    "activeDateTime": "2025-12-09T06:28:13.129716059Z",
    "completeDateTime": "2025-12-09T06:28:43.525618047Z",
    "shutdownDateTime": "2025-12-09T06:28:43.525622217Z",
    "solverStatus": "SOLVING_COMPLETED",
    "score": "0hard/-1medium/0soft",
    "tags": [
      "system.type:from-request",
      "system.profile:default"
    ],
    "validationResult": {
      "summary": "OK"
    }
  },
  "modelOutput": {
    "shifts": [
      {
        "id": "Mon",
        "employee": "Beth"
      },
      {
        "id": "Tue",
        "employee": "Beth"
      },
      {
        "id": "Wed"
      },
      {
        "id": "Thu",
        "employee": "Beth"
      },
      {
        "id": "Fri",
        "employee": "Beth"
      }
    ],
    "employees": [
      {
        "id": "Beth",
        "metrics": {
          "assignedShifts": 4,
          "durationWorked": "PT48H"
        }
      }
    ]
  },
  "inputMetrics": {
    "employees": 1,
    "shifts": 5,
    "pinnedShifts": 0,
    "mandatoryShifts": 5,
    "optionalShifts": 0
  },
  "kpis": {
    "assignedShifts": 4,
    "unassignedShifts": 1,
    "disruptionPercentage": 0,
    "activatedEmployees": 1,
    "assignedMandatoryShifts": 4
  },
  "run": {
    "id": "ID",
    "originId": "ID",
    "name": "Required consecutive days and shift sequences example",
    "submitDateTime": "2025-12-09T06:28:06.099733892Z",
    "startDateTime": "2025-12-09T06:28:13.039909151Z",
    "activeDateTime": "2025-12-09T06:28:13.129716059Z",
    "completeDateTime": "2025-12-09T06:28:43.525618047Z",
    "shutdownDateTime": "2025-12-09T06:28:43.525622217Z",
    "solverStatus": "SOLVING_COMPLETED",
    "score": "0hard/-1medium/0soft",
    "tags": [
      "system.type:from-request",
      "system.profile:default"
    ],
    "validationResult": {
      "summary": "OK"
    }
  }
}

2.2. Flexible delay

minDelayBetweenDifferentSequences is a flexible function that allows you to specify a delay until a specific moment, for example next Monday or in 2 full calendar days.

{
  "consecutiveDaysWorkedRules": [
    {
      "id": "sequenceExample",
      "shiftTypeTagCategories": ["AM", "PM"],
      "minDelayBetweenDifferentSequences": {
        "minStartDateAdjuster": "NEXT_DAY",
        "minStartDateAdjusterIncrement": 2,
        "minStartTime": "00:00:00"
      }
    }
  ]
}

The function has 3 components:

minStartDateAdjuster: The name of the adjuster function for the date part of the start of the following sequence.

The following functions are supported:

  • SAME_DAY

  • NEXT_DAY

  • NEXT_MONTH

  • NEXT_MONDAY

  • NEXT_TUESDAY

  • NEXT_WEDNESDAY

  • NEXT_THURSDAY

  • NEXT_FRIDAY

  • NEXT_SATURDAY

  • NEXT_SUNDAY

minStartDateAdjusterIncrement: The increment that determines how many times minStartDateAdjuster is applied. The default value is 1.

minStartTime: The time part (ISO 8601 local datetime) of the following sequence start (inclusive).

For example, the following sequence is required to start no sooner than in 1 full calendar day, plus the remaining part of the day, where the previous sequence ends. If the prior sequence ends on 1st Feb 2027, 12:00, the next sequence can start no sooner than 3rd Feb 2027, 00:00.

{
  "consecutiveDaysWorkedRules": [
    {
      "id": "sequenceExample",
      "shiftTypeTagCategories": ["AM", "PM"],
      "minDelayBetweenDifferentSequences": {
        "minStartDateAdjuster": "NEXT_DAY",
        "minStartDateAdjusterIncrement": 2,
        "minStartTime": "00:00:00"
      }
    }
  ]
}

3. Preferred minimum time between different sequences

When the satisfiability of the rule is PREFERRED, the Preferred minimum time between different sequences not met for employee soft constraint is invoked, which makes sure there is enough time between two sequences of different types, for example AM and PM.

Every soft constraint has a weight that can be configured to change the relative importance of the constraint compared to other constraints.

Learn about constraint weights.

There are 2 ways to define the required time interval between the sequences:

3.1. Absolute duration

minDurationBetweenDifferentSequences specifies the absolute duration (ISO 8601 format), for example 24 hours (PT24H).

In that case, the following sequence is required to start no sooner than 24 hours after the previous sequence ended. If the prior sequence ended on 1st Feb 2027, 12:00, the next sequence can start no sooner than 2nd Feb 2027, 12:00.

{
  "consecutiveDaysWorkedRules": [
    {
      "id": "sequenceExample",
      "shiftTypeTagCategories": ["AM", "PM"],
      "minDurationBetweenDifferentSequences": "PT24H"
    }
  ]
}

In the following example, there are 5 shifts and 1 employee. A consecutiveDaysWorkedRules that specifies the employee can work 3 consecutive shifts in a sequence and there should preferably be 24 hours between different shift sequences.

The shifts on Monday and Tuesday are tagged PM, the shifts on Wednesday, Thursday, and Friday are tagged AM Beth is assigned all 5 shifts, and a soft score penalty is applied to the dataset score between there is not 24 hours between the Tuesday and Wednesday 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": "Preferred consecutive days and shift sequences example"
    }
  },
  "modelInput": {
    "contracts": [
      {
        "id": "fullTimeContract",
        "consecutiveDaysWorkedRules": [
          {
            "id": "Max3Consecutive12HourShifts",
            "maximum": 3,
            "minDurationBetweenDifferentSequences": "PT24H",
            "shiftTypeTagCategories": ["AM", "PM"],
            "satisfiability": "PREFERRED"
          }
        ]
      }
    ],
    "employees": [
      {
        "id": "Beth",
        "contracts": [
          "fullTimeContract"
        ]
      }
    ],
    "shifts": [
      {
        "id": "Mon",
        "start": "2027-02-01T12:00:00Z",
        "end": "2027-02-02T00:00:00Z",
        "tags": ["PM"]
      },
      {
        "id": "Tue",
        "start": "2027-02-02T12:00:00Z",
        "end": "2027-02-03T00:00:00Z",
        "tags": ["PM"]
      },
      {
        "id": "Wed",
        "start": "2027-02-03T00:00:00Z",
        "end": "2027-02-03T12:00:00Z",
        "tags": ["AM"]
      },
      {
        "id": "Thu",
        "start": "2027-02-04T00:00:00Z",
        "end": "2027-02-04T12:00:00Z",
        "tags": ["AM"]
      },
      {
        "id": "Fri",
        "start": "2027-02-05T00:00:00Z",
        "end": "2027-02-05T12:00:00Z",
        "tags": ["AM"]
      }
    ]
  }
}
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>
{
  "metadata": {
    "id": "ID",
    "originId": "ID",
    "name": "Preferred consecutive days and shift sequences example",
    "submitDateTime": "2025-12-09T06:54:47.566282117Z",
    "startDateTime": "2025-12-09T06:54:54.609456133Z",
    "activeDateTime": "2025-12-09T06:54:54.697771431Z",
    "completeDateTime": "2025-12-09T06:55:25.046055215Z",
    "shutdownDateTime": "2025-12-09T06:55:25.046059095Z",
    "solverStatus": "SOLVING_COMPLETED",
    "score": "0hard/0medium/-2880soft",
    "tags": [
      "system.type:from-request",
      "system.profile:default"
    ],
    "validationResult": {
      "summary": "OK"
    }
  },
  "modelOutput": {
    "shifts": [
      {
        "id": "Mon",
        "employee": "Beth"
      },
      {
        "id": "Tue",
        "employee": "Beth"
      },
      {
        "id": "Wed",
        "employee": "Beth"
      },
      {
        "id": "Thu",
        "employee": "Beth"
      },
      {
        "id": "Fri",
        "employee": "Beth"
      }
    ],
    "employees": [
      {
        "id": "Beth",
        "metrics": {
          "assignedShifts": 5,
          "durationWorked": "PT60H"
        }
      }
    ]
  },
  "inputMetrics": {
    "employees": 1,
    "shifts": 5,
    "pinnedShifts": 0,
    "mandatoryShifts": 5,
    "optionalShifts": 0
  },
  "kpis": {
    "assignedShifts": 5,
    "unassignedShifts": 0,
    "disruptionPercentage": 0,
    "activatedEmployees": 1,
    "assignedMandatoryShifts": 5
  },
  "run": {
    "id": "ID",
    "originId": "ID",
    "name": "Preferred consecutive days and shift sequences example",
    "submitDateTime": "2025-12-09T06:54:47.566282117Z",
    "startDateTime": "2025-12-09T06:54:54.609456133Z",
    "activeDateTime": "2025-12-09T06:54:54.697771431Z",
    "completeDateTime": "2025-12-09T06:55:25.046055215Z",
    "shutdownDateTime": "2025-12-09T06:55:25.046059095Z",
    "solverStatus": "SOLVING_COMPLETED",
    "score": "0hard/0medium/-2880soft",
    "tags": [
      "system.type:from-request",
      "system.profile:default"
    ],
    "validationResult": {
      "summary": "OK"
    }
  }
}

3.2. Flexible delay

minDelayBetweenDifferentSequences is a flexible function that allows to specify delay until a specific moment, for example next Monday, or in 2 full calendar days.

{
  "consecutiveDaysWorkedRules": [
    {
      "id": "sequenceExample",
      "shiftTypeTagCategories": ["AM", "PM"],
      "minDelayBetweenDifferentSequences": {
        "minStartDateAdjuster": "NEXT_DAY",
        "minStartDateAdjusterIncrement": 2,
        "minStartTime": "00:00:00"
      }
    }
  ]
}

The function has 3 components:

minStartDateAdjuster: The name of the adjuster function for the date part of the following sequence start.

The following functions are supported:

  • SAME_DAY

  • NEXT_DAY

  • NEXT_MONTH,

  • NEXT_MONDAY

  • NEXT_TUESDAY

  • NEXT_WEDNESDAY

  • NEXT_THURSDAY

  • NEXT_FRIDAY

  • NEXT_SATURDAY

  • NEXT_SUNDAY

minStartDateAdjusterIncrement: The increment that determines how many times minStartDateAdjuster is applied. The default value is 1.

minStartTime: The time part (ISO 8601 local datetime) of the following sequence start (inclusive).

For example, the following sequence can start no sooner than after 1 full calendar day, plus the remaining part of the day when the previous sequence ended. If the prior sequence ends on 1st Feb 2027, 12:00, the next sequence can start no sooner than 3rd Feb 2027, 00:00.

{
  "consecutiveDaysWorkedRules": [
    {
      "id": "sequenceExample",
      "shiftTypeTagCategories": ["AM", "PM"],
      "minDelayBetweenDifferentSequences": {
        "minStartDateAdjuster": "NEXT_DAY",
        "minStartDateAdjusterIncrement": 2,
        "minStartTime": "00:00:00"
      }
    }
  ]
}

4. Required sequence start day

You can specify which days of the week sequences can start on:

{
  "consecutiveDaysWorkedRules": [
    {
      "id": "sequenceExample",
      "shiftTypeTagCategories": ["morning", "afternoon"],
      "allowedSequenceStartDays": ["MONDAY"]
    }
  ]
}

The Required sequence start day not met for employee hard constraint is invoked when the end of the previous sequence prevents the start of the following sequence on one of the configured allowed days.

The constraint is automatically enabled when allowedSequenceStartDays is configured in consecutiveDaysWorkedRules.

Consider the following example that uses allowedSequenceStartDays set to MONDAY.

  • Shift 1 morning 2025-01-01 (Wednesday)

  • Shift 2 morning 2025-01-02 (Thursday)

  • Shift 3 morning 2025-01-03 (Friday)

  • Shift 4 morning 2025-01-04 (Saturday)

  • Shift 5 morning 2025-01-06 (Monday)

  • Shift 6 afternoon 2025-01-07 (Tuesday)

In this example, the morning sequence ends on Monday, effectively blocking the afternoon shift from starting because the next sequence cannot start until the following Monday.

As a consequence the next afternoon shift sequence could start the following week:

  • Shift 1 morning 2025-01-01 (Wednesday)

  • Shift 2 morning 2025-01-02 (Thursday)

  • Shift 3 morning 2025-01-03 (Friday)

  • Shift 4 morning 2025-01-04 (Saturday)

  • Shift 5 morning 2025-01-06 (Monday)

  • Shift 6 afternoon 2025-01-13 (Monday)

When the solver does not assign shifts on allowedSequenceStartDays and the previous sequence ends before those days, the constraint is not violated.

In the following example the previous sequence ends on 2025-01-04 (Saturday) and the next sequence starts on 2025-01-08 (Wednesday).

  • Shift 1 morning 2025-01-01 (Wednesday)

  • Shift 2 morning 2025-01-02 (Thursday)

  • Shift 3 morning 2025-01-03 (Friday)

  • Shift 4 morning 2025-01-04 (Saturday)

  • Shift 5 afternoon 2025-01-08 (Wednesday)

5. Preferred sequence start day

The Preferred sequence start day met for employee soft constraint is invoked when a sequence starts on one of the configured allowed days. The constraint adds a soft reward when this happens.

Every soft constraint has a weight that can be configured to change the relative importance of the constraint compared to other constraints.

Learn about constraint weights.

The constraint is automatically enabled when allowedSequenceStartDays is configured in consecutiveDaysWorkedRules.

{
  "consecutiveDaysWorkedRules": [
    {
      "id": "sequenceExample",
      "shiftTypeTagCategories": ["morning", "afternoon"],
      "allowedSequenceStartDays": ["MONDAY"],
      "satisfiability": "PREFERRED"
    }
  ]
}

6. Sequence ends on allowed sequence start day

The Sequence ends on allowed sequence start day for employee soft constraint is invoked when a sequence ends on one of the configured allowed days. The idea is to keep these days open to possibly start a new sequence. This constraint adds a soft penalty to the dataset score for any matches to the constraint, incentivizing Timefold to find an alternative solution.

Every soft constraint has a weight that can be configured to change the relative importance of the constraint compared to other constraints.

Learn about constraint weights.

The constraint is automatically enabled when allowedSequenceStartDays is configured in consecutiveDaysWorkedRules.

{
  "consecutiveDaysWorkedRules": [
    {
      "id": "sequenceExample",
      "shiftTypeTagCategories": ["morning", "afternoon"],
      "allowedSequenceStartDays": ["MONDAY"]
    }
  ]
}

7. Employee works compact sequence

The Employee works compact sequence soft constraint is invoked when two shifts of the same type (for example morning) are planned on consecutive days. The constraint adds a soft reward for such pairs. This keeps the sequences compact and minimizes the gaps in such sequences.

Every soft constraint has a weight that can be configured to change the relative importance of the constraint compared to other constraints.

Learn about constraint weights.

The constraint is automatically enabled when rewardCompactSequences is set to true in consecutiveDaysWorkedRules.

{
  "consecutiveDaysWorkedRules": [
    {
      "id": "sequenceExample",
      "shiftTypeTagCategories": ["morning", "afternoon"],
      "rewardCompactSequences": true
    }
  ]
}

Next

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

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

  • See other options for managing employees' work hours: Work limits.

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