Docs
  • Solver
  • Models
    • Field Service Routing
    • Employee Shift Scheduling
  • Platform
Try models
  • Employee Shift Scheduling
  • Employee resource constraints
  • Time off
  • Days off per period

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 rules
        • Shifts worked per period
        • Shifts worked in a rolling window
      • Time off
        • Time off
        • 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 type diversity
      • Shift rotation and patterns
      • Fairness
      • Pairing employees
      • Shift travel and locations
    • Shift service constraints
      • Alternative shifts
      • Cost management
      • Demand and supply
      • Mandatory and optional shifts
      • 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

Days off per period

There are different techniques for managing employees' time off.

This guide shows you how to include days off per period. Days off per period can be specified per week, month, or the entire schedule.

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. Define days off per period

Period rules are configured in contracts:

{
  "contracts": [
    {
      "id": "fullTimeContract",
      "periodRules": [
        {
          "id": "min2DaysOff",
          "period": "WEEK",
          "daysOffLimit":
          {
            "daysOffMin": 2
          },
          "satisfiability": "REQUIRED"
        }
      ]
    }
  ]
}

PeriodRules must include an ID.

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

Further information about period:

DAY spans a single day and occurs every day in the schedule.

WEEK spans 7 days and occurs every week (including partial weeks) in the schedule. The default start of the week is Monday, but this can be overridden to any day in the week:

{
  "scheduleParameterization": {
    "weekStart": "THURSDAY"
  }
}

MONTH spans the entire month. MONTH has a variable number of days depending on the days in the month and occurs every month (including partial months) in the schedule.

SCHEDULE spans the entire schedule.

You can define custom periods to apply to this rule in the following way:
{
  "scheduleParameterization": {
    "periods": [
      {
        "id": "PAY_PERIOD",
        "dateSpans": [
          {
            "start": "2023-01-01",
            "end": "2023-01-15"
          }
        ]
      }
    ]
  }
}

The start and end dates are inclusive.

The daysOffLimit object must include daysOffMin with the minimum number of days off.

You can optionally use includeDays to specify the days of the week that count toward the total. If includeDays is not included, all days count toward the total.

To create a rule that only counts weekdays and excludes weekends:

{
  "contracts": [
    {
      "id": "fullTimeContract",
      "periodRules": [
        {
          "id": "min2DaysOff",
          "period": "WEEK",
          "daysOffLimit":
          {
            "daysOffMin": 2,
            "includeDays": ["MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY"]
          },
          "satisfiability": "REQUIRED"
        }
      ]
    }
  ]
}

Each employee must specify which contracts apply to them:

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

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

2. Required days off per period

When the satisfiability of the rule is REQUIRED, the Days off per period not in required range for employee hard constraint is invoked, which makes sure the number of days off in the period is not below the limit specified in daysOffMin.

Shifts will be left unassigned if assigning them would break the Days off per period not in required range for employee constraint.

In the following example, there are 7 shifts, but Ann’s contract has a daysOffMin of 2. She is assigned 5 shifts, 2 shifts are left unassigned, and Ann has 2 days off.

required days off per period
  • 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 days off per period example"
    }
  },
  "modelInput": {
    "contracts": [
      {
        "id": "fullTimeContract",
        "periodRules": [
          {
            "id": "min2DaysOff",
            "period": "WEEK",
            "daysOffLimit":
            {
              "daysOffMin": 2
            },
            "satisfiability": "REQUIRED"
          }
        ]
      }
    ],
    "employees": [
      {
        "id": "Ann",
        "contracts": [
          "fullTimeContract"
        ]
      }
    ],
    "shifts": [
      {
        "id": "Mon",
        "start": "2027-02-01T09:00:00Z",
        "end": "2027-02-01T17:00:00Z"
      },
      {
        "id": "Tue",
        "start": "2027-02-02T09:00:00Z",
        "end": "2027-02-02T17:00:00Z"
      },
      {
        "id": "Wed",
        "start": "2027-02-03T09:00:00Z",
        "end": "2027-02-03T17:00:00Z"
      },
      {
        "id": "Thu",
        "start": "2027-02-04T09:00:00Z",
        "end": "2027-02-04T17:00:00Z"
      },
      {
        "id": "Fri",
        "start": "2027-02-05T09:00:00Z",
        "end": "2027-02-05T17:00:00Z"
      },
      {
        "id": "Sat",
        "start": "2027-02-06T09:00:00Z",
        "end": "2027-02-06T17:00:00Z"
      },
      {
        "id": "Sun",
        "start": "2027-02-07T09:00:00Z",
        "end": "2027-02-07T17: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": "Required days off per period example",
    "submitDateTime": "2025-05-15T04:12:48.841418699Z",
    "startDateTime": "2025-05-15T04:13:06.647742868Z",
    "activeDateTime": "2025-05-15T04:13:06.879503897Z",
    "completeDateTime": "2025-05-15T04:13:37.675762988Z",
    "shutdownDateTime": "2025-05-15T04:13:37.960206521Z",
    "solverStatus": "SOLVING_COMPLETED",
    "score": "0hard/-2medium/0soft",
    "tags": [
      "system.profile:default"
    ],
    "validationResult": {
      "summary": "OK"
    }
  },
  "modelOutput": {
    "shifts": [
      {
        "id": "Mon",
        "employee": "Ann"
      },
      {
        "id": "Tue",
        "employee": "Ann"
      },
      {
        "id": "Wed",
        "employee": "Ann"
      },
      {
        "id": "Thu",
        "employee": "Ann"
      },
      {
        "id": "Fri",
        "employee": "Ann"
      },
      {
        "id": "Sat",
        "employee": null
      },
      {
        "id": "Sun",
        "employee": null
      }
    ]
  },
  "inputMetrics": {
    "employees": 1,
    "shifts": 7,
    "pinnedShifts": 0
  },
  "kpis": {
    "assignedShifts": 5,
    "unassignedShifts": 2,
    "workingTimeFairnessPercentage": null,
    "disruptionPercentage": 0.0,
    "averageDurationOfEmployeesPreferencesMet": null,
    "minimumDurationOfPreferencesMetAcrossEmployees": null,
    "averageDurationOfEmployeesUnpreferencesViolated": null,
    "maximumDurationOfUnpreferencesViolatedAcrossEmployees": null,
    "activatedEmployees": 1,
    "assignedMandatoryShifts": 5,
    "assignedOptionalShifts": 0,
    "assignedShiftGroups": null,
    "unassignedShiftGroups": null,
    "travelDistance": 0
  }
}

modelOutput contains the schedule with Ann not being assigned shifts on 2 days of the week.

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

KPIs provides the KPIs for the output including:

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

3. Preferred days off per period

When the satisfiability of the rule is PREFERRED, the Days off per period not in preferred range for employee soft constraint is invoked.

With PREFERRED satisfiability you can also use daysOffMax to specify the maximum number of days off.

{
  "contracts": [
    {
      "id": "fullTimeContract",
      "periodRules": [
        {
          "id": "min2DaysOff",
          "period": "WEEK",
          "daysOffLimit":
          {
            "daysOffMin": 1,
            "daysOffMax": 3
          },
          "satisfiability": "PREFERRED"
        }
      ]
    }
  ]
}

This constraint adds a soft penalty if the number of days off is below the limit set in daysoffMin or above the limit set in daysOffMax.

Timefold is incentivized to use the solution with the best score.

In the following example, there are 7 shifts, but Ann’s contract has a daysOffMin of 1 and a daysOffMax of 3. She is assigned all 7 shifts, and a soft penalty is applied to the run score.

preferred days off per period example
  • 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 days off per period example"
    }
  },
  "modelInput": {
    "contracts": [
      {
        "id": "fullTimeContract",
        "periodRules": [
          {
            "id": "min2DaysOff",
            "period": "WEEK",
            "daysOffLimit":
            {
              "daysOffMin": 1,
              "daysOffMax": 3
            },
            "satisfiability": "PREFERRED"
          }
        ]
      }
    ],
    "employees": [
      {
        "id": "Ann",
        "contracts": [
          "fullTimeContract"
        ]
      }
    ],
    "shifts": [
      {
        "id": "Mon",
        "start": "2027-02-01T09:00:00Z",
        "end": "2027-02-01T17:00:00Z"
      },
      {
        "id": "Tue",
        "start": "2027-02-02T09:00:00Z",
        "end": "2027-02-02T17:00:00Z"
      },
      {
        "id": "Wed",
        "start": "2027-02-03T09:00:00Z",
        "end": "2027-02-03T17:00:00Z"
      },
      {
        "id": "Thu",
        "start": "2027-02-04T09:00:00Z",
        "end": "2027-02-04T17:00:00Z"
      },
      {
        "id": "Fri",
        "start": "2027-02-05T09:00:00Z",
        "end": "2027-02-05T17:00:00Z"
      },
      {
        "id": "Sat",
        "start": "2027-02-06T09:00:00Z",
        "end": "2027-02-06T17:00:00Z"
      },
      {
        "id": "Sun",
        "start": "2027-02-07T09:00:00Z",
        "end": "2027-02-07T17: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 days off per period example",
    "submitDateTime": "2025-05-15T09:43:13.255755752Z",
    "startDateTime": "2025-05-15T09:43:25.755410242Z",
    "activeDateTime": "2025-05-15T09:43:25.932572105Z",
    "completeDateTime": "2025-05-15T09:43:56.770085733Z",
    "shutdownDateTime": "2025-05-15T09:43:57.086570792Z",
    "solverStatus": "SOLVING_COMPLETED",
    "score": "0hard/0medium/-960soft",
    "tags": [
      "system.profile:default"
    ],
    "validationResult": {
      "summary": "OK"
    }
  },
  "modelOutput": {
    "shifts": [
      {
        "id": "Mon",
        "employee": "Ann"
      },
      {
        "id": "Tue",
        "employee": "Ann"
      },
      {
        "id": "Wed",
        "employee": "Ann"
      },
      {
        "id": "Thu",
        "employee": "Ann"
      },
      {
        "id": "Fri",
        "employee": "Ann"
      },
      {
        "id": "Sat",
        "employee": "Ann"
      },
      {
        "id": "Sun",
        "employee": "Ann"
      }
    ]
  },
  "inputMetrics": {
    "employees": 1,
    "shifts": 7,
    "pinnedShifts": 0
  },
  "kpis": {
    "assignedShifts": 7,
    "unassignedShifts": 0,
    "workingTimeFairnessPercentage": null,
    "disruptionPercentage": 0.0,
    "averageDurationOfEmployeesPreferencesMet": null,
    "minimumDurationOfPreferencesMetAcrossEmployees": null,
    "averageDurationOfEmployeesUnpreferencesViolated": null,
    "maximumDurationOfUnpreferencesViolatedAcrossEmployees": null,
    "activatedEmployees": 1,
    "assignedMandatoryShifts": 7,
    "assignedOptionalShifts": 0,
    "assignedShiftGroups": null,
    "unassignedShiftGroups": null,
    "travelDistance": 0
  }
}

modelOutput contains the schedule with all 7 shifts assigned.

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

KPIs provides the KPIs for the output including:

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

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' Time off.

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