> ## Documentation Index
> Fetch the complete documentation index at: https://restoo.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Availability Flow

> Which endpoints to call to check availability before creating a booking, and in what order.

## Overview

Before creating a booking, your integration must walk through a sequence of availability checks. Each step validates that the requested combination of date, shift, experience, and add-ons is available for the given party size.

The diagram below shows the complete flow at a high level.

```mermaid theme={null}
flowchart TD
    START(["Start: pax, date, time"])

    CALENDAR["Get Calendar Availability
    (optional)"]

    START -.->|"optional"| CALENDAR
    CALENDAR -.->|"user picks a date"| START
    START --> WD

    WD["Get Work Day Availability"]
    WD --> WD_CHECK{"availability.isAvailable?"}
    WD_CHECK -->|"false"| WD_FALLBACK(["Handle fallbackOptions →"])
    WD_CHECK -->|"true"| USER_SHIFT

    USER_SHIFT["User selects a Shift"]
    USER_SHIFT --> HAS_EXP{"shift.hasExperiences?"}

    HAS_EXP -->|"true"| EXP_BLOCK
    HAS_EXP -->|"false"| SHIFT_AV

    EXP_BLOCK(["Experiences &
    Experience Add-ons →"])

    EXP_BLOCK --> SHIFT_AV

    SHIFT_AV["Get Shift Availability"]
    SHIFT_AV --> SHIFT_CHECK{"availability.isAvailable?"}
    SHIFT_CHECK -->|"false"| SHIFT_FALLBACK(["Handle fallbackOptions →"])
    SHIFT_CHECK -->|"true"| USER_SLOT

    USER_SLOT["User selects a Time Slot"]
    USER_SLOT --> HAS_ADDONS{"shift.hasAddOns?"}

    HAS_ADDONS -->|"true"| ADDONS_BLOCK
    HAS_ADDONS -->|"false"| BOOKING_AV

    ADDONS_BLOCK(["Time Slot Add-ons →"])
    ADDONS_BLOCK --> BOOKING_AV

    BOOKING_AV["Get Booking Availability"]
    BOOKING_AV --> BOOKING_CHECK{"availability.isAvailable?"}
    BOOKING_CHECK -->|"false"| BOOKING_FALLBACK(["Handle fallbackOptions →"])
    BOOKING_CHECK -->|"true"| CREATE

    CREATE(["Create Booking Flow →"])

    style CALENDAR fill:#e9e9e9,stroke:#999999,color:#000000
    style START fill:#28a745,stroke:#1e7e34,color:#ffffff
    style CREATE fill:#28a745,stroke:#1e7e34,color:#ffffff
    style EXP_BLOCK fill:#4a90d9,stroke:#2c6fad,color:#ffffff
    style ADDONS_BLOCK fill:#4a90d9,stroke:#2c6fad,color:#ffffff
    style WD_FALLBACK fill:#ffc107,stroke:#d39e00,color:#000000
    style SHIFT_FALLBACK fill:#ffc107,stroke:#d39e00,color:#000000
    style BOOKING_FALLBACK fill:#ffc107,stroke:#d39e00,color:#000000
```

***

## The availability object

Throughout this flow, many objects in the response carry their own `availability` object. **Whenever an object includes an `availability`, it manages its own availability independently** — this applies at every level, from the top-level request down to individual entities such as work days, shifts, time slots, experiences, add-on groups, individual add-ons, and floor plan areas, among others.

This means a parent being available does not imply its children are: a shift can be available while a specific experience or add-on within it is not. Always check the `availability` of every object before offering it to the customer, not just the top-level one.

Each `availability` object exposes:

* `isAvailable` — whether that specific object is available. Always check this first.
* When `isAvailable` is `false` — `outcome`, `code`, and `detail` explain why, and `fallbackOptions` lists alternative actions to offer the customer (see [Handle fallbackOptions](#handle-fallbackoptions)).

***

## Identifying a shift and a time slot

Several endpoints in this flow carry `{date}` and `{time}` in the path. Both
values come from the response that offered you the option — copy them, do not
compute them.

| Path                                          | `{time}` identifies | Take it from                 |
| :-------------------------------------------- | :------------------ | :--------------------------- |
| `/work-days/{date}/shifts/{time}/…`           | The shift           | The shift's `firstBookingAt` |
| `/work-days/{date}/time-slots/{time}/add-ons` | The time slot       | The slot's `startAt`         |
| `/availability/bookings/{date}/{time}`        | The time slot       | The slot's `startAt`         |

Shifts have no public identifier on purpose: the IDs behind a work day's shifts
can change at any time, so a stored one would go stale without warning. The
booking window is the stable handle instead — **any** time between
`firstBookingAt` and `lastBookingAt` resolves to that shift, and the response
covers the whole shift. Calling once per candidate time returns the same thing
every time.

<Warning>
  `{date}` is the calendar date of `{time}`, not the work day. The two differ
  when a shift crosses midnight: a shift running from
  `2025-03-20T20:00:00+02:00` to `2025-03-21T01:00:00+02:00` belongs to the work
  day starting on `2025-03-20`, but its `00:30` slot is requested with `{date}`
  set to `2025-03-21`. Taking both values from the same ISO datetime gets this
  right without having to think about it.
</Warning>

Send `{time}` as the local time inside that ISO value, with no offset — see
[Time Zones](/getting-started/api-essentials#time-zones).

***

## Handle fallbackOptions

When `availability.isAvailable` is `false`, the response includes a `fallbackOptions` array with one or more actions to offer the user. Always check this array before showing a generic error.

`code` sits beside them and says exactly which rule the request hit. Read it
when you want to tell the customer what to change rather than offer a generic
alternative — see [AvailabilityCode](/getting-started/enums#availabilitycode).

```mermaid theme={null}
flowchart TD
    START(["availability.isAvailable = false"])

    START --> HAS_FALLBACK{"fallbackOptions is empty?"}
    HAS_FALLBACK -->|"true"| NO_OPTIONS["Show generic no-availability message"]
    HAS_FALLBACK -->|"false"| SHOW_OPTIONS

    SHOW_OPTIONS["Present options to the user (highlight isPreferred = true)"]

    SHOW_OPTIONS --> ACTION{"User selects action"}

    ACTION -->|"CONTACT"| CONTACT["Redirect user to contact the venue"]

    ACTION -->|"REQUEST_BOOKING"| REQUEST["Allow user to submit a booking request"]

    ACTION -->|"JOIN_WAIT_LIST"| WAITLIST["Allow user to join the venue wait list"]

    ACTION -->|"UPDATE_SEARCH"| UPDATE["Ask user to update search parameters"]

    ACTION -->|"SEARCH_ALTERNATIVE_DATES"| ALT_DATES["Search for availability on nearby dates"]

    ACTION -->|"SEARCH_ALTERNATIVE_TENANTS"| ALT_TENANTS["Search for availability across other venues in the group"]

    style START fill:#ffc107,stroke:#d39e00,color:#000000
    style NO_OPTIONS fill:#e9e9e9,stroke:#999999,color:#000000
```

***

## Step 1 — Get Calendar Availability (optional)

Use this endpoint to retrieve availability across a date range. This is typically used to populate a calendar view before the user picks a specific date. It is not required — you can go directly to [Get Work Day Availability](#step-2-—-get-work-day-availability) if you already have a date.

**Endpoint:** [`POST /availability/calendar`](/booking-channel-api/methods/availability/calendar)

***

## Step 2 — Get Work Day Availability

Retrieves availability for a specific date and returns the list of available shifts for the given party size.

**Endpoint:** [`POST /availability/work-days/{date}`](/booking-channel-api/methods/availability/work-day)

<Note>
  Always send `status: CONFIRMED` on all availability requests. If no
  availability is found, `fallbackOptions` may offer alternative actions — such
  as requesting the booking or joining the wait list — depending on the venue
  configuration.
</Note>

The response includes a `shifts[]` array. Each shift exposes two flags that determine the next steps:

| Flag                   | Description                                                                                                                                          |
| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| `shift.hasExperiences` | The shift has experiences. You must go through the [Experiences flow](#step-3-—-experiences) before proceeding to Get Shift Availability.            |
| `shift.hasAddOns`      | The shift has time slot add-ons. You must call [Get Time Slot Add-ons Availability](#step-6-—-time-slot-add-ons) after the user selects a time slot. |

***

## Step 3 — Experiences

If `shift.hasExperiences` is `true`, you must check experience availability before proceeding to Get Shift Availability.

```mermaid theme={null}
flowchart TD
    START(["From: User selects a Shift"])

    START --> EXP_AV

    EXP_AV["Get Experiences Availability"]
    EXP_AV --> EXP_CHECK{"availability.isAvailable?"}
    EXP_CHECK -->|"false"| EXP_FALLBACK(["Handle fallbackOptions →"])
    EXP_CHECK -->|"true"| EXP_REQUIRED

    EXP_REQUIRED{"shift.isExperienceRequired?"}
    EXP_REQUIRED -->|"true"| USER_EXP_REQUIRED["User must select an Experience"]
    EXP_REQUIRED -->|"false"| USER_EXP_OPTIONAL["User may optionally select an Experience"]

    USER_EXP_OPTIONAL --> EXP_CHOSEN{"Experience selected?"}
    EXP_CHOSEN -->|"no"| END_NO_EXP(["Continue to: Get Shift Availability →"])
    EXP_CHOSEN -->|"yes"| USER_EXP

    USER_EXP_REQUIRED --> USER_EXP
    USER_EXP["User selects an Experience from experiences[]"]

    USER_EXP --> HAS_ADDONS{"Experience has addOns?"}
    HAS_ADDONS -->|"false"| END_EXP
    HAS_ADDONS -->|"true"| ADDONS_BLOCK(["Experience Add-ons →"])
    ADDONS_BLOCK --> END_EXP

    END_EXP(["Continue to: Get Shift Availability →"])

    style START fill:#4a90d9,stroke:#2c6fad,color:#ffffff
    style END_EXP fill:#4a90d9,stroke:#2c6fad,color:#ffffff
    style END_NO_EXP fill:#4a90d9,stroke:#2c6fad,color:#ffffff
    style ADDONS_BLOCK fill:#4a90d9,stroke:#2c6fad,color:#ffffff
    style EXP_FALLBACK fill:#ffc107,stroke:#d39e00,color:#000000
```

Retrieves the experiences available for the selected shift. The response includes `shift.isExperienceRequired`, which determines whether the user must select an experience to continue or may proceed without one.

**Endpoint:** [`POST /availability/work-days/{date}/shifts/{time}/experiences`](/booking-channel-api/methods/availability/experiences)

### Ticket quantity

`tickets` is always required. `automaticTicketQuantity` decides who picks the
value.

**When it is `false`**, the customer picks. Offer this range:

| Bound       | Value                                            |
| :---------- | :----------------------------------------------- |
| **Minimum** | `minTicketsPerBooking`, or `1` when it is `null` |
| **Maximum** | `maxTicketsAvailable` **minus** `soldTickets`    |

<Warning>
  `maxTicketsAvailable` still counts tickets that are already sold. Offer it as
  the maximum and the customer picks a number the venue cannot honour, which
  [Get Shift Availability](/booking-channel-api/methods/availability/shift)
  rejects on the very next call with `SHIFT_AVAILABLE_TICKETS_EXCEEDED`.
</Warning>

**When it is `true`**, compute the value instead of asking the customer:

```
max( ceil(bookingSize / paxPerTicket), minTicketsPerBooking ?? 1 )
```

where `bookingSize` is the
[booking size](/getting-started/key-concepts#booking-size).

<Warning>
  The match must be exact. A different value makes the experience unavailable
  with `EXPERIENCE_AUTO_TICKETS_AND_BOOKING_SIZE_NOT_MATCH`, and if it reaches
  [`Create Booking`](/booking-channel-api/methods/bookings/create), HTTP 409
  `BOOKING_AVAILABILITY_NOT_FOUND` — which says nothing about tickets.
</Warning>

Booking size is checked separately against `minBookingSize` and
`maxBookingSize`; it does not constrain the ticket count.

***

## Step 4 — Experience Add-ons

If the selected experience has add-ons, call this endpoint to retrieve the available add-ons.

```mermaid theme={null}
flowchart TD
    START(["From: Experience has add-ons"])

    START --> ADDONS_AV["Get Experience Add-ons Availability"]
    ADDONS_AV --> REQ_CHECK{"availability.isAvailable?"}
    REQ_CHECK -->|"false"| ADDONS_FALLBACK(["Handle fallbackOptions →"])
    REQ_CHECK -->|"true"| GROUP_CHECK{"addOnGroup.availability.isAvailable?"}

    GROUP_CHECK -->|"false"| SKIP(["Continue without add-ons →"])
    GROUP_CHECK -->|"true"| MIN_QUANTITY{"addOnGroup.minTotalQuantity gt 0?"}

    MIN_QUANTITY -->|"true - required"| USER_ADDON_REQUIRED["User must select from available Add-on(s)"]
    MIN_QUANTITY -->|"false - optional"| USER_ADDON_OPTIONAL["User may optionally select from available Add-on(s)"]

    USER_ADDON_REQUIRED --> END
    USER_ADDON_OPTIONAL --> END

    END(["Continue to: Get Shift Availability →"])

    style START fill:#4a90d9,stroke:#2c6fad,color:#ffffff
    style END fill:#4a90d9,stroke:#2c6fad,color:#ffffff
    style SKIP fill:#4a90d9,stroke:#2c6fad,color:#ffffff
    style ADDONS_FALLBACK fill:#ffc107,stroke:#d39e00,color:#000000
```

**Endpoint:** `POST /availability/work-days/{date}/shifts/{time}/experiences/add-ons`

Add-on availability is checked at three levels — the request (`availability`), the group (`addOnGroup.availability`), and each individual add-on (`addOnGroup.addOns[].availability`). Offer only the add-ons whose `isAvailable` is `true`, and if the group itself is unavailable, continue without add-ons. Once the group is available, use `addOnGroup.minTotalQuantity` to determine whether selecting an add-on is mandatory (`> 0`) or optional (`= 0`).

***

## Step 5 — Get Shift Availability

Retrieves the available time slots within the selected shift.

**Endpoint:** [`POST /availability/work-days/{date}/shifts/{time}/time-slots`](/booking-channel-api/methods/availability/shift)

### How timeSlots\[] is shaped

One entry per bookable combination of start time and floor plan area. `startAt`
is not unique — the same time comes back once per area, each entry with its own
`availability` — and the entries are not in chronological order.

Do not deduplicate by time: you would keep one area's availability at random,
hiding times that were free and offering times that were full.

`floorPlanAreas[]` can be wider than what is bookable. An area appears there even
when it has no times of its own, so offer only the ones that also appear in
`timeSlots[]`.

### Carry the floor plan area forward

Every entry in `timeSlots[]` brings its own `floorPlanAreaId`. Send it on each
later call — add-ons, the final availability check and Create Booking. A `null`
means there is no area to send.

<Warning>
  When the shift offers areas open to online booking, leaving `floorPlanAreaId`
  out is not a validation error: the shift comes back **unavailable**, with
  `SHIFT_BOOKING_FLOOR_PLAN_AREA_MUST_BE_DEFINED`. The time was free — only the
  area was missing.
</Warning>

***

## Step 6 — Time Slot Add-ons

If `shift.hasAddOns` is `true`, you must check time slot add-on availability after the user selects a time slot.

```mermaid theme={null}
flowchart TD
    START(["From: User selects a Time Slot"])

    START --> ADDONS_AV["Get Time Slot Add-ons Availability"]
    ADDONS_AV --> REQ_CHECK{"availability.isAvailable?"}
    REQ_CHECK -->|"false"| ADDONS_FALLBACK(["Handle fallbackOptions →"])
    REQ_CHECK -->|"true"| GROUP_CHECK{"addOnGroup.availability.isAvailable?"}

    GROUP_CHECK -->|"false"| SKIP(["Continue without add-ons →"])
    GROUP_CHECK -->|"true"| MIN_QUANTITY{"addOnGroup.minTotalQuantity gt 0?"}

    MIN_QUANTITY -->|"true - required"| USER_ADDON_REQUIRED["User must select from available Add-on(s)"]
    MIN_QUANTITY -->|"false - optional"| USER_ADDON_OPTIONAL["User may optionally select from available Add-on(s)"]

    USER_ADDON_REQUIRED --> END
    USER_ADDON_OPTIONAL --> END

    END(["Continue to: Get Booking Availability →"])

    style START fill:#4a90d9,stroke:#2c6fad,color:#ffffff
    style END fill:#4a90d9,stroke:#2c6fad,color:#ffffff
    style SKIP fill:#4a90d9,stroke:#2c6fad,color:#ffffff
    style ADDONS_FALLBACK fill:#ffc107,stroke:#d39e00,color:#000000
```

Add-on availability is checked at three levels — the request (`availability`), the group (`addOnGroup.availability`), and each individual add-on (`addOnGroup.addOns[].availability`). Offer only the add-ons whose `isAvailable` is `true`, and if the group itself is unavailable, continue without add-ons. Once the group is available, use `addOnGroup.minTotalQuantity` to determine whether selecting an add-on is mandatory (`> 0`) or optional (`= 0`).

**Endpoint:** `POST /availability/work-days/{date}/time-slots/{time}/add-ons`

***

## Step 7 — Get Booking Availability

The final availability check before creating a booking. This endpoint validates the complete booking request and returns the applicable booking conditions, such as the cancellation policy, that must be presented to the user before proceeding.

**Endpoint:** [`POST /availability/bookings/{date}/{time}`](/booking-channel-api/methods/availability/booking)

<Warning>
  This step is mandatory. The response includes final booking conditions such as
  the cancellation policy that must be presented to the user before creating the
  booking.
</Warning>

### Add-on names and prices

The top-level `addOns` resolves everything you requested — the shift's add-ons
and the experience's, flattened — with their current name, price and currency,
in the language of the request. Build the booking summary from it rather than
from values cached earlier in the funnel, which can carry a price the venue has
since changed, or the language the customer had selected then.
