Docs
  • Solver
  • Models
    • Field Service Routing
    • Employee Shift Scheduling
    • Pick-up and Delivery Routing
    • Task Scheduling
  • Platform
Try models
  • Employee Shift Scheduling
  • Shift service constraints
  • Priority and optional shifts
  • latest
    • latest
    • 1.32.x

Employee Shift Scheduling

    • Introduction
    • Getting started: Hello world
    • User guide
      • Terminology
      • Use case guide
      • Scheduling API concepts
      • Integration
      • Constraints
      • Using the API
        • Using the OpenAPI spec
        • API tooling
      • Demo datasets
      • Input datasets
        • Model configuration
        • Model input
        • Planning window
      • Planning window
      • Time zones and Daylight Saving Time (DST)
      • Tags and tag types
      • Input validation
      • Output datasets
        • Metadata
        • Model output
        • Input metrics
        • Key performance indicators (KPIs)
      • Metrics and optimization goals
      • Score analysis
      • Visualizations
    • Employee resource constraints
      • Employee availability and preferences
        • Employee availability
        • Employee preferences
      • Employee contracts
      • Employee priority
      • Pairing employees
      • Shift travel and locations
      • Shift Breaks
      • Employee activation
      • 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
        • Consecutive shifts worked
      • 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
        • Consecutive weekends off per period
      • Shift rotations and patterns
        • Shift rotations
        • Single day shift sequence patterns
        • Minimize gaps between shifts
        • Multi-day shift sequence patterns
        • Daily shift pairings
        • Overlapping shifts
        • Shift start times differences
        • Minutes between shifts
      • Shift type diversity
        • Shift tag types
        • Shift types worked per period
        • Unique tags per period
      • Fairness
        • Balance time worked
        • Balance shift count
    • Shift service constraints
      • Alternative shifts
      • Cost management
        • Cost groups
        • Employee rates
      • Demand-based scheduling
      • Priority and optional shifts
      • Skills and risk factors
      • Shift assignments
        • Shift selection
        • Employee selection
    • Manual intervention
    • Recommendations
    • Real-time planning
    • Scenarios
      • Configuring labor law compliance
      • Configuring employee well-being
      • Self-rostering and optimization
    • Changelog
    • Upgrade to the latest version
    • Feature requests

Priority and optional shifts

In employee shift scheduling, resource-limited planning occurs when there are not enough employees to cover every shift that needs to be staffed.

You have two tools to control what happens when this occurs. Shift priorities control which shifts Timefold assigns first, and marking a shift as optional lets Timefold leave it unassigned when that produces a better schedule.

This guide explains both tools, along with the constraints that manage unassigned shifts.

  • Shift priorities
  • Mandatory and optional shifts

Shift priorities

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 Manage tenant, 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.

By default, every shift must be assigned. When there are not enough employees, Timefold leaves a shift unassigned, and without priorities it can be any of the shifts.

Scheduling without priorities

In the following example, three shifts need to be staffed, but only two employees can cover them. One of the shifts is left unassigned.

scheduling without priorities
  • 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": "Resource-limited planning - no priorities"
    }
  },
  "modelInput": {
    "employees": [
      {
        "id": "Ann"
      },
      {
        "id": "Beth"
      }
    ],
    "shifts": [
      {
        "id": "Monday Cashier",
        "start": "2027-02-01T09:00:00Z",
        "end": "2027-02-01T17:00:00Z"
      },
      {
        "id": "Monday Greeter",
        "start": "2027-02-01T09:00:00Z",
        "end": "2027-02-01T17:00:00Z"
      },
      {
        "id": "Monday Stockroom",
        "start": "2027-02-01T09:00:00Z",
        "end": "2027-02-01T17: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": "Resource-limited planning - no priorities",
    "submitDateTime": "2025-01-13T06:56:41.254037504Z",
    "startDateTime": "2025-01-13T06:56:51.713981203Z",
    "activeDateTime": "2025-01-13T06:56:51.776161178Z",
    "completeDateTime": "2025-01-13T07:01:51.934602715Z",
    "shutdownDateTime": "2025-01-13T07:01:52.199287953Z",
    "solverStatus": "SOLVING_COMPLETED",
    "score": "0hard/-1medium/0soft",
    "tags": [],
    "validationResult": {
      "summary": "OK"
    }
  },
  "modelOutput": {
    "shifts": [
      {
        "id": "Monday Cashier",
        "employee": "Ann"
      },
      {
        "id": "Monday Greeter",
        "employee": "Beth"
      },
      {
        "id": "Monday Stockroom",
        "employee": null
      }
    ]
  },
  "kpis": {
    "assignedShifts": 2,
    "unassignedShifts": 1,
    "workingTimeFairnessPercentage": null,
    "disruptionPercentage": 0.0,
    "averageDurationOfEmployeesPreferencesMet": null,
    "minimumDurationOfPreferencesMetAcrossEmployees": null,
    "averageDurationOfEmployeesUnpreferencesViolated": null,
    "maximumDurationOfUnpreferencesViolatedAcrossEmployees": null
  }
}

modelOutput contains Ann’s and Beth’s assigned shifts.

To control which shifts are assigned first, you can add priorities to shifts. There are ten built-in priorities. Priority 1 is the highest and priority 10 is the lowest.

Priorities are added to shifts:

{
  "shifts": [
    {
      "id": "Monday Cashier",
      "start": "2027-02-01T09:00:00Z",
      "end": "2027-02-01T17:00:00Z",
      "priority": "1"
    },
    {
      "id": "Monday Greeter",
      "start": "2027-02-01T09:00:00Z",
      "end": "2027-02-01T17:00:00Z",
      "priority": "5"
    },
    {
      "id": "Monday Stockroom",
      "start": "2027-02-01T09:00:00Z",
      "end": "2027-02-01T17:00:00Z",
      "priority": "3"
    }
  ]
}

Scheduling with priorities

In the following example, a delivery is due on Monday, and a staff member must be present to accept the delivery and store the items. The Monday shifts have the following priorities:

  • Cashier: priority 1

  • Stockroom: priority 3

  • Greeter: priority 5

  • 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": "Resource-limited planning - with priorities"
    }
  },
  "modelInput": {
    "employees": [
      {
        "id": "Ann"
      },
      {
        "id": "Beth"
      }
    ],
    "shifts": [
      {
        "id": "Monday Cashier",
        "start": "2027-02-01T09:00:00Z",
        "end": "2027-02-01T17:00:00Z",
        "priority": "1"
      },
      {
        "id": "Monday Greeter",
        "start": "2027-02-01T09:00:00Z",
        "end": "2027-02-01T17:00:00Z",
        "priority": "5"
      },
      {
        "id": "Monday Stockroom",
        "start": "2027-02-01T09:00:00Z",
        "end": "2027-02-01T17:00:00Z",
        "priority": "3"
      }
    ]
  }
}
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": "Resource-limited planning - with priorities",
    "submitDateTime": "2025-01-13T07:43:38.942914528Z",
    "startDateTime": "2025-01-13T07:43:45.837804099Z",
    "activeDateTime": "2025-01-13T07:43:45.927185678Z",
    "completeDateTime": "2025-01-13T07:48:46.126452389Z",
    "shutdownDateTime": "2025-01-13T07:48:46.291891316Z",
    "solverStatus": "SOLVING_COMPLETED",
    "score": "0hard/-100000medium/0soft",
    "tags": [],
    "validationResult": {
      "summary": "OK"
    }
  },
  "modelOutput": {
    "shifts": [
      {
        "id": "Monday Cashier",
        "employee": "Ann"
      },
      {
        "id": "Monday Greeter",
        "employee": null
      },
      {
        "id": "Monday Stockroom",
        "employee": "Beth"
      }
    ]
  },
  "kpis": {
    "assignedShifts": 2,
    "unassignedShifts": 1,
    "workingTimeFairnessPercentage": null,
    "disruptionPercentage": 0.0,
    "averageDurationOfEmployeesPreferencesMet": null,
    "minimumDurationOfPreferencesMetAcrossEmployees": null,
    "averageDurationOfEmployeesUnpreferencesViolated": null,
    "maximumDurationOfUnpreferencesViolatedAcrossEmployees": null
  }
}

modelOutput contains Ann’s and Beth’s assigned shifts.

This time, the Monday Greeter shift is left unassigned because it has the lowest priority.

Mandatory and optional shifts

Shifts are mandatory by default, which means Timefold must assign them. You can also mark a shift as optional, so Timefold can leave it unassigned if that leads to a higher overall score.

Two constraints manage unassigned shifts:

  • The Unassigned mandatory shift medium constraint is broken when a mandatory shift has no assigned employee, and it affects the medium score of the solution.

  • The Unassigned optional shift soft constraint is broken when an optional shift has no assigned employee, and it affects the soft score of the solution.

The Unassigned optional shift constraint adds a soft penalty equal to the shift’s resolved unassigned shift priority weight, which incentivizes Timefold to assign optional shifts, especially those with a higher priority weight. Making a shift optional is useful when other soft constraints, such as employee preferences or multi-day shift patterns, can be satisfied instead of assigning the shift.

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.

Configuration

To define mandatory and optional shifts, add a globalRules object with an unassignedShiftRule that sets the weight and assignment for each priority level:

{
  "globalRules": {
    "unassignedShiftRule": {
      "id": "unassignedShiftRule",
      "priorityWeights": [
        {
          "priority": "1",
          "weight": 100,
          "assignment": "MANDATORY"
        },
        {
          "priority": "3",
          "weight": 10,
          "assignment": "MANDATORY"
        },
        {
          "priority": "5",
          "weight": 1,
          "assignment": "OPTIONAL"
        }
      ]
    }
  }
}

In this rule, priority 1 is the highest priority with the greatest weight, and priority 5 is the lowest priority with a weight of 1. Shifts with a priority level of 1 or 3 are mandatory, and shifts with a priority level of 5 are optional.

Example

In the following example, three mandatory shifts need to be staffed by three employees. Ann and Beth have a preference not to work at the same time as Carl.

See unpreferred pairings for information about employee preferences.

Because all three shifts are mandatory, Timefold assigns Carl alongside Ann and Beth, breaking their preference:

with mandatory 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": "Resource-limited planning - with mandatory shifts"
    }
  },
  "modelInput": {
    "employees": [
      {
        "id": "Ann",
        "unpreferredPairings": [
          {
            "pairedEmployee": "Carl"
          }
        ]
      },
      {
        "id": "Beth",
        "unpreferredPairings": [
          {
            "pairedEmployee": "Carl"
          }
        ]
      },
      {
        "id": "Carl"
      }
    ],
    "shifts": [
      {
        "id": "Monday Cashier",
        "start": "2027-02-01T09:00:00Z",
        "end": "2027-02-01T17:00:00Z",
        "priority": "1"
      },
      {
        "id": "Monday Greeter",
        "start": "2027-02-01T09:00:00Z",
        "end": "2027-02-01T17:00:00Z",
        "priority": "5"
      },
      {
        "id": "Monday Stockroom",
        "start": "2027-02-01T09:00:00Z",
        "end": "2027-02-01T17:00:00Z",
        "priority": "3"
      }
    ]
  }
}
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": "Resource-limited planning - with mandatory shifts",
    "submitDateTime": "2025-01-13T08:17:10.602715243Z",
    "startDateTime": "2025-01-13T08:17:17.257947788Z",
    "activeDateTime": "2025-01-13T08:17:17.332768003Z",
    "completeDateTime": "2025-01-13T08:22:17.537407491Z",
    "shutdownDateTime": "2025-01-13T08:22:17.69476057Z",
    "solverStatus": "SOLVING_COMPLETED",
    "score": "0hard/0medium/-1920soft",
    "tags": [],
    "validationResult": {
      "summary": "OK"
    }
  },
  "modelOutput": {
    "shifts": [
      {
        "id": "Monday Cashier",
        "employee": "Ann"
      },
      {
        "id": "Monday Greeter",
        "employee": "Beth"
      },
      {
        "id": "Monday Stockroom",
        "employee": "Carl"
      }
    ]
  },
  "kpis": {
    "assignedShifts": 3,
    "unassignedShifts": 0,
    "workingTimeFairnessPercentage": null,
    "disruptionPercentage": 0.0,
    "averageDurationOfEmployeesPreferencesMet": null,
    "minimumDurationOfPreferencesMetAcrossEmployees": null,
    "averageDurationOfEmployeesUnpreferencesViolated": "PT5H20M",
    "maximumDurationOfUnpreferencesViolatedAcrossEmployees": "PT8H"
  }
}

modelOutput contains Ann’s, Beth’s, and Carl’s assigned shifts.

If the Monday Greeter shift is made optional, Timefold assigns Ann and Beth to the two mandatory shifts and leaves the optional shift unassigned, satisfying their preference not to work with Carl:

unassigned optional shift
  • 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": "Resource-limited planning - with optional shifts"
    }
  },
  "modelInput": {
    "globalRules": {
      "unassignedShiftRule": {
        "id": "unassignedShiftRule",
        "priorityWeights": [
          {
            "priority": "1",
            "weight": 100,
            "assignment": "MANDATORY"
          },
          {
            "priority": "3",
            "weight": 10,
            "assignment": "MANDATORY"
          },
          {
            "priority": "5",
            "weight": 1,
            "assignment": "OPTIONAL"
          }
        ]
      }
    },
    "employees": [
      {
        "id": "Ann",
        "unpreferredPairings": [
          {
            "pairedEmployee": "Carl"
          }
        ]
      },
      {
        "id": "Beth",
        "unpreferredPairings": [
          {
            "pairedEmployee": "Carl"
          }
        ]
      },
      {
        "id": "Carl"
      }
    ],
    "shifts": [
      {
        "id": "Monday Cashier",
        "start": "2027-02-01T09:00:00Z",
        "end": "2027-02-01T17:00:00Z",
        "priority": "1"
      },
      {
        "id": "Monday Greeter",
        "start": "2027-02-01T09:00:00Z",
        "end": "2027-02-01T17:00:00Z",
        "priority": "5"
      },
      {
        "id": "Monday Stockroom",
        "start": "2027-02-01T09:00:00Z",
        "end": "2027-02-01T17:00:00Z",
        "priority": "3"
      }
    ]
  }
}
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": "Resource-limited planning - with optional shifts",
    "submitDateTime": "2025-01-13T08:30:18.984846197Z",
    "startDateTime": "2025-01-13T08:30:25.717208027Z",
    "activeDateTime": "2025-01-13T08:30:25.82002715Z",
    "completeDateTime": "2025-01-13T08:35:26.026473606Z",
    "shutdownDateTime": "2025-01-13T08:35:26.184985497Z",
    "solverStatus": "SOLVING_COMPLETED",
    "score": "0hard/0medium/-1soft",
    "tags": [],
    "validationResult": {
      "summary": "OK"
    }
  },
  "modelOutput": {
    "shifts": [
      {
        "id": "Monday Cashier",
        "employee": "Beth"
      },
      {
        "id": "Monday Greeter",
        "employee": null
      },
      {
        "id": "Monday Stockroom",
        "employee": "Ann"
      }
    ]
  },
  "kpis": {
    "assignedShifts": 2,
    "unassignedShifts": 1,
    "workingTimeFairnessPercentage": null,
    "disruptionPercentage": 0.0,
    "averageDurationOfEmployeesPreferencesMet": null,
    "minimumDurationOfPreferencesMetAcrossEmployees": null,
    "averageDurationOfEmployeesUnpreferencesViolated": "PT0S",
    "maximumDurationOfUnpreferencesViolatedAcrossEmployees": "PT0S"
  }
}

modelOutput contains Ann’s and Beth’s assigned shifts.

Next

  • See the full API spec or try the online API.

  • Learn more about employee shift scheduling from our YouTube playlist.

  • Manage Employee availability and preferences.

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