Docs
  • Solver
  • Models
    • Field Service Routing
    • Employee Shift Scheduling
  • Platform
Try models
  • Employee Shift Scheduling
  • Employee resource constraints
  • Shift rotations and patterns
  • Minutes between shifts

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

Minutes between shifts

Employees need time between shifts for their lives outside work.

Contractually, employees could be entitled to a minimum time between shifts, for instance, if an employee works a night shift, it might be included in their contract that they don’t work for the following 12 hours.

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 minutes between shifts

minutesBetweenShiftRules are defined in contracts.

{
  "contracts": [
    {
      "id": "fullTimeContract",
      "minutesBetweenShiftsRules": [
        {
          "id": "Min12Max24HoursBetweenShiftsFullTime",
          "minimumMinutesBetweenShifts": 720,
          "maximumMinutesBetweenShifts": 1440,
          "scope": {
            "type": "duration",
            "duration": "P1D"
          },
          "satisfiability": "REQUIRED"
        }
      ]
    }
  ]
}

minutesBetweenShiftsRules must include an id.

In this instance, the minimumMinutesBetweenShifts is 720 minutes, which ensures employees have 12 hours between assigned shifts.

The maximumMinutesBetweenShifts is 1440 minutes, which means the next shift should be assigned no more than 24 hours later.

scope limits the shift pairs the rule applies to. The rule excludes shift pairs where the start of the after shift is after the end of the prior shift plus the duration. scope must include type and duration.

type currently only supports duration.

duration The duration in ISO 8601 format, for instance, P1D.

requiredAfterShiftTags can be optionally set to limit the rule so that it only applies when the tag or tags specified by the requiredAfterShiftTags are present in the next shift that is assigned to the employee. In the following example, the rule will only if the next shift assigned to the employee is tagged with "Night".

{
  "minutesBetweenShiftsRules": [
    {
      "id": "Min12Max24HoursBetweenShiftsFullTime",
      "minimumMinutesBetweenShifts": 720,
      "maximumMinutesBetweenShifts": 1440,
      "requiredAfterShiftTags": ["Night"],
      "shiftTagMatches": "ALL",
      "scope": {
        "type": "duration",
        "duration": "P1D"
      },
      "satisfiability": "REQUIRED"
    }
  ]
}

requiredPriorShiftTags can be optionally set to limit the rule so that it only applies when the tag or tags specified by the requiredPriorShiftTags are present in the previous shift assigned to the employee.

{
  "minutesBetweenShiftsRules": [
    {
      "id": "Min12Max24HoursBetweenShiftsFullTime",
      "minimumMinutesBetweenShifts": 720,
      "maximumMinutesBetweenShifts": 1440,
      "requiredPriorShiftTags": ["Night"],
      "shiftTagMatches": "ALL",
      "scope": {
        "type": "duration",
        "duration": "P1D"
      },
      "satisfiability": "REQUIRED"
    }
  ]
}

In the following example, the rule will only if the previous shift assigned to the employee is tagged with "Night".

shiftTagMatches can be set to ALL or ANY. When set to ALL, all tags defined by the rule must be present in the shift. set to ANY, at least one tag defined by the rule must be present in the shift. The default behavior for shiftTagMatches is ALL, and if omitted, the default ALL will be used.

With requiredAfterShiftTags, you can optionally define a minimumConsecutivePriorShifts attribute to work with a daily sequence of prior shifts. minimumConsecutivePriorShifts can be either 1 or 2. When the value is 1, the rule will be invoked after a sequence of 1 prior shift that includes the tags specified in requiredAfterShiftTags shift assigned to the employee. When the value is 2, the rule will be invoked after a sequence of 2 prior shifts that include the tags specified in requiredAfterShiftTags shift assigned to the employee.

The satisfiability of the rule can be REQUIRED or PREFERRED. If omitted, REQUIRED is the default.

2. Required minutes between shifts

When the satisfiability of the rule is REQUIRED, the Minutes between shifts not in required range for employee hard constraint is invoked, making sure the number of minutes between shifts assigned to an employee is not below the minimumMinutesBetweenShifts or above the maximumMinutesBetweenShifts.

Shifts will be left unassigned if assigning the shifts breaks the constraint.

In the following example, Carl works the night shift between 00:00 and 08:00, he will not be eligible for another shift until 12 hours later at or after 20:00. There are 5 night shift and 5 day shifts. Carl is assigned the night shifts and the day shift are left unassigned.

required minutes between 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 minutes between shifts example",
      "tags": []
    }
  },
  "modelInput": {
    "contracts": [
      {
        "id": "fullTimeContract",
        "minutesBetweenShiftsRules": [
          {
            "id": "Min12Max24HoursBetweenShiftsFullTime",
            "minimumMinutesBetweenShifts": 720,
            "maximumMinutesBetweenShifts": 1440,
            "scope": {
              "type": "duration",
              "duration": "P1D"
            },
            "satisfiability": "REQUIRED"
          }
        ]
      }
    ],
    "employees": [
      {
        "id": "Carl",
        "contracts": [
          "fullTimeContract"
        ]
      }
    ],
    "shifts": [
      {
        "id": "Mon 1",
        "start": "2027-02-01T00:00:00Z",
        "end": "2027-02-01T08:00:00Z"
      },
      {
        "id": "Mon 2",
        "start": "2027-02-01T08:00:00Z",
        "end": "2027-02-01T16:00:00Z"
      },
      {
        "id": "Tue 1",
        "start": "2027-02-02T00:00:00Z",
        "end": "2027-02-02T08:00:00Z"
      },
      {
        "id": "Tue 2",
        "start": "2027-02-02T08:00:00Z",
        "end": "2027-02-02T16:00:00Z"
      },
      {
        "id": "Wed 1",
        "start": "2027-02-03T00:00:00Z",
        "end": "2027-02-03T08:00:00Z"
      },
      {
        "id": "Wed 2",
        "start": "2027-02-03T08:00:00Z",
        "end": "2027-02-03T16:00:00Z"
      },
      {
        "id": "Thu 1",
        "start": "2027-02-04T00:00:00Z",
        "end": "2027-02-04T08:00:00Z"
      },
      {
        "id": "Thu 2",
        "start": "2027-02-04T08:00:00Z",
        "end": "2027-02-04T16:00:00Z"
      },
      {
        "id": "Fri 1",
        "start": "2027-02-05T00:00:00Z",
        "end": "2027-02-05T08:00:00Z"
      },
      {
        "id": "Fri 2",
        "start": "2027-02-05T08:00:00Z",
        "end": "2027-02-05T16: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",
    "parentId": null,
    "originId": "ID",
    "name": "Required minutes between shifts example",
    "submitDateTime": "2025-06-20T03:55:08.805661318Z",
    "startDateTime": "2025-06-20T03:55:19.324204216Z",
    "activeDateTime": "2025-06-20T03:55:19.494220139Z",
    "completeDateTime": "2025-06-20T03:55:50.142101896Z",
    "shutdownDateTime": "2025-06-20T03:55:50.397703352Z",
    "solverStatus": "SOLVING_COMPLETED",
    "score": "0hard/-5medium/0soft",
    "tags": [
      "system.profile:default"
    ],
    "validationResult": {
      "summary": "OK"
    }
  },
  "modelOutput": {
    "shifts": [
      {
        "id": "Mon 1",
        "employee": "Carl"
      },
      {
        "id": "Mon 2"
      },
      {
        "id": "Tue 1",
        "employee": "Carl"
      },
      {
        "id": "Tue 2"
      },
      {
        "id": "Wed 1",
        "employee": "Carl"
      },
      {
        "id": "Wed 2"
      },
      {
        "id": "Thu 1",
        "employee": "Carl"
      },
      {
        "id": "Thu 2"
      },
      {
        "id": "Fri 1",
        "employee": "Carl"
      },
      {
        "id": "Fri 2"
      }
    ]
  },
  "inputMetrics": {
    "employees": 1,
    "shifts": 10,
    "pinnedShifts": 0
  },
  "kpis": {
    "assignedShifts": 5,
    "unassignedShifts": 5,
    "disruptionPercentage": 0.0,
    "activatedEmployees": 1,
    "assignedMandatoryShifts": 5,
    "assignedOptionalShifts": 0,
    "travelDistance": 0
  }
}

modelOutput contains the employee schedule with Carl assigned shifts with more than 12 hours between each shift.

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

KPIs provides the KPIs for the output including:

{
  "assignedShifts": 3,
  "unassignedShifts": 3,
  "activatedEmployees": 1,
  "assignedMandatoryShifts": 3
}

3. Preferred minutes between shifts

When the satisfiability of the rule is PREFERRED, the Minutes between shifts not in preferred range for employee soft constraint is invoked. Employees might be assigned shifts that are below the minimumMinutesBetweenShifts or above the maximumMinutesBetweenShifts, but the constraint adds a soft penalty to the run score for any matches to the constraint, incentivizing Timefold to find an alternative solution.

{
  "contracts": [
    {
      "id": "fullTimeContract",
      "minutesBetweenShiftsRules": [
        {
          "id": "Minimum12HoursBetweenShiftsFullTime",
          "minimumMinutesBetweenShifts": 720,
          "maximumMinutesBetweenShifts": 1440,
          "scope": {
            "type": "duration",
            "duration": "P1D"
          },
          "satisfiability": "PREFERRED"
        }
      ]
    }
  ]
}

In the following example, there are 5 night shift and 5 day shifts. There is a minutes between shifts rules with a preferred satisfiability that states Carl should not be assigned another shift until 720 minutes after his last shift. Carl is assigned all the shifts and a soft penalty is applied to the run score.

preferred minutes between 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 minutes between shifts example",
      "tags": []
    }
  },
  "modelInput": {
    "contracts": [
      {
        "id": "fullTimeContract",
        "minutesBetweenShiftsRules": [
          {
            "id": "Min12Max24HoursBetweenShiftsFullTime",
            "minimumMinutesBetweenShifts": 720,
            "maximumMinutesBetweenShifts": 1440,
            "scope": {
              "type": "duration",
              "duration": "P1D"
            },
            "satisfiability": "PREFERRED"
          }
        ]
      }
    ],
    "employees": [
      {
        "id": "Carl",
        "contracts": [
          "fullTimeContract"
        ]
      }
    ],
    "shifts": [
      {
        "id": "Mon 1",
        "start": "2027-02-01T00:00:00Z",
        "end": "2027-02-01T08:00:00Z"
      },
      {
        "id": "Mon 2",
        "start": "2027-02-01T08:00:00Z",
        "end": "2027-02-01T16:00:00Z"
      },
      {
        "id": "Tue 1",
        "start": "2027-02-02T00:00:00Z",
        "end": "2027-02-02T08:00:00Z"
      },
      {
        "id": "Tue 2",
        "start": "2027-02-02T08:00:00Z",
        "end": "2027-02-02T16:00:00Z"
      },
      {
        "id": "Wed 1",
        "start": "2027-02-03T00:00:00Z",
        "end": "2027-02-03T08:00:00Z"
      },
      {
        "id": "Wed 2",
        "start": "2027-02-03T08:00:00Z",
        "end": "2027-02-03T16:00:00Z"
      },
      {
        "id": "Thu 1",
        "start": "2027-02-04T00:00:00Z",
        "end": "2027-02-04T08:00:00Z"
      },
      {
        "id": "Thu 2",
        "start": "2027-02-04T08:00:00Z",
        "end": "2027-02-04T16:00:00Z"
      },
      {
        "id": "Fri 1",
        "start": "2027-02-05T00:00:00Z",
        "end": "2027-02-05T08:00:00Z"
      },
      {
        "id": "Fri 2",
        "start": "2027-02-05T08:00:00Z",
        "end": "2027-02-05T16: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",
    "parentId": null,
    "originId": "ID",
    "name": "Preferred minutes between shifts example",
    "submitDateTime": "2025-06-20T04:18:51.093250351Z",
    "startDateTime": "2025-06-20T04:19:05.093870302Z",
    "activeDateTime": "2025-06-20T04:19:05.227695908Z",
    "completeDateTime": "2025-06-20T04:19:36.166447379Z",
    "shutdownDateTime": "2025-06-20T04:19:36.464541895Z",
    "solverStatus": "SOLVING_COMPLETED",
    "score": "0hard/0medium/-9120soft",
    "tags": [
      "system.profile:default"
    ],
    "validationResult": {
      "summary": "OK"
    }
  },
  "modelOutput": {
    "shifts": [
      {
        "id": "Mon 1",
        "employee": "Carl"
      },
      {
        "id": "Mon 2",
        "employee": "Carl"
      },
      {
        "id": "Tue 1",
        "employee": "Carl"
      },
      {
        "id": "Tue 2",
        "employee": "Carl"
      },
      {
        "id": "Wed 1",
        "employee": "Carl"
      },
      {
        "id": "Wed 2",
        "employee": "Carl"
      },
      {
        "id": "Thu 1",
        "employee": "Carl"
      },
      {
        "id": "Thu 2",
        "employee": "Carl"
      },
      {
        "id": "Fri 1",
        "employee": "Carl"
      },
      {
        "id": "Fri 2",
        "employee": "Carl"
      }
    ]
  },
  "inputMetrics": {
    "employees": 1,
    "shifts": 10,
    "pinnedShifts": 0
  },
  "kpis": {
    "assignedShifts": 10,
    "unassignedShifts": 0,
    "disruptionPercentage": 0.0,
    "activatedEmployees": 1,
    "assignedMandatoryShifts": 10,
    "assignedOptionalShifts": 0,
    "travelDistance": 0
  }
}

modelOutput contains the schedule with Carl assigned to all shifts.

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

KPIs provides the KPIs for the output including:

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

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 Shift rotations and patterns.

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