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:
-
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. 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.
-
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.

-
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.