Docs
  • Solver
  • Models
    • Field Service Routing
    • Employee Shift Scheduling
    • Pick-up and Delivery Routing
    • Task Scheduling
  • Platform
Try models
  • Employee Shift Scheduling
  • Recommendations
  • 1.29.x
    • latest
    • 1.29.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
      • Mandatory and optional shifts
      • Skills and risk factors
      • Shift assignments
        • Shift selection
        • Employee selection
    • Manual intervention
    • Recommendations
    • Real-time planning
    • Real-time planning with patches
    • Scenarios
      • Configuring labor law compliance
      • Configuring employee well-being
      • Self-rostering and optimization
    • Changelog
    • Upgrade to the latest version
    • Feature requests

Recommendations

Managing complex schedules for a large number of employees is a difficult task, and when an employee is suddenly unavailable to cover a shift they’ve been scheduled for, it can be difficult to know who to schedule in their place.

Timefold’s recommendations API can provide recommendations ordered by the best fit to cover a shift at the last minute.

This guide explains recommendations with the following examples.

  • Recommendations for a shift
  • Recommendations and employee skills
  • Recommendations with employee preferences
  • Batch recommendations

Recommendations for a shift

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.

In the following example, Ann and Beth have been assigned shifts, but Ann has called in sick for her shift.

Timefold’s recommendations API can provide recommendations for the best employee to assign to Ann’s shift.

The recommendations input dataset

A recommendation input dataset includes the following:

Max number of recommendations

The maximum number of employees to recommend for a shift:

{
  "maxNumberOfRecommendations": 2
}

Fit shift ID

The ID of the shift the recommended employee will be assigned.

{
  "fitShiftId": "2027-02-01"
}

Model input

The model input must include the employees and the shifts.

{
  "modelInput": {
    "employees": [
      {
        "id": "Ann",
        "unavailableTimeSpans": [
          {
            "start": "2027-02-01T00:00:00Z",
            "end": "2027-02-02T00:00:00Z"
          }
        ],
        "availableTimeSpans": [
          {
            "start": "2027-02-02T00:00:00Z",
            "end": "2027-02-08T00:00:00Z"
          }
        ]
      },
      {
        "id": "Beth",
        "availableTimeSpans": [
          {
            "start": "2027-02-01T00:00:00Z",
            "end": "2027-02-08T00:00:00Z"
          }
        ]
      },
      {
        "id": "Carl",
        "availableTimeSpans": [
          {
            "start": "2027-02-01T00:00:00Z",
            "end": "2027-02-08T00:00:00Z"
          }
        ]
      },
      {
        "id": "Dan",
        "availableTimeSpans": [
          {
            "start": "2027-02-01T00:00:00Z",
            "end": "2027-02-08T00:00:00Z"
          }
        ]
      },
      {
        "id": "Elsa",
        "availableTimeSpans": [
          {
            "start": "2027-02-01T00:00:00Z",
            "end": "2027-02-08T00:00:00Z"
          }
        ]
      },
      {
        "id": "Flo",
        "availableTimeSpans": [
          {
            "start": "2027-02-01T00:00:00Z",
            "end": "2027-02-08T00:00:00Z"
          }
        ]
      }
    ],
    "shifts": [
      {
        "id": "2027-02-01-a",
        "start": "2027-02-01T09:00:00Z",
        "end": "2027-02-01T17:00:00Z"
      },
      {
        "id": "2027-02-01-b",
        "start": "2027-02-01T09:00:00Z",
        "end": "2027-02-01T17:00:00Z",
        "employee": "Beth"
      }
    ]
  }
}

Ann is unavailable for her shift, so an unavailability time span has been added for Ann:

{
  "id": "Ann",
  "unavailableTimeSpans": [
    {
      "start": "2027-02-01T00:00:00Z",
      "end": "2027-02-02T00:00:00Z"
    }
  ],
  "availableTimeSpans": [
    {
      "start": "2027-02-02T00:00:00Z",
      "end": "2027-02-08T00:00:00Z"
    }
  ]
}

Learn more about Employee availability and preferences.

Any shifts that are already assigned must include the employee ID of the employee they are assigned to. Shifts that have been dropped by an employee don’t include an employee ID. In this case, shift 2027-02-01-a was assigned to Ann, but Ann has called in sick, so no employee ID is included.

{
  "shifts": [
    {
      "id": "2027-02-01-a",
      "start": "2027-02-01T09:00:00Z",
      "end": "2027-02-01T17:00:00Z"
    },
    {
      "id": "2027-02-01-b",
      "start": "2027-02-01T09:00:00Z",
      "end": "2027-02-01T17:00:00Z",
      "employee": "Beth"
    }
  ]
}

Submit the input dataset

Submit the recommendations input dataset to the API endpoint: /v1/schedules/recommendations/recommend-employees.

If you want individual constraint match justifications included in the response, you can specify an optional boolean includeJustifications query parameter: /v1/schedules/recommendations/recommend-employees?includeJustifications=true.

The HTTP response provides the recommendations.

  • Input

  • Output (without justifications)

  • Output (with justifications)

Try this example in Timefold Platform by saving this JSON into a file called sample.json and make the following API call:

To get recommendations without constraint match justifications (default):

curl -X POST -H "Content-type: application/json" -H 'X-API-KEY: <API_KEY>' https://app.timefold.ai/api/models/employee-scheduling/v1/schedules/recommendations/recommend-employees [email protected]

To get recommendations with constraint match justifications:

curl -X POST -H "Content-type: application/json" -H 'X-API-KEY: <API_KEY>' https://app.timefold.ai/api/models/employee-scheduling/v1/schedules/recommendations/recommend-employees?includeJustifications=true [email protected]
{
  "maxNumberOfRecommendations": 2,
  "fitShiftId": "2027-02-01-a",
  "modelInput": {
    "employees": [
      {
        "id": "Ann",
        "unavailableTimeSpans": [
          {
            "start": "2027-02-01T00:00:00Z",
            "end": "2027-02-02T00:00:00Z"
          }
        ],
        "availableTimeSpans": [
          {
            "start": "2027-02-02T00:00:00Z",
            "end": "2027-02-08T00:00:00Z"
          }
        ]
      },
      {
        "id": "Beth",
        "availableTimeSpans": [
          {
            "start": "2027-02-01T00:00:00Z",
            "end": "2027-02-08T00:00:00Z"
          }
        ]
      },
      {
        "id": "Carl",
        "availableTimeSpans": [
          {
            "start": "2027-02-01T00:00:00Z",
            "end": "2027-02-08T00:00:00Z"
          }
        ]
      },
      {
        "id": "Dan",
        "availableTimeSpans": [
          {
            "start": "2027-02-01T00:00:00Z",
            "end": "2027-02-08T00:00:00Z"
          }
        ]
      },
      {
        "id": "Elsa",
        "availableTimeSpans": [
          {
            "start": "2027-02-01T00:00:00Z",
            "end": "2027-02-08T00:00:00Z"
          }
        ]
      },
      {
        "id": "Flo",
        "availableTimeSpans": [
          {
            "start": "2027-02-01T00:00:00Z",
            "end": "2027-02-08T00:00:00Z"
          }
        ]
      }
    ],
    "shifts": [
      {
        "id": "2027-02-01-a",
        "start": "2027-02-01T09:00:00Z",
        "end": "2027-02-01T17:00:00Z"
      },
      {
        "id": "2027-02-01-b",
        "start": "2027-02-01T09:00:00Z",
        "end": "2027-02-01T17:00:00Z",
        "employee": "Beth"
      }
    ]
  }
}
{
  "recommendations": [
    {
      "employeeId": "Carl",
      "scoreDiff": "0hard/1medium/0soft",
      "constraintDiffs": [
        {
          "score": "0hard/1medium/0soft",
          "constraintName": "Unassigned mandatory shift",
          "matchCountDiff": -1
        }
      ]
    },
    {
      "employeeId": "Dan",
      "scoreDiff": "0hard/1medium/0soft",
      "constraintDiffs": [
        {
          "score": "0hard/1medium/0soft",
          "constraintName": "Unassigned mandatory shift",
          "matchCountDiff": -1
        }
      ]
    }
  ]
}

modelOutput contains two recommendations to cover shift 2027-02-01-a.

{
  "recommendations": [
    {
      "employeeId": "Carl",
      "scoreDiff": "0hard/1medium/0soft",
      "constraintDiffs": [
        {
          "score": "0hard/1medium/0soft",
          "constraintName": "Unassigned mandatory shift",
          "matchesDiff": [
            {
              "score": "0hard/1medium/0soft",
              "justification": {
                "shift": "2027-02-01-a",
                "description": "Mandatory shift '2027-02-01-a' at '2027-02-01T09:00Z - 2027-02-01T17:00Z' is unassigned."
              }
            }
          ],
          "matchCountDiff": -1
        }
      ]
    },
    {
      "employeeId": "Dan",
      "scoreDiff": "0hard/1medium/0soft",
      "constraintDiffs": [
        {
          "score": "0hard/1medium/0soft",
          "constraintName": "Unassigned mandatory shift",
          "matchesDiff": [
            {
              "score": "0hard/1medium/0soft",
              "justification": {
                "shift": "2027-02-01-a",
                "description": "Mandatory shift '2027-02-01-a' at '2027-02-01T09:00Z - 2027-02-01T17:00Z' is unassigned."
              }
            }
          ],
          "matchCountDiff": -1
        }
      ]
    }
  ]
}

modelOutput contains two recommendations to cover shift 2027-02-01-a including constraint match justifications.

The recommendations

There are two recommendations for this input dataset: Dan and Carl.

recommendations a shift

Each recommendation includes the following:

  • The employee: The employee being recommended.

  • The scoreDiffs: The overall score difference implementing the recommendation will have.

  • The constraintDiffs: The score difference to individual constraints.

Constraint scores without justifications

The score for each constraint that has changed, for instance:

{
  "score": "0hard/1medium/0soft",
  "constraintName": "Unassigned mandatory shift",
  "matchCountDiff": -1
}

Which indicates the medium score has improved by 1, because the shift is no longer unassigned.

Constraint scores with justifications

The score and justifications for each constraint that has changed, for instance:

{
  "score": "0hard/1medium/0soft",
  "constraintName": "Unassigned mandatory shift",
  "matchesDiff": [
    {
      "score": "0hard/1medium/0soft",
      "justification": {
        "shift": "2027-02-01-a",
        "description": "Mandatory shift '2027-02-01-a' at '2027-02-01T09:00Z - 2027-02-01T17:00Z' is unassigned."
      }
    }
  ],
  "matchCountDiff": -1
}

Recommendations and employee skills

In the previous example, any one of the available employees could have been assigned to the shift Ann dropped.

However, employees generally have different skills and availability which could help determine which employee is considered the best fit.

In the following example, Ann is a shift supervisor and the shift that needs to be filled must be filled by another shift supervisor.

  • 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/recommendations/recommend-employees [email protected]
{
  "maxNumberOfRecommendations": 2,
  "fitShiftId": "2027-02-01-a",
  "modelInput": {
    "employees": [
      {
        "id": "Ann",
        "skills": [
          {
            "id": "supervisor"
          }
        ],
        "unavailableTimeSpans": [
          {
            "start": "2027-02-01T00:00:00Z",
            "end": "2027-02-02T00:00:00Z"
          }
        ],
        "availableTimeSpans": [
          {
            "start": "2027-02-02T00:00:00Z",
            "end": "2027-02-08T00:00:00Z"
          }
        ]
      },
      {
        "id": "Beth",
        "availableTimeSpans": [
          {
            "start": "2027-02-01T00:00:00Z",
            "end": "2027-02-08T00:00:00Z"
          }
        ]
      },
      {
        "id": "Carl",
        "availableTimeSpans": [
          {
            "start": "2027-02-01T00:00:00Z",
            "end": "2027-02-08T00:00:00Z"
          }
        ]
      },
      {
        "id": "Dan",
        "skills": [
          {
            "id": "supervisor"
          }
        ],
        "availableTimeSpans": [
          {
            "start": "2027-02-01T00:00:00Z",
            "end": "2027-02-08T00:00:00Z"
          }
        ]
      },
      {
        "id": "Elsa",
        "skills": [
          {
            "id": "supervisor"
          }
        ],
        "availableTimeSpans": [
          {
            "start": "2027-02-01T00:00:00Z",
            "end": "2027-02-08T00:00:00Z"
          }
        ]
      },
      {
        "id": "Flo",
        "availableTimeSpans": [
          {
            "start": "2027-02-01T00:00:00Z",
            "end": "2027-02-08T00:00:00Z"
          }
        ]
      }
    ],
    "shifts": [
      {
        "id": "2027-02-01-a",
        "start": "2027-02-01T09:00:00Z",
        "end": "2027-02-01T17:00:00Z",
        "requiredSkills": [
          "supervisor"
        ]
      },
      {
        "id": "2027-02-01-b",
        "start": "2027-02-01T09:00:00Z",
        "end": "2027-02-01T17:00:00Z",
        "employee": "Beth"
      }
    ]
  }
}
{
  "recommendations": [
    {
      "employeeId": "Dan",
      "scoreDiff": "0hard/1medium/0soft",
      "constraintDiffs": [
        {
          "score": "0hard/1medium/0soft",
          "constraintName": "Unassigned mandatory shift",
          "matchCountDiff": -1
        }
      ]
    },
    {
      "employeeId": "Elsa",
      "scoreDiff": "0hard/1medium/0soft",
      "constraintDiffs": [
        {
          "score": "0hard/1medium/0soft",
          "constraintName": "Unassigned mandatory shift",
          "matchCountDiff": -1
        }
      ]
    }
  ]
}

modelOutput contains two recommendations to cover shift 2027-02-01-a.

This time the recommendations are for Dan and Elsa who both have the "supervisor" skill.

recommendations skills

Recommendations with employee preferences

Employees can define a range of preferences including unpreferred work times and who they prefer to work with.

In the following example, Dan prefers not to work on Mondays.

  • 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/recommendations/recommend-employees [email protected]
{
  "maxNumberOfRecommendations": 2,
  "fitShiftId": "2027-02-01-a",
  "modelInput": {
    "employees": [
      {
        "id": "Ann",
        "skills": [
          {
            "id": "supervisor"
          }
        ],
        "unavailableTimeSpans": [
          {
            "start": "2027-02-01T00:00:00Z",
            "end": "2027-02-02T00:00:00Z"
          }
        ],
        "availableTimeSpans": [
          {
            "start": "2027-02-02T00:00:00Z",
            "end": "2027-02-08T00:00:00Z"
          }
        ]
      },
      {
        "id": "Beth",
        "availableTimeSpans": [
          {
            "start": "2027-02-01T00:00:00Z",
            "end": "2027-02-08T00:00:00Z"
          }
        ]
      },
      {
        "id": "Carl",
        "availableTimeSpans": [
          {
            "start": "2027-02-01T00:00:00Z",
            "end": "2027-02-08T00:00:00Z"
          }
        ]
      },
      {
        "id": "Dan",
        "skills": [
          {
            "id": "supervisor"
          }
        ],
        "availableTimeSpans": [
          {
            "start": "2027-02-01T00:00:00Z",
            "end": "2027-02-08T00:00:00Z"
          }
        ],
        "unpreferredTimeSpans": [
          {
            "start": "2027-02-01T00:00:00Z",
            "end": "2027-02-02T00:00:00Z"
          }
        ]
      },
      {
        "id": "Elsa",
        "skills": [
          {
            "id": "supervisor"
          }
        ],
        "availableTimeSpans": [
          {
            "start": "2027-02-01T00:00:00Z",
            "end": "2027-02-08T00:00:00Z"
          }
        ]
      },
      {
        "id": "Flo",
        "availableTimeSpans": [
          {
            "start": "2027-02-01T00:00:00Z",
            "end": "2027-02-08T00:00:00Z"
          }
        ]
      }
    ],
    "shifts": [
      {
        "id": "2027-02-01-a",
        "start": "2027-02-01T09:00:00Z",
        "end": "2027-02-01T17:00:00Z",
        "requiredSkills": [
          "supervisor"
        ]
      },
      {
        "id": "2027-02-01-b",
        "start": "2027-02-01T09:00:00Z",
        "end": "2027-02-01T17:00:00Z",
        "employee": "Beth"
      }
    ]
  }
}
{
  "recommendations": [
    {
      "employeeId": "Elsa",
      "scoreDiff": "0hard/1medium/0soft",
      "constraintDiffs": [
        {
          "score": "0hard/1medium/0soft",
          "constraintName": "Unassigned mandatory shift",
          "matchCountDiff": -1
        }
      ]
    },
    {
      "employeeId": "Dan",
      "scoreDiff": "0hard/1medium/-960soft",
      "constraintDiffs": [
        {
          "score": "0hard/0medium/-960soft",
          "constraintName": "Employee works during unpreferred time",
          "matchCountDiff": 1
        },
        {
          "score": "0hard/1medium/0soft",
          "constraintName": "Unassigned mandatory shift",
          "matchCountDiff": -1
        }
      ]
    }
  ]
}

modelOutput contains two recommendations to cover shift 2027-02-01-a.

In this scenario, Dan is still recommended because only he and Elsa have the required skill of supervisor, but this time, Elsa is recommended first as she is the best fit, and Dan is recommended second.

In the second recommendation, we see:

{
  "score": "0hard/0medium/-960soft",
  "constraintName": "Employee works during unpreferred time"
}

This shows a soft score penalty because the recommendation would involve Dan working during an unpreferred time.

recommendations preferred hours

Batch recommendations

The examples above request recommendations for a single shift. When several shifts need to be filled at once, you can request recommendations for all of them in a single call instead of calling the API once per shift.

All requested shifts are fitted against the provided model input. Accepting any one recommendation invalidates all the others. After you apply an assignment, request batch recommendations again for the remaining shifts, if needed.

The input is the same as for a single shift, except that fitShiftId is replaced by fitShiftIds, which holds the list of the shift IDs to be fitted.

The maximum number of shift IDs in fitShiftIds parameter is 100.

As with the single-shift API, you can add the optional includeJustifications=true query parameter to include individual constraint match justifications in the response. There are two batch endpoints, which differ only in how the recommendations are ordered: per shift or globally across all shifts.

Per-shift recommendations

The recommend-employees-batch-per-shift endpoint returns a separate list of recommended employees for each requested shift, ordered best fit first. Use this endpoint to get recommendations for many shifts at once, instead of calling the single-shift endpoint once per shift. For this endpoint, maxNumberOfRecommendations is the maximum number of recommendations returned per shift.

In the following example, three shifts on 2027-02-01 are unassigned. Ann, Beth and Carl are all available and each has a preferred time span covering all three shifts, but with different priorities: Ann has priority 10, Beth has priority 9, and Carl has priority 8.

{
  "maxNumberOfRecommendations": 3,
  "fitShiftIds": ["2027-02-01-a", "2027-02-01-b", "2027-02-01-c"],
  "modelInput": {
    "employees": [
      {
        "id": "Ann",
        "priority": "10",
        "availableTimeSpans": [
          { "start": "2027-02-01T00:00:00Z", "end": "2027-02-08T00:00:00Z" }
        ],
        "preferredTimeSpans": [
          { "start": "2027-02-01T00:00:00Z", "end": "2027-02-08T00:00:00Z" }
        ]
      },
      {
        "id": "Beth",
        "priority": "9",
        "availableTimeSpans": [
          { "start": "2027-02-01T00:00:00Z", "end": "2027-02-08T00:00:00Z" }
        ],
        "preferredTimeSpans": [
          { "start": "2027-02-01T00:00:00Z", "end": "2027-02-08T00:00:00Z" }
        ]
      },
      {
        "id": "Carl",
        "priority": "8",
        "availableTimeSpans": [
          { "start": "2027-02-01T00:00:00Z", "end": "2027-02-08T00:00:00Z" }
        ],
        "preferredTimeSpans": [
          { "start": "2027-02-01T00:00:00Z", "end": "2027-02-08T00:00:00Z" }
        ]
      }
    ],
    "shifts": [
      { "id": "2027-02-01-a", "start": "2027-02-01T09:00:00Z", "end": "2027-02-01T17:00:00Z" },
      { "id": "2027-02-01-b", "start": "2027-02-01T13:00:00Z", "end": "2027-02-01T21:00:00Z" },
      { "id": "2027-02-01-c", "start": "2027-02-01T17:00:00Z", "end": "2027-02-02T01:00:00Z" }
    ]
  }
}

Submit this dataset to /v1/schedules/recommendations/recommend-employees-batch-per-shift:

curl -X POST -H "Content-type: application/json" -H 'X-API-KEY: <API_KEY>' https://app.timefold.ai/api/models/employee-scheduling/v1/schedules/recommendations/recommend-employees-batch-per-shift [email protected]

The response contains one entry per requested shift, in the order the shifts were submitted, each with its own list of recommendations ordered best fit first. Each shift’s 480-minute duration overlaps fully with every employee’s preferred time span. Because the soft reward is multiplied by the employee’s priority weight (Carl: ×3, Beth: ×2, Ann: ×1), Carl always scores highest and Ann lowest:

{
  "recommendations": [
    {
      "shiftId": "2027-02-01-a",
      "recommendations": [
        { "employeeId": "Carl", "scoreDiff": "0hard/1medium/1440soft", "constraintDiffs": [ ... ] },
        { "employeeId": "Beth", "scoreDiff": "0hard/1medium/960soft", "constraintDiffs": [ ... ] },
        { "employeeId": "Ann", "scoreDiff": "0hard/1medium/480soft", "constraintDiffs": [ ... ] }
      ]
    },
    {
      "shiftId": "2027-02-01-b",
      "recommendations": [
        { "employeeId": "Carl", "scoreDiff": "0hard/1medium/1440soft", "constraintDiffs": [ ... ] },
        { "employeeId": "Beth", "scoreDiff": "0hard/1medium/960soft", "constraintDiffs": [ ... ] },
        { "employeeId": "Ann", "scoreDiff": "0hard/1medium/480soft", "constraintDiffs": [ ... ] }
      ]
    },
    {
      "shiftId": "2027-02-01-c",
      "recommendations": [
        { "employeeId": "Carl", "scoreDiff": "0hard/1medium/1440soft", "constraintDiffs": [ ... ] },
        { "employeeId": "Beth", "scoreDiff": "0hard/1medium/960soft", "constraintDiffs": [ ... ] },
        { "employeeId": "Ann", "scoreDiff": "0hard/1medium/480soft", "constraintDiffs": [ ... ] }
      ]
    }
  ]
}

Global recommendations

The recommend-employees-batch-global endpoint returns a single list of (shift, employee) assignment candidates ordered across all requested shifts, best fit first. The first entry answers the question "which shift should I assign next?". For this endpoint, maxNumberOfRecommendations is the maximum number of recommendations in total across all shifts.

Reusing the same scenario, three shifts on 2027-02-01 are unassigned, and Ann (priority 10), Beth (priority 9) and Carl (priority 8) are all available, each with a preferred time span covering all three shifts:

{
  "maxNumberOfRecommendations": 4,
  "fitShiftIds": ["2027-02-01-a", "2027-02-01-b", "2027-02-01-c"],
  "modelInput": {
    "employees": [
      {
        "id": "Ann",
        "priority": "10",
        "availableTimeSpans": [
          { "start": "2027-02-01T00:00:00Z", "end": "2027-02-08T00:00:00Z" }
        ],
        "preferredTimeSpans": [
          { "start": "2027-02-01T00:00:00Z", "end": "2027-02-08T00:00:00Z" }
        ]
      },
      {
        "id": "Beth",
        "priority": "9",
        "availableTimeSpans": [
          { "start": "2027-02-01T00:00:00Z", "end": "2027-02-08T00:00:00Z" }
        ],
        "preferredTimeSpans": [
          { "start": "2027-02-01T00:00:00Z", "end": "2027-02-08T00:00:00Z" }
        ]
      },
      {
        "id": "Carl",
        "priority": "8",
        "availableTimeSpans": [
          { "start": "2027-02-01T00:00:00Z", "end": "2027-02-08T00:00:00Z" }
        ],
        "preferredTimeSpans": [
          { "start": "2027-02-01T00:00:00Z", "end": "2027-02-08T00:00:00Z" }
        ]
      }
    ],
    "shifts": [
      { "id": "2027-02-01-a", "start": "2027-02-01T09:00:00Z", "end": "2027-02-01T17:00:00Z" },
      { "id": "2027-02-01-b", "start": "2027-02-01T13:00:00Z", "end": "2027-02-01T21:00:00Z" },
      { "id": "2027-02-01-c", "start": "2027-02-01T17:00:00Z", "end": "2027-02-02T01:00:00Z" }
    ]
  }
}

Submit this dataset to /v1/schedules/recommendations/recommend-employees-batch-global:

curl -X POST -H "Content-type: application/json" -H 'X-API-KEY: <API_KEY>' https://app.timefold.ai/api/models/employee-scheduling/v1/schedules/recommendations/recommend-employees-batch-global [email protected]

The response is a single list of assignment candidates ordered across all shifts, best fit first. Each entry identifies both the shift and the recommended employee, so the first entry tells you which shift to assign next. Because Carl has the highest priority (lowest number), his preferred time spans carry the greatest weight and he scores highest for every shift. The first three entries are therefore all Carl; Beth appears in the fourth position:

{
  "recommendations": [
    { "shiftId": "2027-02-01-a", "recommendation": { "employeeId": "Carl", "scoreDiff": "0hard/1medium/1440soft", "constraintDiffs": [ ... ] } },
    { "shiftId": "2027-02-01-b", "recommendation": { "employeeId": "Carl", "scoreDiff": "0hard/1medium/1440soft", "constraintDiffs": [ ... ] } },
    { "shiftId": "2027-02-01-c", "recommendation": { "employeeId": "Carl", "scoreDiff": "0hard/1medium/1440soft", "constraintDiffs": [ ... ] } },
    { "shiftId": "2027-02-01-a", "recommendation": { "employeeId": "Beth", "scoreDiff": "0hard/1medium/960soft", "constraintDiffs": [ ... ] } }
  ]
}

The scoreDiff and constraintDiffs of each recommendation have the same meaning as for single-shift recommendations.

Next

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

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

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