Real-time planning
Employee shift schedules are typically published a few weeks in advance to allow employees to plan the rest of their lives around the shifts they’ll be working.
This means employees don’t need to organize things (social events, appointments, child care, life administration) at the last minute which reduces stress and improves their job satisfaction.
However, even the best schedule can be disrupted at the last minute by employees calling in sick or a sudden need for additional staff.
Real-time planning makes it possible to assign new employees to shifts, due to sick leave or additional demand, and to minimize the disruption caused by the re-planning process.
This guide explains real-time planning with the following examples:
Real-time planning due to illness
Learn how to configure an API Key to run the examples in this guide:
In the examples, replace |
In the following example, the wait staff at a restaurant are scheduled with staggered start and end times to ensure there are enough people to cover the busy period. A typical night at the restaurant requires 4 wait staff working the following shifts:
-
1 person from 16:00 to 20:00 to set up, take early orders, and cover the busy period.
-
2 people from 17:00 to 21:00 to cover the busy period.
-
1 person from 18:00 to 22:00 to cover the busy period, help pack up, and prepare for the following day.
The schedule was generated with the following input dataset:
-
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": "Published restaurant schedule"
}
},
"modelInput": {
"employees": [
{
"id": "Ann",
"availableTimeSpans": [
{
"start": "2027-02-01T16:00:00Z",
"end": "2027-02-01T22:00:00Z"
}
]
},
{
"id": "Beth",
"availableTimeSpans": [
{
"start": "2027-02-01T16:00:00Z",
"end": "2027-02-01T22:00:00Z"
}
]
},
{
"id": "Carl",
"availableTimeSpans": [
{
"start": "2027-02-01T16:00:00Z",
"end": "2027-02-01T22:00:00Z"
}
]
},
{
"id": "Dan",
"availableTimeSpans": [
{
"start": "2027-02-01T16:00:00Z",
"end": "2027-02-01T22:00:00Z"
}
]
},
{
"id": "Elsa",
"availableTimeSpans": [
{
"start": "2027-02-01T16:00:00Z",
"end": "2027-02-01T22:00:00Z"
}
]
},
{
"id": "Flo",
"availableTimeSpans": [
{
"start": "2027-02-01T16:00:00Z",
"end": "2027-02-01T22:00:00Z"
}
]
}
],
"shifts": [
{
"id": "Dinner early",
"start": "2027-02-01T16:00:00Z",
"end": "2027-02-01T20:00:00Z"
},
{
"id": "Dinner 1",
"start": "2027-02-01T17:00:00Z",
"end": "2027-02-01T21:00:00Z"
},
{
"id": "Dinner 2",
"start": "2027-02-01T17:00:00Z",
"end": "2027-02-01T21:00:00Z"
},
{
"id": "Dinner late",
"start": "2027-02-01T18:00:00Z",
"end": "2027-02-01T22: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>
{
"metadata": {
"id": "ID",
"name": "Published restaurant schedule",
"submitDateTime": "2025-02-03T07:37:53.753457408Z",
"startDateTime": "2025-02-03T07:38:00.02735691Z",
"activeDateTime": "2025-02-03T07:38:00.122121923Z",
"completeDateTime": "2025-02-03T07:43:00.263111216Z",
"shutdownDateTime": "2025-02-03T07:43:00.391541184Z",
"solverStatus": "SOLVING_COMPLETED",
"score": "0hard/0medium/0soft",
"tags": [],
"validationResult": {
"summary": "OK"
}
},
"modelOutput": {
"shifts": [
{
"id": "Dinner early",
"employee": "Ann"
},
{
"id": "Dinner 1",
"employee": "Beth"
},
{
"id": "Dinner 2",
"employee": "Carl"
},
{
"id": "Dinner late",
"employee": "Dan"
}
]
},
"kpis": {
"assignedShifts": 4,
"unassignedShifts": 0,
"workingTimeFairnessPercentage": null,
"disruptionPercentage": 0.0,
"averageDurationOfEmployeesPreferencesMet": null,
"minimumDurationOfPreferencesMetAcrossEmployees": null,
"averageDurationOfEmployeesUnpreferencesViolated": null,
"maximumDurationOfUnpreferencesViolatedAcrossEmployees": null
}
}
modelOutput contains the shift assignments for the schedule.
Unfortunately, Carl has called in sick for his shift, so the schedule needs to be regenerated.
The input dataset needs to be updated and resubmitted.
First, make Carl unavailable by removing his availableTimeSpans and adding an unavailableTimeSpan.
Carl is sick for the entire day and unavailable for any shifts that day:
{
"id": "Carl",
"unavailableTimeSpans": [
{
"start": "2027-02-01T00:00:00Z",
"end": "2027-02-02T00:00:00Z"
}
]
}
Learn more about Employee availability.
Next, the shifts that have already been assigned need to be pinned with the name of the employee they have been assigned to, to prevent changes to those shift assignments. Because Carl is ill, the shift he was assigned to is not pinned.
{
"shifts": [
{
"id": "Dinner early",
"start": "2027-02-01T16:00:00Z",
"end": "2027-02-01T20:00:00Z",
"pinned": true,
"employee": "Ann"
},
{
"id": "Dinner 1",
"start": "2027-02-01T17:00:00Z",
"end": "2027-02-01T21:00:00Z",
"pinned": true,
"employee": "Beth"
},
{
"id": "Dinner 2",
"start": "2027-02-01T17:00:00Z",
"end": "2027-02-01T21:00:00Z"
},
{
"id": "Dinner late",
"start": "2027-02-01T18:00:00Z",
"end": "2027-02-01T22:00:00Z",
"pinned": true,
"employee": "Dan"
}
]
}
Finally, submit the updated input dataset:
-
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": "Real-time restaurant schedule due to illness"
}
},
"modelInput": {
"employees": [
{
"id": "Ann",
"availableTimeSpans": [
{
"start": "2027-02-01T16:00:00Z",
"end": "2027-02-01T22:00:00Z"
}
]
},
{
"id": "Beth",
"availableTimeSpans": [
{
"start": "2027-02-01T16:00:00Z",
"end": "2027-02-01T22:00:00Z"
}
]
},
{
"id": "Carl",
"unavailableTimeSpans": [
{
"start": "2027-02-01T00:00:00Z",
"end": "2027-02-02T00:00:00Z"
}
]
},
{
"id": "Dan",
"availableTimeSpans": [
{
"start": "2027-02-01T16:00:00Z",
"end": "2027-02-01T22:00:00Z"
}
]
},
{
"id": "Elsa",
"availableTimeSpans": [
{
"start": "2027-02-01T16:00:00Z",
"end": "2027-02-01T22:00:00Z"
}
]
},
{
"id": "Flo",
"availableTimeSpans": [
{
"start": "2027-02-01T16:00:00Z",
"end": "2027-02-01T22:00:00Z"
}
]
}
],
"shifts": [
{
"id": "Dinner early",
"start": "2027-02-01T16:00:00Z",
"end": "2027-02-01T20:00:00Z",
"pinned": true,
"employee": "Ann"
},
{
"id": "Dinner 1",
"start": "2027-02-01T17:00:00Z",
"end": "2027-02-01T21:00:00Z",
"pinned": true,
"employee": "Beth"
},
{
"id": "Dinner 2",
"start": "2027-02-01T17:00:00Z",
"end": "2027-02-01T21:00:00Z"
},
{
"id": "Dinner late",
"start": "2027-02-01T18:00:00Z",
"end": "2027-02-01T22:00:00Z",
"pinned": true,
"employee": "Dan"
}
]
}
}
| 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",
"name": "Real-time restaurant schedule due to illness",
"submitDateTime": "2025-02-03T08:00:19.893547713Z",
"startDateTime": "2025-02-03T08:00:26.191535664Z",
"activeDateTime": "2025-02-03T08:00:26.265615945Z",
"completeDateTime": "2025-02-03T08:05:26.428686234Z",
"shutdownDateTime": "2025-02-03T08:05:26.557861632Z",
"solverStatus": "SOLVING_COMPLETED",
"score": "0hard/0medium/0soft",
"tags": [],
"validationResult": {
"summary": "OK"
}
},
"modelOutput": {
"shifts": [
{
"id": "Dinner early",
"employee": "Ann"
},
{
"id": "Dinner 1",
"employee": "Beth"
},
{
"id": "Dinner 2",
"employee": "Elsa"
},
{
"id": "Dinner late",
"employee": "Dan"
}
]
},
"kpis": {
"assignedShifts": 4,
"unassignedShifts": 0,
"workingTimeFairnessPercentage": null,
"disruptionPercentage": 0.0,
"averageDurationOfEmployeesPreferencesMet": null,
"minimumDurationOfPreferencesMetAcrossEmployees": null,
"averageDurationOfEmployeesUnpreferencesViolated": null,
"maximumDurationOfUnpreferencesViolatedAcrossEmployees": null
}
}
modelOutput contains the updated shift assignments for the schedule with Elsa assigned to the shift Dan dropped.
In the new schedule, Ann, Beth, and Dan all have the same shifts they were originally assigned.
Real-time planning due to increased demand
If the restaurant has an unexpectedly busy night, for instance, a last minute booking for a large party, new shifts can be added to the input dataset, and the original shift assignments can be pinned to avoid changing employees' assigned shifts at the last minute.
In the following example, Carl doesn’t call in sick, but two more staff members are needed to cover the additional work.
-
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": "Real-time restaurant schedule due to increased demand"
}
},
"modelInput": {
"employees": [
{
"id": "Ann",
"availableTimeSpans": [
{
"start": "2027-02-01T16:00:00Z",
"end": "2027-02-01T22:00:00Z"
}
]
},
{
"id": "Beth",
"availableTimeSpans": [
{
"start": "2027-02-01T16:00:00Z",
"end": "2027-02-01T22:00:00Z"
}
]
},
{
"id": "Carl",
"availableTimeSpans": [
{
"start": "2027-02-01T16:00:00Z",
"end": "2027-02-01T22:00:00Z"
}
]
},
{
"id": "Dan",
"availableTimeSpans": [
{
"start": "2027-02-01T16:00:00Z",
"end": "2027-02-01T22:00:00Z"
}
]
},
{
"id": "Elsa",
"availableTimeSpans": [
{
"start": "2027-02-01T16:00:00Z",
"end": "2027-02-01T22:00:00Z"
}
]
},
{
"id": "Flo",
"availableTimeSpans": [
{
"start": "2027-02-01T16:00:00Z",
"end": "2027-02-01T22:00:00Z"
}
]
}
],
"shifts": [
{
"id": "Dinner early",
"start": "2027-02-01T16:00:00Z",
"end": "2027-02-01T20:00:00Z",
"pinned": true,
"employee": "Ann"
},
{
"id": "Dinner 1",
"start": "2027-02-01T17:00:00Z",
"end": "2027-02-01T21:00:00Z",
"pinned": true,
"employee": "Beth"
},
{
"id": "Dinner 2",
"start": "2027-02-01T17:00:00Z",
"end": "2027-02-01T21:00:00Z",
"pinned": true,
"employee": "Carl"
},
{
"id": "Dinner late",
"start": "2027-02-01T18:00:00Z",
"end": "2027-02-01T22:00:00Z",
"pinned": true,
"employee": "Dan"
},
{
"id": "Dinner 3",
"start": "2027-02-01T17:00:00Z",
"end": "2027-02-01T21:00:00Z"
},
{
"id": "Dinner late extra",
"start": "2027-02-01T18:00:00Z",
"end": "2027-02-01T22: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>
{
"metadata": {
"id": "ID",
"name": "Real-time restaurant schedule due to increased demand",
"submitDateTime": "2025-02-03T08:30:29.025201073Z",
"startDateTime": "2025-02-03T08:30:35.194175247Z",
"activeDateTime": "2025-02-03T08:30:35.320436545Z",
"completeDateTime": "2025-02-03T08:35:35.467520964Z",
"shutdownDateTime": "2025-02-03T08:35:35.6188066Z",
"solverStatus": "SOLVING_COMPLETED",
"score": "0hard/0medium/0soft",
"tags": [],
"validationResult": {
"summary": "OK"
}
},
"modelOutput": {
"shifts": [
{
"id": "Dinner early",
"employee": "Ann"
},
{
"id": "Dinner 1",
"employee": "Beth"
},
{
"id": "Dinner 2",
"employee": "Carl"
},
{
"id": "Dinner late",
"employee": "Dan"
},
{
"id": "Dinner 3",
"employee": "Elsa"
},
{
"id": "Dinner late extra",
"employee": "Flo"
}
]
},
"kpis": {
"assignedShifts": 6,
"unassignedShifts": 0,
"workingTimeFairnessPercentage": null,
"disruptionPercentage": 0.0,
"averageDurationOfEmployeesPreferencesMet": null,
"minimumDurationOfPreferencesMetAcrossEmployees": null,
"averageDurationOfEmployeesUnpreferencesViolated": null,
"maximumDurationOfUnpreferencesViolatedAcrossEmployees": null
}
}
modelOutput contains the updated schedule with Elsa and Flo assigned to the new shifts.
Minimizing disruption due to real-time planning
It’s not always possible (or even preferable) to pin every existing shift when real-time planning is necessary. However, you can minimize the disruption real-time planning causes by adding disruption configurations to employee contracts.
{
"contracts": [
{
"id": "Disruption rule contract",
"disruptionRules": [
{
"id": "disruptionRule",
"disruptionWeight": 1
}
]
}
]
}
Each entry in disruptionRules must include an id and a disruptionWeight.
The disruptionWeight field is a multiplier on the soft score which controls the importance of any disrupted shifts which match that rule.
This field must be an integer. Rules which are more important should get a higher disruptionWeight than those which are less important.
Control when different contract disruption rules apply
Contract disruption rules can be limited in the following ways:
-
Validity time spans can limit when the rules apply.
-
Shifts with specific tags can be included or excluded from the rules.
Validity Date Time Span
To define a time span when the rule is applied, add validityDateTimeSpan with start and end times.
If not provided, the rule is always valid in the period configured.
{
"validityDateTimeSpan": {
"start": "2027-02-01T00:00:00Z",
"end": "2027-02-08T00:00:00Z"
}
}
Example rule with validity date time span
{
"disruptionRules": [
{
"id": "disruptionRule",
"disruptionWeight": 1,
"validityDateTimeSpan": {
"start": "2025-10-13T00:00",
"end": "2025-10-20T00:00"
}
}
]
}
Further information about including or excluding shifts with shift tags:
Shifts with specific tags can be included or excluded by the rule. Tags are defined in shifts:
{
"shifts": [
{
"id": "2027-02-01",
"start": "2027-02-01T09:00:00Z",
"end": "2027-02-01T17:00:00Z",
"tags": ["Part-time"]
}
]
}
Use includeShiftTags to include shifts with specific tags or excludeShiftTags to exclude shifts with specific tags.
shiftTagMatches can be set to ALL or ANY.
The default behavior for shiftTagMatches is ALL, and if omitted, the default ALL will be used.
The rule can define either includeShiftTags or excludeShiftTags, but not both.
{
"includeShiftTags": ["Part-time", "Weekend"],
"shiftTagMatches": "ALL"
}
With shiftTagMatches set to ALL, all tags defined by the rule’s includeShiftTags attribute must be present in the shift. With shiftTagMatches set to ANY, at least one tag defined by the rule’s includeShiftTags attribute must be present in the shift.
{
"excludeShiftTags": ["Part-time", "Weekend"],
"shiftTagMatches": "ALL"
}
With shiftTagMatches set to ALL, all tags defined by the rule’s excludeShiftTags attribute cannot be present in the shift.
This is useful when you want to exclude things in combination with each other.
For instance, excluding the shift tags Part-time and Weekend with shiftTagMatches set to All, will exclude shifts that include the tags Part-time and Weekend from the rule.
Shifts tagged only Part-time or only Weekend will not be excluded.
With shiftTagMatches set to ANY, any of the tags defined by the rule’s excludeShiftTags attribute cannot be present in the shift.
This is useful when you need to exclude tags regardless of their relationship to other tags.
For instance, excluding the shift tags Part-time and Weekend with shiftTagMatches set to ANY, will exclude any shift that includes the tags Part-time or Weekend, whether they occur together or not.
Example include shift tags
{
"disruptionRules": [
{
"id": "disruptionRule",
"disruptionWeight": 1,
"validityDateTimeSpan": {
"start": "2025-10-13T00:00",
"end": "2025-10-20T00:00"
},
"includeShiftTags": ["ICU"]
}
]
}
Any disruption to the schedule will invoke two soft constraints and impact the soft score of the final solution, which incentivizes Timefold to minimize the disruption:
-
Minimize outgoing disruption per employee: penalizes when an employee loses shifts that were originally assigned to them. -
Minimize incoming disruption per employee: penalizes when an employee receives shifts that were not originally assigned to them.
This includes both shifts reassigned from another employee and completely new shifts added during replanning. From the employee’s perspective, any shift they did not have in the original schedule is a disruption to their personal arrangements.
| This rule is more likely to be satisfied for employees with a higher employee priority. See Employee priority for more details. |
|
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. |
|
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. |
In the following example, Ann, Beth, Carl, and Dan are assigned shifts in the restaurant, but Carl calls in sick.
This time, instead of pinning Ann’s, Beth’s, and Dan’s shifts, the employees who were originally assigned to the shifts
are added to the shift, pinned is set to false (or omitted altogether), and Carl is unavailable.
It is okay to disrupt Elsa, Flo and Carl, so they are given a contract with a low weight for the disruption rule.
It is much more negatively impactful to disrupt Ann, Beth or Dan, so they are given a contract with a high weight for the disruption rule.
-
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": "Real-time restaurant schedule with minimize disruptions"
}
},
"modelInput": {
"contracts": [
{
"id": "Disruption rule contract low importance",
"disruptionRules": [
{
"id": "disruption rule low weight",
"validityDateTimeSpan": {
"start": "2027-02-01T00:00:00Z",
"end": "2027-02-03T00:00:00Z"
},
"disruptionWeight": 1
}
]
},
{
"id": "Disruption rule contract high importance",
"disruptionRules": [
{
"id": "disruption rule high weight",
"validityDateTimeSpan": {
"start": "2027-02-01T00:00:00Z",
"end": "2027-02-03T00:00:00Z"
},
"disruptionWeight": 1000
}
]
}
],
"employees": [
{
"id": "Ann",
"contracts": [
"Disruption rule contract high importance"
],
"availableTimeSpans": [
{
"start": "2027-02-01T16:00:00Z",
"end": "2027-02-01T22:00:00Z"
}
]
},
{
"id": "Beth",
"contracts": [
"Disruption rule contract high importance"
],
"availableTimeSpans": [
{
"start": "2027-02-01T16:00:00Z",
"end": "2027-02-01T22:00:00Z"
}
]
},
{
"id": "Carl",
"contracts": [
"Disruption rule contract low importance"
],
"unavailableTimeSpans": [
{
"start": "2027-02-01T00:00:00Z",
"end": "2027-02-02T00:00:00Z"
}
]
},
{
"id": "Dan",
"contracts": [
"Disruption rule contract high importance"
],
"availableTimeSpans": [
{
"start": "2027-02-01T16:00:00Z",
"end": "2027-02-01T22:00:00Z"
}
]
},
{
"id": "Elsa",
"contracts": [
"Disruption rule contract low importance"
],
"availableTimeSpans": [
{
"start": "2027-02-01T16:00:00Z",
"end": "2027-02-01T22:00:00Z"
}
]
},
{
"id": "Flo",
"contracts": [
"Disruption rule contract low importance"
],
"availableTimeSpans": [
{
"start": "2027-02-01T16:00:00Z",
"end": "2027-02-01T22:00:00Z"
}
]
}
],
"shifts": [
{
"id": "2027-02-01-dinner-early",
"start": "2027-02-01T16:00:00Z",
"end": "2027-02-01T20:00:00Z",
"employee": "Ann"
},
{
"id": "2027-02-01-dinner-1",
"start": "2027-02-01T17:00:00Z",
"end": "2027-02-01T21:00:00Z",
"employee": "Beth"
},
{
"id": "2027-02-01-dinner-2",
"start": "2027-02-01T17:00:00Z",
"end": "2027-02-01T21:00:00Z",
"employee": "Carl"
},
{
"id": "2027-02-01-dinner-late",
"start": "2027-02-01T18:00:00Z",
"end": "2027-02-01T22:00:00Z",
"employee": "Dan"
}
]
}
}
| 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",
"name": "Real-time restaurant schedule with minimize disruptions",
"submitDateTime": "2026-02-25T16:37:35.420931+01:00",
"startDateTime": "2026-02-25T16:37:35.488812+01:00",
"activeDateTime": "2026-02-25T16:37:35.490316+01:00",
"completeDateTime": "2026-02-25T16:38:05.547415+01:00",
"shutdownDateTime": "2026-02-25T16:38:05.547423+01:00",
"solverStatus": "SOLVING_COMPLETED",
"score": "0hard/0medium/-1920soft",
"validationResult": {
"summary": "OK"
}
},
"modelOutput": {
"shifts": [
{
"id": "2027-02-01-dinner-early",
"employee": "Ann"
},
{
"id": "2027-02-01-dinner-1",
"employee": "Beth"
},
{
"id": "2027-02-01-dinner-2",
"employee": "Elsa"
},
{
"id": "2027-02-01-dinner-late",
"employee": "Dan"
}
],
"employees": [
{
"id": "Ann",
"metrics": {
"assignedShifts": 1,
"durationWorked": "PT4H",
"disruptedShifts": 0,
"durationOfDisruptedShifts": "PT0S",
"incomingDisruptedShifts": 0,
"durationOfIncomingDisruptedShifts": "PT0S",
"outgoingDisruptedShifts": 0,
"durationOfOutgoingDisruptedShifts": "PT0S"
}
},
{
"id": "Beth",
"metrics": {
"assignedShifts": 1,
"durationWorked": "PT4H",
"disruptedShifts": 0,
"durationOfDisruptedShifts": "PT0S",
"incomingDisruptedShifts": 0,
"durationOfIncomingDisruptedShifts": "PT0S",
"outgoingDisruptedShifts": 0,
"durationOfOutgoingDisruptedShifts": "PT0S"
}
},
{
"id": "Carl",
"metrics": {
"assignedShifts": 0,
"durationWorked": "PT0S",
"disruptedShifts": 1,
"durationOfDisruptedShifts": "PT4H",
"incomingDisruptedShifts": 0,
"durationOfIncomingDisruptedShifts": "PT0S",
"outgoingDisruptedShifts": 1,
"durationOfOutgoingDisruptedShifts": "PT4H"
}
},
{
"id": "Dan",
"metrics": {
"assignedShifts": 1,
"durationWorked": "PT4H",
"disruptedShifts": 0,
"durationOfDisruptedShifts": "PT0S",
"incomingDisruptedShifts": 0,
"durationOfIncomingDisruptedShifts": "PT0S",
"outgoingDisruptedShifts": 0,
"durationOfOutgoingDisruptedShifts": "PT0S"
}
},
{
"id": "Elsa",
"metrics": {
"assignedShifts": 1,
"durationWorked": "PT4H",
"disruptedShifts": 1,
"durationOfDisruptedShifts": "PT4H",
"incomingDisruptedShifts": 1,
"durationOfIncomingDisruptedShifts": "PT4H",
"outgoingDisruptedShifts": 0,
"durationOfOutgoingDisruptedShifts": "PT0S"
}
},
{
"id": "Flo",
"metrics": {
"assignedShifts": 0,
"durationWorked": "PT0S",
"disruptedShifts": 0,
"durationOfDisruptedShifts": "PT0S",
"incomingDisruptedShifts": 0,
"durationOfIncomingDisruptedShifts": "PT0S",
"outgoingDisruptedShifts": 0,
"durationOfOutgoingDisruptedShifts": "PT0S"
}
}
]
},
"inputMetrics": {
"employees": 6,
"shifts": 4,
"pinnedShifts": 0,
"mandatoryShifts": 4,
"optionalShifts": 0
},
"kpis": {
"assignedShifts": 4,
"unassignedShifts": 0,
"disruptionPercentage": 25.0,
"activatedEmployees": 4,
"assignedMandatoryShifts": 4
}
}
modelOutput contains the updated schedule with Elsa assigned to the shift Carl dropped.
The output includes the disruptionPercentage KPI which shows 25% of the shifts were disrupted due to the change.
Real-time planning and on-call shifts
In some industries it is important to have employees on standby (or on call) who can step in to take over a shift if another employee is suddenly unavailable. Real-time planning can take advantage of this kind of backup planning.
Shifts can be assigned different priorities, where 1 is the highest priority and 10 is the lowest:
{
"shifts": [
{
"id": "2027-02-01-icu-1",
"start": "2027-02-01T08:00:00Z",
"end": "2027-02-01T16:00:00Z",
"priority": "1"
},
{
"id": "2027-02-01-icu-2",
"start": "2027-02-01T08:00:00Z",
"end": "2027-02-01T16:00:00Z",
"priority": "1"
},
{
"id": "2027-02-01-icu-3",
"start": "2027-02-01T08:00:00Z",
"end": "2027-02-01T16:00:00Z",
"priority": "1"
},
{
"id": "2027-02-01-icu-on-call",
"start": "2027-02-01T08:00:00Z",
"end": "2027-02-01T16:00:00Z",
"priority": "5"
}
]
}
If there are enough employees, 3 will be assigned to the high priority shifts, and 1 will be assigned to the lower priority on-call shift. If one of the 3 employees assigned to the high priority shifts calls in sick, the on-call employee will be ready to step in and take their place.
Next
-
See the full API spec or try the online API.
-
Learn more about employee shift scheduling from our YouTube playlist.
-
Manage Employee availability.