{
  "openapi": "3.0.0",
  "info": {
    "title": "Hotelumo API",
    "version": "1.0.0"
  },
  "servers": [
    {
      "url": "https://api.hotelumo.com"
    }
  ],
  "components": {
    "securitySchemes": {
      "apiKey": {
        "type": "http",
        "scheme": "basic",
        "description": "HTTP Basic auth carrying only the API key secret: Authorization: Basic base64(<key secret>)."
      },
      "accessToken": {
        "type": "apiKey",
        "in": "header",
        "name": "Authorization",
        "description": "Operator session token issued by the Hotelumo dashboard: Authorization: Token base64(<access token>)."
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "message",
          "status"
        ],
        "properties": {
          "message": {
            "type": "string"
          },
          "status": {
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "details": {
            "type": "object",
            "additionalProperties": true
          }
        }
      },
      "DailyRate": {
        "type": "object",
        "required": [
          "_id"
        ],
        "properties": {
          "_id": {
            "type": "string"
          },
          "hotelId": {
            "type": "string"
          },
          "ratePlanId": {
            "type": "string"
          },
          "date": {
            "type": "string",
            "format": "date"
          },
          "price": {
            "type": "number"
          },
          "availability": {
            "type": "integer",
            "minimum": 0
          },
          "closedToArrival": {
            "type": "boolean"
          },
          "closedToDeparture": {
            "type": "boolean"
          },
          "stopSell": {
            "type": "boolean"
          },
          "creationTime": {
            "type": "string",
            "format": "date-time",
            "readOnly": true
          },
          "lastEditTime": {
            "type": "string",
            "format": "date-time"
          }
        },
        "additionalProperties": true
      },
      "Guest": {
        "type": "object",
        "required": [
          "_id"
        ],
        "properties": {
          "_id": {
            "type": "string"
          },
          "hotelId": {
            "type": "string"
          },
          "firstName": {
            "type": "string"
          },
          "lastName": {
            "type": "string"
          },
          "email": {
            "type": "string",
            "format": "email"
          },
          "phone": {
            "type": "string"
          },
          "language": {
            "type": "string"
          },
          "creationTime": {
            "type": "string",
            "format": "date-time",
            "readOnly": true
          },
          "lastEditTime": {
            "type": "string",
            "format": "date-time"
          }
        },
        "additionalProperties": true
      },
      "RatePlan": {
        "type": "object",
        "required": [
          "_id"
        ],
        "properties": {
          "_id": {
            "type": "string"
          },
          "hotelId": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "basePrice": {
            "type": "number"
          },
          "mealPlan": {
            "type": "string"
          },
          "sellMode": {
            "type": "string"
          },
          "rateMode": {
            "type": "string"
          },
          "isDefault": {
            "type": "boolean"
          },
          "creationTime": {
            "type": "string",
            "format": "date-time",
            "readOnly": true
          },
          "lastEditTime": {
            "type": "string",
            "format": "date-time"
          }
        },
        "additionalProperties": true
      },
      "Reservation": {
        "type": "object",
        "required": [
          "_id"
        ],
        "properties": {
          "_id": {
            "type": "string"
          },
          "hotelId": {
            "type": "string"
          },
          "guestId": {
            "type": "string"
          },
          "checkInDate": {
            "type": "string",
            "format": "date"
          },
          "checkOutDate": {
            "type": "string",
            "format": "date"
          },
          "status": {
            "type": "string"
          },
          "source": {
            "type": "string"
          },
          "rooms": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "creationTime": {
            "type": "string",
            "format": "date-time",
            "readOnly": true
          },
          "lastEditTime": {
            "type": "string",
            "format": "date-time"
          }
        },
        "additionalProperties": true
      },
      "Room": {
        "type": "object",
        "required": [
          "_id"
        ],
        "properties": {
          "_id": {
            "type": "string"
          },
          "hotelId": {
            "type": "string"
          },
          "roomTypeId": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "housekeepingStatus": {
            "type": "string"
          },
          "creationTime": {
            "type": "string",
            "format": "date-time",
            "readOnly": true
          },
          "lastEditTime": {
            "type": "string",
            "format": "date-time"
          }
        },
        "additionalProperties": true
      },
      "RoomType": {
        "type": "object",
        "required": [
          "_id"
        ],
        "properties": {
          "_id": {
            "type": "string"
          },
          "hotelId": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "maxGuests": {
            "type": "integer",
            "minimum": 1
          },
          "photos": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uri"
            }
          },
          "facilities": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "creationTime": {
            "type": "string",
            "format": "date-time",
            "readOnly": true
          },
          "lastEditTime": {
            "type": "string",
            "format": "date-time"
          }
        },
        "additionalProperties": true
      },
      "AvailabilityOffer": {
        "type": "object",
        "required": [
          "roomTypeId"
        ],
        "properties": {
          "roomTypeId": {
            "type": "string"
          },
          "ratePlanId": {
            "type": "string"
          },
          "availableRooms": {
            "type": "integer",
            "minimum": 0
          },
          "totalPrice": {
            "type": "number"
          }
        },
        "additionalProperties": true
      },
      "BookingResult": {
        "type": "object",
        "required": [
          "requestId",
          "status"
        ],
        "properties": {
          "requestId": {
            "type": "string"
          },
          "status": {
            "type": "string"
          }
        },
        "additionalProperties": true
      },
      "DailyRateBatch": {
        "type": "object",
        "required": [
          "values"
        ],
        "properties": {
          "values": {
            "type": "array",
            "minItems": 1,
            "maxItems": 500,
            "items": {
              "$ref": "#/components/schemas/DailyRate"
            }
          }
        },
        "additionalProperties": false
      },
      "WebhookSubscription": {
        "type": "object",
        "required": [
          "_id"
        ],
        "properties": {
          "_id": {
            "type": "string"
          },
          "hotelId": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "active": {
            "type": "boolean"
          },
          "consecutiveFailures": {
            "type": "integer",
            "minimum": 0
          },
          "creationTime": {
            "type": "string",
            "format": "date-time",
            "readOnly": true
          },
          "lastEditTime": {
            "type": "string",
            "format": "date-time"
          }
        },
        "additionalProperties": true
      },
      "WebhookSubscriptionWithSecret": {
        "allOf": [
          {
            "$ref": "#/components/schemas/WebhookSubscription"
          },
          {
            "type": "object",
            "required": [
              "secret"
            ],
            "properties": {
              "secret": {
                "type": "string",
                "readOnly": true,
                "description": "Signing secret returned exactly once on creation."
              }
            }
          }
        ]
      },
      "DeletedResource": {
        "type": "object",
        "required": [
          "_id"
        ],
        "properties": {
          "_id": {
            "type": "string"
          }
        },
        "additionalProperties": false
      }
    }
  },
  "paths": {
    "/api/hotels/{hotelId}/availability": {
      "get": {
        "tags": [
          "Availability"
        ],
        "summary": "Find public room offers for a stay",
        "description": "Returns sanitized room-type offers for the requested dates and occupancy. Prices and inventory are calculated by the server.\n",
        "security": [
          {
            "apiKey": []
          },
          {}
        ],
        "parameters": [
          {
            "in": "path",
            "name": "hotelId",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "checkInDate",
            "required": true,
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "in": "query",
            "name": "checkOutDate",
            "required": true,
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "in": "query",
            "name": "adults",
            "schema": {
              "type": "integer",
              "default": 2
            }
          },
          {
            "in": "query",
            "name": "children",
            "schema": {
              "type": "integer",
              "default": 0
            }
          },
          {
            "in": "query",
            "name": "infants",
            "schema": {
              "type": "integer",
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Public room offers",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/AvailabilityOffer"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid dates or occupancy",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Hotel not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "default": {
            "description": "Request failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getApiHotelsHotelIdAvailability"
      }
    },
    "/api/hotels/{hotelId}/book-reservation": {
      "post": {
        "tags": [
          "Availability"
        ],
        "summary": "Request a room reservation",
        "description": "Revalidates the selected offer, then creates a REQUESTED reservation with one unassigned PENDING room for hotel staff to approve.\n",
        "security": [
          {
            "apiKey": []
          },
          {}
        ],
        "responses": {
          "201": {
            "description": "Reservation request created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BookingResult"
                }
              }
            }
          },
          "409": {
            "description": "The selected offer is no longer available",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "default": {
            "description": "Request failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "postApiHotelsHotelIdBookReservation"
      }
    },
    "/api/dailyRates": {
      "get": {
        "tags": [
          "Daily rates"
        ],
        "summary": "List daily rates",
        "description": "Reads per-day prices/inventory for a rate plan over a date window (the ARI surface). Pass `hotelId`, `ratePlanId`, `dateFrom` and `dateTo`. Requires the `rates:read` scope.\n",
        "security": [
          {
            "apiKey": []
          },
          {
            "accessToken": []
          }
        ],
        "parameters": [
          {
            "in": "query",
            "name": "hotelId",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "ratePlanId",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "dateFrom",
            "required": true,
            "schema": {
              "type": "string",
              "example": "2026-05-01"
            }
          },
          {
            "in": "query",
            "name": "dateTo",
            "required": true,
            "schema": {
              "type": "string",
              "example": "2026-05-31"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Array of daily rates",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/DailyRate"
                  }
                }
              }
            }
          },
          "403": {
            "description": "API key is missing the `rates:read` scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "default": {
            "description": "Request failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getApiDailyRates"
      },
      "post": {
        "tags": [
          "Daily rates"
        ],
        "summary": "Upsert daily rates",
        "description": "Batch-upserts per-day prices/inventory (max 500 per call). Requires the `rates:write` scope.\n",
        "security": [
          {
            "apiKey": []
          },
          {
            "accessToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "The upserted daily rates",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/DailyRate"
                  }
                }
              }
            }
          },
          "403": {
            "description": "API key is missing the `rates:write` scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "default": {
            "description": "Request failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "postApiDailyRates",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DailyRateBatch"
              }
            }
          }
        }
      }
    },
    "/api/guests": {
      "get": {
        "tags": [
          "Guests"
        ],
        "summary": "List guests",
        "description": "Lists the guests of your organization, optionally filtered by `hotelId`, `email` or `_ids`. Requires the `guests:read` scope.\n",
        "security": [
          {
            "apiKey": []
          },
          {
            "accessToken": []
          }
        ],
        "parameters": [
          {
            "in": "query",
            "name": "hotelId",
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "email",
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "_ids",
            "description": "Comma-separated list of guest ids",
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "fields",
            "description": "Comma-separated projection of fields to return",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Array of guests",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Guest"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "API key is missing the `guests:read` scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "API key rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "default": {
            "description": "Request failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getApiGuests"
      },
      "post": {
        "tags": [
          "Guests"
        ],
        "summary": "Create a guest",
        "description": "Requires the `guests:write` scope.",
        "security": [
          {
            "apiKey": []
          },
          {
            "accessToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "The created guest",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Guest"
                }
              }
            }
          },
          "403": {
            "description": "API key is missing the `guests:write` scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "default": {
            "description": "Request failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "postApiGuests",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Guest"
              }
            }
          }
        }
      }
    },
    "/api/guests/{guestId}": {
      "get": {
        "tags": [
          "Guests"
        ],
        "summary": "Get a guest",
        "description": "Returns a single guest by id. Guests belonging to another organization respond 404. Requires the `guests:read` scope.\n",
        "security": [
          {
            "apiKey": []
          },
          {
            "accessToken": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "guestId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The guest",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Guest"
                }
              }
            }
          },
          "404": {
            "description": "Not found (or owned by another organization)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "default": {
            "description": "Request failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getApiGuestsGuestId"
      },
      "put": {
        "tags": [
          "Guests"
        ],
        "summary": "Update a guest",
        "description": "Updates a guest by id. Guests belonging to another organization respond 404. Requires the `guests:write` scope.\n",
        "security": [
          {
            "apiKey": []
          },
          {
            "accessToken": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "guestId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The updated guest",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Guest"
                }
              }
            }
          },
          "404": {
            "description": "Not found (or owned by another organization)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "default": {
            "description": "Request failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "putApiGuestsGuestId",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Guest"
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Guests"
        ],
        "summary": "Delete a guest",
        "description": "Deletes a guest by id. Guests belonging to another organization respond 404. Requires the `guests:write` scope.\n",
        "security": [
          {
            "apiKey": []
          },
          {
            "accessToken": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "guestId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The deleted guest",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Guest"
                }
              }
            }
          },
          "404": {
            "description": "Not found (or owned by another organization)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "default": {
            "description": "Request failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "deleteApiGuestsGuestId"
      }
    },
    "/api/ratePlans": {
      "get": {
        "tags": [
          "Rate plans"
        ],
        "summary": "List rate plans",
        "description": "Lists the rate plans for a hotel (pass `hotelId`). Rate plans are part of the ARI pricing surface, so this requires the `rates:read` scope.\n",
        "security": [
          {
            "apiKey": []
          },
          {
            "accessToken": []
          }
        ],
        "parameters": [
          {
            "in": "query",
            "name": "hotelId",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "roomTypeId",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Array of rate plans",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/RatePlan"
                  }
                }
              }
            }
          },
          "403": {
            "description": "API key is missing the `rates:read` scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "default": {
            "description": "Request failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getApiRatePlans"
      },
      "post": {
        "tags": [
          "Rate plans"
        ],
        "summary": "Create or update a rate plan",
        "description": "Requires the `rates:write` scope.",
        "security": [
          {
            "apiKey": []
          },
          {
            "accessToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "The created rate plan",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RatePlan"
                }
              }
            }
          },
          "403": {
            "description": "API key is missing the `rates:write` scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "default": {
            "description": "Request failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "postApiRatePlans",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RatePlan"
              }
            }
          }
        }
      }
    },
    "/api/reservations": {
      "get": {
        "tags": [
          "Reservations"
        ],
        "summary": "List reservations",
        "description": "Lists the reservations of your organization. Optionally filter by `hotelId`, `guestId`, a legacy `startTime`/`endTime` window, or an overlapping stay window with `stayDateFrom`/`stayDateTo`. Requires the `reservations:read` scope.\n",
        "security": [
          {
            "apiKey": []
          },
          {
            "accessToken": []
          }
        ],
        "parameters": [
          {
            "in": "query",
            "name": "hotelId",
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "guestId",
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "startTime",
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "endTime",
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "stayDateFrom",
            "description": "Include stays checking out after this date",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "in": "query",
            "name": "stayDateTo",
            "description": "Include stays checking in on or before this date",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "integer",
              "default": 0
            }
          },
          {
            "in": "query",
            "name": "skip",
            "schema": {
              "type": "integer",
              "default": 0
            }
          },
          {
            "in": "query",
            "name": "sortField",
            "schema": {
              "type": "string",
              "example": "startTime"
            }
          },
          {
            "in": "query",
            "name": "sortDirection",
            "schema": {
              "type": "string",
              "enum": [
                "ASC",
                "DESC"
              ]
            }
          },
          {
            "in": "query",
            "name": "fields",
            "description": "Comma-separated projection of fields to return",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Array of reservations",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Reservation"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "API key is missing the `reservations:read` scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "API key rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "default": {
            "description": "Request failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getApiReservations"
      },
      "post": {
        "tags": [
          "Reservations"
        ],
        "summary": "Create a reservation",
        "description": "Creates a reservation (and its ReservationRooms from the `rooms` array). Requires the `reservations:write` scope.\n",
        "security": [
          {
            "apiKey": []
          },
          {
            "accessToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "The created reservation",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Reservation"
                }
              }
            }
          },
          "403": {
            "description": "API key is missing the `reservations:write` scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "default": {
            "description": "Request failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "postApiReservations",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Reservation"
              }
            }
          }
        }
      }
    },
    "/api/reservations/{reservationId}": {
      "get": {
        "tags": [
          "Reservations"
        ],
        "summary": "Get a reservation",
        "description": "Returns a single reservation by id, with its `rooms`. Reservations belonging to another organization respond 404. Requires the `reservations:read` scope.\n",
        "security": [
          {
            "apiKey": []
          },
          {
            "accessToken": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "reservationId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The reservation",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Reservation"
                }
              }
            }
          },
          "404": {
            "description": "Not found (or owned by another organization)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "default": {
            "description": "Request failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getApiReservationsReservationId"
      },
      "put": {
        "tags": [
          "Reservations"
        ],
        "summary": "Update a reservation",
        "description": "Updates a reservation by id. Reservations belonging to another organization respond 404. Requires the `reservations:write` scope.\n",
        "security": [
          {
            "apiKey": []
          },
          {
            "accessToken": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "reservationId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The updated reservation",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Reservation"
                }
              }
            }
          },
          "403": {
            "description": "API key is missing the `reservations:write` scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found (or owned by another organization)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "default": {
            "description": "Request failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "putApiReservationsReservationId",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Reservation"
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Reservations"
        ],
        "summary": "Delete a reservation",
        "description": "Deletes a reservation by id (cascading its rooms, folio and charges). Reservations belonging to another organization respond 404. Requires the `reservations:write` scope.\n",
        "security": [
          {
            "apiKey": []
          },
          {
            "accessToken": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "reservationId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The deleted reservation",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Reservation"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not found (or owned by another organization)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "default": {
            "description": "Request failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "deleteApiReservationsReservationId"
      }
    },
    "/api/reservations/{reservationId}/approve": {
      "post": {
        "tags": [
          "Reservations"
        ],
        "summary": "Approve a requested reservation",
        "description": "Rechecks inventory, promotes the parent to CONFIRMED and each PENDING room to CONFIRMED, then starts the normal folio/reminder workflow. Requires the `reservations:write` scope.\n",
        "security": [
          {
            "apiKey": []
          },
          {
            "accessToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "Approved reservation",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Reservation"
                }
              }
            }
          },
          "409": {
            "description": "The request cannot be approved or inventory changed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "default": {
            "description": "Request failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "postApiReservationsReservationIdApprove"
      }
    },
    "/api/rooms": {
      "get": {
        "tags": [
          "Rooms"
        ],
        "summary": "List rooms",
        "description": "Lists the physical rooms of your organization, optionally filtered by `hotelId` or `roomTypeId`. Requires the `rooms:read` scope.\n",
        "security": [
          {
            "apiKey": []
          },
          {
            "accessToken": []
          }
        ],
        "parameters": [
          {
            "in": "query",
            "name": "hotelId",
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "roomTypeId",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Array of rooms",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Room"
                  }
                }
              }
            }
          },
          "403": {
            "description": "API key is missing the `rooms:read` scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "API key rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "default": {
            "description": "Request failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getApiRooms"
      },
      "post": {
        "tags": [
          "Rooms"
        ],
        "summary": "Create a room",
        "description": "Requires the `rooms:write` scope.",
        "security": [
          {
            "apiKey": []
          },
          {
            "accessToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "The created room",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Room"
                }
              }
            }
          },
          "403": {
            "description": "API key is missing the `rooms:write` scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "default": {
            "description": "Request failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "postApiRooms",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Room"
              }
            }
          }
        }
      }
    },
    "/api/roomTypes": {
      "get": {
        "tags": [
          "Room types"
        ],
        "summary": "List room types",
        "description": "Lists the room types of your organization, optionally filtered by `hotelId`. Room types are part of the physical inventory, so this requires the `rooms:read` scope.\n",
        "security": [
          {
            "apiKey": []
          },
          {
            "accessToken": []
          }
        ],
        "parameters": [
          {
            "in": "query",
            "name": "hotelId",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Array of room types",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/RoomType"
                  }
                }
              }
            }
          },
          "403": {
            "description": "API key is missing the `rooms:read` scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "default": {
            "description": "Request failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getApiRoomTypes"
      },
      "post": {
        "tags": [
          "Room types"
        ],
        "summary": "Create a room type",
        "description": "Requires the `rooms:write` scope.",
        "security": [
          {
            "apiKey": []
          },
          {
            "accessToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "The created room type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RoomType"
                }
              }
            }
          },
          "403": {
            "description": "API key is missing the `rooms:write` scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "default": {
            "description": "Request failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "postApiRoomTypes",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RoomType"
              }
            }
          }
        }
      }
    },
    "/api/webhooksubscriptions": {
      "get": {
        "tags": [
          "Webhook subscriptions"
        ],
        "summary": "List webhook subscriptions",
        "description": "Webhook subscriptions deliver `reservation.created`, `reservation.updated`, `reservation.deleted`, `guest.created`, `guest.updated` and `guest.deleted` events to your server as signed POST requests (`X-Hotelumo-Signature: t=<timestamp>,v1=<hex HMAC-SHA256 of \"timestamp.body\">`). An endpoint failing 20 times in a row is disabled automatically. Subscriptions are managed with an operator access token; the `secret` is only returned once, on create.\n",
        "security": [
          {
            "accessToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "Array of webhook subscriptions (without secrets)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/WebhookSubscription"
                  }
                }
              }
            }
          },
          "default": {
            "description": "Request failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getApiWebhooksubscriptions"
      },
      "post": {
        "tags": [
          "Webhook subscriptions"
        ],
        "summary": "Create a webhook subscription",
        "description": "The response includes the signing `secret` exactly once — store it; it cannot be retrieved again.\n",
        "security": [
          {
            "accessToken": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "hotelId",
                  "url"
                ],
                "properties": {
                  "hotelId": {
                    "type": "string"
                  },
                  "url": {
                    "type": "string",
                    "example": "https://example.com/hotelumo-webhook"
                  },
                  "events": {
                    "type": "array",
                    "description": "Empty array subscribes to all events",
                    "items": {
                      "type": "string",
                      "enum": [
                        "reservation.created",
                        "reservation.updated",
                        "reservation.deleted",
                        "guest.created",
                        "guest.updated",
                        "guest.deleted"
                      ]
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The created subscription, including its secret",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookSubscriptionWithSecret"
                }
              }
            }
          },
          "default": {
            "description": "Request failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "postApiWebhooksubscriptions"
      }
    },
    "/api/webhooksubscriptions/{webhookSubscriptionId}": {
      "put": {
        "tags": [
          "Webhook subscriptions"
        ],
        "summary": "Update a webhook subscription",
        "description": "`url`, `events` and `active` are editable; the secret and hotel are immutable. Re-enabling an auto-disabled endpoint is done by setting `active` back to true.\n",
        "security": [
          {
            "accessToken": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "webhookSubscriptionId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookSubscription"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated subscription (without secret)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookSubscription"
                }
              }
            }
          },
          "default": {
            "description": "Request failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "putApiWebhooksubscriptionsWebhookSubscriptionId"
      },
      "delete": {
        "tags": [
          "Webhook subscriptions"
        ],
        "summary": "Delete a webhook subscription",
        "security": [
          {
            "accessToken": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "webhookSubscriptionId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeletedResource"
                }
              }
            }
          },
          "default": {
            "description": "Request failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "deleteApiWebhooksubscriptionsWebhookSubscriptionId",
        "description": "Delete a webhook subscription"
      }
    }
  },
  "tags": []
}
