openapi: 3.1.0
info:
  title: Provyl — Open PMS API
  version: "1.0"
  summary: Any PMS sends its reservations; rooms, TVs and guests' phones follow.
  description: |
    For a PMS without a built-in connection (Mews and Apaleo have one). The PMS — or an integration platform between —
    sends **the current state of a reservation** whenever it changes: booked, in house, checked out, canceled, no-show.

    - **One address per hotel.** In the console, **Settings → Integrations → PMS → Any PMS (Open PMS API)**, the
      connection shows its **webhook address**. The address is the credential: keep it secret; **New address** replaces it.
    - **State, not events.** Send the whole reservation every time. Sending it twice, or out of order, gives the same result.
      `in_house` checks the guest into the room (the TV greets them, the phone app opens); `checked_out` checks them out;
      a `booked` reservation gets a guest link for before arrival; `canceled` and `no_show` close it.
    - **Rooms by number, room types by name** — exactly as in the console (Settings → Property).
    - Guest names are used only to greet the guest on the TV while in house; the console shows the first letter.
    - Invalid bodies are answered `400` with every problem listed, so the sender can fix them at once.

    Guide with examples: https://pcr-docs.pages.dev/pms-api
servers:
  - url: https://nnilcrwcusuhmauixpaf.supabase.co/functions/v1/integrations/v1
paths:
  /hooks/{key}:
    parameters:
      - name: key
        in: path
        required: true
        description: The 64-character key in the hotel's webhook address (Settings → Integrations → PMS).
        schema: { type: string, pattern: '^[0-9a-f]{64}$' }
    post:
      operationId: sendReservations
      summary: Send one or more reservations
      requestBody:
        required: true
        content:
          application/json:
            schema:
              oneOf:
                - type: object
                  required: [reservations]
                  properties:
                    reservations: { type: array, minItems: 1, maxItems: 100, items: { $ref: '#/components/schemas/Reservation' } }
                - type: object
                  required: [reservation]
                  properties:
                    reservation: { $ref: '#/components/schemas/Reservation' }
            examples:
              checkIn:
                summary: A guest checks in to room 632
                value:
                  reservation:
                    id: R-10045
                    status: in_house
                    room: '632'
                    roomType: Suite
                    arrival: '2026-10-25'
                    departure: '2026-10-27'
                    guest: { name: Jane Doe, language: en }
              booked:
                summary: A new booking (no room yet)
                value:
                  reservation: { id: R-10046, status: booked, roomType: Deluxe, arrival: '2026-11-02', departure: '2026-11-05', guest: { name: Kim Minjun, language: ko } }
              checkOut:
                summary: Check-out
                value:
                  reservation: { id: R-10045, status: checked_out, room: '632', arrival: '2026-10-25', departure: '2026-10-27' }
      responses:
        '200':
          description: Received. The reservations are applied right after the answer; the console's connection log shows each result.
          content:
            application/json:
              schema:
                type: object
                properties:
                  received: { const: true }
                  ignored: { type: boolean, description: true when the hotel has switched the connection off. }
        '400':
          description: The body is not valid — nothing was applied.
          content:
            application/json:
              schema:
                type: object
                properties:
                  received: { const: false }
                  errors:
                    type: array
                    items: { type: string }
                    examples: [['reservations[0].status: one of booked, in_house, checked_out, canceled, no_show', 'reservations[1].room: required when status is in_house']]
        '404': { description: Unknown address (wrong or replaced key). }
        '413': { description: The body is larger than 256 KB. }
    get:
      operationId: checkAddress
      summary: Check the address
      description: Answers 200 when the address is valid — for the PMS's "test connection" button.
      responses:
        '200': { description: The address is valid }
        '404': { description: Unknown address }
components:
  schemas:
    Reservation:
      type: object
      required: [id, status]
      properties:
        id:
          type: string
          description: "The PMS's reservation id — the same id on every update. Letters, digits and . _ : - /"
          maxLength: 120
          examples: [R-10045]
        status:
          enum: [booked, in_house, checked_out, canceled, no_show]
        room:
          type: string
          maxLength: 20
          description: The room number as in the console. Required when status is in_house; optional before arrival.
          examples: ['632']
        roomType:
          type: string
          maxLength: 60
          description: The room type's name as in the console — before arrival the guest link shows that type.
          examples: [Suite]
        arrival: { type: string, format: date, description: 'Hotel-local date, YYYY-MM-DD' }
        departure: { type: string, format: date, description: 'Hotel-local date, YYYY-MM-DD' }
        guest:
          type: object
          properties:
            name: { type: string, maxLength: 120, description: For the TV's greeting while in house. Defaults to Guest. }
            language: { type: string, description: 'A code such as en, de, ko, zh — the TV and guest app use it. Defaults to en.', examples: [en] }
