---
title: "Search Employee Calendars"
method: POST
path: "/timeoff/calendars/employees/search"
tags: ["Calendar events"]
---

# Search Employee Calendars

`POST /timeoff/calendars/employees/search`

Resolves which holiday calendar applies to each employee in a single bulk call.

Each employee's effective calendar is computed server-side using the canonical
resolution rule (matches Bob's product behaviour):

1. **Employment override** — if `payroll.employment.calendarId` is set on the
   employee's employment record **and** that calendar currently exists in the
   company, it is returned with `source = employment`.
2. **Site default** — otherwise, the calendar assigned to the employee's site
   is returned with `source = site`.
3. **None** — if neither layer supplies a calendar (no override and the site
   has no calendar configured, or the employee has no site), the item is
   returned with `source = none` and `calendarId` / `calendarName` set to `null`.

**Missing employees:** Employee IDs that don't exist in the caller's company
are silently dropped from `items` (the request does **not** fail). Diff the
requested IDs against the returned `employeeId`s to detect unknowns.

**OAuth scopes (app-user):** `timeoff.calendars:write` AND `employee_data:read`.

**IAM permission:** `Settings / Calendars / Manage` (company-scoped).

## Request body

- SearchEmployeeCalendarsInput — Request body for employee-calendar search. `filters` is required and must always include at least one filter on `/employeeCalendar/employeeId` (the batch of employees to resolve). `fields` is optional — omit it to return every field; `limit` and `cursor` are optional and fall back to server defaults (see examples for typical payloads).
  - `fields` EmployeeCalendarFieldId[], nullable — List of field IDs to include in each result item (see `EmployeeCalendarFieldId` for the available field IDs — they match the keys on each item in the response). When omitted, all available fields are returned.
  - `filters` EmployeeCalendarSearchFilter[], required — Filters to apply to the search. You MUST always provide exactly one filter on `/employeeCalendar/employeeId` listing the employees to resolve. The list of values is deduplicated server-side and may contain at most **5000** employee IDs per request; otherwise the API returns **400** with key `exception.employeeCalendar.publicApi.tooManyEmployeeIds`. Only the `equals` operator is supported for this filter. On top of the mandatory `employeeId` filter you may add optional filters to narrow the results. Supported filter field IDs (see `EmployeeCalendarFilterFieldId`): `/employeeCalendar/employeeId`, `/employeeCalendar/calendarId`, `/employeeCalendar/siteId`, `/employeeCalendar/source`. Any other `fieldId` under `filters` is rejected with **400** and key `exception.employeeCalendar.publicApi.unsupportedFilterField`. Example minimal payload: `[{"fieldId":"/employeeCalendar/employeeId","operator":"equals","values":["3001234567890123456"]}]`
    - `fieldId` '/employeeCalendar/employeeId' | '/employeeCalendar/calendarId' | '/employeeCalendar/siteId' | '/employeeCalendar/source', required — Field IDs that can be used as `fieldId` on an employee-calendar search filter. Only these paths are applied by the search implementation; other `EmployeeCalendarFieldId` values are available in `fields` but must not be used as filters. `EMPLOYEE_ID` is mandatory — every request must include at least one filter on `/employeeCalendar/employeeId` listing the employees to resolve.
    - `operator` string, required — Comparison operator for this filter. Only `equals` is supported for every filter field in this operation.
    - `values` string[], required — Filter values, always sent as JSON strings. **Important:** Some API consoles (including ReadMe "Try It") rebuild the request from YAML examples and may drop quotes around large numeric-looking strings, which turns them into JSON numbers and breaks this API (and can truncate snowflake-style ids past `Number.MAX_SAFE_INTEGER`). If that happens, edit the JSON body so every id is a **string** (e.g. `"3332883884017713938"`). - For `/employeeCalendar/employeeId`: provide one or more employee IDs as strings. - For `/employeeCalendar/calendarId` and `/employeeCalendar/siteId`: provide one or more IDs as strings, even when they look numeric, so that large IDs are not truncated by JSON number precision in clients. - For `/employeeCalendar/source`: provide one or more values from `EmployeeCalendarSource` (`employment`, `site`, `none`).
  - `limit` integer, nullable — Maximum number of results per page (allowed range 1–1000). When omitted, the server applies its own default (100).
  - `cursor` string, nullable — Opaque cursor from a previous response's `response_metadata.next_cursor`. Omit on the first request; when present it must be a non-empty opaque token (empty string is not valid).

## Response `200`

Success

- SearchEmployeeCalendarsOutput
  - `items` EmployeeCalendar[], required — Page of resolved employee calendars. Each item is keyed by field ID (e.g. `/employeeCalendar/employeeId`, `/employeeCalendar/calendarId`); only the fields requested in `fields` are populated. Items for employees that don't exist in the caller's company are silently omitted (no error). Diff the requested employee IDs against the returned `employeeId`s to detect unknowns.
    - `/employeeCalendar/employeeId` string, nullable — Identifier of the employee this row resolves the calendar for. Echoed back from the requested `employeeId` filter values.
    - `/employeeCalendar/calendarId` string, nullable — Identifier of the holiday calendar that applies to this employee, after resolution. Sent as a string for consistency with `/calendarEvent/calendarId` and to avoid JSON-number precision loss for snowflake-style IDs. `null` when `source = none` (no calendar configured).
    - `/employeeCalendar/calendarName` string, nullable — Display name of the resolved calendar. `null` when `calendarId` is `null`.
    - `/employeeCalendar/source` 'employment' | 'site' | 'none' — Closed list of supported sources returned in `/employeeCalendar/source` (and accepted as filter values for that field). Reflects the calendar-resolution rule applied per employee: `employment` (explicit override on the employment record) takes precedence over `site` (inherited from the employee's site); `none` means no calendar is configured for the employee.
    - `/employeeCalendar/siteId` string, nullable — Identifier of the employee's site (independent of which source supplied the calendar). Sent as a string for consistency with `/calendarEvent/siteId` and to avoid JSON-number precision loss for snowflake-style IDs. `null` when the employee has no site assigned.
  - `response_metadata` ResponseMetadata, required — Pagination metadata returned on every search response.
    - `next_cursor` string, nullable — Opaque cursor that identifies the start of the next page of results. Pass this value back as `cursor` on the next request to continue paging. `null` on the last page, when there are no more results.

## Other responses

- `400` — Bad request — the request was malformed or missing required parameters
- `401` — Unauthorized — authentication credentials are missing or invalid
- `403` — Forbidden — the authenticated user does not have permission to perform this operation
- `429` — Too many requests — rate limit exceeded, retry after the indicated delay
- `500` — Internal server error — an unexpected error occurred on the server

---

[API](https://skmtc.net/hibob/apis/employee-data-api.md) · [All operations](https://skmtc.net/hibob/apis/employee-data-api/llms.txt) · [OpenAPI document](https://skmtc-service-staging.skmtc.workers.dev/v1/apis/hibob/employee-data-api/revisions/0781ebbdda91/schema)
