Docs
  • Solver
  • Models
    • Field Service Routing
    • Employee Shift Scheduling
  • Platform
Try models
  • Employee Shift Scheduling
  • Employee resource constraints
  • Time off
  • Shifts to avoid close to day off requests

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

Shifts to avoid close to day off requests

There are different techniques for managing employee’s time off.

When Employees have days off, there are shifts it would be better not to assign them before or after the day off. For instance, an afternoon shift before a day off and an early shift after a day off.

This guide shows you how to avoid assigning certain shifts to employees before and after their day off requests.

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 which shifts to avoid close to employee day off requests

Assigning an afternoon shift to an employee directly before a day off might make it difficult for the employee to use their day off as they intended. Similarly, employees might prefer not to have an early shift after a day off.

Days off are defined by employee availability. See Employee availability for details.

avoidShiftCloseToDayOffRequestRules are defined in contracts:

{
  "contracts": [
    {
      "id": "fullTimeContract",
      "avoidShiftCloseToDayOffRequestRules": [
        {
          "id": "noNightAndMorningShiftsNearDayOff",
          "avoidPriorShiftTags": [ "Afternoon" ],
          "avoidAfterShiftTags": [ "Morning" ],
          "shiftTagMatches": "ANY",
          "satisfiability": "PROHIBITED"
        }
      ]
    }
  ]
}

avoidShiftCloseToDayOffRequestRules must include an ID.

avoidPriorShiftTags must include the tags for the shifts to exclude prior to a day off request, for instance, Afternoon. If no tags are provided, the rule has no effect on shifts the day prior to a day off request.

avoidAfterShiftTags must include the tags for the shifts to exclude after a day off request, for instance, Morning. If no tags are provided, the rule has no effect on shifts the day after a day off request.

Tags are defined in shifts:

{
  "id": "Mon 1",
  "start": "2027-02-01T06:00:00-04:00",
  "end": "2027-02-01T14:00:00-04:00",
  "tags": [
    "Morning"
  ]
}

shiftTagMatches can be set to ALL or ANY. shiftTagMatches is optional and set to ALL by default if omitted.

With shiftTagMatches set to ALL, all tags defined by the rule’s avoidPriorShiftTags and avoidAfterShiftTags attributes must be present in the shift.

With shiftTagMatches set to ANY, at least one tag defined by the rule’s avoidPriorShiftTags and avoidAfterShiftTags attributes must be present in the shift.

The satisfiability of the rule can be PROHIBITED or UNPREFERRED. If omitted PROHIBITED is used by default.

2. Prohibited shifts to avoid close to employee day off requests

When the satisfiability of the rule is PROHIBITED, the Employee has prohibited shift near day off request hard constraint is invoked, which makes sure shifts with tags referenced in avoidPriorShiftTags and avoidAfterShiftTags are not assigned before or after a day off request respectively.

Shifts will be left unassigned if assigning them would break the Employee has prohibited shift near day off request constraint.

In the following example, Ann can be assigned morning, afternoon, or night shifts.

She has Wednesday off as defined by the employee unavailableTimeSpans. The unavailable timespan starts at 00:00 on Wednesday and ends at 00:00 on Thursday, and she cannot be assigned to any shifts that overlap this time period.

The avoidShiftCloseToDayOffRequestRules prohibits Ann from working an afternoon shift the day before her day off and from working a morning shift the day after her day off.

prohibited shifts to avoid close to day off requests

  • 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 shifts to avoid close to day off requests example"
    }
  },
  "modelInput": {
    "contracts": [
      {
        "id": "fullTimeContract",
        "consecutiveDaysWorkedRules": [
          {
            "id": "Max5ConsecutiveDaysFullTime",
            "maximum": 5
          }
        ],
        "periodRules": [
          {
            "id": "Max8HoursPerDayFullTime",
            "period": "DAY",
            "minutesWorkedMax": 480
          },
          {
            "id": "Max40HoursPerWeekFullTime",
            "period": "WEEK",
            "minutesWorkedMax": 2400
          }
        ],
        "minutesBetweenShiftsRules": [
          {
            "id": "Minimum12HoursBetweenShiftsFullTime",
            "minimumMinutesBetweenShifts": 720
          }
        ],
        "avoidShiftCloseToDayOffRequestRules": [
          {
            "id": "noMorningShiftsNearDayOff",
            "avoidPriorShiftTags": [
              "Afternoon"
            ],
            "avoidAfterShiftTags": [
              "Morning"
            ],
            "shiftTagMatches": "ANY",
            "satisfiability": "PROHIBITED"
          }
        ]
      }
    ],
    "employees": [
      {
        "id": "Ann",
        "contracts": [
          "fullTimeContract"
        ],
        "unavailableTimeSpans": [
          {
            "start": "2027-02-03T00:00:00-04:00",
            "end": "2027-02-04T00:00:00-04:00"
          }
        ]
      }
    ],
    "shifts": [
      {
        "id": "Mon 1",
        "start": "2027-02-01T06:00:00-04:00",
        "end": "2027-02-01T14:00:00-04:00",
        "tags": [
          "Morning"
        ]
      },
      {
        "id": "Mon 2",
        "start": "2027-02-01T14:00:00-04:00",
        "end": "2027-02-01T22:00:00-04:00",
        "tags": [
          "Afternoon"
        ]
      },
      {
        "id": "Mon 3",
        "start": "2027-02-01T22:00:00-04:00",
        "end": "2027-02-02T06:00:00-04:00",
        "tags": [
          "Night"
        ]
      },
      {
        "id": "Tue 1",
        "start": "2027-02-02T06:00:00-04:00",
        "end": "2027-02-02T14:00:00-04:00",
        "tags": [
          "Morning"
        ]
      },
      {
        "id": "Tue 2",
        "start": "2027-02-02T14:00:00-04:00",
        "end": "2027-02-02T22:00:00-04:00",
        "tags": [
          "Afternoon"
        ]
      },
      {
        "id": "Tue 3",
        "start": "2027-02-02T22:00:00-04:00",
        "end": "2027-02-03T06:00:00-04:00",
        "tags": [
          "Night"
        ]
      },
      {
        "id": "Wed 1",
        "start": "2027-02-03T06:00:00-04:00",
        "end": "2027-02-03T14:00:00-04:00",
        "tags": [
          "Morning"
        ]
      },
      {
        "id": "Wed 2",
        "start": "2027-02-03T14:00:00-04:00",
        "end": "2027-02-03T22:00:00-04:00",
        "tags": [
          "Afternoon"
        ]
      },
      {
        "id": "Wed 3",
        "start": "2027-02-03T22:00:00-04:00",
        "end": "2027-02-04T06:00:00-04:00",
        "tags": [
          "Night"
        ]
      },
      {
        "id": "Thu 1",
        "start": "2027-02-04T06:00:00-04:00",
        "end": "2027-02-04T14:00:00-04:00",
        "tags": [
          "Morning"
        ]
      },
      {
        "id": "Thu 2",
        "start": "2027-02-04T14:00:00-04:00",
        "end": "2027-02-04T22:00:00-04:00",
        "tags": [
          "Afternoon"
        ]
      },
      {
        "id": "Thu 3",
        "start": "2027-02-04T22:00:00-04:00",
        "end": "2027-02-05T06:00:00-04:00",
        "tags": [
          "Night"
        ]
      },
      {
        "id": "Fri 1",
        "start": "2027-02-05T06:00:00-04:00",
        "end": "2027-02-05T14:00:00-04:00",
        "tags": [
          "Morning"
        ]
      },
      {
        "id": "Fri 2",
        "start": "2027-02-05T14:00:00-04:00",
        "end": "2027-02-05T22:00:00-04:00",
        "tags": [
          "Afternoon"
        ]
      }
    ]
  }
}
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 shifts to avoid close to day off requests example",
    "submitDateTime": "2025-05-20T10:12:21.305841929Z",
    "startDateTime": "2025-05-20T10:12:32.814384363Z",
    "activeDateTime": "2025-05-20T10:12:32.998286725Z",
    "completeDateTime": "2025-05-20T10:13:03.911662405Z",
    "shutdownDateTime": "2025-05-20T10:13:04.214871231Z",
    "solverStatus": "SOLVING_COMPLETED",
    "score": "0hard/-10medium/0soft",
    "tags": [
      "system.profile:default"
    ],
    "validationResult": {
      "summary": "OK"
    }
  },
  "modelOutput": {
    "shifts": [
      {
        "id": "Mon 1",
        "employee": "Ann"
      },
      {
        "id": "Mon 2",
        "employee": null
      },
      {
        "id": "Mon 3",
        "employee": null
      },
      {
        "id": "Tue 1",
        "employee": "Ann"
      },
      {
        "id": "Tue 2",
        "employee": null
      },
      {
        "id": "Tue 3",
        "employee": null
      },
      {
        "id": "Wed 1",
        "employee": null
      },
      {
        "id": "Wed 2",
        "employee": null
      },
      {
        "id": "Wed 3",
        "employee": null
      },
      {
        "id": "Thu 1",
        "employee": null
      },
      {
        "id": "Thu 2",
        "employee": "Ann"
      },
      {
        "id": "Thu 3",
        "employee": null
      },
      {
        "id": "Fri 1",
        "employee": null
      },
      {
        "id": "Fri 2",
        "employee": "Ann"
      }
    ]
  },
  "inputMetrics": {
    "employees": 1,
    "shifts": 14,
    "pinnedShifts": 0
  },
  "kpis": {
    "assignedShifts": 4,
    "unassignedShifts": 10,
    "workingTimeFairnessPercentage": null,
    "disruptionPercentage": 0.0,
    "averageDurationOfEmployeesPreferencesMet": null,
    "minimumDurationOfPreferencesMetAcrossEmployees": null,
    "averageDurationOfEmployeesUnpreferencesViolated": null,
    "maximumDurationOfUnpreferencesViolatedAcrossEmployees": null,
    "activatedEmployees": 1,
    "assignedMandatoryShifts": 4,
    "assignedOptionalShifts": 0,
    "assignedShiftGroups": null,
    "unassignedShiftGroups": null,
    "travelDistance": 0
  }
}

modelOutput contains the schedule with Ann not assigned an Afternoon shift before her day off or a Morning shift after her day off.

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

KPIs provides the KPIs for the output including:

{
  "assignedShifts": 4,
  "unassignedShifts": 10,
  "activatedEmployees": 1,
  "assignedMandatoryShifts": 4
}

3. Unpreferred shifts to avoid close to employee day off requests

When the satisfiability of the rule is UNPREFERRED, the Employee has unpreferred shift near day off request soft constraint is invoked.

{
  "contracts": [
    {
      "id": "fullTimeContract",
      "avoidShiftCloseToDayOffRequestRules": [
        {
          "id": "noNightAndMorningShiftsNearDayOff",
          "avoidPriorShiftTags": [ "Afternoon" ],
          "avoidAfterShiftTags": [ "Morning" ],
          "shiftTagMatches": "ANY",
          "satisfiability": "UNPREFERRED"
        }
      ]
    }
  ]
}

Shifts with tags referenced in avoidPriorShiftTags and avoidAfterShiftTags might be assigned before or after a day off request respectively, but this constraint adds a soft penalty to the run score for any matches to the constraint, incentivizing Timefold to find an alternative solution.

In the following example, Ann can be assigned morning, afternoon, or night shifts.

She has Wednesday off as defined by the employee unavailableTimeSpans. The unavailable timespan starts at 00:00 on Wednesday and ends at 00:00 on Thursday, and she cannot be assigned to any shifts that overlap this time period.

The avoidShiftCloseToDayOffRequestRules prefer Ann not work an afternoon shift the day before her day off and from working a morning shift the day after her day off.

unpreferred shifts to avoid close to day off requests
  • 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 shifts to avoid close to day off requests example"
    }
  },
  "modelInput": {
    "contracts": [
      {
        "id": "fullTimeContract",
        "consecutiveDaysWorkedRules": [
          {
            "id": "Max5ConsecutiveDaysFullTime",
            "maximum": 5
          }
        ],
        "periodRules": [
          {
            "id": "Max8HoursPerDayFullTime",
            "period": "DAY",
            "minutesWorkedMax": 480
          },
          {
            "id": "Max40HoursPerWeekFullTime",
            "period": "WEEK",
            "minutesWorkedMax": 2400
          }
        ],
        "minutesBetweenShiftsRules": [
          {
            "id": "Minimum12HoursBetweenShiftsFullTime",
            "minimumMinutesBetweenShifts": 720
          }
        ],
        "avoidShiftCloseToDayOffRequestRules": [
          {
            "id": "noMorningShiftsNearDayOff",
            "avoidPriorShiftTags": [
              "Afternoon"
            ],
            "avoidAfterShiftTags": [
              "Morning"
            ],
            "shiftTagMatches": "ANY",
            "satisfiability": "UNPREFERRED"
          }
        ]
      }
    ],
    "employees": [
      {
        "id": "Ann",
        "contracts": [
          "fullTimeContract"
        ],
        "unavailableTimeSpans": [
          {
            "start": "2027-02-03T00:00:00-04:00",
            "end": "2027-02-04T00:00:00-04:00"
          }
        ]
      }
    ],
    "shifts": [
      {
        "id": "Mon 1",
        "start": "2027-02-01T06:00:00-04:00",
        "end": "2027-02-01T14:00:00-04:00",
        "tags": [
          "Morning"
        ]
      },
      {
        "id": "Mon 2",
        "start": "2027-02-01T14:00:00-04:00",
        "end": "2027-02-01T22:00:00-04:00",
        "tags": [
          "Afternoon"
        ]
      },
      {
        "id": "Mon 3",
        "start": "2027-02-01T22:00:00-04:00",
        "end": "2027-02-02T06:00:00-04:00",
        "tags": [
          "Night"
        ]
      },
      {
        "id": "Tue 1",
        "start": "2027-02-02T06:00:00-04:00",
        "end": "2027-02-02T14:00:00-04:00",
        "tags": [
          "Morning"
        ]
      },
      {
        "id": "Tue 2",
        "start": "2027-02-02T14:00:00-04:00",
        "end": "2027-02-02T22:00:00-04:00",
        "tags": [
          "Afternoon"
        ]
      },
      {
        "id": "Tue 3",
        "start": "2027-02-02T22:00:00-04:00",
        "end": "2027-02-03T06:00:00-04:00",
        "tags": [
          "Night"
        ]
      },
      {
        "id": "Wed 1",
        "start": "2027-02-03T06:00:00-04:00",
        "end": "2027-02-03T14:00:00-04:00",
        "tags": [
          "Morning"
        ]
      },
      {
        "id": "Wed 2",
        "start": "2027-02-03T14:00:00-04:00",
        "end": "2027-02-03T22:00:00-04:00",
        "tags": [
          "Afternoon"
        ]
      },
      {
        "id": "Wed 3",
        "start": "2027-02-03T22:00:00-04:00",
        "end": "2027-02-04T06:00:00-04:00",
        "tags": [
          "Night"
        ]
      },
      {
        "id": "Thu 1",
        "start": "2027-02-04T06:00:00-04:00",
        "end": "2027-02-04T14:00:00-04:00",
        "tags": [
          "Morning"
        ]
      },
      {
        "id": "Thu 2",
        "start": "2027-02-04T14:00:00-04:00",
        "end": "2027-02-04T22:00:00-04:00",
        "tags": [
          "Afternoon"
        ]
      },
      {
        "id": "Thu 3",
        "start": "2027-02-04T22:00:00-04:00",
        "end": "2027-02-05T06:00:00-04:00",
        "tags": [
          "Night"
        ]
      },
      {
        "id": "Fri 1",
        "start": "2027-02-05T06:00:00-04:00",
        "end": "2027-02-05T14:00:00-04:00",
        "tags": [
          "Morning"
        ]
      },
      {
        "id": "Fri 2",
        "start": "2027-02-05T14:00:00-04:00",
        "end": "2027-02-05T22:00:00-04:00",
        "tags": [
          "Afternoon"
        ]
      }
    ]
  }
}
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 shifts to avoid close to day off requests example",
    "submitDateTime": "2025-05-20T10:12:35.153186825Z",
    "startDateTime": "2025-05-20T10:12:53.790666141Z",
    "activeDateTime": "2025-05-20T10:12:53.99578904Z",
    "completeDateTime": "2025-05-20T10:13:25.690988115Z",
    "shutdownDateTime": "2025-05-20T10:13:25.956686042Z",
    "solverStatus": "SOLVING_COMPLETED",
    "score": "0hard/-10medium/0soft",
    "tags": [
      "system.profile:default"
    ],
    "validationResult": {
      "summary": "OK"
    }
  },
  "modelOutput": {
    "shifts": [
      {
        "id": "Mon 1",
        "employee": "Ann"
      },
      {
        "id": "Mon 2",
        "employee": null
      },
      {
        "id": "Mon 3",
        "employee": null
      },
      {
        "id": "Tue 1",
        "employee": "Ann"
      },
      {
        "id": "Tue 2",
        "employee": null
      },
      {
        "id": "Tue 3",
        "employee": null
      },
      {
        "id": "Wed 1",
        "employee": null
      },
      {
        "id": "Wed 2",
        "employee": null
      },
      {
        "id": "Wed 3",
        "employee": null
      },
      {
        "id": "Thu 1",
        "employee": null
      },
      {
        "id": "Thu 2",
        "employee": "Ann"
      },
      {
        "id": "Thu 3",
        "employee": null
      },
      {
        "id": "Fri 1",
        "employee": null
      },
      {
        "id": "Fri 2",
        "employee": "Ann"
      }
    ]
  },
  "inputMetrics": {
    "employees": 1,
    "shifts": 14,
    "pinnedShifts": 0
  },
  "kpis": {
    "assignedShifts": 4,
    "unassignedShifts": 10,
    "workingTimeFairnessPercentage": null,
    "disruptionPercentage": 0.0,
    "averageDurationOfEmployeesPreferencesMet": null,
    "minimumDurationOfPreferencesMetAcrossEmployees": null,
    "averageDurationOfEmployeesUnpreferencesViolated": null,
    "maximumDurationOfUnpreferencesViolatedAcrossEmployees": null,
    "activatedEmployees": 1,
    "assignedMandatoryShifts": 4,
    "assignedOptionalShifts": 0,
    "assignedShiftGroups": null,
    "unassignedShiftGroups": null,
    "travelDistance": 0
  }
}

modelOutput contains the schedule with Ann not assigned an Afternoon shift before her day off or a Morning shift after her day off.

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

KPIs provides the KPIs for the output including:

{
  "assignedShifts": 4,
  "unassignedShifts": 10,
  "activatedEmployees": 1,
  "assignedMandatoryShifts": 4
}

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