openapi: 3.0.2
info:
  title: Jammed Bookings API
  version: 2.4.0
  license:
    name: Jammed Bookings
    url: https://jammed.app
  contact:
    email: api@jammed.app
    url: https://support.jammed.app
  x-logo:
    url: "https://fileshare.jammedapp.com/Profile%20Images/Artboard%205.png"
    altText: Jammed logo
  description: |
    Jammed API allows you to integrate with the Jammed system, and automate key events within third-party systems. We are adding endpoints and features as we need them and as studios request them. If there's an endpoint you need, please contact us.

    By subscribing to and listening to webhooks your studio can automate sending reminders, automate customer lists and mailing
    systems, finance and accounting packages, and anything you can imagine really.

    # Introduction
    Jammed is powering scheduling and bookings for independent creative spaces, and we're building a new way to connect with them.
    You can read more about [Jammed on the support pages](https://support.jammedapp.com).

    Jammed offers webhooks and a polling API, so you can integrate how ever you like with the system.

    You can also see the version history and roadmap for the [Jammed API on the public Github project](https://github.com/jammed-org/api).

    # Authentication
    You authenticate with the API using an API key - this is found in the website settings of the admin section.

    Issue this header with all requests to the API or it will return a `401 Unauthorized` error.

    <SecurityDefinitions />

    # Getting started
    To get started, you'll need to create a Jammed account and create a studio account.

    Once you have setup your studio, head the to Website Setting page and press the 'Enable webhooks' button. From there, you can set the webhook URLs you want Jammed to send to.

x-tagGroups:
- name: Endpoints
  tags:
  - Booking Endpoints
  - Customer Endpoints
  - Room Endpoints
  - Extra Endpoints
  - Notification Endpoints
  - Blocked Time Endpoints
  - Reporting Endpoints
  - Group Endpoints
  - Coupon Endpoints
  - Promocode Endpoints
  - Studio Endpoints
- name: Webhooks
  tags:
  - Booking Webhooks
  - Customer Webhooks
  - Booking Webhooks
- name: Models
  tags:
  - booking_model
  - customer_model
  - group_model
  - room_model
  - coupon_model
  - promocode_model
tags:
- name: Booking Webhooks
  description: Events that happen with a booking
- name: Customer Webhooks
  description: Events that happen with a customer
- name: Group Webhooks
  description: Events that happen with a group/band
- name: Booking Endpoints
  description: Polling API for bookings
- name: Customer Endpoints
  description: Polling API for customers
- name: Group Endpoints
  description: Polling API for groups
- name: Room Endpoints
  description: Polling API for rooms
- name: booking_model
  x-displayName: Booking Model
  description: |
    <SchemaDefinition schemaRef="#/components/schemas/booking" />
- name: customer_model
  x-displayName: Customer Model
  description: |
    <SchemaDefinition schemaRef="#/components/schemas/customer" />
- name: group_model
  x-displayName: Group Model
  description: |
    <SchemaDefinition schemaRef="#/components/schemas/group" />
- name: room_model
  x-displayName: Room Model
  description: |
    <SchemaDefinition schemaRef="#/components/schemas/room" />
- name: coupon_model
  x-displayName: Coupon Model
  description: |
    <SchemaDefinition schemaRef="#/components/schemas/coupon" />
- name: promocode_model
  x-displayName: Promocode Model
  description: |
    <SchemaDefinition schemaRef="#/components/schemas/promocode" />
- name: Studio Endpoints
  description: Studio configuration and feature discovery
servers:
- url: https://api.jammed.app/api/v2/
  description: Jammed API endpoint

paths:
  /bookings.json:
    get:
      tags:
      - Booking Endpoints
      summary: All bookings
      description: Returns all non-cancelled bookings with optional sorting and pagination support.
      operationId: getBookings
      responses:
        "200":
          description: successful operation
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/booking'
        "401":
          description: Unauthorized - Invalid API key
      parameters:
      - name: Authorization
        in: header
        required: true
        schema:
          type: string
        example: "Bearer <api_key>"
      - name: sort_by
        in: query
        required: false
        description: Field to sort by
        schema:
          type: string
          enum:
          - created_at
          - updated_at
          - start_at
          - end_at
          - customer_name
          - room_name
          default: created_at
        example: "created_at"
      - name: sort_order
        in: query
        required: false
        description: Sort order (ascending or descending)
        schema:
          type: string
          enum:
          - asc
          - desc
          default: desc
        example: "desc"
      - name: page
        in: query
        required: false
        description: Page number for pagination
        schema:
          type: integer
          minimum: 1
          default: 1
        example: 1
      - name: per_page
        in: query
        required: false
        description: Number of items per page
        schema:
          type: integer
          minimum: 1
          maximum: 100
          default: 25
        example: 25
    post:
      tags:
      - Booking Endpoints
      summary: Create a booking
      description: |
        Create a new booking on Jammed as an admin.

        This creates a booking in the same way as a staff member would from the admin dashboard.

        You must provide **booking_details** with at least a `start_at`, `end_at`, `room_id`, and `price`.

        For the customer, you can either:
        - Provide a `customer_id` to book for an existing customer
        - Provide `customer_details` with `name` and `email` to create a new customer inline

        Optionally provide `band_details` with a `name` to associate a group/band, and `extra_ids` to add extras to the booking.

        The booking is created with source `manually_added` and platform `api_v2`.
      operationId: createBooking
      responses:
        "201":
          description: Booking created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/booking'
        "401":
          description: Unauthorized - Invalid API key
        "422":
          description: Unprocessable - the booking parameters you provided are invalid
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/unprocessable_error'
      requestBody:
        description: Create a new booking in Jammed
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/booking_create'
      parameters:
      - name: Authorization
        in: header
        required: true
        schema:
          type: string
        example: "Bearer <api_key>"

  /bookings/{code}.json:
    get:
      tags:
      - Booking Endpoints
      summary: Find a booking by code
      description: Returns booking with the given code
      operationId: getBooking
      responses:
        "200":
          description: successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/booking'
        "401":
          description: Unauthorized - Invalid API key
        "404":
          description: Not found - Booking was not found
      parameters:
      - name: Authorization
        in: header
        required: true
        schema:
          type: string
        example: "Bearer <api_key>"
      - name: code
        in: path
        required: true
        schema:
          type: string
        description: The booking code
    patch:
      tags:
      - Booking Endpoints
      summary: Update a booking
      description: |
        Update an existing booking as an admin.

        This uses the same booking update flow as staff members would from the admin dashboard.

        You can update various booking details including time, room, pricing, customer information,
        and other booking attributes.

        The booking is identified by its code (same as the GET endpoint).
      operationId: updateBooking
      responses:
        "200":
          description: Booking updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/booking'
        "401":
          description: Unauthorized - Invalid API key
        "404":
          description: Not found - Booking was not found
        "422":
          description: Unprocessable - the booking parameters you provided are invalid
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/unprocessable_error'
      requestBody:
        description: Update booking details
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/booking_update'
      parameters:
      - name: Authorization
        in: header
        required: true
        schema:
          type: string
        example: "Bearer <api_key>"
      - name: code
        in: path
        required: true
        schema:
          type: string
        description: The booking code

  /bookings/cancelled.json:
    get:
      tags:
      - Booking Endpoints
      summary: Cancelled bookings
      description: Returns all cancelled bookings, ordered by cancellation date descending. Supports pagination.
      operationId: getBookingsCancelled
      responses:
        "200":
          description: successful operation
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/booking'
        "401":
          description: Unauthorized - Invalid API key
      parameters:
      - name: Authorization
        in: header
        required: true
        schema:
          type: string
        example: "Bearer <api_key>"

  /bookings/started.json:
    get:
      tags:
      - Booking Endpoints
      summary: Started bookings
      description: Returns all upcoming bookings (starting in the future), ordered by start time descending. Supports 
        pagination.
      operationId: getBookingsStarted
      responses:
        "200":
          description: successful operation
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/booking'
        "401":
          description: Unauthorized - Invalid API key
      parameters:
      - name: Authorization
        in: header
        required: true
        schema:
          type: string
        example: "Bearer <api_key>"

  /bookings/finished.json:
    get:
      tags:
      - Booking Endpoints
      summary: Finished bookings
      description: Returns all bookings that have not yet ended, ordered by start time descending. Supports pagination.
      operationId: getBookingsFinished
      responses:
        "200":
          description: successful operation
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/booking'
        "401":
          description: Unauthorized - Invalid API key
      parameters:
      - name: Authorization
        in: header
        required: true
        schema:
          type: string
        example: "Bearer <api_key>"

  /bookings/reminder.json:
    get:
      tags:
      - Booking Endpoints
      summary: Reminder bookings
      description: Returns all bookings starting 24 hours or more from now, ordered by start time descending. Matches 
        the reminder webhook window.
      operationId: getBookingsReminder
      responses:
        "200":
          description: successful operation
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/booking'
        "401":
          description: Unauthorized - Invalid API key
      parameters:
      - name: Authorization
        in: header
        required: true
        schema:
          type: string
        example: "Bearer <api_key>"

  /bookings/one_hour_before.json:
    get:
      tags:
      - Booking Endpoints
      summary: One hour before bookings
      description: Returns all bookings starting 1 hour or more from now, ordered by start time descending. Matches the 
        one_hour_before webhook window.
      operationId: getBookingsOneHourBefore
      responses:
        "200":
          description: successful operation
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/booking'
        "401":
          description: Unauthorized - Invalid API key
      parameters:
      - name: Authorization
        in: header
        required: true
        schema:
          type: string
        example: "Bearer <api_key>"

  /bookings/updated.json:
    get:
      tags:
      - Booking Endpoints
      summary: Updated bookings
      description: Returns all non-cancelled bookings, ordered by last updated date descending. Useful for polling for 
        recently changed bookings.
      operationId: getBookingsUpdated
      responses:
        "200":
          description: successful operation
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/booking'
        "401":
          description: Unauthorized - Invalid API key
      parameters:
      - name: Authorization
        in: header
        required: true
        schema:
          type: string
        example: "Bearer <api_key>"

  /bookings/{code}/transactions.json:
    get:
      tags:
      - Booking Endpoints
      summary: List booking transactions
      description: |
        Returns all money log transactions for the specified booking, ordered by most recent first.

        This includes payments, refunds, credit notes, and other financial transactions
        associated with the booking.
      operationId: getBookingTransactions
      responses:
        "200":
          description: successful operation
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/money_log'
        "401":
          description: Unauthorized - Invalid API key
        "404":
          description: Not found - Booking was not found
      parameters:
      - name: Authorization
        in: header
        required: true
        schema:
          type: string
        example: "Bearer <api_key>"
      - name: code
        in: path
        required: true
        schema:
          type: string
        description: The booking code
    post:
      tags:
      - Booking Endpoints
      summary: Create a transaction for a booking
      description: |
        Create a new transaction (money log entry) for the specified booking.

        This creates a credit note transaction with the specified amount and description.
        The transaction will be associated with the booking and its customer.

        All amounts should be provided as positive integers in the smallest currency unit
        (cents/pence) - e.g., £10.50 should be provided as 1050.
      operationId: createBookingTransaction
      responses:
        "201":
          description: Transaction created successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/money_log'
        "401":
          description: Unauthorized - Invalid API key
        "404":
          description: Not found - Booking was not found
        "422":
          description: Unprocessable - the transaction parameters you provided are invalid
      requestBody:
        description: Create a new transaction for the booking
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/transaction_create'
      parameters:
      - name: Authorization
        in: header
        required: true
        schema:
          type: string
        example: "Bearer <api_key>"
      - name: code
        in: path
        required: true
        schema:
          type: string
        description: The booking code

  /bookings/{code}/studio_notes.json:
    get:
      tags:
      - Booking Endpoints
      summary: Get booking studio notes
      description: |
        Returns the studio notes for the specified booking.

        Studio notes are internal notes that studios can add to bookings for their own reference.
        These are not visible to customers.
      operationId: getBookingStudioNotes
      responses:
        "200":
          description: successful operation
          content:
            application/json:
              schema:
                type: object
                properties:
                  studio_notes:
                    type: string
                    description: The studio notes text
                    nullable: true
        "401":
          description: Unauthorized - Invalid API key
        "404":
          description: Not found - Booking was not found
      parameters:
      - name: Authorization
        in: header
        required: true
        schema:
          type: string
        example: "Bearer <api_key>"
      - name: code
        in: path
        required: true
        schema:
          type: string
        description: The booking code
    patch:
      tags:
      - Booking Endpoints
      summary: Update booking studio notes
      description: |
        Update the studio notes for the specified booking.

        Studio notes are internal notes that studios can add to bookings for their own reference.
        These are not visible to customers.
      operationId: updateBookingStudioNotes
      responses:
        "200":
          description: Studio notes updated successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  studio_notes:
                    type: string
                    description: The updated studio notes text
                    nullable: true
        "401":
          description: Unauthorized - Invalid API key
        "404":
          description: Not found - Booking was not found
      requestBody:
        description: Update studio notes for the booking
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/studio_notes_update'
      parameters:
      - name: Authorization
        in: header
        required: true
        schema:
          type: string
        example: "Bearer <api_key>"
      - name: code
        in: path
        required: true
        schema:
          type: string
        description: The booking code

  /customers.json:
    get:
      tags:
      - Customer Endpoints
      summary: Find all customers
      description: Returns all customers
      operationId: getCustomers
      responses:
        "200":
          description: successful operation
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/customer'
        "401":
          description: Unauthorized - Invalid API key
      parameters:
      - name: Authorization
        in: header
        required: true
        schema:
          type: string
        example: "Bearer <api_key>"
    post:
      tags:
      - Customer Endpoints
      summary: Create a customer
      description: |
        Create a new customer on Jammed.

        With a new customer, you must at least provide an **name** and **email address**.

        You can also issue `actions` to the endpoint, to create and invite the customer to set a password - Jammed will then send them an email from your account with a link to set their password.

        You can't create a customer with an email address that already exists as a registered customer on Jammed. You can create a customer with an email address that already exists, so long as the customer is not yet registered on Jammed.

        **Jammed API doesn't accept passwords for customer creation process** - they must be set by the customer themselves in the invitation process to verify the email and to improve security.
      operationId: createCustomer
      responses:
        "201":
          description: Customer created
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/customer'
        "401":
          description: Unauthorized - Invalid API key
        "422":
          description: Unprocessable - the customer parameters you provided are invalid
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/unprocessable_error'

      requestBody:
        description: Create a new customer in Jammed
        required: true
        content:
          form-data:
            schema:
              $ref: '#/components/schemas/customer_create'
      parameters:
      - name: Authorization
        in: header
        required: true
        schema:
          type: string
        example: "Bearer <api_key>"

  /customers/{id}.json:
    get:
      tags:
      - Customer Endpoints
      summary: Find a customer by ID
      description: Returns customer with the given ID
      operationId: getCustomer
      responses:
        "200":
          description: successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/customer'
        "401":
          description: Unauthorized - Invalid API key
        "404":
          description: Not found - Customer was not found
      parameters:
      - name: Authorization
        in: header
        required: true
        schema:
          type: string
        example: "Bearer <api_key>"
      - name: id
        in: path
        required: true
        schema:
          type: integer

  /coupons/{id}.json:
    get:
      tags:
      - Coupon Endpoints
      summary: Find a coupon by ID
      description: Returns coupon with the given ID
      operationId: getCoupon
      responses:
        "200":
          description: successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/coupon'
        "401":
          description: Unauthorized - Invalid API key
        "404":
          description: Not found - Coupon was not found
      parameters:
      - name: Authorization
        in: header
        required: true
        schema:
          type: string
        example: "Bearer <api_key>"
      - name: id
        in: path
        required: true
        schema:
          type: integer

  /coupons.json:
    get:
      tags:
      - Coupon Endpoints
      summary: Find all coupons
      description: Returns all coupons
      operationId: getCoupons
      responses:
        "200":
          description: successful operation
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/coupon'
        "401":
          description: Unauthorized - Invalid API key
      parameters:
      - name: Authorization
        in: header
        required: true
        schema:
          type: string
        example: "Bearer <api_key>"
    post:
      tags:
      - Coupon Endpoints
      summary: Create a coupon
      description: |
        Create a new coupon on Jammed.

        With a new coupon, you must at least provide an **name**, **amount** and **discount_type**.

        `discount type` is either `fixed_amount`, or `percentage` - and `amount` is in pence/cents or whole pencentage points.

        Once created, a Coupon can be used by staff members only on a booking. To allow customers to use the Coupon, you need to generate Promocode for the Coupon.
      operationId: createCoupon
      responses:
        "201":
          description: Coupon created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/coupon'
        "401":
          description: Unauthorized - Invalid API key
        "422":
          description: Unprocessable - the coupon parameters you provided are invalid
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/unprocessable_error'
      requestBody:
        description: Create a new coupon in Jammed
        required: true
        content:
          form-data:
            schema:
              $ref: '#/components/schemas/coupon_create'
      parameters:
      - name: Authorization
        in: header
        required: true
        schema:
          type: string
        example: "Bearer <api_key>"

  /coupons/{coupon_id}/promocodes.json:
    get:
      tags:
      - Promocode Endpoints
      summary: Find all promocodes
      description: Returns all promocodes
      operationId: getPromocodes
      responses:
        "200":
          description: successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/promocode'
        "401":
          description: Unauthorized - Invalid API key
      parameters:
      - name: Authorization
        in: header
        required: true
        schema:
          type: string
        example: "Bearer <api_key>"
      - name: coupon_id
        description: Coupon ID to list the promocodes for
        in: path
        required: true
        schema:
          type: integer
        example: 123
    post:
      tags:
      - Promocode Endpoints
      summary: Create a promocode
      description: |
        Create a promocode for a coupon

        You can provide the API with a **code**, but if you don't we'll generate one for you.

        You can just `POST` an empty body to create a promocode for a coupon with a generated code.

        With promocodes you can specify minimum booking values, redemptions limits, limit only to one customer, and limit one use per customer.

        Once created, a Promocode can be used to apply the Coupon to a booking via the customer booking form
      operationId: createPromocode
      responses:
        "201":
          description: Promocode created
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/promocode'
        "401":
          description: Unauthorized - Invalid API key
        "422":
          description: Unprocessable - the promocode parameters you provided are invalid
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/unprocessable_error'
      requestBody:
        description: Create a new promocode in Jammed
        required: true
        content:
          form-data:
            schema:
              $ref: '#/components/schemas/promocode_create'
      parameters:
      - name: Authorization
        in: header
        required: true
        schema:
          type: string
        example: "Bearer <api_key>"
      - name: coupon_id
        description: Coupon ID to create promocode for
        in: path
        required: true
        schema:
          type: integer
        example: 123

  /coupons/{coupon_id}/assign_to_customer.json:
    post:
      tags:
      - Coupon Endpoints
      summary: Assign Coupon to Customer
      description: |
        Assign a Coupon to a Customer

        Using the Coupon ID and a Customer ID, we can assign a Coupon to a Customer

        This will mean that the Customer will automatically be able to use the Coupon when booking without the use of a promocode
      operationId: assignPromocode
      responses:
        "201":
          description: Promocode assigned
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/promocode'
        "401":
          description: Unauthorized - Invalid API key
        "422":
          description: Coupon has already been assigned to this Customer
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/unprocessable_error'
      requestBody:
        description: Assign a Coupon to a Customer
        required: true
        content:
          form-data:
            schema:
              $ref: '#/components/schemas/coupon_assign_to_customer'
      parameters:
      - name: Authorization
        in: header
        required: true
        schema:
          type: string
        example: "Bearer <api_key>"
      - name: coupon_id
        description: Coupon ID to create promocode for
        in: path
        required: true
        schema:
          type: integer
        example: 123
  /groups.json:
    get:
      tags:
      - Group Endpoints
      summary: Find all groups
      description: Returns all groups
      operationId: getGroups
      responses:
        "200":
          description: successful operation
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/group'
        "401":
          description: Unauthorized - Invalid API key
      parameters:
      - name: Authorization
        in: header
        required: true
        schema:
          type: string
        example: "Bearer <api_key>"

  /rooms.json:
    get:
      tags:
      - Room Endpoints
      summary: Find all rooms
      description: Returns all rooms
      operationId: getRooms
      responses:
        "200":
          description: successful operation
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/room'
        "401":
          description: Unauthorized - Invalid API key
      parameters:
      - name: Authorization
        in: header
        required: true
        schema:
          type: string
        example: "Bearer <api_key>"

  /studio.json:
    get:
      tags:
      - Studio Endpoints
      summary: Studio configuration
      description: |
        Returns studio branding, feature toggles, usage flags, payment setup, wordings, and plan info.
        Designed to be called on app launch and cached client-side (recommended: 1 hour).
      operationId: getStudio
      parameters:
      - $ref: '#/components/parameters/authHeader'
      responses:
        "200":
          description: Studio configuration
          content:
            application/json:
              schema:
                type: object
                properties:
                  studio:
                    type: object
                    properties:
                      name:
                        type: string
                        description: Studio display name
                      slug:
                        type: string
                        description: Studio subdomain identifier
                      url:
                        type: string
                        description: Studio's default Jammed URL
                      logo:
                        type: object
                        properties:
                          default:
                            type: string
                            nullable: true
                            description: Cropped logo URL
                          square:
                            type: string
                            nullable: true
                            description: Square (512x512) logo URL
                          app:
                            type: string
                            nullable: true
                            description: App-sized (100x100) logo URL
                      timezone:
                        type: string
                        description: IANA timezone (e.g. Europe/London)
                      currency_code:
                        type: string
                        description: ISO 4217 currency code (e.g. GBP)
                      currency_symbol:
                        type: string
                        description: Currency symbol (e.g. £)
                      country_code:
                        type: string
                        description: ISO country code (e.g. GB)
                      opening_time:
                        type: integer
                        description: Opening time in seconds from midnight (e.g. 32400 = 9am)
                      closing_time:
                        type: integer
                        description: Closing time in seconds from midnight (e.g. 82800 = 11pm)
                      slot_length:
                        type: integer
                        description: Booking slot length in seconds (e.g. 1800 = 30min)
                      tax_rate:
                        type: number
                        description: Tax rate percentage (e.g. 20.0), 0 if none configured
                      rooms_count:
                        type: integer
                        description: Total number of rooms
                  features:
                    type: object
                    description: System-wide feature toggles
                    properties:
                      bands_and_groups:
                        type: boolean
                      bookable_staff:
                        type: boolean
                      approval_required:
                        type: boolean
                      enquiries:
                        type: boolean
                      memberships:
                        type: boolean
                      smart_lock_provider:
                        type: string
                        nullable: true
                        description: Smart lock provider name, or null if not configured
                  wordings:
                    type: object
                    description: Custom terminology for the studio
                    properties:
                      customer:
                        type: string
                        description: What the studio calls customers (default 'customer')
                      group:
                        type: string
                        description: What the studio calls groups (default 'group')
                      room:
                        type: string
                        description: What the studio calls rooms (default 'room')
                  usage:
                    type: object
                    description: Subsystem usage flags — true if the studio has data in that subsystem
                    properties:
                      extras:
                        type: boolean
                      recording_sessions:
                        type: boolean
                      audio_uploads:
                        type: boolean
                      custom_questions:
                        type: boolean
                      regular_bookings:
                        type: boolean
                      coupons:
                        type: boolean
                      credit_balance:
                        type: boolean
                      multi_location:
                        type: boolean
                      locations:
                        type: boolean
                  payments:
                    type: object
                    properties:
                      stripe_connected:
                        type: boolean
                      square_connected:
                        type: boolean
                      square_terminal:
                        type: boolean
                      manual_payments:
                        type: boolean
                        description: True when no online payment gateway is configured
                  plan:
                    type: object
                    properties:
                      name:
                        type: string
                        nullable: true
                        description: Plan name (e.g. 'standard')
                      max_rooms:
                        type: integer
                        nullable: true
                        description: Maximum rooms allowed on the plan

  /dashboard:
    get:
      tags:
      - Booking Endpoints
      summary: Dashboard composite data
      description: |
        Returns rooms, today's bookings, blocked times, opening time, slot length, and timezone in a single call.
        Powers the ePOS home screen. Accepts optional `from` and `to` date parameters for a date range,
        or a single `date` parameter. Defaults to today.
      operationId: getDashboard
      parameters:
      - $ref: '#/components/parameters/authHeader'
      - name: date
        in: query
        schema:
          type: string
          format: date
        description: Single date (defaults to today)
      - name: from
        in: query
        schema:
          type: string
          format: date
        description: Start of date range
      - name: to
        in: query
        schema:
          type: string
          format: date
        description: End of date range
      responses:
        "200":
          description: Dashboard data
          content:
            application/json:
              schema:
                type: object
                properties:
                  rooms:
                    type: array
                    items:
                      $ref: '#/components/schemas/room'
                  bookings:
                    type: array
                    items:
                      $ref: '#/components/schemas/dashboard_booking'
                  blocked_times:
                    type: array
                    items:
                      $ref: '#/components/schemas/blocked_time'
                  opening_time:
                    type: integer
                    description: Opening time in seconds from midnight (e.g. 32400 = 9am)
                  slot_length:
                    type: integer
                    description: Slot length in seconds (e.g. 3600 = 1 hour)
                  timezone:
                    type: string

  # --- Booking Management ---
      x-epos: true
  /bookings/{code}:
    delete:
      tags:
      - Booking Endpoints
      summary: Cancel a booking
      description: Soft-deletes (cancels) a booking. Optionally sends cancellation email.
      operationId: cancelBooking
      parameters:
      - $ref: '#/components/parameters/authHeader'
      - $ref: '#/components/parameters/bookingCode'
      - name: send_email
        in: query
        schema:
          type: boolean
      responses:
        "200":
          description: Booking cancelled
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/status_response'
        "404":
          description: Booking not found

      x-epos: true
  /bookings/{code}/uncancel:
    patch:
      tags:
      - Booking Endpoints
      summary: Uncancel a booking
      description: Restores a previously cancelled booking.
      operationId: uncancelBooking
      parameters:
      - $ref: '#/components/parameters/authHeader'
      - $ref: '#/components/parameters/bookingCode'
      responses:
        "200":
          description: Booking restored
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/booking'
        "404":
          description: Booking not found

      x-epos: true
  /bookings/search:
    get:
      tags:
      - Booking Endpoints
      summary: Search bookings
      description: Search bookings by customer name, email, mobile, band name, or booking code. Supports status 
        filtering.
      operationId: searchBookings
      parameters:
      - $ref: '#/components/parameters/authHeader'
      - name: q
        in: query
        schema:
          type: string
        description: Search query
      - name: status_filter
        in: query
        schema:
          type: string
          enum: [active, cancelled, cancelled_and_active]
        description: Filter by booking status
      - $ref: '#/components/parameters/page'
      - $ref: '#/components/parameters/perPage'
      responses:
        "200":
          description: Search results
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/booking'

      x-epos: true
  /bookings/by_dates:
    get:
      tags:
      - Booking Endpoints
      summary: Bookings by date range
      description: Returns bookings for a date range, optimized for calendar/schedule display.
      operationId: bookingsByDates
      parameters:
      - $ref: '#/components/parameters/authHeader'
      - name: from
        in: query
        required: true
        schema:
          type: string
          format: date
      - name: to
        in: query
        required: true
        schema:
          type: string
          format: date
      responses:
        "200":
          description: Bookings for date range
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/dashboard_booking'

      x-epos: true
  /bookings/awaiting_approval:
    get:
      tags:
      - Booking Endpoints
      summary: Bookings awaiting approval
      description: List bookings pending manager approval.
      operationId: bookingsAwaitingApproval
      parameters:
      - $ref: '#/components/parameters/authHeader'
      - $ref: '#/components/parameters/page'
      - $ref: '#/components/parameters/perPage'
      responses:
        "200":
          description: Pending bookings
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/booking'

      x-epos: true
  /bookings/{code}/approve:
    post:
      tags:
      - Booking Endpoints
      summary: Approve a booking
      description: Approve a booking that is awaiting approval.
      operationId: approveBooking
      parameters:
      - $ref: '#/components/parameters/authHeader'
      - $ref: '#/components/parameters/bookingCode'
      responses:
        "200":
          description: Booking approved
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/booking'

      x-epos: true
  /bookings/{code}/reject:
    post:
      tags:
      - Booking Endpoints
      summary: Reject a booking
      description: Reject a booking that is awaiting approval.
      operationId: rejectBooking
      parameters:
      - $ref: '#/components/parameters/authHeader'
      - $ref: '#/components/parameters/bookingCode'
      - name: made_by_user_id
        in: query
        schema:
          type: integer
      responses:
        "200":
          description: Booking rejected
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/booking'

      x-epos: true
  /bookings/{code}/pdf:
    get:
      tags:
      - Booking Endpoints
      summary: Generate booking PDF
      description: Generate a receipt/confirmation PDF for the booking.
      operationId: bookingPdf
      parameters:
      - $ref: '#/components/parameters/authHeader'
      - $ref: '#/components/parameters/bookingCode'
      responses:
        "200":
          description: PDF file
          content:
            application/pdf:
              schema:
                type: string
                format: binary

      x-epos: true
  /bookings/{code}/resend_confirmation:
    post:
      tags:
      - Booking Endpoints
      summary: Resend confirmation email
      description: Re-send the booking confirmation email to the customer.
      operationId: resendConfirmation
      parameters:
      - $ref: '#/components/parameters/authHeader'
      - $ref: '#/components/parameters/bookingCode'
      responses:
        "200":
          description: Email sent
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/status_response'

      x-epos: true
  /bookings/{code}/refund_transaction:
    post:
      tags:
      - Booking Endpoints
      summary: Refund a transaction
      description: Refund a specific payment transaction on a booking (Stripe, Square, or Jammed Pay).
      operationId: refundTransaction
      parameters:
      - $ref: '#/components/parameters/authHeader'
      - $ref: '#/components/parameters/bookingCode'
      - name: transaction_id
        in: query
        required: true
        schema:
          type: integer
      responses:
        "200":
          description: Transaction refunded
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/status_response'

      x-epos: true
  /bookings/{code}/refunds:
    post:
      tags:
      - Booking Endpoints
      summary: Full refund
      description: Cancel and refund all payments on a booking.
      operationId: fullRefund
      parameters:
      - $ref: '#/components/parameters/authHeader'
      - $ref: '#/components/parameters/bookingCode'
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                send_email:
                  type: boolean
                refund_reason:
                  type: string
      responses:
        "200":
          description: Booking refunded
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/status_response'

      x-epos: true
  /bookings/{code}/credit_payments:
    post:
      tags:
      - Booking Endpoints
      summary: Apply credit balance
      description: Pay a booking using the customer's credit balance.
      operationId: applyCreditPayment
      parameters:
      - $ref: '#/components/parameters/authHeader'
      - $ref: '#/components/parameters/bookingCode'
      responses:
        "200":
          description: Credit applied
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/status_response'

      x-epos: true
  /bookings/{code}/credit_balances:
    post:
      tags:
      - Booking Endpoints
      summary: Credit to customer balance
      description: Credit the booking amount to the customer's balance.
      operationId: creditToCustomerBalance
      parameters:
      - $ref: '#/components/parameters/authHeader'
      - $ref: '#/components/parameters/bookingCode'
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                credit_amount:
                  type: integer
                send_email:
                  type: boolean
      responses:
        "200":
          description: Booking credited
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/status_response'

      x-epos: true
  /bookings/{code}/notes:
    patch:
      tags:
      - Booking Endpoints
      summary: Update booking notes
      description: Update internal staff notes and colour override for a booking.
      operationId: updateBookingNotes
      parameters:
      - $ref: '#/components/parameters/authHeader'
      - $ref: '#/components/parameters/bookingCode'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                booking:
                  type: object
                  properties:
                    studio_notes:
                      type: string
                    block_colour_override:
                      type: string
      responses:
        "200":
          description: Notes updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/booking'

      x-epos: true
  /bookings/{code}/notifications:
    get:
      tags:
      - Booking Endpoints
      summary: Booking notifications
      description: View notification history for a booking.
      operationId: bookingNotifications
      parameters:
      - $ref: '#/components/parameters/authHeader'
      - $ref: '#/components/parameters/bookingCode'
      responses:
        "200":
          description: Notifications list
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object

      x-epos: true
  /bookings/{code}/history.json:
    get:
      tags:
      - Booking Endpoints
      summary: Booking history
      description: |
        Returns a chronological timeline of all activity on a booking,
        sorted ascending by time. Includes transactions, notifications,
        status changes, price changes, coupon redemptions, and more.
      operationId: getBookingHistory
      parameters:
      - $ref: '#/components/parameters/authHeader'
      - $ref: '#/components/parameters/bookingCode'
      responses:
        "200":
          description: History timeline
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/booking_history_entry'
        "401":
          description: Unauthorized - Invalid API key
        "404":
          description: Not found - Booking was not found
  /bookings/{code}/payment_requests:
    post:
      tags:
      - Booking Endpoints
      summary: Send payment request
      description: Create and send a payment link to the customer.
      operationId: createPaymentRequest
      parameters:
      - $ref: '#/components/parameters/authHeader'
      - $ref: '#/components/parameters/bookingCode'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                price:
                  type: integer
                  description: Amount in minor currency units (pence/cents). Must be an integer.
              required:
              - price
      responses:
        "200":
          description: Payment request sent
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/status_response'

      x-epos: true
  /bookings/{code}/payment_requests/charge:
    post:
      tags:
      - Booking Endpoints
      summary: Charge saved card
      description: Create a payment request and immediately charge the customer's saved card. This is a collection 
        action — it creates a new payment request and settles it in one step.
      operationId: chargePaymentRequest
      parameters:
      - $ref: '#/components/parameters/authHeader'
      - $ref: '#/components/parameters/bookingCode'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                price:
                  type: integer
                  description: Amount in minor currency units (pence/cents). Must be an integer.
                use_payment_method:
                  type: string
              required:
              - price
      responses:
        "200":
          description: Payment taken
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/status_response'

      x-epos: true
  /bookings/{code}/payment_requests/{id}/mark_as_paid:
    post:
      tags:
      - Booking Endpoints
      summary: Mark payment as paid
      description: Record that a manual payment has been received.
      operationId: markPaymentAsPaid
      parameters:
      - $ref: '#/components/parameters/authHeader'
      - $ref: '#/components/parameters/bookingCode'
      - name: id
        in: path
        required: true
        schema:
          type: integer
      responses:
        "200":
          description: Marked as paid
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/status_response'

      x-epos: true
  /bookings/{code}/payment_requests/{id}/cancel:
    post:
      tags:
      - Booking Endpoints
      summary: Cancel payment request
      description: Cancel an open payment request.
      operationId: cancelPaymentRequest
      parameters:
      - $ref: '#/components/parameters/authHeader'
      - $ref: '#/components/parameters/bookingCode'
      - name: id
        in: path
        required: true
        schema:
          type: integer
      responses:
        "200":
          description: Payment request cancelled
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/status_response'

      x-epos: true
  /bookings/{code}/settlement_supported:
    get:
      tags:
      - Booking Endpoints
      summary: Check settlement support
      description: Check if the booking's customer can be charged remotely (has saved payment methods).
      operationId: settlementSupported
      parameters:
      - $ref: '#/components/parameters/authHeader'
      - $ref: '#/components/parameters/bookingCode'
      responses:
        "200":
          description: Settlement check result
          content:
            application/json:
              schema:
                type: object
                properties:
                  settlement_supported:
                    type: boolean
                  payment_methods:
                    type: array
                    items:
                      type: object

  # --- Customer Management ---
      x-epos: true
  /customers/{id}:
    put:
      tags:
      - Customer Endpoints
      summary: Update customer
      description: Update an existing customer's details.
      operationId: updateCustomer
      parameters:
      - $ref: '#/components/parameters/authHeader'
      - name: id
        in: path
        required: true
        schema:
          type: integer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                customer:
                  $ref: '#/components/schemas/customer_create_body'
      responses:
        "200":
          description: Customer updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/customer'

      x-epos: true
  /customers/search:
    get:
      tags:
      - Customer Endpoints
      summary: Search customers
      description: Autocomplete search by name, email, mobile, or band name. Returns top 10 results by booking count.
      operationId: searchCustomers
      parameters:
      - $ref: '#/components/parameters/authHeader'
      - name: q
        in: query
        required: true
        schema:
          type: string
        description: Search query (min 1 character)
      - name: limit
        in: query
        schema:
          type: integer
          default: 10
      responses:
        "200":
          description: Matching customers
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/short_customer'

      x-epos: true
  /customers/{id}/notes:
    patch:
      tags:
      - Customer Endpoints
      summary: Update customer notes
      description: Add or update internal staff notes about a customer.
      operationId: updateCustomerNotes
      parameters:
      - $ref: '#/components/parameters/authHeader'
      - name: id
        in: path
        required: true
        schema:
          type: integer
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                customer:
                  type: object
                  properties:
                    notes:
                      type: string
      responses:
        "200":
          description: Notes updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/customer'

      x-epos: true
  /customers/{id}/invite:
    post:
      tags:
      - Customer Endpoints
      summary: Invite an existing customer
      description: |
        Send a registration invitation to an existing customer who has not yet created their account.

        Jammed emails the customer a link to set their password, exactly as if they had been created with the `invite` action on `POST /customers.json` - but without creating a duplicate customer record. Use this when the customer already exists (for example, they've already made a booking) and you want them to register.

        On success, the customer's `status` becomes `invited`. The invitation link is valid for 2 days - after it expires, calling this endpoint again issues a fresh invitation.

        Returns a `422` error if the customer has already registered an account, or if they already have a valid (unexpired) invitation. Returns a `404` if the customer does not exist or has been deleted.
      operationId: inviteCustomer
      parameters:
      - $ref: '#/components/parameters/authHeader'
      - name: id
        in: path
        required: true
        schema:
          type: integer
      responses:
        "200":
          description: Invitation sent
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/customer'
        "401":
          description: Unauthorized - Invalid API key
        "404":
          description: Not found - Customer was not found
        "422":
          description: Unprocessable - the customer has already registered, or already has a valid invite
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/unprocessable_error'
  /customers/{id}/bookings:
    get:
      tags:
      - Customer Endpoints
      summary: Customer bookings
      description: List a customer's completed, non-cancelled bookings.
      operationId: customerBookings
      parameters:
      - $ref: '#/components/parameters/authHeader'
      - name: id
        in: path
        required: true
        schema:
          type: integer
      - $ref: '#/components/parameters/page'
      - $ref: '#/components/parameters/perPage'
      responses:
        "200":
          description: Customer's bookings
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/booking'

      x-epos: true
  /customers/{id}/credit_balance:
    get:
      tags:
      - Customer Endpoints
      summary: Get credit balance
      description: View the customer's current credit balance.
      operationId: getCustomerCreditBalance
      parameters:
      - $ref: '#/components/parameters/authHeader'
      - name: id
        in: path
        required: true
        schema:
          type: integer
      responses:
        "200":
          description: Credit balance
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: integer
                  credit_balance:
                    type: integer
      x-epos: true
    patch:
      tags:
      - Customer Endpoints
      summary: Update credit balance
      description: Adjust the customer's credit balance. Creates a credit movement record.
      operationId: updateCustomerCreditBalance
      parameters:
      - $ref: '#/components/parameters/authHeader'
      - name: id
        in: path
        required: true
        schema:
          type: integer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                customer:
                  type: object
                  properties:
                    credit_balance:
                      type: integer
                      description: New credit balance in minor currency units
      responses:
        "200":
          description: Balance updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: integer
                  credit_balance:
                    type: integer

      x-epos: true
  /customers/{id}/credit_movements:
    get:
      tags:
      - Customer Endpoints
      summary: Credit movements
      description: View credit transaction history for a customer.
      operationId: customerCreditMovements
      parameters:
      - $ref: '#/components/parameters/authHeader'
      - name: id
        in: path
        required: true
        schema:
          type: integer
      - $ref: '#/components/parameters/page'
      - $ref: '#/components/parameters/perPage'
      responses:
        "200":
          description: Credit movements
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/credit_movement'

      x-epos: true
  /customers/{id}/status:
    get:
      tags:
      - Customer Endpoints
      summary: Customer status
      description: Check if a customer is banned or behind on payment.
      operationId: customerStatus
      parameters:
      - $ref: '#/components/parameters/authHeader'
      - name: id
        in: path
        required: true
        schema:
          type: integer
      responses:
        "200":
          description: Customer status
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: integer
                  status:
                    type: string
                  banned:
                    type: boolean
                  behind_payment:
                    type: boolean
                  status_reason_text:
                    type: string

      x-epos: true
  /customers/{id}/payment_methods:
    get:
      tags:
      - Customer Endpoints
      summary: Payment methods
      description: List a customer's saved payment methods (for remote charging).
      operationId: customerPaymentMethods
      parameters:
      - $ref: '#/components/parameters/authHeader'
      - name: id
        in: path
        required: true
        schema:
          type: integer
      responses:
        "200":
          description: Payment methods
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object

  # --- Pricing ---
      x-epos: true
  /booking_price:
    post:
      tags:
      - Booking Endpoints
      summary: Calculate booking price
      description: |
        Real-time price calculation with extras, tax, and staff costs.
        Provide either a `booking_code` to calculate from an existing booking,
        or `price.hours` and `price.price` for a custom calculation.
      operationId: calculateBookingPrice
      parameters:
      - $ref: '#/components/parameters/authHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                booking_price:
                  type: object
                  properties:
                    booking_code:
                      type: string
                    price:
                      type: object
                      properties:
                        hours:
                          type: string
                        days:
                          type: string
                        price:
                          type: string
                    extras:
                      type: array
                      items:
                        type: object
                        properties:
                          id:
                            type: integer
                          quantity:
                            type: integer
                    staff_ids:
                      type: array
                      items:
                        type: integer
      responses:
        "200":
          description: Price breakdown
          content:
            application/json:
              schema:
                type: object
                properties:
                  hours:
                    type: number
                  days:
                    type: integer
                  time_cost:
                    type: integer
                  extras:
                    type: array
                    items:
                      type: object
                  extras_total:
                    type: integer
                  staff_total:
                    type: integer
                  subtotal:
                    type: integer
                  tax_total:
                    type: integer
                  total:
                    type: integer

      x-epos: true
  /slot_projection:
    post:
      tags:
      - Booking Endpoints
      summary: Project time slot
      description: Preview cost and check conflicts for a time slot.
      operationId: slotProjection
      parameters:
      - $ref: '#/components/parameters/authHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                projection_params:
                  type: object
                  properties:
                    start_time:
                      type: string
                    end_time:
                      type: string
                    date:
                      type: string
                      format: date
                    start_at:
                      type: string
                    end_at:
                      type: string
                    room_id:
                      type: integer
                    room_code:
                      type: string
                    booking_code:
                      type: string
      responses:
        "200":
          description: Slot projection
          content:
            application/json:
              schema:
                type: object
                properties:
                  cost:
                    type: integer
                  hours:
                    type: number
                  days:
                    type: integer
                  overlapping_bookings:
                    type: array
                    items:
                      type: object
                  explanation:
                    type: string

  # --- Extras ---
      x-epos: true
  /extras:
    get:
      tags:
      - Extra Endpoints
      summary: List extras
      description: All available add-on items with pricing.
      operationId: listExtras
      parameters:
      - $ref: '#/components/parameters/authHeader'
      responses:
        "200":
          description: Extras list
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/extra'

      x-epos: true
  /extras/{id}:
    get:
      tags:
      - Extra Endpoints
      summary: Show extra
      description: Single extra with stock and pricing detail.
      operationId: showExtra
      parameters:
      - $ref: '#/components/parameters/authHeader'
      - name: id
        in: path
        required: true
        schema:
          type: integer
      responses:
        "200":
          description: Extra detail
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/extra'

  # --- Rooms (show) ---
      x-epos: true
  /rooms/{id}:
    get:
      tags:
      - Room Endpoints
      summary: Show room
      description: Room detail with images, pricing, and capacity.
      operationId: showRoom
      parameters:
      - $ref: '#/components/parameters/authHeader'
      - name: id
        in: path
        required: true
        schema:
          type: integer
      responses:
        "200":
          description: Room detail
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/room'

  # --- Terminals ---
  /notifications:
    get:
      tags:
      - Notification Endpoints
      summary: List notifications
      description: Recent notifications from the last 7 days.
      operationId: listNotifications
      parameters:
      - $ref: '#/components/parameters/authHeader'
      responses:
        "200":
          description: Notifications
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/notification'

      x-epos: true
  /notifications/{id}/read:
    post:
      tags:
      - Notification Endpoints
      summary: Mark as read
      operationId: markNotificationRead
      parameters:
      - $ref: '#/components/parameters/authHeader'
      - name: id
        in: path
        required: true
        schema:
          type: integer
      responses:
        "200":
          description: Marked as read
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/status_response'

      x-epos: true
  /notifications/{id}/archive:
    post:
      tags:
      - Notification Endpoints
      summary: Archive notification
      operationId: archiveNotification
      parameters:
      - $ref: '#/components/parameters/authHeader'
      - name: id
        in: path
        required: true
        schema:
          type: integer
      responses:
        "200":
          description: Archived
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/status_response'

      x-epos: true
  /notifications/clear:
    post:
      tags:
      - Notification Endpoints
      summary: Clear all notifications
      operationId: clearNotifications
      parameters:
      - $ref: '#/components/parameters/authHeader'
      responses:
        "200":
          description: All notifications cleared
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/status_response'

  # --- Blocked Times ---
      x-epos: true
  /blocked_times:
    get:
      tags:
      - Blocked Time Endpoints
      summary: List blocked times
      description: Get all future schedule blocks.
      operationId: listBlockedTimes
      parameters:
      - $ref: '#/components/parameters/authHeader'
      responses:
        "200":
          description: Blocked times
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/blocked_time'
      x-epos: true
    post:
      tags:
      - Blocked Time Endpoints
      summary: Create blocked time
      description: Block a room/time slot for maintenance, private events, etc.
      operationId: createBlockedTime
      parameters:
      - $ref: '#/components/parameters/authHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                blocked_time:
                  type: object
                  properties:
                    room_id:
                      type: integer
                    start_at:
                      type: string
                      format: date-time
                    end_at:
                      type: string
                      format: date-time
                    reason:
                      type: string
                  required:
                  - room_id
                  - start_at
                  - end_at
      responses:
        "201":
          description: Blocked time created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/blocked_time'

      x-epos: true
  /blocked_times/{id}:
    patch:
      tags:
      - Blocked Time Endpoints
      summary: Update blocked time
      operationId: updateBlockedTime
      parameters:
      - $ref: '#/components/parameters/authHeader'
      - name: id
        in: path
        required: true
        schema:
          type: integer
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                blocked_time:
                  type: object
                  properties:
                    room_id:
                      type: integer
                    start_at:
                      type: string
                      format: date-time
                    end_at:
                      type: string
                      format: date-time
                    reason:
                      type: string
      responses:
        "200":
          description: Blocked time updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/blocked_time'
      x-epos: true
    delete:
      tags:
      - Blocked Time Endpoints
      summary: Delete blocked time
      operationId: deleteBlockedTime
      parameters:
      - $ref: '#/components/parameters/authHeader'
      - name: id
        in: path
        required: true
        schema:
          type: integer
      responses:
        "200":
          description: Blocked time deleted
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/status_response'

  # --- Reporting ---
      x-epos: true
  /recent_transactions:
    get:
      tags:
      - Reporting Endpoints
      summary: Recent transactions
      description: Today's and yesterday's transaction totals by payment method (Stripe, Square, manual 
        cash/card/other).
      operationId: recentTransactions
      parameters:
      - $ref: '#/components/parameters/authHeader'
      responses:
        "200":
          description: Transaction summaries
          content:
            application/json:
              schema:
                type: object
                properties:
                  today:
                    $ref: '#/components/schemas/transaction_summary'
                  yesterday:
                    $ref: '#/components/schemas/transaction_summary'

      x-epos: true
  /money_ledger:
    get:
      tags:
      - Reporting Endpoints
      summary: Money ledger
      description: Searchable transaction history with pagination.
      operationId: moneyLedger
      parameters:
      - $ref: '#/components/parameters/authHeader'
      - name: q
        in: query
        schema:
          type: string
        description: Search query (source, transaction ID, booking code, customer name/email)
      - $ref: '#/components/parameters/page'
      - $ref: '#/components/parameters/perPage'
      responses:
        "200":
          description: Money logs
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/money_ledger_entry'

      x-epos: true
  /bookings/{code}/credit_notes:
    post:
      tags:
      - Booking Endpoints
      x-epos: true
      summary: Create credit note
      description: Apply a manual credit note to a booking (cash, card, or other payment).
      operationId: createCreditNote
      parameters:
      - $ref: '#/components/parameters/authHeader'
      - $ref: '#/components/parameters/bookingCode'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                price:
                  type: integer
                  description: Amount in minor currency units (pence/cents). Must be an integer.
                description:
                  type: string
                manual_type:
                  type: string
                  enum:
                  - cash
                  - card
                  - other
              required:
              - price
      responses:
        '200':
          description: Credit note created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/status_response'
  /bookings/{code}/credit_notes/{id}:
    delete:
      tags:
      - Booking Endpoints
      x-epos: true
      summary: Delete credit note
      description: Remove a credit note from a booking.
      operationId: deleteCreditNote
      parameters:
      - $ref: '#/components/parameters/authHeader'
      - $ref: '#/components/parameters/bookingCode'
      - name: id
        in: path
        required: true
        schema:
          type: integer
        description: Credit note (MoneyLog) ID
      responses:
        '200':
          description: Credit note removed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/status_response'
        '404':
          description: Credit note or booking not found
x-webhooks:
  booking.created:
    get:
      summary: booking.created
      description: Made when a Jammed booking is made by a customer online, or made by an admin user. Use the taken_by 
        parameter to distinguish. When taken online, taken_by=online - otherwise it is the name of the admin user that 
        took the booking.
      tags:
      - Booking Webhooks
      responses:
        "200":
          description: Response
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/booking"
              example:
                "$ref": "#/components/examples/booking_example/value"
  booking.updated:
    get:
      summary: booking.updated
      description: Triggered when a booking is updated by a staff member
      tags:
      - Booking Webhooks
      responses:
        "200":
          description: Response
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/booking"
              example:
                "$ref": "#/components/examples/booking_example/value"
  booking.cancelled:
    get:
      summary: booking.cancelled
      description: Triggered when a booking is cancelled by a customer or by a staff member. Cancellations will free up 
        this time to other customers wishing to book the same time.
      tags:
      - Booking Webhooks
      responses:
        "200":
          description: Response
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/booking"
              example:
                "$ref": "#/components/examples/booking_example/value"
  booking.reminder:
    get:
      summary: booking.reminder
      description: Booking reminders in Jammed are customisable, but by default are triggered 24 hours prior to the 
        start of the booking. An email is sent to the user, but this event exists so you can also hook in other 
        integrations at this point
      tags:
      - Booking Webhooks
      responses:
        "200":
          description: Response
          content:
            application/json:
              example:
                "$ref": "#/components/examples/booking_example/value"
  booking.one_hour_before:
    get:
      summary: booking.one_hour_before
      description: Triggered 1 hour before the booking starts
      tags:
      - Booking Webhooks
      responses:
        "200":
          description: Response
          content:
            application/json:
              example:
                "$ref": "#/components/examples/booking_example/value"
  booking.started:
    get:
      summary: booking.started
      description: Triggered when the booking starts
      tags:
      - Booking Webhooks
      responses:
        "200":
          description: Response
          content:
            application/json:
              example:
                "$ref": "#/components/examples/booking_example/value"
  booking.finished:
    get:
      summary: booking.finished
      description: Triggered when the booking finishes
      tags:
      - Booking Webhooks
      responses:
        "200":
          description: Response
          content:
            application/json:
              example:
                "$ref": "#/components/examples/booking_example/value"
  booking.abandoned:
    get:
      summary: booking.abandoned
      description: Triggered when a customer abandons their booking during the checkout process and does not complete 
        the booking. This booking is automatically cancelled by Jammed, but this hook allows you to connect a custom 
        workflow to this event.
      tags:
      - Booking Webhooks
      responses:
        "200":
          description: Response
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/booking"
              example:
                "$ref": "#/components/examples/booking_example/value"
  booking.proceeding_to_payment:
    get:
      summary: booking.proceeding_to_payment
      description: Triggered as soon as a booking that has to be paid for is sent to the hosted checkout page. It 
        fires immediately and does not wait for the payment to be taken, so pair it with booking.payment to find out 
        whether the checkout was completed. Bookings that skip checkout, such as free bookings, unpaid 
        reservations and pay on arrival, never send this event.
      tags:
      - Booking Webhooks
      responses:
        "200":
          description: Response
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/booking"
              example:
                "$ref": "#/components/examples/booking_example/value"
  group.created:
    get:
      summary: group.created
      description: Trigger when a group is made on Jammed (also known as bands). It can be manually made in the admin 
        interface, or made at booking time if the group is new
      tags:
      - Group Webhooks
      responses:
        "200":
          description: Response
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/group"
              example:
                "$ref": "#/components/examples/group_example/value"
  group.updated:
    get:
      summary: group.updated
      description: Trigger when a group is updated on Jammed (also known as bands)
      tags:
      - Group Webhooks
      responses:
        "200":
          description: Response
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/group"
              example:
                "$ref": "#/components/examples/group_example/value"
  group.deleted:
    get:
      summary: group.deleted
      description: Trigger when a group is deleted on Jammed (also known as bands)
      tags:
      - Group Webhooks
      responses:
        "200":
          description: Response
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/group"
              example:
                "$ref": "#/components/examples/group_example/value"
  customer.created:
    get:
      summary: customer.created
      description: Trigger when a customer is made on Jammed. It can be manually made in the admin interface, or made at
        booking time if the customer is new
      tags:
      - Customer Webhooks
      responses:
        "200":
          description: Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/customer"
              example:
                $ref: "#/components/examples/customer_example/value"
  customer.updated:
    get:
      summary: customer.updated
      description: Trigger when a customer is updated on Jammed
      tags:
      - Customer Webhooks
      responses:
        "200":
          description: Response
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/customer"
              example:
                "$ref": "#/components/examples/customer_example/value"
  customer.deleted:
    get:
      summary: customer.deleted
      description: Trigger when a customer is deleted on Jammed
      tags:
      - Customer Webhooks
      responses:
        "200":
          description: Response
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/customer"
              example:
                "$ref": "#/components/examples/customer_example/value"

  # ===== ePOS ENDPOINTS =====

  # --- Dashboard ---
components:
  schemas:
    booking_history_entry:
      type: object
      title: Booking history entry
      properties:
        title:
          type: string
          description: Short description of the event
        timestamp:
          type: integer
          description: Unix epoch timestamp of the event
        description:
          type: string
          nullable: true
          description: Detailed description of the event
      required:
      - title
      - timestamp
    customer_create_body:
      type: object
      title: Customer info
      properties:
        name:
          type: string
          title: Name
          description: "The customers given full name"
        email:
          type: string
          title: Email
          description: "The customers email address"
        mobile:
          type: string
          title: Mobile
          description: "The customers mobile phone number"
        reminders_opt_in:
          type: boolean
          title: Reminders opt-in
          description: "Whether this customer wants to receive reminders"
        mailing_list_opt_in:
          type: boolean
          title: Mailing list opt-in
          description: "Whether this customer wants to be on the mailing list"
      required:
      - name
      - email
    customer_create_actions:
      type: object
      title: Customer creation actions - optional
      properties:
        invite:
          type: boolean
          title: Invite
          description: Invite this customer to set a password on Jammed. An email will be sent to them and the invite 
            code will last 2 days

    customer_create:
      type: object
      description: ""
      properties:
        customer:
          "$ref": "#/components/schemas/customer_create_body"
        actions:
          "$ref": "#/components/schemas/customer_create_actions"

    short_room:
      type: object
      title: Basic room info
      properties:
        name:
          type: string
          title: Name
          description: "The name of the room"
        code:
          type: string
          title: Code
          description: "The code of the room - used internally by Jammed"
      required:
      - name
      - code
      additionalProperties: false
    short_extra:
      type: object
      title: Basic extra info
      properties:
        name:
          type: string
          title: Name
          description: "The name of the extra"
        category:
          type: string
          title: Category
          description: "The category of the extra"
      required:
      - name
      additionalProperties: false
    short_customer:
      type: object
      title: Basic customer info
      description: ""
      properties:
        id:
          type: integer
          title: ID
          description: "Use to identify the customer in other API calls"
        name:
          type: string
          title: Name
          description: "The customers given full name"
          properties: {}
        email:
          type: string
          title: Email
          description: "The customers email address"
          properties: {}
        mobile:
          type: string
          title: Mobile
          description: "The customers mobile phone number"
          properties: {}
        avatar:
          type: string
          title: Avatar
          description: "The customers avatar image URL, if any"
          properties: {}
        reminders_opt_in:
          type: boolean
          title: Reminders opt-in
          description: "Whether this customer wants to receive reminders"
        mailing_list_opt_in:
          type: boolean
          title: Mailing list opt-in
          description: "Whether this customer wants to be on the mailing list"
      required:
      - name
      - email
      additionalProperties: false
    short_group:
      type: object
      title: Basic group info
      description: ""
      properties:
        name:
          type: string
          title: Name
          description: "The name of the group"
        code:
          type: string
          title: Code
          description: "The code of the group - used internally by Jammed"
        number_of_members:
          type: integer
          title: NumberOfMembers
          description: "The number of members in the group (only present in full group responses)"
        number:
          type: integer
          title: Number
          description: "The number of members in the group (present in customer band responses)"
          nullable: true
        booking_count:
          type: integer
          title: BookingCount
          description: "The number of bookings for this group"
      required:
      - name
      - code
      additionalProperties: false
    booking_custom_answer:
      type: object
      properties:
        question_id:
          type: integer
          title: QuestionId
          description: "The ID of the question that was asked"
        name:
          type: string
          title: Name
          description: "The name of the question"
        response:
          type: string
          title: Response
          description: "The response/answer to the question"
    booking:
      type: object
      properties:
        group:
          "$ref": "#/components/schemas/short_group"
        customer:
          "$ref": "#/components/schemas/short_customer"
        room:
          "$ref": "#/components/schemas/short_room"
        extras:
          type: array
          title: Extras
          description: "The addons/extras added to the booking"
          items:
            "$ref": "#/components/schemas/short_extra"
        custom_answers:
          type: array
          title: Custom Answers
          description: "The responses to custom questions added to the booking"
          items:
            "$ref": "#/components/schemas/booking_custom_answer"
        source_data:
          type: object
          title: Source Data
          description: "The source metadata of the booking, how and where the booking was made"
          properties:
            source:
              type: string
              title: Source
              description: "The source of the booking: app, online, regular_booking etc."
            platform:
              type: string
              title: Platform
              description: "The platform where the booking was made: booking_form, dashboard, staff_section, calendar, app
                etc."
            referrer:
              type: string
              title: Referrer
              description: "The referrer - the URL of the booking page"
        code:
          type: string
          title: Code
          description: "The code of the booking - seen by customers on the booking page"
        id:
          type: string
          title: id
          description: "Same as code"
        recording:
          type: boolean
          title: Recording
          description: "Whether the room has been marked as a recording"
        regular:
          type: boolean
          title: Regular
          description: "Whether the booking is part of a regular booking"
        online_booking:
          type: boolean
          title: OnlineBooking
          description: "Whether the booking was made online by the customer"
        created_at:
          type: integer
          title: CreatedAt
          description: "Unix timestamp from epoch"
        updated_at:
          type: integer
          title: UpdatedAt
          description: "Unix timestamp from epoch"
        approved_at:
          type: integer
          title: ApprovedAt
          description: "Unix timestamp from epoch - when the booking was approved (if approved)"
        rejected_at:
          type: integer
          title: RejectedAt
          description: "Unix timestamp from epoch - when the booking was rejected (if rejected)"
        price:
          type: integer
          title: Price
          description: "The price of the booking time only in cents/pence/units"
        price_currency:
          type: string
          title: PriceCurrency
          description: "The currency of the price"
        title:
          type: string
          title: Title
          description: "The title of the booking - this is generated from the customer and group names"
        start_at:
          type: integer
          title: StartAt
          description: "Unix timestamp from epoch"
        end_at:
          type: integer
          title: EndAt
          description: "Unix timestamp from epoch"
        duration:
          type: integer
          title: Duration
          description: "The duration of the booking in seconds"
        duration_hours:
          type: number
          title: DurationHours
          description: "The duration of the booking in hours, e.g. 1pm-2.30pm = `2.5` hours"
        duration_full_hours:
          type: integer
          title: DurationFullHours
          description: "The duration of the booking in hours, rounded up to each full hour, e.g. 1pm-2.30pm = `3` hours"
        activity_name:
          type: string
          title: ActivityName
          description: "The name of the activity the booking is part of - e.g. 'Rehearsal'"
        dates:
          type: array
          title: Dates
          description: "The dates that the booking is part of in local time - given as strings YYYYMMDD format"
          items:
            type: string
        timezone:
          type: string
          title: Timezone
          description: "The timezone of the booking"
        has_amount_due:
          type: boolean
          title: HasAmountDue
          description: "Whether the booking still has an amount due"
        remaining_amount:
          type: integer
          title: RemainingAmount
          description: "The remaining amount due for the booking"
        cancelled:
          type: boolean
          title: Cancelled
          description: "Whether the booking has been cancelled"
        taken_by:
          type: string
          title: TakenBy
          description: "The name of the staff member who took the booking - if null, the booking was made by the customer
            themselves"
    room:
      type: object
      properties:
        id:
          type: integer
          title: id
          description: "Same as code"
        code:
          type: string
          title: Code
          description: "The code of the room - used internally by Jammed"
        name:
          type: string
          title: Name
          description: "The name of the room"
        description:
          type: string
          title: Description
          description: "The description of the room - you can set this in the admin interface"
        space_for:
          type: integer
          title: SpaceFor
          description: "The capacity of the room"
        order:
          type: integer
          title: Order
        twenty_four_hour_booking:
          type: boolean
          title: TwentyFourHourBooking
          description: "Whether the room can be booked 24 hours a day"
        space_type:
          type: string
          title: SpaceType
          description: "The type of space the room is"
        hex_colour:
          type: string
          title: HexColour
          description: "The hex colour of the room on the dashboard"
        active:
          type: boolean
          title: Active
          description: "Whether the room is active for Jammed online bookings"
        prices_set:
          type: integer
          title: PricesSet
          description: "The count of prices set for the room"
        price_to:
          type: string
          title: PriceTo
          description: "The maximum price for the room"
        price_from:
          type: string
          title: PriceFrom
          description: "The minimum price for the room"
        images:
          type: array
          title: Images
          description: "The images of the room"
          items:
            type: object
            properties:
              original:
                type: string
                title: original
                description: "Image URL"
        data:
          type: object
          title: Data
          description: "The hidden data of the room - can be set and used by back-end systems"

    customer:
      type: object
      properties:
        id:
          type: integer
          title: ID
          description: "Use to identify the customer in other API calls"
        name:
          type: string
          title: Name
          description: "The customers given full name"
        email:
          type: string
          title: Email
          description: "The customers email address"
        mobile:
          type: string
          title: Mobile
          description: "The customers mobile phone number"
        avatar:
          type: string
          title: Avatar
          description: "The customers avatar image URL, if any"
        credit_balance:
          type: integer
          title: CreditBalance
          description: "The customers current credit balance in cents/pence/units\n\nThis cannot be changed via the API"
        status:
          $ref: "#/components/schemas/customer_statuses"
        reminders_opt_in:
          type: boolean
          title: Reminders opt-in
          description: "Whether this customer wants to receive reminders"
        mailing_list_opt_in:
          type: boolean
          title: Mailing list opt-in
          description: "Whether this customer wants to be on the mailing list"
        signed_up:
          type: boolean
          title: SignedUp
          description: "Whether the customer has signed up"
        signed_up_at:
          type: integer
          title: SignedUpAt
          description: "Unix timestamp from epoch - when the customer signed up"
        regular_bookings_count:
          title: RegularBookingsCount
          description: 'The number of regular bookings the customer has made. Note: may return boolean `false` instead of `0` when the customer has no regular bookings.'
          oneOf:
          - type: integer
          - type: boolean
        bookings_count:
          type: integer
          title: BookingsCount
          description: "The number of bookings the customer has made"
        created_at:
          type: integer
          title: CreatedAt
          description: "Unix timestamp from epoch - when the customer was created"
        updated_at:
          type: integer
          title: UpdatedAt
          description: "Unix timestamp from epoch - when the customer was last updated"
        last_booking_at:
          type: integer
          title: LastBookingAt
          description: "Unix timestamp from epoch - when the customer last booked"
        regular_bookings:
          type: array
          title: RegularBookings
          description: "The customer's regular bookings"
          items:
            type: object
            title: ""
            description: ""
            properties:
              name:
                type: string
                title: Name
                description: ""
              id:
                type: integer
                title: Id
                description: ""
        bands:
          type: array
          title: Bands or Groups
          description: ""
          items:
            "$ref": "#/components/schemas/short_group"
        address:
          "$ref": "#/components/schemas/customer_billing_address"
    customer_billing_address:
      type: object
      title: Customer billing address
      description: |
        The customer's billing address, as captured during checkout.

        This is `null` when the customer has not provided a billing address.
      properties:
        company:
          type: string
          nullable: true
          title: Company
          description: "The company name on the billing address, if any"
        address_1:
          type: string
          nullable: true
          title: Address line 1
          description: "The first line of the billing address"
        address_2:
          type: string
          nullable: true
          title: Address line 2
          description: "The second line of the billing address, if any"
        town:
          type: string
          nullable: true
          title: Town/City
          description: "The town or city of the billing address"
        state:
          type: string
          nullable: true
          title: State/County
          description: "The state, county or region of the billing address, if any"
        postcode:
          type: string
          nullable: true
          title: Postcode/ZIP
          description: "The postal or ZIP code of the billing address"
        country:
          type: string
          nullable: true
          title: Country
          description: "The ISO 3166-1 alpha-2 country code of the billing address, e.g. `GB` or `US`"
        formatted_address:
          type: string
          nullable: true
          title: Formatted address
          description: "The full billing address as a single comma-separated string, with the country name expanded (e.g. `United States of America`)"
    group:
      type: object
      properties:
        name:
          type: string
          title: Name
          description: "The name of the group"
        group_type:
          type: string
          title: GroupType
          description: "The type of the group - e.g. band"
        number_of_members:
          type: integer
          title: NumberOfMembers
          description: "The number of members in the group"
        customer_count:
          type: integer
          title: CustomerCount
          description: "The number of customers assigned to the group"
        regular_bookings_count:
          type: integer
          title: RegularBookingsCount
          description: "The number of regular bookings the group has"
        bookings_count:
          type: integer
          title: BookingsCount
          description: "The number of bookings the group has"
        created_at:
          type: integer
          title: CreatedAt
          description: "Unix timestamp from epoch - when the group was created"
        updated_at:
          type: integer
          title: UpdatedAt
          description: "Unix timestamp from epoch - when the group was last updated"
        last_booking_at:
          type: integer
          title: LastBookingAt
          description: "Unix timestamp from epoch - when the group last booked"
        customers:
          type: array
          title: Customers
          description: "The customers assigned to the group"
          items:
            "$ref": "#/components/schemas/short_customer"
        regular_bookings:
          type: array
          title: RegularBookings
          description: "The group's regular bookings"
          items:
            type: object
            title: ""
            description: ""
            properties:
              name:
                type: string
                title: Name
                description: "The name of the regular booking"
              id:
                type: integer
                title: Id
                description: "The ID of the regular booking"

    promocode:
      type: object
      properties:
        id:
          type: integer
        code:
          type: string
          description: |
            The code the customer can use to redeem the promocode, they enter this code when booking.

            The code is case-insensitive and must be unique
        minimum_booking_value:
          type: integer
          description: |
            The minimum booking value the Promocode can be used for.

            If attempted for a booking below this value, the Coupon will not be applied
        first_time_use_only:
          type: boolean
          description: |
            If set to true, the Promocode will only be usable once per customer - applies to logged in customers only
    coupon:
      type: object
      properties:
        id:
          type: integer
        name:
          type: string
          description: A label for the coupon - this is seen by your customers
        discount_type:
          type: string
          description: |
            The type of discount the coupon gives

            Either: 'fixed_amount' or 'percentage'
        amount:
          type: integer
          description: |
            The integer decimal amount, or percentage that the coupon will deduct.

            For `discount_type=percentage`, `10` would mean 10%

            For `discount_type=fixed_amount`, `10` would mean $0.10 or £0.10 etc
        all_new_customers:
          type: boolean
          description: Set to true if you want the coupon to apply to all new customers automatically
        expires_at:
          type: string
          description: The ISO8601 timestamp when the coupon expires. If not set, the coupon will never expire
        created_at:
          type: string
          description: The ISO8601 timestamp when the coupon was created
        fully_used:
          type: boolean
          description: Is set as true if the coupon has been used up, and cannot be used anymore
        expired:
          type: boolean
          description: Is set as true if the coupon has expired
        status:
          type: string
          description: |
            The status of the coupon: `active`, `deleted`, `expired`, `fully used`
        duration:
          type: string
          description: |
            The duration of the coupon: `once`, `forever`

            If `forever`, the coupon can be used over and over by the same customer

            If `once`, the coupon can only be used once by the same customer
        total_number_of_uses:
          type: integer
          description: The total number of times the coupon can be used
        number_of_times_used:
          type: integer
          description: The number of times the coupon has been used
        promocodes:
          type: array
          description: |
            The promocodes that are linked to this coupon.
          items:
            type: object
            properties:
              coupon:
                "$ref": "#/components/schemas/promocode"
    coupon_create:
      type: object
      description: ""
      properties:
        coupon:
          "$ref": "#/components/schemas/coupon_create_body"
    coupon_create_body:
      type: object
      title: Coupon info
      properties:
        name:
          type: string
          description: A label for the coupon - this is seen by your customers
        amount:
          type: string
          description: |
            The integer decimal amount, or percentage that the coupon will deduct.

            For `discount_type=percentage`, `10` would mean 10%

            For `discount_type=fixed_amount`, `10` would mean $0.10 or £0.10 etc
        duration:
          type: string
          description: |
            The duration of the coupon: `once`, `forever`

            If `forever`, the coupon can be used over and over by the same customer

            If `once`, the coupon can only be used once by the same customer
        discount_type:
          type: string
          description: |
            The type of discount the coupon gives

            Either: 'fixed_amount' or 'percentage'
        all_new_customers:
          type: boolean
          description: Set to true if you want the coupon to apply to all new customers automatically
        total_number_of_uses:
          type: integer
          description: The total number of times the coupon can be used
        expires_at:
          type: string
          description: The ISO8601 timestamp when the coupon expires. If not set, the coupon will never expire
      required:
      - name
      - duration
      - amount
      - discount_type

    promocode_create:
      type: object
      description: ""
      properties:
        promocode:
          "$ref": "#/components/schemas/promocode_create_body"
    promocode_create_body:
      type: object
      title: Promocode info
      properties:
        code:
          type: string
          description: |
            The code the customer can use to redeem the promocode, they enter this code when booking.

            The code is case-insensitive and must be unique
        minimum_booking_value:
          type: integer
          description: |
            The minimum booking value the Promocode can be used for.

            If attempted for a booking below this value, the Coupon will not be applied

    coupon_assign_to_customer:
      type: object
      title: Coupon assignment
      properties:
        customer_id:
          type: integer
          description: |
            The Customer's ID to assign the Coupon to


    booking_create:
      type: object
      description: ""
      properties:
        booking:
          $ref: '#/components/schemas/booking_create_body'

    booking_create_body:
      type: object
      title: Booking info
      properties:
        booking_details:
          $ref: '#/components/schemas/booking_create_details'
        customer_id:
          type: integer
          title: Customer ID
          description: |
            The ID of an existing customer to make the booking for.

            If provided, `customer_details` is not required.
        customer_details:
          $ref: '#/components/schemas/booking_create_customer_details'
        band_details:
          $ref: '#/components/schemas/booking_create_band_details'
        band_code:
          type: string
          title: Band code
          description: "The code of an existing band/group to associate with the booking"
        extra_ids:
          type: array
          title: Extra IDs
          description: "Array of extra IDs to add to the booking"
          items:
            type: integer
        is_recording:
          type: boolean
          title: Is recording
          description: "Whether this booking is a recording session"
        number_expected:
          type: integer
          title: Number expected
          description: "The number of people expected for the booking"
        additional_notes:
          type: string
          title: Additional notes
          description: "Any additional notes for the booking"
        send_email:
          type: boolean
          title: Send email
          description: "Whether to send a confirmation email to the customer. Defaults to true"
      required:
      - booking_details

    booking_create_details:
      type: object
      title: Booking time details
      properties:
        start_at:
          type: string
          title: Start at
          description: "The start time of the booking in ISO8601 format"
          example: "2026-03-01T10:00:00Z"
        end_at:
          type: string
          title: End at
          description: "The end time of the booking in ISO8601 format"
          example: "2026-03-01T14:00:00Z"
        room_id:
          type: integer
          title: Room ID
          description: "The ID of the room to book"
        price:
          type: integer
          title: Price
          description: "The price of the booking in cents/pence/units"
          example: 6000
      required:
      - start_at
      - end_at
      - room_id
      - price

    booking_create_customer_details:
      type: object
      title: New customer details
      description: "Provide these if creating a booking for a new customer (instead of using customer_id)"
      properties:
        name:
          type: string
          title: Name
          description: "The customer's full name"
        email:
          type: string
          title: Email
          description: "The customer's email address"
        mobile:
          type: string
          title: Mobile
          description: "The customer's mobile phone number"
      required:
      - name
      - email

    booking_create_band_details:
      type: object
      title: Band/group details
      description: "Provide to create or associate a band/group with the booking"
      properties:
        name:
          type: string
          title: Name
          description: "The name of the band/group"
      required:
      - name

    unprocessable_error:
      type: object
      properties:
        status:
          type: string
          description: Error status
          example: "error"
        message:
          type: string
          description: Error message
          example: "The model parameters you provided are invalid"

    customer_statuses:
      type: string
      title: CustomerStatus
      enum:
      - unregistered
      - invited
      - registered
      - banned
      - behind_payment
      description: |
        The status of the customer

        `unregistered`: The customer has booked but not registered an account
        `invited`: The customer has been invited to register an account
        `registered`: The customer has registered an account
        `banned`: The customer has been marked as banned from booking (and will not be able to book)
        `behind_payment`: The customer has been marked as behind on payments (and will not be able to book)

    booking_update:
      type: object
      description: Booking update parameters
      properties:
        booking:
          type: object
          properties:
            start_at:
              type: string
              format: date-time
              description: Start time of the booking
            end_at:
              type: string
              format: date-time
              description: End time of the booking
            room_id:
              type: integer
              description: ID of the room for the booking
            price:
              type: integer
              description: Price of the booking in cents/pence
            additional_notes:
              type: string
              description: Additional notes for the booking
            studio_notes:
              type: string
              description: Internal studio notes
            number_expected:
              type: integer
              description: Expected number of attendees

    transaction_create:
      type: object
      description: Transaction creation parameters
      properties:
        transaction:
          type: object
          properties:
            amount:
              type: integer
              description: Transaction amount in cents/pence (must be positive)
            description:
              type: string
              description: Description of the transaction
            manual_type:
              type: string
              description: Type of manual transaction
              enum: [card, cash, bank_transfer, other]
              default: card
          required:
          - amount
          - description

    studio_notes_update:
      type: object
      description: Studio notes update parameters
      properties:
        booking:
          type: object
          properties:
            studio_notes:
              type: string
              description: Internal studio notes (visible only to studio staff)

    money_log:
      type: object
      description: A financial transaction record
      properties:
        id:
          type: integer
          description: Unique identifier for the money log entry
        source:
          type: string
          description: The source/type of the transaction
        amount:
          type: integer
          description: Transaction amount in cents/pence
        currency_code:
          type: string
          description: Currency code (e.g., GBP, USD, EUR)
        created_at:
          type: integer
          description: Unix timestamp when the transaction was created
        display_amount:
          type: string
          description: Formatted amount with currency symbol
        refund:
          type: boolean
          description: Whether this transaction is a refund
        success:
          type: boolean
          description: Whether the transaction was successful
        title:
          type: string
          description: Display title for the transaction
        description:
          type: string
          description: Description of the transaction (for credit notes)
          nullable: true
        manual_type:
          type: string
          description: Type of manual transaction (for credit notes)
          nullable: true

    notification:
      type: object
      title: Notification
      properties:
        id:
          type: integer
        title:
          type: string
        body:
          type: string
        severity:
          type: string
          enum: [normal, high]
        label:
          type: string
        read:
          type: boolean
        created_at:
          type: integer
          description: Unix timestamp
        read_at:
          type: integer
          description: Unix timestamp (null if unread)

          nullable: true
    blocked_time:
      type: object
      title: Blocked Time
      properties:
        id:
          type: integer
        room_id:
          type: integer
        reason:
          type: string
        start_at:
          type: integer
          description: Unix timestamp
        end_at:
          type: integer
          description: Unix timestamp
        start_time:
          type: string
          description: "Format: YYYY-MM-DD HH:MM"
        end_time:
          type: string
          description: "Format: YYYY-MM-DD HH:MM"
        room:
          type: object
          properties:
            id:
              type: integer
            name:
              type: string
            code:
              type: string

    extra:
      type: object
      title: Extra
      properties:
        id:
          type: integer
        title:
          type: string
        type:
          type: string
        code:
          type: string
        category:
          type: string
        available:
          type: boolean
        can_add_multiple_quantity:
          type: boolean
        track_stock:
          type: boolean
        price_per_use:
          type: integer
        price_per_hour:
          type: integer
        price_per_daily:
          type: integer
        price_per_weekly:
          type: integer

    dashboard_booking:
      type: object
      title: DashboardBooking
      description: 'A booking as returned by the dashboard endpoint. Note: this format differs significantly from the full booking schema.'
      properties:
        id:
          type: integer
        code:
          type: string
        name:
          type: string
          description: Customer name
        title:
          type: string
          description: Display title, e.g. "John Doe (The Beatles)"
        band_name:
          type: string
          description: Band/group name, empty string if none
        is_recording:
          type: boolean
          nullable: true
        session_name:
          type: string
          nullable: true
        block_colour_override:
          type: string
          nullable: true
          description: Hex colour override for the booking block
        studio_notes:
          type: string
          nullable: true
        additional_notes:
          type: string
          nullable: true
        number_expected:
          type: integer
          nullable: true
        start:
          type: string
          description: 'Start datetime as "YYYY-MM-DD HH:mm"'
          example: "2026-03-24 11:41"
        end:
          type: string
          description: 'End datetime as "YYYY-MM-DD HH:mm"'
          example: "2026-03-24 12:41"
        split:
          type: string
          description: Room code — use to match against rooms[].code
        room_name:
          type: string
          description: Room display name
        has_amount_due:
          type: boolean
        amount_due:
          type: string
          description: 'Formatted currency string e.g. "£1.00"'
        payments_recieved:
          type: boolean
          description: 'Whether any payments have been received (note: API uses this spelling)'
        regular:
          type: boolean
        incomplete:
          type: boolean
        show_studio_notes:
          type: boolean
        extras_summary:
          type: array
          items:
            type: string
          description: Array of extra item descriptions (empty array if none)
        link:
          type: string
          description: 'Admin link path e.g. "/admin/bookings/R9XT9J1JFX"'
        type:
          type: string
          description: '"booking" or "blocked"'
        class:
          type: string
          description: 'CSS class for the booking block e.g. "room-l3j5nwp8"'

    credit_movement:
      type: object
      title: Credit Movement
      properties:
        id:
          type: integer
        amount:
          type: integer
        source:
          type: string
        created_at:
          type: integer
        booking:
          type: object
          properties:
            id:
              type: integer
            code:
              type: string
        made_by:
          type: string

    money_ledger_entry:
      type: object
      title: Money Ledger Entry
      properties:
        id:
          type: integer
        source:
          type: string
        amount:
          type: integer
        currency_code:
          type: string
        transaction_id:
          type: string
        created_at:
          type: integer
        source_data:
          type: object
        booking:
          type: object
          properties:
            code:
              type: string
            name:
              type: string
        customer:
          type: object
          properties:
            id:
              type: integer
            name:
              type: string

    transaction_summary:
      type: object
      title: Transaction Summary
      properties:
        date:
          type: string
        stripe:
          type: integer
        stripe_refunds:
          type: integer
        square:
          type: integer
        square_refunds:
          type: integer
        square_terminal:
          type: integer
        square_terminal_refunds:
          type: integer
        manual_total:
          type: integer
        manual_cash:
          type: integer
        manual_card:
          type: integer
        manual_other:
          type: integer
        total_taken:
          type: integer
        total_refunded:
          type: integer
        total:
          type: integer

    status_response:
      type: object
      title: Status Response
      properties:
        status:
          type: string
          enum: [ok, error]
        message:
          type: string

  examples:
    booking_example:
      value:
        name: Rush
        group_type: band
        number_of_members: 3
        customers:
        - name: Geddy Lee
          email: geddy22@yahoo.ca
        - name: Neil Peart
          email: theprofessor2112@gmail.com
          mobile: "+447584948502"
        - name: Alex Lifeson
          email: alex-lifeson@me.com
          mobile: "+442072983029"
        customer_count: 3
        regular_bookings:
        - name: Monday 8pm Red Room
          id: 503
        regular_bookings_count: 1
        bookings_count: 61
        created_at: 1646247874
        updated_at: 1646247874

    customer_example:
      value:
        email: Geddy Lee
        avatar: https://www.gravatar.com/avatar/02e414a53149473ddd8ed541fac40b6f?d=mp&s=45
        status: unregistered
        signed_up: false
        bands:
        - name: Rush
          code: DI3EK30SP
          number: 3
          booking_count: 9
        regular_bookings_count: 1
        regular_bookings:
        - name: Geddy Lee (Rush)
          id: 2112
        bookings_count: 9
        last_booking_at: 1643799530
        created_at: 1643799530
        updated_at: 1643799530
        address:
          company: Rush
          address_1: 2112 Tom Sawyer Avenue
          address_2: Studio B
          town: Toronto
          state: ON
          postcode: M5V 2T6
          country: CA
          formatted_address: 2112 Tom Sawyer Avenue, Studio B, Toronto, ON, M5V 2T6, Canada

    group_example:
      value:
        name: Rush
        group_type: band
        number_of_members: 3
        customers:
        - name: Geddy Lee
          email: geddy22@yahoo.ca
          mobile: "+447584948502"
        - name: Neil Peart
          email: theprofessor2112@gmail.com
          mobile: "+447584948502"
        - name: Alex Lifeson
          email: alex-lifeson@me.com
          mobile: "+442072983029"
        customer_count: 3
        regular_bookings:
        - name: Geddy Lee (Rush)
          id: 2112
        regular_bookings_count: 1
        bookings_count: 6171
        created_at: 1646247874
        updated_at: 1646247874

    # ePOS schemas
  parameters:
    authHeader:
      name: Authorization
      in: header
      required: true
      schema:
        type: string
      example: "Bearer <api_key>"
    bookingCode:
      name: code
      in: path
      required: true
      schema:
        type: string
      description: Booking code
    page:
      name: page
      in: query
      schema:
        type: integer
        minimum: 1
        default: 1
    perPage:
      name: per_page
      in: query
      schema:
        type: integer
        minimum: 1
        maximum: 100
        default: 25

  securitySchemes:
    apiKey:
      type: apiKey
      name: "Authorization: 'Bearer <api-key>'"
      in: header
      description: |
        Issue a http header with the key 'Authorization' and value 'Bearer api_key'
