Calendar and Availability
Calendar returns what is already booked inside a queried window, with recurrence expanded into individual occurrences. Availability returns the open time where a new booking can be placed. Both exist at every scope: a single bookable, a bookable type, a location or an organization.
The difference from GET /bookable/{id}/booking and similar list endpoints is that those return raw booking rows with recurring bookings as single rows. Calendar and availability expand and compose that data across the queried window.
| Endpoint | Returns | Reads from |
|---|---|---|
| Calendar | Booked occurrences inside the queried window, with recurrence expanded. | booking rows on the resource. |
| Availability | Open time windows inside the queried window where a new booking can be placed. | timeslot rows attached to the resource, minus booking rows on the resource. |
Input
Both endpoints take the same query parameters.
| Parameter | Required | Notes |
|---|---|---|
start_date | yes | RFC 3339 UTC, for example 2026-05-15T00:00:00Z. |
end_date | yes | RFC 3339 UTC. Must be after start_date. |
format | no | ical, nimcal, or vector. Omit for default JSON. Values are case sensitive. |
See Schedule for the timestamp rules that apply to every date field in the api.
Format options
Omit format to get the default JSON response. The other options are:
ical— an iCal feed suitable for calendar subscriptions.nimcal— the raw NimCal rule set, useful when you want to process recurrence yourself.vector— a pre-computed time vector array over the queried window. Requiresstart_dateandend_date. Rendering time-based data in a grid, histogram, or calendar component means mapping timestamps to pixel positions, which is repetitive work. The vector format does that computation server-side and returns a flat array where each entry maps directly to a slot, making it straightforward to drive a UI component without writing your own time arithmetic.
How availability is computed
For every bookable in scope, the server walks the queried window. The open surface starts as the union of all attached timeslot rules with available: true. From that surface the server subtracts the union of all rules with available: false, then subtracts every non cancelled booking whose schedule intersects the window. What remains is what the response returns.
A bookable with no directly attached timeslots is treated as open for the entire queried window. The only subtraction is existing bookings.
WARNING
Two things to know before you trust the surface. First, the booking handler and the availability calculation both read timeslots keyed on the bookable itself. Timeslots placed at a Bookable Type, Location or Organization do not propagate down into the per bookable surface, so a bookable's open hours need to live on the bookable itself. Second, priority on a timeslot binding does not affect the result. Multiple rules on the same bookable merge as union(available=true) minus union(available=false) regardless of priority.
How calendar is computed
Calendar is straightforward: take every non cancelled booking on the resource whose schedule intersects the queried window, expand recurrence inside the window, and return one entry per occurrence. A calendar at a higher scope unions the occurrences across every bookable in that scope.
The difference between calendar and a raw booking list call is recurrence. A weekly recurring booking shows up once in GET /bookable/{id}/booking, but it shows up once per occurring week in GET /bookable/{id}/calendar. Use the list when you want the records, use the calendar when you want a timetable.
Aggregation by scope
The composition is the same at every scope. Only the input set of bookables differs.
| Scope | Input set |
|---|---|
| Bookable | The bookable. |
| Bookable Type | Every bookable assigned to the type, visible to the caller. |
| Location | Every bookable placed at the location or any descendant location. |
| Organization | Every bookable owned by the organization. |