openapi: 3.0.3
info:
  title: Listings API V1
  version: 1.0.0
  contact:
    name: Bookable
    url: https://www.bookabletech.com
    email: hello@bookabletech.com
  description: >
    ## Overview


    The Listings API V1 is a **venue-aggregated** discovery interface.


    The response root carries `meta` and `listings[]`. Each entry in
    `listings[]`

    is a `ListingGroup` — one per operator (venue-group / chain), carrying a

    `groupName` and an `items[]` array.


    Each `item` represents a single venue (or a standalone event when no venue
    is

    known): it carries the venue `name`, a `location`, and a `capabilities[]`

    array. Each entry in `capabilities[]` is a `Listing` with exactly **one**

    capability (`table_reservation` or `event_ticket`), and carries its own

    `photos[]`. A venue that also hosts a ticketed event therefore yields a
    single

    item with two capabilities — one for the venue, one for the event.


    All bookable units (venue products, event ticket types, future capability

    types) are unified under a single `bookableProducts[]` array. Every entry is
    a

    single `Bookable` shape; capability-specific fields are nullable and the

    parent Listing's `capability` sets the context.


    Temporal information (venue opening hours, event occurrences, blackout

    dates) is unified under a single `availabilityOccurrence[]` field of
    `CalendarRule`s.

    Both capabilities use the same shape.


    Pre-order assets (packages, menus) are unified under a single `preorders`

    container — applicable to either capability when the listing supports

    pre-orders.


    ## ID formats — two distinct identifiers, both CompositeId-encoded


    `Listing.id` (venue or event, as returned in `/v1/listings`) is 3 segments:

    `{VenueGroupId}|{RmsSlug}|{ResourceId}`. For a hybrid entity the event-side

    row uses the EventExt's CompositeId (different `RmsSlug` + `ResourceId`) so

    the two rows are unambiguous.


    `Bookable.compositeId` (a specific bookable product/ticket type — used in

    `bookableProducts[]`, and as the `compositeId` in `/v1/availabilities` and

    `/v1/orders`) is 4 segments:
    `{VenueGroupId}|{RmsSlug}|{ResourceId}|{AdditionalId}`.

    These are two different identifiers for two different things, not two

    interchangeable formats for the same one — a `Listing.id` never appears
    where

    a `Bookable.compositeId` is expected, or vice versa.
servers:
  - url: https://api.bookabletech.com
    description: Live
  - url: https://api-sandbox.bookabletech.com
    description: Sandbox
tags:
  - name: Listings
    description: Discover and retrieve bookable listings
  - name: Orders
    description: Create and manage orders
paths:
  /v1/listings:
    get:
      operationId: getListings
      summary: Retrieve the full bookable catalogue (paginated, venue-aggregated)
      description: >
        Returns the entire bookable catalogue available to the caller, paginated
        and

        venue-aggregated. Results are grouped by operator (`ListingGroup`); each
        group

        carries its venues (`ListingItem`), and each venue carries one `Listing`
        per

        capability it offers (`table_reservation`, `event_ticket`).


        A venue that also hosts a ticketed event therefore appears once, with
        two

        entries in its `capabilities[]` — one per capability. Optional query
        filters

        (capability, name, location, features, date, …) narrow the catalogue;
        with no

        filters the whole catalogue is returned page by page.
      tags:
        - Listings
      parameters:
        - $ref: '#/components/parameters/CorrelationID'
        - $ref: '#/components/parameters/PartnerReference'
        - name: capability
          in: query
          required: false
          description: |
            Restrict results to a single capability. Venues are still returned
            aggregated; only their matching capability entries are included.
          schema:
            type: string
            enum:
              - table_reservation
              - event_ticket
          example: table_reservation
        - name: venueName
          in: query
          required: false
          description: Filter by listing name — partial, case-insensitive match.
          schema:
            type: string
            minLength: 2
            maxLength: 100
          example: dishoom
        - name: operatorName
          in: query
          required: false
          description: Filter by operator (venue-group / chain) name.
          schema:
            type: string
            minLength: 2
            maxLength: 100
          example: stonegate
        - name: city
          in: query
          required: false
          schema:
            type: string
            minLength: 2
            maxLength: 100
          example: london
        - name: area
          in: query
          required: false
          schema:
            type: string
            minLength: 2
            maxLength: 100
          example: shoreditch
        - name: features
          in: query
          required: false
          description: >-
            Filter by type / feature slugs (matched against `types`).
            Comma-separated list.
          schema:
            type: array
            items:
              type: string
            minItems: 1
            maxItems: 10
          style: form
          explode: false
          example:
            - dog_friendly
            - live_music
        - name: date
          in: query
          required: false
          description: >-
            Filter listings that have availability on a specific date
            (YYYY-MM-DD).
          schema:
            type: string
            format: date
          example: '2026-06-15'
        - name: fields
          in: query
          required: false
          description: >
            Field selection using Google Fields syntax. Comma-separated list of
            top-level

            Listing fields to include. When omitted, all fields are returned.
          schema:
            type: string
          example: >-
            id,name,capability,location(lat,lng,city),bookableProducts(compositeId,name)
        - name: pageNumber
          in: query
          required: false
          schema:
            type: integer
            minimum: 1
            default: 1
        - name: pageSize
          in: query
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
        - name: sortBy
          in: query
          required: false
          schema:
            type: string
            enum:
              - default
              - name
              - city
              - area
              - relevance
            default: default
        - $ref: '#/components/parameters/SortDirection'
      responses:
        '200':
          description: Paginated list of listings.
          headers:
            X-Correlation-ID:
              schema:
                type: string
            X-RateLimit-Limit:
              $ref: '#/components/headers/RateLimitLimit'
            X-RateLimit-Remaining:
              $ref: '#/components/headers/RateLimitRemaining'
            X-RateLimit-Reset:
              $ref: '#/components/headers/RateLimitReset'
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/Page'
                  - type: object
                    properties:
                      listings:
                        type: array
                        items:
                          $ref: '#/components/schemas/ListingGroup'
              examples:
                tableReservationListing:
                  $ref: '#/components/examples/TableReservationListingPage'
                eventTicketListing:
                  $ref: '#/components/examples/EventTicketListingPage'
                hybridListing:
                  $ref: '#/components/examples/HybridListingPage'
            application/vnd.bookable.toon+json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/Page'
                  - type: object
                    properties:
                      listings:
                        type: array
                        items:
                          $ref: '#/components/schemas/ListingGroup'
              examples:
                tableReservationListing:
                  $ref: '#/components/examples/TableReservationListingPage'
                eventTicketListing:
                  $ref: '#/components/examples/EventTicketListingPage'
                hybridListing:
                  $ref: '#/components/examples/HybridListingPage'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        default:
          $ref: '#/components/responses/InternalServerError'
      security:
        - Live:
            - listing:read
        - Sandbox:
            - listing:read
  /v1/availabilities:
    post:
      operationId: checkAvailabilities
      summary: Check availability for multiple bookable products in one call
      description: >
        Batch availability check. Modelled as a **command** (POST), not a
        search.


        The request carries root-level `date`, `quantity` and optional `time`,
        plus

        a `bookableProducts[]` list. Each entry has a required `compositeId` and
        may

        override `date`, `quantity` and `time`. When an entry omits any of
        those,

        the root-level value is used.


        `quantity` carries the count for either capability, same as in `POST
        /v1/orders`

        — number of covers for a table reservation, number of tickets for an

        event-ticket line.


        Every entry must end up with `date` **and** `quantity` resolved (either
        from

        the entry or the root). `time` is optional — event capabilities are
        queried

        by date alone; for table reservations it narrows the slot when supplied.
        An

        entry that cannot resolve `date` and `quantity` is reported as an error

        result and does **not** block the others.


        Searches run independently: a failure on one `compositeId` yields an
        `error`

        on that result only — all other results are returned normally. The
        response

        carries one `AvailabilityResult` per requested `compositeId`, preserving

        request order.


        There is no `404` response: an unknown/nonexistent `compositeId` is not
        a

        request-level failure — it's reported as an `error` on that entry's

        `AvailabilityResult`, same as any other per-entry failure (e.g.
        unresolved

        `date`/`quantity`), never as an HTTP-level not-found for the whole call.
      tags:
        - Listings
      parameters:
        - $ref: '#/components/parameters/CorrelationID'
        - $ref: '#/components/parameters/PartnerReference'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AvailabilitiesRequest'
            example:
              date: '2026-06-15'
              quantity: 4
              time: '19:00'
              bookableProducts:
                - compositeId: 29|CS|275cc44dd2e2496fba44857c9257443a|prod1
                - compositeId: 29|CS|275cc44dd2e2496fba44857c9257443a|prod2
                  quantity: 2
                  time: '20:00'
                - compositeId: 29|CS|999|prod3
      responses:
        '200':
          description: |
            One result per requested `compositeId`. Each result carries either
            `availability` (success) or `error` (failure); never both.
          headers:
            X-Correlation-ID:
              schema:
                type: string
            X-RateLimit-Limit:
              $ref: '#/components/headers/RateLimitLimit'
            X-RateLimit-Remaining:
              $ref: '#/components/headers/RateLimitRemaining'
            X-RateLimit-Reset:
              $ref: '#/components/headers/RateLimitReset'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AvailabilitiesResponse'
              examples:
                mixed:
                  $ref: '#/components/examples/AvailabilitiesResponseExample'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        default:
          $ref: '#/components/responses/InternalServerError'
      security:
        - Live:
            - listing:read
        - Sandbox:
            - listing:read
  /v1/orders:
    post:
      operationId: createOrder
      summary: Create an order — one or more independent bookings, across any platform
      description: >
        Creates an order containing one or more bookings. Each item is resolved
        and

        booked **independently** — there is no cross-item transaction. A failure
        on

        one item (e.g. the venue/event on one platform is fully booked) does not

        block or roll back the others. The response carries one
        `OrderItemResult`

        per requested item, preserving request order — mirroring how

        `POST /v1/availabilities` reports one result per `compositeId`.


        Because every item is self-sufficient via its own `compositeId` (which
        embeds

        `VenueGroupId` + `RmsSlug`), a single order can span multiple venues and
        even

        multiple RMS platforms (e.g. one item on Collins, another on Zonal) —
        there is

        no top-level `listingId` constraining the order to one platform.


        Each `OrderItem` is unified (no `capability` field, no `oneOf`) —
        `quantity`

        carries the count for either capability (covers for a table reservation,
        ticket

        count for an event-ticket line); `time` additionally applies to table

        reservations. Capability is resolved server-side from the
        `compositeId`'s RMS

        slug, not from which fields are populated. An order's items may mix

        capabilities and platforms freely.
      tags:
        - Orders
      parameters:
        - $ref: '#/components/parameters/CorrelationID'
        - $ref: '#/components/parameters/PartnerReference'
        - $ref: '#/components/parameters/IntegrationSlug'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateOrderRequest'
            examples:
              tableReservation:
                summary: >-
                  Create table reservations across two different platforms in
                  one order
                value:
                  items:
                    - compositeId: >-
                        29|CO|275cc44dd2e2496fba44857c9257443a|e4d909c290d0fb1ca068ffaddf22cbd0
                      date: '2026-06-15'
                      time: '19:30'
                      duration: 90
                      quantity: 4
                      type: book
                    - compositeId: 41|ZO|9a1c20-5b7e-4c1a|8c2d4e60-9a1b-4f3c
                      date: '2026-06-15'
                      time: '20:00'
                      quantity: 2
                      type: book
                  customer:
                    firstName: John
                    lastName: Doe
                    email: john.doe@example.com
                    phone: '+441234567890'
              eventTicket:
                summary: Purchase tickets for two ticket types on the same event date
                value:
                  items:
                    - compositeId: 29|TN|69f87ae8c7475734752555b4|69f87a8568063a182c46c198
                      date: '2026-07-19'
                      quantity: 2
                    - compositeId: 29|TN|69f87ae8c7475734752555b4|69f87a97c74757172d362198
                      date: '2026-07-19'
                      quantity: 1
                  customer:
                    firstName: Jane
                    lastName: Smith
                    email: jane.smith@example.com
                    phone: '+441234567891'
      responses:
        '200':
          description: >-
            One result per requested item, in request order — every item
            succeeded.
          headers:
            X-Correlation-ID:
              schema:
                type: string
            X-RateLimit-Limit:
              $ref: '#/components/headers/RateLimitLimit'
            X-RateLimit-Remaining:
              $ref: '#/components/headers/RateLimitRemaining'
            X-RateLimit-Reset:
              $ref: '#/components/headers/RateLimitReset'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Order'
              examples:
                tableReservation:
                  $ref: >-
                    #/components/examples/CreateOrderTableReservationResponseExample
                eventTicket:
                  $ref: '#/components/examples/CreateOrderEventTicketResponseExample'
            application/vnd.bookable.toon+json:
              schema:
                $ref: '#/components/schemas/Order'
        '207':
          description: >
            One result per requested item, in request order. Each result carries

            either `booking` (success) or `error` (failure); never both.
            Returned

            whenever one or more items failed — including when **every** item

            failed — so a bulk success is never conflated with a partial or
            total

            failure; check each result.
          headers:
            X-Correlation-ID:
              schema:
                type: string
            X-RateLimit-Limit:
              $ref: '#/components/headers/RateLimitLimit'
            X-RateLimit-Remaining:
              $ref: '#/components/headers/RateLimitRemaining'
            X-RateLimit-Reset:
              $ref: '#/components/headers/RateLimitReset'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Order'
              examples:
                partialFailure:
                  $ref: '#/components/examples/CreateOrderPartialFailureExample'
            application/vnd.bookable.toon+json:
              schema:
                $ref: '#/components/schemas/Order'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        default:
          $ref: '#/components/responses/InternalServerError'
      security:
        - Live:
            - order:create
        - Sandbox:
            - order:create
  /v1/orders/{orderId}:
    patch:
      operationId: updateOrder
      summary: Update one or more bookings within an order
      description: >
        Partial update — each `OrderItemUpdate` only carries the fields being
        changed;

        anything omitted keeps its current value. Items are resolved and updated

        **independently**, same isolation model as `POST /v1/orders`: a failure
        on one

        item does not block the others.


        Capability (table reservation vs. event ticket) is resolved server-side
        from the

        existing booking, not from the request — every field below applies
        uniformly to

        either capability at the API level, even where the RMS-side effect
        currently

        differs (noted per field).


        `compositeId` moves the booking to a different bookable product — the
        same

        identifier used everywhere else (`Bookable.compositeId`, the only thing
        taken

        from a listing's `bookableProducts[]`), not a ticket-specific field.
        Supported for

        both capabilities: for event tickets it changes the ticket type (the

        `compositeId`'s 4th segment); for table reservations it changes the
        booked

        product/space. The new `compositeId` must reference the same venue
        group, RMS and

        venue/event as the booking's current product — only the trailing
        product/ticket-type

        segment may differ; a cross-venue or cross-event move is rejected (400
        `ORDER-N-007`).


        `firstName`/`lastName`/`email`/`phone` update the guest's details,
        either capability.


        Preorders (table-reservation only in practice — event tickets have no
        preorder

        concept, these fields are accepted but a no-op for them today):
        `preorders` is a

        **full replacement** of the current preorder set (omit to leave
        unchanged, send

        empty arrays to clear). `preorderChanges` is the incremental alternative
        — add or

        remove specific packages/menus without resending the rest; mutually
        exclusive with

        `preorders` on the same item (400 `ORDER-N-008`).
      tags:
        - Orders
      parameters:
        - $ref: '#/components/parameters/CorrelationID'
        - $ref: '#/components/parameters/PartnerReference'
        - name: orderId
          in: path
          required: true
          schema:
            type: string
          example: ORD-9980140810
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/OrderUpdateRequest'
            examples:
              changeTicketType:
                summary: Move a ticket-type line to a different ticket type and date
                value:
                  items:
                    - bookingId: 1|TN|6a59f0a0f58eb1c0fa00d040
                      compositeId: 1|TN|6a2bbc0285bf210719631c44|6a2bbb580ed61600150618f7
                      date: '2027-02-22'
                      notes: Customer upgraded to Kids ticket
              rescheduleTable:
                summary: Change a table reservation's time and party size
                value:
                  items:
                    - bookingId: 1|CO|BKG-8841203|B
                      time: '20:00'
                      quantity: 6
              changeCustomerAndProduct:
                summary: >-
                  Update the guest's name/phone and move a table reservation to
                  a different product, same venue
                value:
                  items:
                    - bookingId: 29|CO|BKG-8841203|B
                      firstName: Jonathan
                      phone: '+441234567899'
                      compositeId: >-
                        29|CO|275cc44dd2e2496fba44857c9257443a|8c2d4e60-9a1b-4f3c-8d2e-2b3c4d5e6f70
              addPreorderPackage:
                summary: >-
                  Add a preorder package to a table reservation without
                  resending the rest of the preorders
                value:
                  items:
                    - bookingId: 29|CO|BKG-8841203|B
                      preorderChanges:
                        addPackages:
                          - id: 3f9a1c20-5b7e-4c1a-9e2d-1a2b3c4d5e6f
                            quantity: 2
      responses:
        '200':
          description: >-
            One result per requested item, in request order — every item
            succeeded.
          headers:
            X-Correlation-ID:
              schema:
                type: string
            X-RateLimit-Limit:
              $ref: '#/components/headers/RateLimitLimit'
            X-RateLimit-Remaining:
              $ref: '#/components/headers/RateLimitRemaining'
            X-RateLimit-Reset:
              $ref: '#/components/headers/RateLimitReset'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Order'
              examples:
                tableReservation:
                  $ref: >-
                    #/components/examples/UpdateOrderTableReservationResponseExample
                eventTicket:
                  $ref: '#/components/examples/UpdateOrderEventTicketResponseExample'
                changeCustomerAndProduct:
                  $ref: >-
                    #/components/examples/UpdateOrderCustomerAndProductResponseExample
                addPreorderPackage:
                  $ref: '#/components/examples/UpdateOrderAddPreorderResponseExample'
        '207':
          description: >
            One result per requested item, in request order. Each result carries

            either `booking` (success) or `error` (failure); never both.
            Returned

            whenever one or more items failed — including when every item failed
            —

            so a full success is never conflated with a partial or total
            failure.
          headers:
            X-Correlation-ID:
              schema:
                type: string
            X-RateLimit-Limit:
              $ref: '#/components/headers/RateLimitLimit'
            X-RateLimit-Remaining:
              $ref: '#/components/headers/RateLimitRemaining'
            X-RateLimit-Reset:
              $ref: '#/components/headers/RateLimitReset'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Order'
              examples:
                bookingNotFound:
                  $ref: '#/components/examples/UpdateOrderFailureExample'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'
        default:
          $ref: '#/components/responses/InternalServerError'
      security:
        - Live:
            - order:create
        - Sandbox:
            - order:create
    delete:
      operationId: deleteOrder
      summary: Cancel bookings within an order, or the entire order
      description: >
        Cancels the bookings listed in `bookingIds`. When `bookingIds` is
        omitted or

        empty, cancels **every** booking belonging to `orderId`. Each
        cancellation is

        independent — one failure does not block the others.
      tags:
        - Orders
      parameters:
        - $ref: '#/components/parameters/CorrelationID'
        - $ref: '#/components/parameters/PartnerReference'
        - name: orderId
          in: path
          required: true
          schema:
            type: string
          example: ORD-9980140810
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/OrderCancelRequest'
            examples:
              wholeOrder:
                summary: Cancel every booking in the order (empty/omitted body)
                value: {}
              singleBooking:
                summary: Cancel one booking within the order
                value:
                  bookingIds:
                    - 1|TN|423458
      responses:
        '200':
          description: >-
            One result per cancelled item, in request order — every cancellation
            succeeded.
          headers:
            X-Correlation-ID:
              schema:
                type: string
            X-RateLimit-Limit:
              $ref: '#/components/headers/RateLimitLimit'
            X-RateLimit-Remaining:
              $ref: '#/components/headers/RateLimitRemaining'
            X-RateLimit-Reset:
              $ref: '#/components/headers/RateLimitReset'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Order'
              examples:
                wholeOrder:
                  $ref: '#/components/examples/CancelOrderWholeResponseExample'
                singleBooking:
                  $ref: >-
                    #/components/examples/CancelOrderSingleBookingResponseExample
        '207':
          description: >
            One result per cancelled item. Each result carries either `booking`

            (the now-cancelled booking) or `error` (failure); never both.
            Returned

            whenever one or more cancellations failed — including when every one

            failed — so a full success is never conflated with a partial or
            total

            failure.
          headers:
            X-Correlation-ID:
              schema:
                type: string
            X-RateLimit-Limit:
              $ref: '#/components/headers/RateLimitLimit'
            X-RateLimit-Remaining:
              $ref: '#/components/headers/RateLimitRemaining'
            X-RateLimit-Reset:
              $ref: '#/components/headers/RateLimitReset'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Order'
              examples:
                alreadyCancelled:
                  $ref: '#/components/examples/CancelOrderFailureResponseExample'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'
        default:
          $ref: '#/components/responses/InternalServerError'
      security:
        - Live:
            - order:create
        - Sandbox:
            - order:create
components:
  parameters:
    CorrelationID:
      in: header
      name: X-Correlation-ID
      required: false
      description: Optional ID for cross-system transaction tracking.
      schema:
        type: string
        example: 11C46F5F-CDEF-4865-94B2-0EE0EDCC26DA
    PartnerReference:
      in: header
      name: X-Partner-Reference
      required: false
      description: >-
        Required for Bookable Agents — identifies the partner on whose behalf
        the request is made.
      schema:
        type: string
        example: bottomlessbrunch.com
    SortDirection:
      in: query
      name: sortDirection
      required: false
      schema:
        type: string
        enum:
          - ASC
          - DESC
        default: ASC
    IntegrationSlug:
      in: header
      name: X-Booking-Source
      required: false
      description: |
        Identifies the source or channel through which the order was created.
        Defaults to "api" when not provided.
      schema:
        type: string
        enum:
          - email
          - phone
          - api
          - webhook
          - js-tag
          - chatbot
          - puller
          - portal
        example: api
  schemas:
    Capability:
      type: string
      description: Identifies a booking capability.
      enum:
        - table_reservation
        - event_ticket
      example: table_reservation
    Location:
      type: object
      nullable: true
      description: Physical location. `null` for virtual listings (e.g. online events).
      properties:
        street:
          type: string
          example: 1 Upper St Martin's Lane
        area:
          type: string
          description: Neighbourhood(s), pipe-separated.
          example: Covent Garden|Seven Dials
        city:
          type: string
          example: London
        postCode:
          type: string
          example: WC2H 9FB
        lat:
          type: number
          format: double
          example: 51.513
        lng:
          type: number
          format: double
          example: -0.128
        gPlace:
          type: string
          example: ChIJN1t_tDeuEmsRUsoyG83frY4
    AutoConfirmRule:
      type: object
      properties:
        date:
          type: string
          format: date
          nullable: true
          example: '2026-06-15'
        dateType:
          type: string
          nullable: true
          example: weekday
        minPeople:
          type: integer
          nullable: true
          example: 1
        maxPeople:
          type: integer
          nullable: true
          example: 6
        timeFrom:
          type: string
          pattern: ^([0-1]?[0-9]|2[0-3]):[0-5][0-9]$
          nullable: true
          example: '12:00'
        timeTo:
          type: string
          pattern: ^([0-1]?[0-9]|2[0-3]):[0-5][0-9]$
          nullable: true
          example: '22:30'
    Package:
      type: object
      required:
        - id
        - name
        - price
        - type
      properties:
        id:
          type: string
          example: 3f9a1c20-5b7e-4c1a-9e2d-1a2b3c4d5e6f
        name:
          type: string
          example: Sunday Roast
        price:
          type: number
          format: double
          example: 28.5
        description:
          type: string
          nullable: true
          example: Slow-roasted beef, Yorkshire pudding, seasonal vegetables.
        type:
          type: string
          example: main
        sub_type:
          type: string
          nullable: true
          example: roast
        diet_types:
          type: array
          items:
            type: string
          example:
            - gluten_free_option
        allergens:
          type: array
          items:
            type: string
          example:
            - gluten
            - dairy
    Menu:
      type: object
      required:
        - id
        - name
        - items
      properties:
        id:
          type: string
          example: b4e6f810-1c2d-4a3b-9c8d-3c4d5e6f7081
        name:
          type: string
          example: Spring Tasting Menu
        description:
          type: string
          nullable: true
          example: Five-course seasonal tasting menu with optional wine pairing.
        items:
          type: array
          items:
            $ref: '#/components/schemas/MenuItem'
    MenuItem:
      type: object
      required:
        - packageId
      properties:
        packageId:
          type: string
          example: c5f70921-2d3e-4b4c-8d9e-4d5e6f708192
        name:
          type: string
          nullable: true
          example: Amuse Bouche
        price:
          type: number
          format: float
          nullable: true
          example: 0
        description:
          type: string
          nullable: true
          example: Chef's seasonal selection.
        type:
          type: string
          nullable: true
          example: starter
        subType:
          type: string
          nullable: true
          example: amuse
        displayOrder:
          type: integer
          nullable: true
          example: 1
    Preorders:
      type: object
      nullable: true
      description: |
        Pre-order assets for a listing. Either capability MAY expose packages
        and/or menus that can be selected at booking time. `null` when the
        listing does not support pre-orders.
      properties:
        packages:
          type: array
          nullable: true
          items:
            $ref: '#/components/schemas/Package'
        menus:
          type: array
          nullable: true
          items:
            $ref: '#/components/schemas/Menu'
    CalendarRuleType:
      type: string
      description: |
        Type of calendar rule:
        - `date_range`: continuous date range
        - `recurring`: weekday/month pattern, no time
        - `time_based`: time window only, applies every day
        - `specific_dates`: explicit list of dates
        - `specific_dates_with_time`: explicit dates with time window
        - `date_range_with_time`: date range with time window
        - `recurring_with_time`: weekday/month pattern with time window
      enum:
        - date_range
        - recurring
        - time_based
        - specific_dates
        - specific_dates_with_time
        - date_range_with_time
        - recurring_with_time
      example: recurring_with_time
    CalendarRule:
      type: object
      description: |
        Unified temporal rule. Describes when a listing is available:
        venue opening hours, event occurrences, blackout dates and exceptions.
        Discriminated by `type`; rule-specific fields live under `occurrences`.
        Rules of the same `type`, `status` and time window are aggregated into a
        single entry (recurring rules combine their `daysOfWeek`, specific-date
        rules combine their `dates`).
      required:
        - type
        - occurrences
      properties:
        type:
          $ref: '#/components/schemas/CalendarRuleType'
        name:
          type: string
          nullable: true
          example: Weekday dinner service
        description:
          type: string
          nullable: true
          example: Standard weekday opening hours, kitchen open until 21:30.
        status:
          type: string
          enum:
            - open
            - closed
          default: open
          description: |
            `open` → listing IS available during this rule.
            `closed` → listing IS NOT available during this rule (overrides
            an underlying `open` rule, e.g. a holiday closure).
          example: open
        notes:
          type: string
          nullable: true
          example: Last seating 21:30.
        occurrences:
          description: Rule-specific payload — shape determined by `type`.
      discriminator:
        propertyName: type
        mapping:
          date_range: '#/components/schemas/DateRangeCalendarRule'
          recurring: '#/components/schemas/RecurringCalendarRule'
          time_based: '#/components/schemas/TimeBasedCalendarRule'
          specific_dates: '#/components/schemas/SpecificDatesCalendarRule'
          specific_dates_with_time: '#/components/schemas/SpecificDatesWithTimeCalendarRule'
          date_range_with_time: '#/components/schemas/DateRangeWithTimeCalendarRule'
          recurring_with_time: '#/components/schemas/RecurringWithTimeCalendarRule'
    DateRangeCalendarRule:
      description: 'Calendar rule for a continuous date range. `type: date_range`.'
      allOf:
        - $ref: '#/components/schemas/CalendarRule'
        - type: object
          properties:
            occurrences:
              type: object
              required:
                - startDate
                - endDate
              properties:
                startDate:
                  type: string
                  format: date
                  description: Start of the date range (inclusive).
                  example: '2026-06-01'
                endDate:
                  type: string
                  format: date
                  description: End of the date range (inclusive).
                  example: '2026-08-31'
    RecurringCalendarRule:
      description: >-
        Calendar rule for a recurring weekday/month pattern (no time). `type:
        recurring`.
      allOf:
        - $ref: '#/components/schemas/CalendarRule'
        - type: object
          properties:
            occurrences:
              type: object
              required:
                - daysOfWeek
                - months
              properties:
                daysOfWeek:
                  type: array
                  items:
                    type: integer
                    minimum: 0
                    maximum: 6
                  description: Weekdays the rule applies to (0=Sunday … 6=Saturday).
                  minItems: 1
                  maxItems: 7
                  example:
                    - 4
                months:
                  type: array
                  items:
                    type: integer
                    minimum: 1
                    maximum: 12
                  description: Months the rule applies to (1=January … 12=December).
                  minItems: 1
                  maxItems: 12
                  example:
                    - 6
                    - 7
                    - 8
                ordinal:
                  type: integer
                  nullable: true
                  minimum: -1
                  maximum: 5
                  description: >
                    Restricts the rule to only the Nth occurrence of each
                    matching

                    weekday within the month (1=1st … 5=5th, -1=last). `null`
                    (the

                    default) means every occurrence of the weekday — a plain
                    weekly

                    pattern. E.g. "1st Thursday of the month" → `daysOfWeek:
                    [4]`,

                    `ordinal: 1`.
                  example: 1
    TimeBasedCalendarRule:
      description: 'Calendar rule for a daily time window. `type: time_based`.'
      allOf:
        - $ref: '#/components/schemas/CalendarRule'
        - type: object
          properties:
            occurrences:
              type: object
              required:
                - startTime
                - endTime
              properties:
                startTime:
                  type: string
                  pattern: ^([0-1]?[0-9]|2[0-3]):[0-5][0-9]$
                  description: Start of the daily window (HH:mm, 24-hour).
                  example: '12:00'
                endTime:
                  type: string
                  pattern: ^([0-1]?[0-9]|2[0-3]):[0-5][0-9]$
                  description: End of the daily window (HH:mm, 24-hour).
                  example: '23:00'
    SpecificDatesCalendarRule:
      description: 'Calendar rule for an explicit list of dates. `type: specific_dates`.'
      allOf:
        - $ref: '#/components/schemas/CalendarRule'
        - type: object
          properties:
            occurrences:
              type: object
              required:
                - dates
              properties:
                dates:
                  type: array
                  items:
                    type: string
                    format: date
                  minItems: 1
                  description: Explicit dates (YYYY-MM-DD) the rule applies to.
                  example:
                    - '2026-12-25'
                    - '2026-12-26'
                    - '2027-01-01'
    SpecificDatesWithTimeCalendarRule:
      description: >-
        Calendar rule for an explicit list of dates with a time window. `type:
        specific_dates_with_time`.
      allOf:
        - $ref: '#/components/schemas/CalendarRule'
        - type: object
          properties:
            occurrences:
              type: object
              required:
                - dates
                - startTime
                - endTime
              properties:
                dates:
                  type: array
                  items:
                    type: string
                    format: date
                  minItems: 1
                  example:
                    - '2026-08-15'
                startTime:
                  type: string
                  pattern: ^([0-1]?[0-9]|2[0-3]):[0-5][0-9]$
                  example: '20:00'
                endTime:
                  type: string
                  pattern: ^([0-1]?[0-9]|2[0-3]):[0-5][0-9]$
                  example: '23:30'
    DateRangeWithTimeCalendarRule:
      description: >-
        Calendar rule for a date range with a daily time window. `type:
        date_range_with_time`.
      allOf:
        - $ref: '#/components/schemas/CalendarRule'
        - type: object
          properties:
            occurrences:
              type: object
              required:
                - startDate
                - endDate
                - startTime
                - endTime
              properties:
                startDate:
                  type: string
                  format: date
                  example: '2026-06-01'
                endDate:
                  type: string
                  format: date
                  example: '2026-08-31'
                startTime:
                  type: string
                  pattern: ^([0-1]?[0-9]|2[0-3]):[0-5][0-9]$
                  example: '19:00'
                endTime:
                  type: string
                  pattern: ^([0-1]?[0-9]|2[0-3]):[0-5][0-9]$
                  example: '22:00'
    RecurringWithTimeCalendarRule:
      description: >-
        Calendar rule for a recurring weekday/month pattern with a daily time
        window. `type: recurring_with_time`.
      allOf:
        - $ref: '#/components/schemas/CalendarRule'
        - type: object
          properties:
            occurrences:
              type: object
              required:
                - daysOfWeek
                - months
                - startTime
                - endTime
              properties:
                daysOfWeek:
                  type: array
                  items:
                    type: integer
                    minimum: 0
                    maximum: 6
                  minItems: 1
                  maxItems: 7
                  example:
                    - 1
                    - 2
                    - 3
                    - 4
                    - 5
                months:
                  type: array
                  items:
                    type: integer
                    minimum: 1
                    maximum: 12
                  minItems: 1
                  maxItems: 12
                  example:
                    - 1
                    - 2
                    - 3
                    - 4
                    - 5
                    - 6
                    - 7
                    - 8
                    - 9
                    - 10
                    - 11
                    - 12
                startTime:
                  type: string
                  pattern: ^([0-1]?[0-9]|2[0-3]):[0-5][0-9]$
                  example: '12:00'
                endTime:
                  type: string
                  pattern: ^([0-1]?[0-9]|2[0-3]):[0-5][0-9]$
                  example: '22:30'
                ordinal:
                  type: integer
                  nullable: true
                  minimum: -1
                  maximum: 5
                  description: >
                    Restricts the rule to only the Nth occurrence of each
                    matching

                    weekday within the month (1=1st … 5=5th, -1=last). `null`
                    (the

                    default) means every occurrence of the weekday — a plain
                    weekly

                    pattern. E.g. "1st Thursday of the month" → `daysOfWeek:
                    [4]`,

                    `ordinal: 1`.
                  example: 1
    ListingGroup:
      type: object
      description: |
        A group of listing items belonging to a single operator
        (venue-group / chain). Resolved from the VenueGroup referenced by the
        first segment of the CompositeId.
      required:
        - groupName
        - items
      properties:
        groupName:
          type: string
          description: Operator (venue-group / chain) display name.
          example: Stonegate Group
        items:
          type: array
          description: Venues (and standalone events) belonging to this operator.
          items:
            $ref: '#/components/schemas/ListingItem'
    ListingItem:
      type: object
      description: >
        A single venue, aggregating every capability offered there. `name` is
        the

        venue name (or the event name when the event has no associated venue).

        A venue that also hosts a ticketed event yields two `capabilities`

        entries — one `table_reservation`, one `event_ticket`.
      required:
        - name
        - capabilities
      properties:
        name:
          type: string
          description: Venue name, or event name when there is no associated venue.
          example: The Crown & Anchor
        location:
          $ref: '#/components/schemas/Location'
        capabilities:
          type: array
          description: The bookable capabilities offered at this venue.
          items:
            $ref: '#/components/schemas/Listing'
    Listing:
      type: object
      description: >
        A single bookable capability — a venue's table reservations OR an
        event's

        tickets. Exactly one capability per entry.
      required:
        - id
        - name
        - capability
        - bookableProducts
      properties:
        id:
          type: string
          description: CompositeId — `{VenueGroupId}|{RmsSlug}|{ResourceId}`.
          example: 29|CS|275cc44dd2e2496fba44857c9257443a
        name:
          type: string
          example: The Crown & Anchor
        operatorName:
          type: string
          nullable: true
          description: Operator (venue-group / chain) display name.
          example: Stonegate Group
        hostedBy:
          type: string
          nullable: true
          description: |
            Name of the venue hosting this listing. Populated for `event_ticket`
            listings whose event is associated with a physical venue (resolved
            from Collins); the venue's address is reflected in `location`.
            `null` when there is no associated venue (e.g. virtual events).
          example: The Crown & Anchor
        capability:
          $ref: '#/components/schemas/Capability'
        types:
          type: array
          description: |
            Listing types and feature tag slugs, merged into a single list
            (e.g. venue categories like `pub`, `bar` plus feature tags like
            `dog_friendly`, `live_music`).
          items:
            type: string
          example:
            - pub
            - bar
            - dog_friendly
            - live_music
        content:
          type: string
          nullable: true
          example: >-
            A traditional gastropub in the heart of Covent Garden, serving
            seasonal British food and an award-winning cask ale selection.
        faqs:
          type: array
          nullable: true
          description: >
            Operator-managed public-facing FAQ list. Omitted/null when none are
            configured.

            For an `event_ticket` listing this is inherited from the hosting
            venue (`hostedBy`)

            when one exists — events have no FAQ list of their own.
          items:
            $ref: '#/components/schemas/FaqItem'
          example:
            - question: What is your cancellation policy?
              answer: >-
                See our **cancellation policy**
                [here](https://example.com/policy).
        photos:
          type: array
          description: Photo URLs for this capability.
          items:
            type: string
          example:
            - >-
              https://cdn.bookabletech.com/venues/29/photos/crown-anchor-front.jpg
            - >-
              https://cdn.bookabletech.com/venues/29/photos/crown-anchor-dining.jpg
        phoneNumber:
          type: string
          nullable: true
          example: '+442079461234'
        website:
          type: string
          format: uri
          nullable: true
          example: https://www.crown-and-anchor.example/
        availabilityOccurrence:
          type: array
          description: >
            Unified temporal description of the listing. Use
            `recurring_with_time`

            rules for weekly opening hours, `specific_dates*` for event dates or

            holiday closures, `date_range*` for seasonal availability, etc.

            Multiple rules may combine — `status: closed` overrides `status:
            open`.

            Rules sharing the same `type`, `status` and time window are
            aggregated:

            recurring rules combine their `daysOfWeek`, specific-date rules
            combine

            their `dates`.
          items:
            $ref: '#/components/schemas/CalendarRule'
        preorders:
          $ref: '#/components/schemas/Preorders'
        bookableProducts:
          type: array
          description: |
            All bookable units for this listing. Every entry is a `Bookable` —
            a single unified shape. Capability-specific fields are populated
            according to the row's `capability`.
          items:
            $ref: '#/components/schemas/Bookable'
    Bookable:
      type: object
      description: >
        Bookable unit for any capability. Common fields (`compositeId`, `name`,

        `availabilityOccurrenceOverrides`) always apply. Capability-specific
        fields are

        nullable: table-reservation fields (`timeInterval`, `category`,

        `preorders`, `deposit`, `autoConfirmRule`, `noAvailabilityAction`) are

        populated when the parent `capability: table_reservation`; event-ticket

        fields (`price`, `capacity`, `visibility`) when `capability:
        event_ticket`.
      properties:
        compositeId:
          type: string
          description: >
            CompositeId of the bookable unit.

            - table_reservation →
            `{VenueGroupId}|{RmsSlug}|{VenueId}|{ProductId}`

            - event_ticket → `{VenueGroupId}|{RmsSlug}|{EventId}|{TicketTypeId}`
          example: >-
            29|CS|275cc44dd2e2496fba44857c9257443a|e4d909c290d0fb1ca068ffaddf22cbd0
        name:
          type: string
          example: Dining Room
        timeInterval:
          type: integer
          nullable: true
          example: 30
        category:
          type: string
          nullable: true
          example: Dinner
        spaces:
          type: array
          nullable: true
          description: Spaces/areas available within this bookable product.
          items:
            $ref: '#/components/schemas/Space'
        preorders:
          $ref: '#/components/schemas/ProductPreorders'
          nullable: true
        deposit:
          $ref: '#/components/schemas/BookableDeposit'
          nullable: true
        autoConfirmRule:
          $ref: '#/components/schemas/AutoConfirmRule'
          nullable: true
        noAvailabilityAction:
          type: string
          nullable: true
          example: waitlist
        price:
          $ref: '#/components/schemas/Price'
          nullable: true
        capacity:
          $ref: '#/components/schemas/Capacity'
          nullable: true
        visibility:
          type: string
          nullable: true
          example: on_sale
        availabilityOccurrenceOverrides:
          type: array
          nullable: true
          description: >
            Bookable-scoped `CalendarRule` entries that refine or restrict the

            listing-level `availabilityOccurrence` for this specific bookable
            unit (event

            sessions, exceptional dates, closures, …). Same shape as

            `Listing.availabilityOccurrence`; `status: closed` overrides
            `status: open`.
          items:
            $ref: '#/components/schemas/CalendarRule'
    Price:
      type: object
      nullable: true
      description: Monetary amount with currency.
      required:
        - amount
        - currency
      properties:
        amount:
          type: number
          format: double
          example: 25
        currency:
          type: string
          description: ISO 4217 currency code.
          example: GBP
    Capacity:
      type: object
      nullable: true
      description: Capacity for a ticket type.
      properties:
        max:
          type: integer
          nullable: true
          description: Maximum capacity.
          example: 300
        remaining:
          type: integer
          nullable: true
          description: Remaining capacity.
          example: 142
    ProductDepositRule:
      type: object
      nullable: true
      properties:
        perAdult:
          type: number
          format: float
          nullable: true
          example: 10
        perChild:
          type: number
          format: float
          nullable: true
          example: 5
        perBooking:
          type: number
          format: float
          nullable: true
          example: null
    ProductPreorders:
      type: object
      nullable: true
      description: |
        Pre-order configuration for the bookable unit.
      properties:
        required:
          type: boolean
          description: Whether a pre-order is required to complete the booking.
          example: true
        requiredType:
          type: string
          nullable: true
          enum:
            - package
            - menu
            - any
          description: |
            Type of asset the pre-order must reference.
            Only meaningful when `required: true` — otherwise `null`.
          example: package
        packageIds:
          type: array
          nullable: true
          items:
            type: string
          example:
            - 3f9a1c20-5b7e-4c1a-9e2d-1a2b3c4d5e6f
            - 8c2d4e60-9a1b-4f3c-8d2e-2b3c4d5e6f70
        menuIds:
          type: array
          nullable: true
          items:
            type: string
          example:
            - b4e6f810-1c2d-4a3b-9c8d-3c4d5e6f7081
    BookableDeposit:
      type: object
      nullable: true
      description: Deposit configuration for the bookable unit.
      properties:
        required:
          type: boolean
          description: Whether a deposit is required to confirm the booking.
          example: true
        rule:
          $ref: '#/components/schemas/ProductDepositRule'
          nullable: true
    Space:
      type: object
      description: A space or area available within a bookable product (read-only).
      properties:
        id:
          type: integer
          format: int32
        spaceName:
          type: string
          nullable: true
        description:
          type: string
          nullable: true
        floorLocation:
          type: integer
          format: int32
          nullable: true
          description: Floor number (-10 to +10; 0 = ground floor).
        facilities:
          type: array
          nullable: true
          description: Facilities available in the space.
          items:
            $ref: '#/components/schemas/SpaceFacility'
        capacityStanding:
          allOf:
            - $ref: '#/components/schemas/CapacityRange'
          nullable: true
        capacitySeated:
          allOf:
            - $ref: '#/components/schemas/CapacityRange'
          nullable: true
        capacityCabaret:
          allOf:
            - $ref: '#/components/schemas/CapacityRange'
          nullable: true
        capacityTheatre:
          allOf:
            - $ref: '#/components/schemas/CapacityRange'
          nullable: true
        capacityBoardroom:
          allOf:
            - $ref: '#/components/schemas/CapacityRange'
          nullable: true
        capacityClassroom:
          allOf:
            - $ref: '#/components/schemas/CapacityRange'
          nullable: true
        capacityUShape:
          allOf:
            - $ref: '#/components/schemas/CapacityRange'
          nullable: true
        capacityBanquet:
          allOf:
            - $ref: '#/components/schemas/CapacityRange'
          nullable: true
        capacityReception:
          allOf:
            - $ref: '#/components/schemas/CapacityRange'
          nullable: true
        policies:
          allOf:
            - $ref: '#/components/schemas/SpacePoliciesMap'
          nullable: true
    AvailabilitiesRequest:
      type: object
      description: >
        Batch availability command. Root-level `date`, `quantity` and `time` act

        as defaults inherited by every entry in `bookableProducts` that omits
        them.

        Each entry must resolve `date` and `quantity` (from itself or the root)
        to

        be searched; `time` is optional.
      required:
        - bookableProducts
      properties:
        date:
          type: string
          format: date
          nullable: true
          description: >-
            Default booking date (YYYY-MM-DD), inherited by entries that omit
            it.
          example: '2026-06-15'
        quantity:
          type: integer
          minimum: 1
          nullable: true
          description: >
            Default quantity, inherited by entries that omit it — same field as

            `OrderItem.quantity`. For a `table_reservation` bookable, quantity
            is

            the number of people (covers); for an `event_ticket` bookable,
            quantity

            is the number of tickets.
          example: 4
        time:
          type: string
          pattern: ^([0-1]?[0-9]|2[0-3]):[0-5][0-9]$
          nullable: true
          description: >-
            Optional default start time (HH:mm), inherited by entries that omit
            it.
          example: '19:00'
        bookableProducts:
          type: array
          minItems: 1
          maxItems: 100
          description: Bookable products to check. One result is returned per entry.
          items:
            $ref: '#/components/schemas/AvailabilityProductRequest'
    AvailabilityProductRequest:
      type: object
      description: >
        A single availability query. `compositeId` is required; `date`,
        `quantity`

        and `time` are optional overrides — when omitted, the root-level value
        is used.
      required:
        - compositeId
      properties:
        compositeId:
          type: string
          description: CompositeId of the bookable product to check.
          example: >-
            29|CS|275cc44dd2e2496fba44857c9257443a|e4d909c290d0fb1ca068ffaddf22cbd0
        date:
          type: string
          format: date
          nullable: true
          description: Overrides the root `date` for this entry.
          example: '2026-06-15'
        quantity:
          type: integer
          minimum: 1
          nullable: true
          description: >
            Overrides the root `quantity` for this entry — number of people for
            a

            `table_reservation` bookable, number of tickets for an
            `event_ticket` bookable.
          example: 2
        time:
          type: string
          pattern: ^([0-1]?[0-9]|2[0-3]):[0-5][0-9]$
          nullable: true
          description: Overrides the root `time` for this entry.
          example: '20:00'
    AvailabilitiesResponse:
      type: object
      required:
        - results
      properties:
        results:
          type: array
          description: One result per requested `compositeId`, in request order.
          items:
            $ref: '#/components/schemas/AvailabilityResult'
    AvailabilityResult:
      type: object
      description: >
        Outcome for a single `compositeId`. Exactly one of `availability` or
        `error`

        is populated: `availability` on success, `error` on failure (including
        an

        entry that could not resolve `date` and `quantity`).
      required:
        - compositeId
      properties:
        compositeId:
          type: string
          description: The `compositeId` this result refers to.
          example: >-
            29|CS|275cc44dd2e2496fba44857c9257443a|e4d909c290d0fb1ca068ffaddf22cbd0
        availability:
          $ref: '#/components/schemas/Availability'
          nullable: true
        error:
          $ref: '#/components/schemas/ServerError'
          nullable: true
    Availability:
      type: object
      description: >-
        Availability for a single bookable product. Mirrors the BookingApi
        AvailabilityResponse.
      properties:
        name:
          type: string
          nullable: true
          description: Name of the available booking option.
          example: Dinner
        deposit:
          type: number
          format: float
          nullable: true
          description: Deposit amount required for the booking.
          example: 10
        policy:
          type: string
          nullable: true
          description: General policy details regarding the booking.
        autoConfirmRule:
          $ref: '#/components/schemas/AutoConfirmRule'
          nullable: true
        cancellationPolicy:
          type: string
          nullable: true
          description: Terms and conditions for cancelling the booking.
        times:
          type: array
          nullable: true
          items:
            $ref: '#/components/schemas/AvailabilityTime'
        noAvailabilityReason:
          type: string
          nullable: true
          description: Populated only when `times` is empty — human-readable reason.
        noAvailabilityCode:
          type: string
          nullable: true
          description: >
            Populated only when `times` is empty. Machine-readable code. Known
            values:

            `NO_AVAILABILITY`, `NO_ELIGIBLE_PRODUCTS`,
            `FILTERED_BY_OPERATOR_RULES`,

            `FILTERED_BY_CAPACITY`. Treat unknown values as a generic
            no-availability condition.
    AvailabilityType:
      type: string
      nullable: true
      description: >
        How a time slot can be handled. `book` = instant confirmation; `request`
        =

        manual approval via enquiry.
      enum:
        - book
        - request
      example: request
    AvailabilityTime:
      type: object
      properties:
        time:
          type: string
          nullable: true
          description: Available time slot (HH:mm).
          example: '18:00'
        duration:
          type: integer
          nullable: true
          description: Duration in minutes.
          example: 90
        type:
          $ref: '#/components/schemas/AvailabilityType'
        requestReason:
          type: string
          nullable: true
          description: >
            Present only when `type` is `request`. Why the slot requires manual
            approval.

            Known values: `fully_booked`, `exceeds_auto_confirm`,
            `request_only`.

            Handle unknown values gracefully.
        product:
          $ref: '#/components/schemas/AvailabilityProduct'
          nullable: true
        preOrderItems:
          $ref: '#/components/schemas/AvailabilityPreorderItems'
          nullable: true
        unavailableReasons:
          type: array
          nullable: true
          description: >-
            Present only when `includeUnavailable=true` and the slot was
            filtered out.
          items:
            type: string
    AvailabilityProduct:
      type: object
      nullable: true
      description: Product info and applicable space policies for a time slot.
      properties:
        id:
          type: string
          description: CompositeId of the product.
        name:
          type: string
          nullable: true
        spaces:
          type: array
          nullable: true
          description: >-
            Spaces associated with this product, with policies pre-filtered to
            the slot.
          items:
            $ref: '#/components/schemas/AvailabilitySpace'
    AvailabilitySpace:
      type: object
      properties:
        id:
          type: integer
          description: Unique identifier of the space.
        spaceName:
          type: string
          nullable: true
        description:
          type: string
          nullable: true
        floorLocation:
          type: integer
          nullable: true
          description: Floor number (0 = ground floor).
        facilities:
          type: array
          nullable: true
          items:
            $ref: '#/components/schemas/SpaceFacility'
        capacityStanding:
          allOf:
            - $ref: '#/components/schemas/CapacityRange'
          nullable: true
        capacitySeated:
          allOf:
            - $ref: '#/components/schemas/CapacityRange'
          nullable: true
        capacityCabaret:
          allOf:
            - $ref: '#/components/schemas/CapacityRange'
          nullable: true
        capacityTheatre:
          allOf:
            - $ref: '#/components/schemas/CapacityRange'
          nullable: true
        capacityBoardroom:
          allOf:
            - $ref: '#/components/schemas/CapacityRange'
          nullable: true
        capacityClassroom:
          allOf:
            - $ref: '#/components/schemas/CapacityRange'
          nullable: true
        capacityUShape:
          allOf:
            - $ref: '#/components/schemas/CapacityRange'
          nullable: true
        capacityBanquet:
          allOf:
            - $ref: '#/components/schemas/CapacityRange'
          nullable: true
        capacityReception:
          allOf:
            - $ref: '#/components/schemas/CapacityRange'
          nullable: true
        policies:
          $ref: '#/components/schemas/AvailabilitySpacePolicies'
          nullable: true
    AvailabilitySpacePolicies:
      type: object
      description: Space policies pre-filtered to the requested date and time slot.
      properties:
        minimumSpend:
          type: array
          nullable: true
          description: Minimum spend entries applicable to this slot (override wins).
          items:
            type: object
            properties:
              label:
                type: string
                nullable: true
              spendValue:
                type: integer
                nullable: true
                description: Minimum spend in pence (GBP).
        under18s:
          type: object
          nullable: true
          properties:
            enabled:
              type: boolean
            until:
              type: string
              nullable: true
        occasionTypes:
          type: object
          nullable: true
          properties:
            enabled:
              type: boolean
            values:
              type: array
              nullable: true
              items:
                $ref: '#/components/schemas/OccasionType'
        promotedEvents:
          type: object
          nullable: true
          properties:
            enabled:
              type: boolean
            notes:
              type: string
              nullable: true
        accessibility:
          type: object
          nullable: true
          properties:
            enabled:
              type: boolean
            accessible:
              type: boolean
              nullable: true
            notes:
              type: string
              nullable: true
        servesFood:
          type: object
          nullable: true
          properties:
            enabled:
              type: boolean
    AvailabilityPreorderItems:
      type: object
      nullable: true
      description: Which packages and menus are available for this availability result.
      properties:
        packageIds:
          type: array
          nullable: true
          description: Package IDs available when `allowedPackages` is `set`.
          items:
            type: string
        allowedPackages:
          type: string
          nullable: true
          enum:
            - none
            - all
            - set
          description: '`none`: no packages; `all`: all packages; `set`: only `packageIds`.'
        menuIds:
          type: array
          nullable: true
          description: Menu IDs available when `allowedMenus` is `set`.
          items:
            type: string
        allowedMenus:
          type: string
          nullable: true
          enum:
            - none
            - all
            - set
          description: '`none`: no menus; `all`: all menus; `set`: only `menuIds`.'
    Customer:
      type: object
      required:
        - firstName
        - lastName
        - email
        - phone
      properties:
        firstName:
          type: string
          example: John
        lastName:
          type: string
          example: Doe
        email:
          type: string
          format: email
          example: john.doe@example.com
        phone:
          type: string
          description: E.164 format.
          example: '+441234567890'
        dateOfBirth:
          type: string
          format: date
          nullable: true
    PreorderContainer:
      type: object
      description: Pre-order items attached to a table reservation.
      properties:
        packages:
          type: array
          nullable: true
          items:
            $ref: '#/components/schemas/PreorderPackageRequest'
        menus:
          type: array
          nullable: true
          items:
            $ref: '#/components/schemas/PreorderMenuRequest'
    PreorderPackageRequest:
      type: object
      required:
        - id
        - quantity
      properties:
        id:
          type: string
          example: 3f9a1c20-5b7e-4c1a-9e2d-1a2b3c4d5e6f
        quantity:
          type: integer
          minimum: 1
          default: 1
          example: 2
    PreorderMenuRequest:
      type: object
      required:
        - id
        - quantity
      properties:
        id:
          type: string
          example: b4e6f810-1c2d-4a3b-9c8d-3c4d5e6f7081
        quantity:
          type: integer
          minimum: 1
          default: 1
          example: 4
        items:
          type: array
          nullable: true
          items:
            $ref: '#/components/schemas/MenuItemSelection'
    MenuItemSelection:
      type: object
      required:
        - id
        - quantity
      properties:
        id:
          type: string
          example: c5f70921-2d3e-4b4c-8d9e-4d5e6f708192
        quantity:
          type: integer
          minimum: 1
          default: 1
          example: 3
    PreorderChanges:
      type: object
      description: >
        Incremental preorder change for `OrderItemUpdate` — add or remove
        specific

        packages/menus without resending the full current list. Mutually
        exclusive

        with `preorders` on the same item (400 `ORDER-N-008` if both are
        present).

        An id appearing in both an "add" list and its matching "remove" list in
        the

        same request is rejected (400 `ORDER-N-009`) rather than silently
        resolved.
      properties:
        addPackages:
          type: array
          nullable: true
          description: |
            Packages to add. If a package with the same `id` is already on the
            booking, this replaces its quantity (upsert), matching legacy's
            `/preorders/packages/{id}/quantity` behavior — it does not add a
            duplicate line or increment the existing quantity.
          items:
            $ref: '#/components/schemas/PreorderPackageRequest'
        removePackageIds:
          type: array
          nullable: true
          items:
            type: string
        addMenus:
          type: array
          nullable: true
          description: >
            Menus to add. If a menu with the same `id` is already on the
            booking,

            this replaces its quantity and its full `items` selection (upsert) —

            there's no separate operation to change one item within an existing

            menu; resend that menu's full `items` list here to change it.
          items:
            $ref: '#/components/schemas/PreorderMenuRequest'
        removeMenuIds:
          type: array
          nullable: true
          items:
            type: string
    CreateOrderRequest:
      description: >
        Create-order request. No `capability`/`oneOf` split — every item is one

        `OrderItem`, unified (same pattern as `Bookable`/`BookingResult`):

        capability is resolved server-side from each item's `compositeId` RMS
        slug,

        not from which fields are populated. An order's items may mix
        capabilities

        and even RMS platforms; each is booked independently.
      allOf:
        - $ref: '#/components/schemas/CreateOrderRequestBase'
        - type: object
          required:
            - items
          properties:
            items:
              type: array
              minItems: 1
              description: One item per booking to create.
              items:
                $ref: '#/components/schemas/OrderItem'
    CreateOrderRequestBase:
      type: object
      description: >
        No `listingId` — every item carries its own `compositeId`,
        self-sufficient

        (embeds `VenueGroupId` + `RmsSlug`), so a single order's items may span

        multiple listings and even multiple RMS platforms.
      required:
        - customer
      properties:
        customer:
          $ref: '#/components/schemas/Customer'
        partnerOrderId:
          type: string
          nullable: true
          description: Optional external reference for cross-system tracking.
        notes:
          type: string
          nullable: true
        adminNotes:
          type: array
          nullable: true
          items:
            type: string
          description: Internal notes visible to venue operators only.
        marketingConsent:
          type: boolean
          nullable: true
    OrderItem:
      type: object
      description: >
        A single booking to create within an order. Common fields (including

        `notes` and `partnerBookingId`) always apply; `time`/`duration`/`type`/

        `preorders`/`spaceId`/`depositAmount` apply to table reservations only.

        Capability is resolved server-side from the `compositeId`'s RMS slug —
        not

        inferred from which fields are populated. There is no `sessionId`:
        `date`

        identifies the event occurrence directly.
      required:
        - compositeId
        - date
        - quantity
      properties:
        compositeId:
          type: string
          description: >
            CompositeId of the bookable product.

            - table_reservation →
            `{VenueGroupId}|{RmsSlug}|{VenueId}|{ProductId}`

            - event_ticket → `{VenueGroupId}|{RmsSlug}|{EventId}|{TicketTypeId}`
          example: >-
            29|CO|275cc44dd2e2496fba44857c9257443a|e4d909c290d0fb1ca068ffaddf22cbd0
        date:
          type: string
          format: date
          example: '2026-06-15'
        quantity:
          type: integer
          minimum: 1
          description: >
            Covers for a table reservation, or ticket count for an event-ticket
            line.
          example: 4
        notes:
          type: string
          nullable: true
          description: >
            Free-text note or special request for the booking. Applies to either

            capability — table reservation notes or an event-ticket purchase
            comment.
          example: Birthday celebration
        partnerBookingId:
          type: string
          nullable: true
          description: >
            Optional external reference supplied by the caller, used to
            cross-link

            this booking with records in another system. Applies to either
            capability.
          example: ext-ref-123
        commission:
          type: number
          format: double
          nullable: true
          description: >
            Partner commission for this booking. Currently wired through only
            for

            `event_ticket` (Tonic's `partner_commission`) — behaviour for

            `table_reservation` (Collins/Zonal) is not yet defined.
          example: 2.5
        time:
          type: string
          nullable: true
          pattern: ^([0-1]?[0-9]|2[0-3]):[0-5][0-9]$
          example: '19:30'
        duration:
          type: integer
          nullable: true
          description: Duration in minutes.
          example: 90
        type:
          type: string
          nullable: true
          enum:
            - book
            - request
          description: Must match the availability slot type returned for this time.
          example: book
        preorders:
          $ref: '#/components/schemas/PreorderContainer'
          nullable: true
        spaceId:
          type: integer
          nullable: true
          description: >
            Specific space/area to book, overriding automatic selection by the
            RMS.

            Obtain valid ids from
            `AvailabilityResult.availability.times[].product.spaces[].id`

            in the `POST /v1/availabilities` response for this
            `compositeId`/`date`/`time` —

            not from `Bookable.spaces[]`, which is the unfiltered listing-level
            list.
          example: 12
        depositAmount:
          type: number
          format: double
          nullable: true
          description: >
            Deposit amount to charge for this booking. When provided, a payment
            is

            taken from the RMS for this exact amount; when omitted, none is
            taken.
          example: 10
        unitPriceAmount:
          type: number
          format: double
          nullable: true
          example: 25
        priceCurrency:
          type: string
          nullable: true
          example: GBP
    Order:
      type: object
      description: >
        Canonical response for any created order, regardless of capability.

        Order-level fields are always present; per-item outcomes are in
        `results[]` —

        each requested item is resolved independently, so a single order can be

        partially confirmed (e.g. one item's platform is fully booked while
        others

        succeed). There is no order-wide `status`; check each result.
      required:
        - id
        - customer
        - createdAt
        - results
      properties:
        id:
          type: string
          description: Platform-generated unique order identifier.
          example: ORD-9980140810
        partnerOrderId:
          type: string
          nullable: true
        customer:
          $ref: '#/components/schemas/Customer'
        createdAt:
          type: string
          format: date-time
          example: '2026-04-21T14:22:00Z'
        results:
          type: array
          description: One result per requested item, in request order.
          items:
            $ref: '#/components/schemas/OrderItemResult'
    OrderItemResult:
      type: object
      description: |
        Outcome for a single requested order item. Exactly one of `booking` or
        `error` is populated: `booking` on success, `error` on failure. Mirrors
        `AvailabilityResult`'s per-item success-xor-error shape.
      required:
        - compositeId
        - date
      properties:
        compositeId:
          type: string
          description: >
            The `compositeId` this result refers to — the product for a

            table-reservation item, or the ticket-type line for an event-ticket
            line.
          example: >-
            29|CO|275cc44dd2e2496fba44857c9257443a|e4d909c290d0fb1ca068ffaddf22cbd0
        date:
          type: string
          format: date
          description: The date this result refers to.
          example: '2026-06-15'
        booking:
          $ref: '#/components/schemas/BookingResult'
          nullable: true
        error:
          $ref: '#/components/schemas/ServerError'
          nullable: true
    BookingResult:
      type: object
      description: >
        Confirmed booking detail for a single order item. Common fields
        (including

        `quantity` — covers or ticket count) always apply; `time`/`duration`/

        `preorders` apply to table reservations only, `unitPriceAmount`/

        `priceCurrency` to event tickets only. Check `capability` to know which.
      required:
        - id
        - capability
        - status
        - quantity
      properties:
        id:
          type: string
          description: >
            Booking CompositeId assigned by the platform — the `bookingId` to
            pass

            back in `OrderItemUpdate.bookingId` /
            `OrderCancelRequest.bookingIds`

            for a subsequent `PATCH`/`DELETE /v1/orders/{orderId}` call. Opaque
            and

            platform-specific in shape — e.g. Collins/Zonal append a
            booking-type

            flag (`B`/`R`) as a 4th segment, Tonic uses a plain 3-segment
            purchase

            reference. Never equal to the `compositeId` that was booked (a
            different

            resource — the booking itself vs. the product/ticket type); treat it
            as

            an opaque token and round-trip it exactly as received.
          example: 29|CO|BKG-8841203|B
        capability:
          $ref: '#/components/schemas/Capability'
        reference:
          type: string
          nullable: true
          description: Human-readable reference (e.g. DMN-9980140810).
          example: DMN-9980140810
        operatorOrderId:
          type: string
          nullable: true
          description: >-
            Booking reference assigned by the operator's RMS (Collins, Zonal,
            Tonic, etc.).
        status:
          type: string
          enum:
            - Pending
            - InProgress
            - Confirmed
            - Cancelled
            - Deleted
            - Lost
          example: Confirmed
        productName:
          type: string
          nullable: true
        quantity:
          type: integer
          description: >-
            Covers for a table reservation, or ticket count for an event-ticket
            line.
          example: 2
        notes:
          type: string
          nullable: true
          description: >
            Free-text note or special request for the booking. Applies to either

            capability — table reservation notes or an event-ticket purchase
            comment.
          example: Birthday celebration
        partnerBookingId:
          type: string
          nullable: true
          description: |
            External reference supplied by the caller at creation time, used to
            cross-link this booking with records in another system. Applies to
            either capability.
          example: ext-ref-123
        time:
          type: string
          nullable: true
          pattern: ^([0-1]?[0-9]|2[0-3]):[0-5][0-9]$
          example: '19:30'
        duration:
          type: integer
          nullable: true
        preorders:
          $ref: '#/components/schemas/PreorderContainer'
          nullable: true
        spaceId:
          type: integer
          nullable: true
          description: |
            Space/area booked — either the requested `OrderItem.spaceId` or the
            one assigned by the RMS. Matches an id from
            `AvailabilityResult.availability.times[].product.spaces[].id`.
          example: 12
        depositAmount:
          type: number
          format: double
          nullable: true
          description: Deposit amount charged for this booking, when applicable.
          example: 10
        unitPriceAmount:
          type: number
          format: double
          nullable: true
          example: 25
        priceCurrency:
          type: string
          nullable: true
          example: GBP
    OrderUpdateRequest:
      type: object
      required:
        - items
      properties:
        items:
          type: array
          minItems: 1
          description: One entry per booking to update, within this order.
          items:
            $ref: '#/components/schemas/OrderItemUpdate'
    OrderItemUpdate:
      type: object
      description: >
        Partial update for a single booking. Only populated fields are changed —

        anything omitted keeps its current value. Capability is resolved
        server-side

        from the existing booking (looked up via `bookingId`), not from the
        request.
      required:
        - bookingId
      properties:
        bookingId:
          type: string
          description: >-
            The booking's platform id — `BookingResult.id` from the
            create/update/cancel response.
          example: 1|TN|423458
        notes:
          type: string
          nullable: true
          description: >-
            Internal note. Table reservation → booking notes; event ticket →
            Tonic purchase comment.
          example: Customer upgraded to Pay ticket
        firstName:
          type: string
          nullable: true
          description: Guest first name. Applies to either capability.
          example: John
        lastName:
          type: string
          nullable: true
          description: Guest last name. Applies to either capability.
          example: Doe
        email:
          type: string
          format: email
          nullable: true
          description: Guest email address. Applies to either capability.
          example: john.doe@example.com
        phone:
          type: string
          nullable: true
          description: Guest phone number, E.164 format. Applies to either capability.
          example: '+441234567890'
        commission:
          type: number
          format: double
          nullable: true
          description: >
            Partner commission for this booking. Currently wired through for

            `table_reservation` (Collins) on update — `event_ticket` (Tonic)
            only

            supports commission on create today, not on update.
          example: 2.5
        date:
          type: string
          format: date
          nullable: true
          example: '2026-06-15'
        time:
          type: string
          nullable: true
          pattern: ^([0-1]?[0-9]|2[0-3]):[0-5][0-9]$
          example: '20:00'
        duration:
          type: integer
          nullable: true
          example: 90
        quantity:
          type: integer
          minimum: 1
          nullable: true
          description: >
            New covers count for a table reservation, or new ticket count for an

            event-ticket line. For event tickets: a **decrease** is processed as
            a

            partial refund of the difference; an **increase** is rejected with
            400

            (`EVENT-N-003`) — Tonic has no way to add tickets to an existing
            purchase,

            create a new order item instead.
          example: 6
        preorders:
          $ref: '#/components/schemas/PreorderContainer'
          nullable: true
        preorderChanges:
          $ref: '#/components/schemas/PreorderChanges'
          nullable: true
        compositeId:
          type: string
          nullable: true
          description: >
            Full CompositeId of the bookable product to move this booking to —
            same

            shape and meaning as `Bookable.compositeId` (the only identifier
            taken

            from a listing's `bookableProducts[]`), not a ticket-specific field.


            Supported for both capabilities: for event tickets, moves to a
            different

            ticket type on the same event (the compositeId's 4th segment); for
            table

            reservations, moves to a different bookable product/space on the
            same

            venue. Optionally combined with `date` in the same operation — Tonic
            has

            no separate "change date only" call.


            The new compositeId must reference the same venue group, RMS, and

            venue/event as the booking's current product — only the trailing

            product/ticket-type segment (`AdditionalId`) may differ. A
            cross-venue

            or cross-event compositeId is rejected with 400 (`ORDER-N-007`).
          example: 1|TN|6a2bbc0285bf210719631c44|6a2bbb580ed61600150618f7
    OrderCancelRequest:
      type: object
      description: >-
        Cancellation scope for `DELETE /v1/orders/{orderId}`. Omit entirely to
        cancel the whole order.
      properties:
        bookingIds:
          type: array
          nullable: true
          description: >-
            Booking ids to cancel. Omitted or empty → cancel every booking in
            the order.
          items:
            type: string
          example:
            - 1|TN|423458
    ServerError:
      type: object
      properties:
        type:
          type: string
          format: uri
          description: >-
            A URI reference [rfc9457] that identifies the problem type. This
            specification encourages that, when dereferenced, it provides
            human-readable documentation for the problem type (e.g., using HTML
            [W3C.REC-html5-20141028]). When this member is not present, its
            value is assumed to be "about:blank".
        title:
          type: string
          description: >-
            A short, human-readable summary of the problem type. It SHOULD NOT
            change from occurrence to occurrence of the problem, except for
            purposes of localization.
        status:
          type: integer
          format: int32
          description: >-
            The HTTP status code [rfc9457, Section 3.1.12] generated by the
            origin server for this occurrence of the problem.
        detail:
          type: string
          description: >-
            A human-readable explanation specific to this occurrence of the
            problem.
        code:
          type: string
          description: >-
            Error code in the format RESOURCE-X-NNN where X is R (retryable) or
            N (non-retryable). See ErrorCatalog.md for all error codes.
          example: BOOK-N-001
        isRetryable:
          type: boolean
          description: >-
            Indicates whether the error is retryable. If true, the request may
            succeed if retried. If false, the request will fail again with the
            same input.
        traceId:
          type: string
          description: Request trace identifier for debugging and correlation purposes.
          example: 0HNJ2BG2TU3BU:00000001
      required:
        - title
        - status
        - code
        - isRetryable
    Page:
      type: object
      properties:
        meta:
          type: object
          properties:
            currentPage:
              type: integer
              description: The current page number based on `limit` and `offset`.
            pageSize:
              type: integer
              description: The number of items returned per page.
            totalItems:
              type: integer
              description: The total number of items available.
            totalPages:
              type: integer
              description: The total number of pages.
    FaqItem:
      type: object
      description: >
        Single FAQ entry. `question`/`answer` are plain strings and may contain
        markdown syntax; the server does not parse or sanitize it — rendering
        happens client-side.
      properties:
        question:
          type: string
          nullable: true
          example: What is your cancellation policy?
        answer:
          type: string
          nullable: true
          example: See our **cancellation policy** [here](https://example.com/policy).
    SpaceFacility:
      type: string
      description: Facilities available in a space.
      enum:
        - Stage
        - Private Bar
        - Private Toilets
        - Soundproof
        - Own Street Entrance
        - Fixed Seating
        - Outside Space
        - Can Be Decorated
        - DJ Stand
        - Suitable for Meetings
        - Wheelchair Accessible
        - Accessible Toilets
        - Projector
        - Screen / TV
        - Microphone / PA
        - Laptop Connection
        - Own Sound System
        - HDMI / AV
        - In-House Lighting
        - Sports TV
        - Bring Own DJ
        - Serves Food
        - External Catering Permitted
        - Draft Beer
        - Cash Bar
        - Can Bring Own Cake
        - Tea/Coffee Facilities
        - Cloakroom
        - Smoking Area
        - Wifi Available
        - Pet Friendly
        - Parking
        - Air Conditioning
    CapacityRange:
      type: object
      description: Min/max capacity range; both bounds are optional.
      properties:
        min:
          type: integer
          format: int32
          nullable: true
        max:
          type: integer
          format: int32
          nullable: true
    MinimumSpendDateRange:
      type: object
      description: Explicit date interval (inclusive, ISO 8601). Does not recur.
      properties:
        from:
          type: string
          format: date
        to:
          type: string
          format: date
    MinimumSpendTimeRange:
      type: object
      description: Time window of the booking start time (HH:MM, inclusive, Europe/London).
      properties:
        from:
          type: string
          example: '18:00'
        to:
          type: string
          example: '23:59'
    MinimumSpendConditions:
      type: object
      description: |
        Conditions that must all be satisfied for the rule to apply (AND logic).
        An empty object means the rule applies without restriction.
      properties:
        daysOfWeek:
          type: array
          nullable: true
          description: Days of the week (1=Monday, 7=Sunday). Absent means all days.
          items:
            type: integer
            format: int32
        months:
          type: array
          nullable: true
          description: Calendar months (1=January, 12=December). Absent means all months.
          items:
            type: integer
            format: int32
        dateRange:
          allOf:
            - $ref: '#/components/schemas/MinimumSpendDateRange'
          nullable: true
        timeRange:
          allOf:
            - $ref: '#/components/schemas/MinimumSpendTimeRange'
          nullable: true
    MinimumSpendRuleBase:
      type: object
      properties:
        label:
          type: string
          nullable: true
        conditions:
          $ref: '#/components/schemas/MinimumSpendConditions'
        spendValue:
          type: integer
          format: int32
          nullable: true
          description: Minimum spend amount in pence (GBP).
        notes:
          type: string
          nullable: true
    MinimumSpendOverrideBase:
      type: object
      properties:
        date:
          type: string
          format: date
          description: The specific calendar date. Also serves as the URL key.
        label:
          type: string
          nullable: true
        spendValue:
          type: integer
          format: int32
          nullable: true
          description: Minimum spend amount in pence (GBP).
        timeRange:
          allOf:
            - $ref: '#/components/schemas/MinimumSpendTimeRange'
          nullable: true
          description: >-
            Optional time window (Europe/London) the override applies to. Absent
            means the override covers the whole date. Outside the window, normal
            rules apply.
    MinimumSpendPolicyResponse:
      type: object
      properties:
        enabled:
          type: boolean
        rules:
          type: array
          description: >-
            Weekly recurring minimum spend rules. Empty array means none
            configured.
          items:
            $ref: '#/components/schemas/MinimumSpendRuleBase'
        overrides:
          type: array
          description: >-
            Date-specific minimum spend overrides. Empty array means none
            configured.
          items:
            $ref: '#/components/schemas/MinimumSpendOverrideBase'
    OccasionType:
      type: string
      description: Allowed occasion types for a space.
      enum:
        - Christmas Party
        - Corporate Event
        - Engagement Party
        - Funeral/Wake
        - Summer Party
        - Wedding Party
        - Winter Party
        - Work Get Together
        - Working Breakfast
        - Working Lunch
        - Working Dinner
        - Promoted Event
    OccasionTypesPolicyResponse:
      type: object
      properties:
        enabled:
          type: boolean
        values:
          type: array
          nullable: true
          description: Allowed occasion types.
          items:
            $ref: '#/components/schemas/OccasionType'
    Under18sPolicyResponse:
      type: object
      properties:
        enabled:
          type: boolean
        allowed:
          type: boolean
          nullable: true
          description: >
            Whether under-18s are permitted (`true`) or prohibited (`false`) in
            this space.

            `null` means the policy has not been explicitly set.

            `allowed` must not be `true` when `enabled` is `false`.
        until:
          type: string
          nullable: true
          description: Cutoff time for under-18s, e.g. "21:00".
    PromotedEventsPolicyResponse:
      type: object
      properties:
        enabled:
          type: boolean
        allowed:
          type: boolean
          nullable: true
          description: >
            Whether promoted events are permitted (`true`) or prohibited
            (`false`) in this space.

            `null` means the policy has not been explicitly set.

            `allowed` must not be `true` when `enabled` is `false`.
        notes:
          type: string
          nullable: true
          description: Optional notes about promoted events at this space.
    AccessibilityPolicyResponse:
      type: object
      properties:
        enabled:
          type: boolean
        accessible:
          type: boolean
          nullable: true
        notes:
          type: string
          nullable: true
    ServesFoodPolicyResponse:
      type: object
      properties:
        enabled:
          type: boolean
        allowed:
          type: boolean
          nullable: true
          description: |
            Whether the space serves food (`true`) or does not (`false`).
            `null` means the policy has not been explicitly set.
            `allowed` must not be `true` when `enabled` is `false`.
    SpacePoliciesMap:
      type: object
      description: >
        All policies for a space, keyed by policy type. An empty/default object
        means

        that policy has not been configured for this space yet.
      properties:
        minimumSpend:
          $ref: '#/components/schemas/MinimumSpendPolicyResponse'
        occasionTypes:
          $ref: '#/components/schemas/OccasionTypesPolicyResponse'
        under18s:
          $ref: '#/components/schemas/Under18sPolicyResponse'
        promotedEvents:
          $ref: '#/components/schemas/PromotedEventsPolicyResponse'
        accessibility:
          $ref: '#/components/schemas/AccessibilityPolicyResponse'
        servesFood:
          $ref: '#/components/schemas/ServesFoodPolicyResponse'
    ClientError:
      type: object
      properties:
        type:
          type: string
          format: uri
          description: >-
            A URI reference [rfc9457] that identifies the problem type. This
            specification encourages that, when dereferenced, it provides
            human-readable documentation for the problem type (e.g., using HTML
            [W3C.REC-html5-20141028]). When this member is not present, its
            value is assumed to be "about:blank".
        title:
          type: string
          description: >-
            A short, human-readable summary of the problem type. It SHOULD NOT
            change from occurrence to occurrence of the problem, except for
            purposes of localization.
        status:
          type: integer
          format: int32
          description: >-
            The HTTP status code [rfc9457, Section 3.1.12] generated by the
            origin server for this occurrence of the problem.
        detail:
          type: string
          description: >-
            A human-readable explanation specific to this occurrence of the
            problem.
        code:
          type: string
          description: >-
            Error code in the format RESOURCE-X-NNN where X is R (retryable) or
            N (non-retryable). See ErrorCatalog.md for all error codes.
          example: VALID-N-001
        isRetryable:
          type: boolean
          description: >-
            Indicates whether the error is retryable. If true, the request may
            succeed if retried. If false, the request will fail again with the
            same input.
        traceId:
          type: string
          description: Request trace identifier for debugging and correlation purposes.
          example: 0HNJ2BG2TU3BU:00000001
        errors:
          type: object
          nullable: true
          additionalProperties:
            type: array
            items:
              type: string
          description: >-
            Validation errors dictionary mapping field names to arrays of error
            messages. Only present for validation errors (VALID-N-001).
          example:
            Date:
              - Availability date cannot be in the past.
            EndTime:
              - EndTime must be after StartTime.
      required:
        - title
        - status
        - code
        - isRetryable
  examples:
    CreateOrderTableReservationResponseExample:
      summary: >-
        Both items booked successfully (matches the `tableReservation` request
        example, spanning two platforms)
      value:
        id: ORD-8841203A
        partnerOrderId: null
        customer:
          firstName: John
          lastName: Doe
          email: john.doe@example.com
          phone: '+441234567890'
        createdAt: '2026-06-15T09:12:44Z'
        results:
          - compositeId: >-
              29|CO|275cc44dd2e2496fba44857c9257443a|e4d909c290d0fb1ca068ffaddf22cbd0
            date: '2026-06-15'
            booking:
              id: 29|CO|BKG-8841203|B
              capability: table_reservation
              reference: DMN-8841203
              operatorOrderId: '8841203'
              status: Confirmed
              productName: Dining Room
              quantity: 4
              notes: null
              partnerBookingId: null
              time: '19:30'
              duration: 90
              preorders: null
              spaceId: 12
              depositAmount: 10
            error: null
          - compositeId: 41|ZO|9a1c20-5b7e-4c1a|8c2d4e60-9a1b-4f3c
            date: '2026-06-15'
            booking:
              id: 41|ZO|EVT-77210|B
              capability: table_reservation
              reference: null
              operatorOrderId: '77210'
              status: Confirmed
              productName: Roof Terrace
              quantity: 2
              notes: null
              partnerBookingId: null
              time: '20:00'
              duration: 120
              preorders: null
              spaceId: null
              depositAmount: null
            error: null
    CreateOrderEventTicketResponseExample:
      summary: >-
        Both ticket-type lines booked successfully (matches the `eventTicket`
        request example, same event/date)
      value:
        id: ORD-F55BCDC2
        partnerOrderId: null
        customer:
          firstName: Jane
          lastName: Smith
          email: jane.smith@example.com
          phone: '+441234567891'
        createdAt: '2026-07-17T07:55:58Z'
        results:
          - compositeId: 29|TN|69f87ae8c7475734752555b4|69f87a8568063a182c46c198
            date: '2026-07-19'
            booking:
              id: 29|TN|6a59e00dca03551cc60dc839
              capability: event_ticket
              reference: null
              operatorOrderId: '426071'
              status: Confirmed
              productName: null
              quantity: 2
              notes: null
              partnerBookingId: null
              unitPriceAmount: 25
              priceCurrency: GBP
            error: null
          - compositeId: 29|TN|69f87ae8c7475734752555b4|69f87a97c74757172d362198
            date: '2026-07-19'
            booking:
              id: 29|TN|6a59e00eca03551cc60dc840
              capability: event_ticket
              operatorOrderId: '427896'
              status: Confirmed
              quantity: 1
              unitPriceAmount: 50
              priceCurrency: GBP
            error: null
    CreateOrderPartialFailureExample:
      summary: >-
        One item booked, the other failed independently — 207 because at least
        one item failed
      value:
        id: ORD-6B21F9A0
        partnerOrderId: null
        customer:
          firstName: John
          lastName: Doe
          email: john.doe@example.com
          phone: '+441234567890'
        createdAt: '2026-06-15T09:12:44Z'
        results:
          - compositeId: >-
              29|CO|275cc44dd2e2496fba44857c9257443a|e4d909c290d0fb1ca068ffaddf22cbd0
            date: '2026-06-15'
            booking:
              id: 29|CO|BKG-8841204|B
              capability: table_reservation
              reference: DMN-8841204
              operatorOrderId: '8841204'
              status: Confirmed
              productName: Dining Room
              quantity: 4
              time: '19:30'
              duration: 90
              spaceId: 12
              depositAmount: 10
            error: null
          - compositeId: 29|TN|69f87ae8c7475734752555b4|69f87a97c74757172d362198
            date: '2026-07-19'
            booking: null
            error:
              title: Bad request
              status: 409
              code: BOOK-N-014
              isRetryable: false
              detail: This ticket type is sold out for the requested date.
    UpdateOrderEventTicketResponseExample:
      summary: >-
        Ticket-type line moved to a new ticket type and date (matches the
        `changeTicketType` request example)
      value:
        id: ORD-9980140810
        customer:
          firstName: Jane
          lastName: Smith
          email: jane.smith@example.com
          phone: '+441234567891'
        createdAt: '2027-01-10T11:45:03Z'
        results:
          - compositeId: 1|TN|6a59f0a0f58eb1c0fa00d040
            date: '2027-02-22'
            booking:
              id: 1|TN|6a59f0a0f58eb1c0fa00d040
              capability: event_ticket
              operatorOrderId: '429321'
              status: Confirmed
              quantity: 2
              notes: Customer upgraded to Kids ticket
            error: null
    UpdateOrderTableReservationResponseExample:
      summary: >-
        Table reservation's time and party size changed (matches the
        `rescheduleTable` request example)
      value:
        id: ORD-9980140810
        customer:
          firstName: John
          lastName: Doe
          email: john.doe@example.com
          phone: '+441234567890'
        createdAt: '2026-06-10T08:30:00Z'
        results:
          - compositeId: 1|CO|BKG-8841203|B
            date: '2026-06-15'
            booking:
              id: 1|CO|BKG-8841203|B
              capability: table_reservation
              reference: DMN-1002
              operatorOrderId: BKG-8841203
              status: Confirmed
              productName: Dining Room
              quantity: 6
              time: '20:00'
              duration: 90
              spaceId: 12
              depositAmount: 10
            error: null
    UpdateOrderCustomerAndProductResponseExample:
      summary: >-
        Guest name/phone changed and the reservation moved to a different
        product on the same venue (matches the `changeCustomerAndProduct`
        request example)
      value:
        id: ORD-9980140810
        customer:
          firstName: Jonathan
          lastName: Doe
          email: john.doe@example.com
          phone: '+441234567899'
        createdAt: '2026-06-10T08:30:00Z'
        results:
          - compositeId: 29|CO|BKG-8841203|B
            date: '2026-06-15'
            booking:
              id: 29|CO|BKG-8841203|B
              capability: table_reservation
              reference: DMN-1002
              operatorOrderId: BKG-8841203
              status: Confirmed
              productName: Set Lunch Room
              quantity: 4
              time: '19:30'
              duration: 90
              spaceId: null
              depositAmount: null
            error: null
    UpdateOrderAddPreorderResponseExample:
      summary: >-
        Preorder package added without resending the rest of the preorders
        (matches the `addPreorderPackage` request example)
      value:
        id: ORD-9980140810
        customer:
          firstName: John
          lastName: Doe
          email: john.doe@example.com
          phone: '+441234567890'
        createdAt: '2026-06-10T08:30:00Z'
        results:
          - compositeId: 29|CO|BKG-8841203|B
            date: '2026-06-15'
            booking:
              id: 29|CO|BKG-8841203|B
              capability: table_reservation
              reference: DMN-1002
              operatorOrderId: BKG-8841203
              status: Confirmed
              productName: Dining Room
              quantity: 4
              time: '19:30'
              duration: 90
              preorders:
                packages:
                  - id: 3f9a1c20-5b7e-4c1a-9e2d-1a2b3c4d5e6f
                    quantity: 2
              spaceId: 12
              depositAmount: 10
            error: null
    UpdateOrderFailureExample:
      summary: >-
        Booking not found — a stale, mistyped or already-cancelled bookingId was
        supplied
      value:
        id: ORD-9980140810
        customer:
          firstName: ''
          lastName: ''
          email: ''
          phone: ''
        createdAt: '2027-01-10T11:45:03Z'
        results:
          - compositeId: 1|TN|999999
            date: '0001-01-01'
            booking: null
            error:
              title: Bad request
              status: 404
              code: ORDER-N-404
              isRetryable: false
              detail: 'Booking not found: 1|TN|999999'
    CancelOrderWholeResponseExample:
      summary: >-
        Entire order cancelled (empty/omitted request body cancels every booking
        in it)
      value:
        id: ORD-9980140810
        customer:
          firstName: John
          lastName: Doe
          email: john.doe@example.com
          phone: '+441234567890'
        createdAt: '2026-06-15T09:12:44Z'
        results:
          - compositeId: 29|CO|BKG-8841203|B
            date: '2026-06-15'
            booking:
              id: 29|CO|BKG-8841203|B
              capability: table_reservation
              reference: DMN-8841203
              operatorOrderId: '8841203'
              status: Cancelled
              productName: Dining Room
              quantity: 4
              spaceId: 12
              depositAmount: 10
            error: null
          - compositeId: 41|ZO|EVT-77210|B
            date: '2026-06-15'
            booking:
              id: 41|ZO|EVT-77210|B
              capability: table_reservation
              operatorOrderId: '77210'
              status: Cancelled
              productName: Roof Terrace
              quantity: 2
            error: null
    CancelOrderSingleBookingResponseExample:
      summary: >-
        Single booking cancelled within the order (matches the `singleBooking`
        request example)
      value:
        id: ORD-9980140810
        customer:
          firstName: Jane
          lastName: Smith
          email: jane.smith@example.com
          phone: '+441234567891'
        createdAt: '2027-01-10T11:45:03Z'
        results:
          - compositeId: 1|TN|423458
            date: '2027-02-22'
            booking:
              id: 1|TN|423458
              capability: event_ticket
              operatorOrderId: '427896'
              status: Cancelled
              quantity: 1
            error: null
    CancelOrderFailureResponseExample:
      summary: Booking already cancelled — the RMS-side refund fails independently
      value:
        id: ORD-9980140810
        customer:
          firstName: Jane
          lastName: Smith
          email: jane.smith@example.com
          phone: '+441234567891'
        createdAt: '2027-01-10T11:45:03Z'
        results:
          - compositeId: 1|TN|423458
            date: '2027-02-22'
            booking: null
            error:
              title: Bad request
              status: 404
              code: BOOK-N-020
              isRetryable: false
              detail: >-
                No tickets found on the Tonic purchase for this ticket type — it
                may already be cancelled.
    AvailabilitiesResponseExample:
      summary: Mixed batch — two successes and one isolated error
      value:
        results:
          - compositeId: 29|CS|275cc44dd2e2496fba44857c9257443a|prod1
            availability:
              name: Dinner
              deposit: 10
              policy: Please arrive on time.
              cancellationPolicy: Cancel 48h before or a fee applies.
              times:
                - time: '19:00'
                  duration: 90
                  type: book
                  product:
                    id: 29|CS|275cc44dd2e2496fba44857c9257443a|prod1
                    name: Dining Room
            error: null
          - compositeId: 29|CS|275cc44dd2e2496fba44857c9257443a|prod2
            availability:
              name: Bar
              times:
                - time: '20:00'
                  duration: 90
                  type: request
                  requestReason: exceeds_auto_confirm
                  product:
                    id: 29|CS|275cc44dd2e2496fba44857c9257443a|prod2
                    name: Bar Area
            error: null
          - compositeId: 29|CS|999|prod3
            availability: null
            error:
              title: Bad request
              status: 400
              code: AVAIL-N-001
              isRetryable: false
              detail: >-
                Missing date or quantity — cannot run availability for this
                item.
    TableReservationListingPage:
      summary: Single-page result containing a venue with table reservations
      value:
        meta:
          currentPage: 1
          pageSize: 20
          totalItems: 1
          totalPages: 1
        listings:
          - groupName: Stonegate Group
            items:
              - name: The Crown & Anchor
                location:
                  street: 1 Upper St Martin's Lane
                  area: Covent Garden|Seven Dials
                  city: London
                  postCode: WC2H 9FB
                  lat: 51.513
                  lng: -0.128
                  gPlace: ChIJN1t_tDeuEmsRUsoyG83frY4
                capabilities:
                  - id: 29|CS|275cc44dd2e2496fba44857c9257443a
                    name: The Crown & Anchor
                    operatorName: Stonegate Group
                    capability: table_reservation
                    types:
                      - pub
                      - bar
                      - dog_friendly
                      - live_music
                    content: >-
                      A traditional gastropub in the heart of Covent Garden,
                      serving seasonal British food and an award-winning cask
                      ale selection.
                    photos:
                      - >-
                        https://cdn.bookabletech.com/venues/29/photos/crown-anchor-front.jpg
                      - >-
                        https://cdn.bookabletech.com/venues/29/photos/crown-anchor-dining.jpg
                    phoneNumber: '+442079461234'
                    website: https://www.crown-and-anchor.example/
                    availabilityOccurrence:
                      - name: Weekday service
                        description: Mon–Fri kitchen hours.
                        type: recurring_with_time
                        occurrences:
                          daysOfWeek:
                            - 1
                            - 2
                            - 3
                            - 4
                            - 5
                          months:
                            - 1
                            - 2
                            - 3
                            - 4
                            - 5
                            - 6
                            - 7
                            - 8
                            - 9
                            - 10
                            - 11
                            - 12
                          startTime: '12:00'
                          endTime: '22:30'
                        status: open
                        notes: Last seating 21:30.
                      - name: Weekend service
                        description: Sat–Sun extended hours.
                        type: recurring_with_time
                        occurrences:
                          daysOfWeek:
                            - 0
                            - 6
                          months:
                            - 1
                            - 2
                            - 3
                            - 4
                            - 5
                            - 6
                            - 7
                            - 8
                            - 9
                            - 10
                            - 11
                            - 12
                          startTime: '11:00'
                          endTime: '23:00'
                        status: open
                        notes: Brunch from 11:00.
                      - name: Christmas closure
                        description: Closed Christmas Day and Boxing Day.
                        type: specific_dates
                        occurrences:
                          dates:
                            - '2026-12-25'
                            - '2026-12-26'
                        status: closed
                        notes: Reopen 27 Dec at normal hours.
                    preorders:
                      packages:
                        - id: 3f9a1c20-5b7e-4c1a-9e2d-1a2b3c4d5e6f
                          name: Sunday Roast
                          price: 28.5
                          description: >-
                            Slow-roasted beef, Yorkshire pudding, seasonal
                            vegetables.
                          type: main
                          sub_type: roast
                          diet_types:
                            - gluten_free_option
                          allergens:
                            - gluten
                            - dairy
                        - id: 8c2d4e60-9a1b-4f3c-8d2e-2b3c4d5e6f70
                          name: Set Lunch
                          price: 18
                          description: Two courses from the set lunch menu.
                          type: set
                          sub_type: lunch
                          diet_types:
                            - vegetarian_option
                          allergens:
                            - gluten
                      menus:
                        - id: b4e6f810-1c2d-4a3b-9c8d-3c4d5e6f7081
                          name: Spring Tasting Menu
                          description: >-
                            Five-course seasonal tasting menu with optional wine
                            pairing.
                          items:
                            - packageId: c5f70921-2d3e-4b4c-8d9e-4d5e6f708192
                              name: Amuse Bouche
                              price: 0
                              description: Chef's seasonal selection.
                              type: starter
                              subType: amuse
                              displayOrder: 1
                            - packageId: d6081a32-3e4f-4c5d-9eaf-5e6f708192a3
                              name: Heritage Beetroot
                              price: 0
                              description: Whipped goat's curd, walnut, sherry vinegar.
                              type: starter
                              subType: cold
                              displayOrder: 2
                    bookableProducts:
                      - compositeId: >-
                          29|CS|275cc44dd2e2496fba44857c9257443a|e4d909c290d0fb1ca068ffaddf22cbd0
                        name: Dining Room
                        timeInterval: 30
                        category: Dinner
                        preorders:
                          required: true
                          requiredType: package
                          packageIds:
                            - 3f9a1c20-5b7e-4c1a-9e2d-1a2b3c4d5e6f
                            - 8c2d4e60-9a1b-4f3c-8d2e-2b3c4d5e6f70
                          menuIds:
                            - b4e6f810-1c2d-4a3b-9c8d-3c4d5e6f7081
                        deposit:
                          required: true
                          rule:
                            perAdult: 10
                            perChild: 5
                            perBooking: null
                        autoConfirmRule:
                          date: '2026-06-15'
                          dateType: weekday
                          minPeople: 1
                          maxPeople: 6
                          timeFrom: '12:00'
                          timeTo: '22:30'
                        noAvailabilityAction: waitlist
                        availabilityOccurrenceOverrides:
                          - name: Christmas closure
                            description: This product is unavailable over Christmas.
                            type: specific_dates
                            occurrences:
                              dates:
                                - '2026-12-25'
                                - '2026-12-26'
                            status: closed
                            notes: Set menu only on reopening.
    EventTicketListingPage:
      summary: Single-page result containing an event with ticketed sessions
      value:
        meta:
          currentPage: 1
          pageSize: 20
          totalItems: 1
          totalPages: 1
        listings:
          - groupName: Stonegate Live
            items:
              - name: The Crown & Anchor
                location:
                  street: 1 Upper St Martin's Lane
                  area: Covent Garden|Seven Dials
                  city: London
                  postCode: WC2H 9FB
                  lat: 51.513
                  lng: -0.128
                  gPlace: ChIJN1t_tDeuEmsRUsoyG83frY4
                capabilities:
                  - id: 29|TN|evt_summer_2026
                    name: Summer Live Sessions
                    operatorName: Stonegate Live
                    hostedBy: The Crown & Anchor
                    capability: event_ticket
                    types:
                      - live_music
                      - concert
                      - outdoor
                    content: >-
                      A weekly outdoor live music series running every Thursday
                      throughout summer, headlined by emerging UK artists.
                    photos:
                      - >-
                        https://cdn.bookabletech.com/events/evt_summer_2026/poster.jpg
                    phoneNumber: null
                    website: https://www.stonegate-live.example/summer-2026
                    availabilityOccurrence:
                      - name: Weekly Thursday show
                        description: Every Thursday June through August.
                        type: recurring_with_time
                        occurrences:
                          daysOfWeek:
                            - 4
                          months:
                            - 6
                            - 7
                            - 8
                          startTime: '19:00'
                          endTime: '22:00'
                        status: open
                        notes: Doors 18:30, support act 19:00.
                      - name: Special headline night
                        description: One-off headline performance on 15 August.
                        type: specific_dates_with_time
                        occurrences:
                          dates:
                            - '2026-08-15'
                          startTime: '20:00'
                          endTime: '23:30'
                        status: open
                        notes: Headline act announced separately.
                      - name: Next occurrence
                        description: Next upcoming show.
                        type: specific_dates_with_time
                        occurrences:
                          dates:
                            - '2026-06-18'
                          startTime: '19:00'
                          endTime: '22:00'
                        status: open
                      - name: Final occurrence
                        description: Final scheduled show.
                        type: specific_dates_with_time
                        occurrences:
                          dates:
                            - '2026-08-27'
                          startTime: '19:00'
                          endTime: '22:00'
                        status: open
                    preorders: null
                    bookableProducts:
                      - compositeId: 29|TN|evt_summer_2026|tt_standard
                        name: Standard
                        price:
                          amount: 25
                          currency: GBP
                        capacity:
                          max: 300
                          remaining: 142
                        visibility: on_sale
                        availabilityOccurrenceOverrides:
                          - name: Session 2026-06-18
                            description: Scheduled session for this ticket type.
                            type: specific_dates_with_time
                            occurrences:
                              dates:
                                - '2026-06-18'
                              startTime: '19:00'
                              endTime: '22:00'
                            status: open
                            notes: availabilityStatus=available
                          - name: Session 2026-06-25
                            description: Scheduled session for this ticket type.
                            type: specific_dates_with_time
                            occurrences:
                              dates:
                                - '2026-06-25'
                              startTime: '19:00'
                              endTime: '22:00'
                            status: open
                            notes: availabilityStatus=limited
    HybridListingPage:
      summary: >-
        Single venue offering both capabilities (table reservation + ticketed
        event)
      value:
        meta:
          currentPage: 1
          pageSize: 20
          totalItems: 1
          totalPages: 1
        listings:
          - groupName: Stonegate Group
            items:
              - name: The Crown & Anchor
                location:
                  street: 1 Upper St Martin's Lane
                  area: Covent Garden|Seven Dials
                  city: London
                  postCode: WC2H 9FB
                  lat: 51.513
                  lng: -0.128
                  gPlace: ChIJN1t_tDeuEmsRUsoyG83frY4
                capabilities:
                  - id: 29|CS|275cc44dd2e2496fba44857c9257443a
                    name: The Crown & Anchor
                    operatorName: Stonegate Group
                    capability: table_reservation
                    types:
                      - pub
                      - bar
                      - dog_friendly
                      - live_music
                    content: A traditional gastropub in the heart of Covent Garden.
                    photos:
                      - >-
                        https://cdn.bookabletech.com/venues/29/photos/crown-anchor-front.jpg
                    phoneNumber: '+442079461234'
                    website: https://www.crown-and-anchor.example/
                    availabilityOccurrence:
                      - name: Weekday service
                        type: recurring_with_time
                        occurrences:
                          daysOfWeek:
                            - 1
                            - 2
                            - 3
                            - 4
                            - 5
                          months:
                            - 1
                            - 2
                            - 3
                            - 4
                            - 5
                            - 6
                            - 7
                            - 8
                            - 9
                            - 10
                            - 11
                            - 12
                          startTime: '12:00'
                          endTime: '22:30'
                        status: open
                    preorders:
                      packages:
                        - id: 3f9a1c20-5b7e-4c1a-9e2d-1a2b3c4d5e6f
                          name: Sunday Roast
                          price: 28.5
                          type: main
                          sub_type: roast
                          diet_types:
                            - gluten_free_option
                          allergens:
                            - gluten
                            - dairy
                      menus:
                        - id: b4e6f810-1c2d-4a3b-9c8d-3c4d5e6f7081
                          name: Spring Tasting Menu
                          items:
                            - packageId: c5f70921-2d3e-4b4c-8d9e-4d5e6f708192
                              name: Amuse Bouche
                              price: 0
                              type: starter
                              displayOrder: 1
                    bookableProducts:
                      - compositeId: >-
                          29|CS|275cc44dd2e2496fba44857c9257443a|e4d909c290d0fb1ca068ffaddf22cbd0
                        name: Dining Room
                        timeInterval: 30
                        category: Dinner
                        preorders:
                          required: true
                          requiredType: package
                          packageIds:
                            - 3f9a1c20-5b7e-4c1a-9e2d-1a2b3c4d5e6f
                          menuIds:
                            - b4e6f810-1c2d-4a3b-9c8d-3c4d5e6f7081
                  - id: 29|TN|evt_summer_2026
                    name: Summer Live Sessions
                    operatorName: Stonegate Group
                    hostedBy: The Crown & Anchor
                    capability: event_ticket
                    types:
                      - live_music
                      - concert
                      - outdoor
                    content: >-
                      A weekly outdoor live music series every Thursday through
                      summer.
                    photos:
                      - >-
                        https://cdn.bookabletech.com/events/evt_summer_2026/poster.jpg
                    phoneNumber: null
                    website: https://www.stonegate-live.example/summer-2026
                    availabilityOccurrence:
                      - name: Weekly Thursday show
                        type: recurring_with_time
                        occurrences:
                          daysOfWeek:
                            - 4
                          months:
                            - 6
                            - 7
                            - 8
                          startTime: '19:00'
                          endTime: '22:00'
                        status: open
                    preorders: null
                    bookableProducts:
                      - compositeId: 29|TN|evt_summer_2026|tt_standard
                        name: Standard
                        price:
                          amount: 25
                          currency: GBP
                        capacity:
                          max: 300
                          remaining: 142
                        visibility: on_sale
  securitySchemes:
    Live:
      type: oauth2
      description: OAuth 2.0 client credentials (Auth0) — Live.
      flows:
        clientCredentials:
          tokenUrl: https://auth.bookabletech.com/oauth/token
          scopes:
            listing:read: Read access to listings.
            order:create: Permission to create orders.
            order:read: Read access to orders.
            order:update: Permission to update orders.
            order:delete: Permission to cancel orders.
    Sandbox:
      type: oauth2
      description: OAuth 2.0 client credentials (Auth0) — Sandbox.
      flows:
        clientCredentials:
          tokenUrl: https://auth-sandbox.bookabletech.com/oauth/token
          scopes:
            listing:read: Read access to listings.
            order:create: Permission to create orders.
            order:read: Read access to orders.
            order:update: Permission to update orders.
            order:delete: Permission to cancel orders.
  headers:
    RateLimitLimit:
      description: Maximum number of requests allowed per window.
      schema:
        type: integer
      example: 200
    RateLimitRemaining:
      description: Number of requests remaining in the current window.
      schema:
        type: integer
      example: 150
    RateLimitReset:
      description: >-
        Unix timestamp (seconds since epoch) when the current rate-limit window
        resets.
      schema:
        type: integer
        format: int64
      example: 1741651200
  responses:
    InternalServerError:
      description: Internal Server Error - An unexpected error occurred
      headers:
        X-RateLimit-Limit:
          $ref: '#/components/headers/RateLimitLimit'
        X-RateLimit-Remaining:
          $ref: '#/components/headers/RateLimitRemaining'
        X-RateLimit-Reset:
          $ref: '#/components/headers/RateLimitReset'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ServerError'
          example:
            type: https://tools.ietf.org/html/rfc9110#section-15.6.1
            title: Internal Server Error
            status: 500
            detail: An unexpected error occurred while processing the request
            code: SYS-N-001
            isRetryable: false
            traceId: 0HNJ2BG2TU3BU:00000007
        text/toon:
          schema:
            $ref: '#/components/schemas/ServerError'
          example: |
            type: "https://tools.ietf.org/html/rfc9110#section-15.6.1"
            title: Internal Server Error
            status: 500
            detail: An unexpected error occurred while processing the request
            code: SYS-N-001
            isRetryable: false
            traceId: "0HNJ2BG2TU3BU:00000007"
    BadRequest:
      description: Bad Request - The request was invalid or cannot be served
      headers:
        X-RateLimit-Limit:
          $ref: '#/components/headers/RateLimitLimit'
        X-RateLimit-Remaining:
          $ref: '#/components/headers/RateLimitRemaining'
        X-RateLimit-Reset:
          $ref: '#/components/headers/RateLimitReset'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ClientError'
          example:
            type: https://tools.ietf.org/html/rfc9110#section-15.5.1
            title: Validation Failed
            status: 400
            detail: One or more validation errors occurred.
            code: VALID-N-001
            isRetryable: false
            traceId: 0HNJ2BG2TU3BU:00000001
            errors:
              Date:
                - Availability date cannot be in the past.
        text/toon:
          schema:
            $ref: '#/components/schemas/ClientError'
          example: |
            type: "https://tools.ietf.org/html/rfc9110#section-15.5.1"
            title: Validation Failed
            status: 400
            detail: One or more validation errors occurred.
            code: VALID-N-001
            isRetryable: false
            traceId: "0HNJ2BG2TU3BU:00000001"
            errors.Date[1]: "Availability date cannot be in the past."
    Unauthorized:
      description: Unauthorized - Authentication is required
      headers:
        X-RateLimit-Limit:
          $ref: '#/components/headers/RateLimitLimit'
        X-RateLimit-Remaining:
          $ref: '#/components/headers/RateLimitRemaining'
        X-RateLimit-Reset:
          $ref: '#/components/headers/RateLimitReset'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ClientError'
          example:
            type: https://tools.ietf.org/html/rfc9110#section-15.5.5
            title: Invalid User Credentials
            status: 401
            detail: The provided credentials are invalid
            code: USER-N-003
            isRetryable: false
            traceId: 0HNJ2BG2TU3BU:00000002
        text/toon:
          schema:
            $ref: '#/components/schemas/ClientError'
          example: |
            type: "https://tools.ietf.org/html/rfc9110#section-15.5.5"
            title: Invalid User Credentials
            status: 401
            detail: The provided credentials are invalid
            code: USER-N-003
            isRetryable: false
            traceId: "0HNJ2BG2TU3BU:00000002"
    Forbidden:
      description: Forbidden - The request is understood but access is denied
      headers:
        X-RateLimit-Limit:
          $ref: '#/components/headers/RateLimitLimit'
        X-RateLimit-Remaining:
          $ref: '#/components/headers/RateLimitRemaining'
        X-RateLimit-Reset:
          $ref: '#/components/headers/RateLimitReset'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ClientError'
          example:
            type: https://tools.ietf.org/html/rfc9110#section-15.5.4
            title: Venue Not Mapped to Partner
            status: 403
            detail: The requested venue is not mapped to the current partner
            code: PMAP-N-001
            isRetryable: false
            traceId: 0HNJ2BG2TU3BU:00000003
        text/toon:
          schema:
            $ref: '#/components/schemas/ClientError'
          example: |
            type: "https://tools.ietf.org/html/rfc9110#section-15.5.4"
            title: Venue Not Mapped to Partner
            status: 403
            detail: The requested venue is not mapped to the current partner
            code: PMAP-N-001
            isRetryable: false
            traceId: "0HNJ2BG2TU3BU:00000003"
    TooManyRequests:
      description: >-
        Too Many Requests - Rate limit exceeded. Retry after the window
        specified in the Retry-After header.
      headers:
        Retry-After:
          description: Number of seconds to wait before retrying the request.
          schema:
            type: integer
          example: 60
        X-RateLimit-Limit:
          $ref: '#/components/headers/RateLimitLimit'
        X-RateLimit-Remaining:
          $ref: '#/components/headers/RateLimitRemaining'
        X-RateLimit-Reset:
          $ref: '#/components/headers/RateLimitReset'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ClientError'
          example:
            type: https://tools.ietf.org/html/rfc6585#section-4
            title: Rate Limit Exceeded
            status: 429
            detail: >-
              Too many requests have been made in a short period. Please wait
              and retry.
            code: RATE-R-001
            isRetryable: true
            traceId: 0HNJ2BG2TU3BU:00000006
        text/toon:
          schema:
            $ref: '#/components/schemas/ClientError'
          example: >
            type: "https://tools.ietf.org/html/rfc6585#section-4"

            title: Rate Limit Exceeded

            status: 429

            detail: Too many requests have been made in a short period. Please
            wait and retry.

            code: RATE-R-001

            isRetryable: true

            traceId: "0HNJ2BG2TU3BU:00000006"
    ServiceUnavailable:
      description: Service Unavailable - The server is temporarily unavailable
      headers:
        X-RateLimit-Limit:
          $ref: '#/components/headers/RateLimitLimit'
        X-RateLimit-Remaining:
          $ref: '#/components/headers/RateLimitRemaining'
        X-RateLimit-Reset:
          $ref: '#/components/headers/RateLimitReset'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ServerError'
          example:
            type: https://api.bookabletech.com/problems/service-unavailable
            title: Service Unavailable
            status: 503
            detail: The server is temporarily unavailable. Please try again later
            code: SYS-R-503
            isRetryable: true
        text/toon:
          schema:
            $ref: '#/components/schemas/ServerError'
          example: >
            type: "https://api.bookabletech.com/problems/service-unavailable"

            title: Service Unavailable

            status: 503

            detail: The server is temporarily unavailable. Please try again
            later

            code: SYS-R-503

            isRetryable: true
    NotFound:
      description: Not Found - The requested resource was not found
      headers:
        X-RateLimit-Limit:
          $ref: '#/components/headers/RateLimitLimit'
        X-RateLimit-Remaining:
          $ref: '#/components/headers/RateLimitRemaining'
        X-RateLimit-Reset:
          $ref: '#/components/headers/RateLimitReset'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ClientError'
          example:
            type: https://tools.ietf.org/html/rfc9110#section-15.5.5
            title: Venue Not Found
            status: 404
            detail: The requested venue could not be found
            code: VENUE-N-001
            isRetryable: false
            traceId: 0HNJ2BG2TU3BU:00000004
        text/toon:
          schema:
            $ref: '#/components/schemas/ClientError'
          example: |
            type: "https://tools.ietf.org/html/rfc9110#section-15.5.5"
            title: Venue Not Found
            status: 404
            detail: The requested venue could not be found
            code: VENUE-N-001
            isRetryable: false
            traceId: "0HNJ2BG2TU3BU:00000004"
