{"openapi":"3.0.3","info":{"title":"Hotelier Tools API","description":"REST API for hotel management via Little Hotelier integration.\n\n## Authentication\nAll endpoints (except `/health` and `GET /methods`) require a Bearer token:\n```\nAuthorization: Bearer <token>\n```\nSupported: **API Key** (`htk_…`) or **Supabase JWT** from browser session.\n\n## Rate Limiting\n100 requests/minute per IP. Headers: `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`.\n\n## Methods auto-discovery\n`GET /api/v1/methods` lists all callable methods. `POST /api/v1/methods/:name` executes them.\nAdding a method to `methodDefinitions.ts` automatically exposes it here.","version":"1.0.0","contact":{"name":"Hotelier Tools Support","email":"support@hotelier.tools"}},"servers":[{"url":"/api/v1","description":"Current server (relative)"},{"url":"https://dashboard.hotelier.tools/api/v1","description":"Production"}],"security":[{"BearerAuth":[]}],"tags":[{"name":"System","description":"Health and status"},{"name":"Reservations","description":"Reservation listing and filtering"},{"name":"Guests","description":"Guest search by phone number, name or email"},{"name":"Pricing","description":"Room prices and availability"},{"name":"Property","description":"Property information"},{"name":"Methods","description":"All Little Hotelier methods — auto-discovered from methodDefinitions"},{"name":"Availability queue","description":"Stays somebody is waiting for, and the public waiting list"},{"name":"Scheduled tasks","description":"The background jobs that make features actually run"}],"paths":{"/health":{"get":{"summary":"Health Check","description":"Public — no authentication required.","tags":["System"],"security":[],"operationId":"getHealth","responses":{"200":{"description":"API is healthy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HealthResponse"},"example":{"status":"ok","version":"1.0.0","timestamp":"2026-04-26T10:00:00.000Z","endpoints":["GET /api/v1/health","GET /api/v1/reservations","GET /api/v1/prices","GET /api/v1/property","GET /api/v1/room-types","GET /api/v1/availability-watch","POST /api/v1/availability-watch","POST /api/v1/availability-watch/run-now","GET /api/v1/cron/task/:function","POST /api/v1/cron/task/:function","GET /api/v1/methods","POST /api/v1/methods/:methodName"]}}}}}}},"/reservations":{"get":{"summary":"List Reservations","description":"Retrieve reservations. Provide both `startDate` and `endDate` to filter by date range, or omit both to get all reservations.","tags":["Reservations"],"operationId":"listReservations","parameters":[{"name":"startDate","in":"query","description":"Check-in from (YYYY-MM-DD). Requires endDate.","schema":{"type":"string","format":"date","example":"2026-04-01"}},{"name":"endDate","in":"query","description":"Check-in to (YYYY-MM-DD). Requires startDate.","schema":{"type":"string","format":"date","example":"2026-04-30"}},{"name":"status","in":"query","description":"Filter by status.","schema":{"type":"string","enum":["confirmed","cancelled","checked_in","checked_out","no_show"]}},{"name":"limit","in":"query","description":"Max results (1–100, default 50).","schema":{"type":"integer","minimum":1,"maximum":100,"default":50}}],"responses":{"200":{"description":"Reservation list","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"count":{"type":"integer","example":2},"data":{"type":"array","items":{"$ref":"#/components/schemas/Reservation"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"422":{"$ref":"#/components/responses/ValidationError"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/reservations/{id}":{"get":{"summary":"Get Reservation Details","description":"Retrieve details for a specific reservation by its ID. The ID can be the internal numeric ID (bookingReferenceId), the UUID, or a string ID (e.g. LH24061541125354). The response will include an `invoices` array with objects containing an `invoice_number`.","tags":["Reservations"],"operationId":"getReservationDetails","parameters":[{"name":"id","in":"path","required":true,"description":"Numeric reservation ID (e.g. 51672524) or UUID","schema":{"type":"string"}}],"responses":{"200":{"description":"Reservation details","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"type":"object","description":"Full reservation details object"}}}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/reservations/{id}/invoices":{"get":{"summary":"Get Invoice Summary","description":"Retrieve a summary of all invoices for a specific reservation.","tags":["Reservations"],"operationId":"getInvoiceSummary","parameters":[{"name":"id","in":"path","required":true,"description":"Numeric reservation ID (e.g. 51672524), UUID, or string ID (e.g. LH...)","schema":{"type":"string"}}],"responses":{"200":{"description":"Invoice summary retrieved successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"type":"object","properties":{"invoices":{"type":"array","items":{"$ref":"#/components/schemas/Invoice"}}}}}}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"summary":"Generate Invoice","description":"Generates a new invoice for a specific reservation. Note: This will create a new invoice in Little Hotelier. Call `GET /reservations/{id}` afterwards to retrieve the updated invoices list and the new `invoice_number`.","tags":["Reservations"],"operationId":"generateInvoice","parameters":[{"name":"id","in":"path","required":true,"description":"Numeric reservation ID (e.g. 51672524) or UUID","schema":{"type":"string"}}],"responses":{"200":{"description":"Invoice generated successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"message":{"type":"string","example":"Invoice generated successfully"}}}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/reservations/{id}/invoices/{invoiceId}/download":{"get":{"summary":"Download Invoice","description":"Downloads a specific invoice for a reservation as a PDF.","tags":["Reservations"],"operationId":"downloadInvoice","parameters":[{"name":"id","in":"path","required":true,"description":"Numeric reservation ID or UUID","schema":{"type":"string"}},{"name":"invoiceId","in":"path","required":true,"description":"The specific invoice ID. This corresponds to the `invoice_number` field returned in the `invoices` array of the Reservation Details endpoint.","schema":{"type":"string"}}],"responses":{"200":{"description":"Invoice PDF file","content":{"application/pdf":{"schema":{"type":"string","format":"binary"}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/guests":{"get":{"summary":"Search Guests","description":"Search guests by a general query string OR individual field filters. At least one of `query`, `phoneNumber`, `firstName`, `lastName`, or `email` is required. Use `includeReservations=true` to enrich each guest with full reservation details (capped by `maxReservationsPerGuest`).","tags":["Guests"],"operationId":"searchGuests","parameters":[{"name":"query","in":"query","description":"General text search (name, phone, email, booking ref, etc.). When provided, individual field filters are ignored. Recommended when you want a broad match.","schema":{"type":"string","example":"1234"}},{"name":"phoneNumber","in":"query","description":"Phone number (partial match). Ignored when `query` is set.","schema":{"type":"string","example":"636667773"}},{"name":"firstName","in":"query","description":"First name filter. Ignored when `query` is set.","schema":{"type":"string"}},{"name":"lastName","in":"query","description":"Last name filter. Ignored when `query` is set.","schema":{"type":"string"}},{"name":"email","in":"query","description":"Email filter. Ignored when `query` is set.","schema":{"type":"string","format":"email"}},{"name":"perPage","in":"query","description":"Guests per page (1–50, default 10).","schema":{"type":"integer","minimum":1,"maximum":50,"default":10}},{"name":"page","in":"query","description":"Page number (default 1).","schema":{"type":"integer","minimum":1,"default":1}},{"name":"includeReservations","in":"query","description":"Fetch reservation details for each guest (default false).","schema":{"type":"boolean","default":false}},{"name":"maxReservationsPerGuest","in":"query","description":"Max reservations fetched per guest when includeReservations=true (1–10, default 3).","schema":{"type":"integer","minimum":1,"maximum":10,"default":3}}],"responses":{"200":{"description":"Guest search results","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"total":{"type":"integer","description":"Total matching guests in LH","example":1},"page":{"type":"string","example":"1"},"lastPage":{"type":"integer","example":1},"totalPages":{"type":"integer","example":1},"count":{"type":"integer","description":"Guests in this response","example":1},"data":{"type":"array","items":{"$ref":"#/components/schemas/Guest"}}}},"example":{"success":true,"total":1,"page":"1","lastPage":1,"totalPages":1,"count":1,"data":[{"id":55682769,"first_name":"TEODOMIRO","last_name":"MAESTRO TORCA","phone_number":"636667773","email":null,"reservation_ids":{"LH24030138843905":38843905},"reservations":["LH24030138843905"]}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"422":{"$ref":"#/components/responses/ValidationError"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/prices":{"get":{"summary":"Get Prices & Inventory","description":"Retrieve room inventory and pricing. If `propertyId` is omitted, dynamically discovers the slug and scrapes the widget. If `propertyId` is a non-numeric string, it scrapes from the public Booking Button widget. Otherwise, it queries privately via the authenticated extranet client. Automatically chunks ranges wider than 2 weeks.","tags":["Pricing"],"operationId":"getRoomPrices","parameters":[{"name":"startDate","in":"query","required":true,"description":"Start date (YYYY-MM-DD).","schema":{"type":"string","format":"date","example":"2026-04-01"}},{"name":"endDate","in":"query","required":true,"description":"End date (YYYY-MM-DD).","schema":{"type":"string","format":"date","example":"2026-04-30"}},{"name":"propertyId","in":"query","required":false,"description":"Optional Booking Button text slug (e.g. 'hostaldirect') or numeric property ID.","schema":{"type":"string","example":"hostaldirect"}},{"name":"locale","in":"query","required":false,"description":"Locale for public widget scraper.","schema":{"type":"string","default":"en","example":"en"}}],"responses":{"200":{"description":"Inventory and pricing data","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"count":{"type":"integer","example":3},"data":{"type":"object","description":"Inventory data with room types and rate plans"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"422":{"$ref":"#/components/responses/ValidationError"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/property":{"get":{"summary":"Get Property Info","description":"Returns Little Hotelier property ID and linked email for the authenticated user.","tags":["Property"],"operationId":"getPropertyInfo","responses":{"200":{"description":"Property details","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"type":"object","properties":{"propertyId":{"type":"string","example":"123456"},"email":{"type":"string","format":"email"},"userEmail":{"type":"string","format":"email"}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/methods":{"get":{"summary":"List All Methods","description":"Returns metadata for all callable methods including parameter schemas. No authentication required.","tags":["Methods"],"security":[],"operationId":"listMethods","responses":{"200":{"description":"Method list","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"count":{"type":"integer"},"methods":{"type":"array","items":{"$ref":"#/components/schemas/MethodMeta"}}}}}}}}}},"/room-types":{"get":{"summary":"List Room Types","description":"The property's room types: id, name and how many rooms of each exist. These ids are what an availability watch matches on — an id that does not come from here matches nothing, and the watch reports \"still full\" for ever. Read from Little Hotelier inventory and cached for five minutes.","tags":["Property"],"operationId":"listRoomTypes","parameters":[{"name":"startDate","in":"query","description":"Read the inventory from this date (YYYY-MM-DD). Default today.","schema":{"type":"string","format":"date"}}],"responses":{"200":{"description":"Room types","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"cached":{"type":"boolean","description":"Served from the five-minute cache."},"data":{"type":"array","items":{"$ref":"#/components/schemas/RoomType"}}}}}}},"400":{"description":"No Little Hotelier credentials configured"},"502":{"description":"Little Hotelier could not be read"}}}},"/availability-watch":{"get":{"summary":"List the availability queue","description":"Stays somebody is waiting for, including public waiting-list signups (`source: \"public\"`). Also reports whether notifications are armed on this server: a queue that is withholding its messages looks exactly like one that has found nothing.","tags":["Availability queue"],"operationId":"listAvailabilityWatches","parameters":[{"name":"all","in":"query","description":"Include fulfilled, expired and cancelled entries.","schema":{"type":"string","enum":["true"]}}],"responses":{"200":{"description":"The queue","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"notificationsArmed":{"type":"boolean","description":"False means messages are composed and withheld (AVAILABILITY_WATCH_NOTIFY)."},"situations":{"type":"array","items":{"type":"object"},"description":"The catalogue an audience can subscribe to."},"hotelEmailSuggestions":{"type":"array","items":{"type":"string"}},"data":{"type":"array","items":{"$ref":"#/components/schemas/AvailabilityWatch"}}}}}}}}},"post":{"summary":"Queue a stay, or update one","description":"Creates a watch, or updates one of your own when `id` is given. Nothing is reserved and nothing is held — this only looks at inventory and reports. There is no per-watch frequency: the whole queue is checked whenever the `checkAvailabilityWatches` task runs (see /cron/task/{function}). A channel with no address behind it is refused, because the alternative is a watch that finds a room and has nowhere to send it.","tags":["Availability queue"],"operationId":"saveAvailabilityWatch","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AvailabilityWatchInput"},"example":{"checkIn":"2026-10-12","checkOut":"2026-10-14","roomsRequested":2,"roomTypeIds":[],"guestName":"Lucía Marín","guestChannels":["email","whatsapp"],"guestEmail":"lucia@example.com","guestPhone":"+34600111222","guestSituations":["available"],"notifyHotel":true,"hotelChannels":["email"],"hotelSituations":["available","partial","expiring"]}}}},"responses":{"200":{"description":"Created or updated"},"400":{"description":"Bad dates, a stay already started, or a channel with no address"},"404":{"description":"No such watch (or it belongs to another property)"}}}},"/availability-watch/{id}":{"delete":{"summary":"Cancel a watch","description":"Marked cancelled, not deleted — it is the record of what was promised to a guest. An id belonging to another property is a 404, however it was come by.","tags":["Availability queue"],"operationId":"cancelAvailabilityWatch","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Cancelled"},"404":{"description":"No such watch"}}}},"/availability-watch/run-now":{"post":{"summary":"Check the queue now","description":"**Can send messages to guests.** Runs the same code the scheduled task runs, immediately: reads Little Hotelier inventory for every active watch and announces anything bookable. It sends for real where the server has `AVAILABILITY_WATCH_NOTIFY=1`; the response says which. Use it to prove the queue works, not instead of scheduling it.","tags":["Availability queue"],"operationId":"runAvailabilityQueueNow","responses":{"200":{"description":"What the run did","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"armed":{"type":"boolean","description":"False means nothing was actually sent."},"durationMs":{"type":"integer"},"summary":{"type":"object","properties":{"checked":{"type":"integer"},"matched":{"type":"integer"},"notified":{"type":"integer"},"withheld":{"type":"integer"},"expired":{"type":"integer"},"errors":{"type":"array","items":{"type":"string"}}}},"message":{"type":"string"}}}}}},"400":{"description":"No Little Hotelier credentials configured"},"502":{"description":"The check failed"}}}},"/cron/task/{function}":{"get":{"summary":"Is a background task scheduled?","description":"For any schedulable function — `checkAvailabilityWatches`, `processInvoiceOnLeave`, `pollMessagingChannels`, `scrapeCompetitorPrices` and the rest. **Most \"the feature does nothing\" reports are this**: the feature is configured correctly and no task runs it. Also returns the schedules the function may be given and its minimum gap between runs.","tags":["Scheduled tasks"],"operationId":"getScheduledTask","parameters":[{"name":"function","in":"path","required":true,"description":"Registry function name.","schema":{"type":"string","example":"checkAvailabilityWatches"}}],"responses":{"200":{"description":"What runs it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScheduledTask"}}}},"400":{"description":"Not a schedulable function"}}},"post":{"summary":"Schedule, retime, pause or resume a task","description":"Creates the task when none exists, otherwise updates it. `schedule` is a 5-field cron expression — `0 1 * * *` is every night at 01:00. Each function has its own floor (a task that logs into Little Hotelier cannot run every minute) and a schedule under it is refused rather than silently clamped. Refuses with 409 when several tasks already run the function unless `jobId` says which.","tags":["Scheduled tasks"],"operationId":"configureScheduledTask","parameters":[{"name":"function","in":"path","required":true,"schema":{"type":"string","example":"checkAvailabilityWatches"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"schedule":{"type":"string","example":"0 1 * * *","description":"5-field cron expression. Omit to only pause or resume."},"enabled":{"type":"boolean","description":"false pauses without deleting."},"name":{"type":"string","description":"Label on the Cron Jobs page."},"settings":{"type":"object","additionalProperties":true},"jobId":{"type":"string","format":"uuid"}}},"example":{"schedule":"0 1 * * *","enabled":true,"name":"Check the availability queue"}}}},"responses":{"200":{"description":"Created or updated"},"400":{"description":"Unknown function, malformed expression, or under the floor"},"409":{"description":"Several tasks run this function — pass jobId"}}}},"/methods/getReservations":{"post":{"summary":"getReservations","description":"Retrieves a list of reservations.","tags":["Methods"],"operationId":"run_getReservations","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"params":{"type":"object","description":"Named method parameters (alternative: use `args` array)","required":["queryParams"],"properties":{"queryParams":{"type":"object","properties":{"reservation_filter__date_from":{"type":"string","format":"date","example":"2026-04-01","description":"Filter by check-in date from (YYYY-MM-DD)"},"reservation_filter__date_to":{"type":"string","format":"date","example":"2026-04-01","description":"Filter by check-in date to (YYYY-MM-DD)"},"reservation_filter__date_type":{"type":"string","enum":["CheckIn","CheckOut","BookedOn"],"description":"Type of date filter"},"reservation_filter__status":{"type":"string","enum":["confirmed","cancelled","checked-in","checked-out"],"description":"Filter by reservation status"},"reservation_filter__guest_name":{"type":"string","description":"Filter by guest name"}},"description":"Filter parameters for reservations"},"uuidProperty":{"type":"string","description":"Optional property UUID"}}},"args":{"type":"array","description":"Positional arguments (alternative to `params`)","items":{},"example":[]}}}}}},"responses":{"200":{"description":"Method executed successfully","content":{"application/json":{"schema":{"type":"object","required":["success","method","durationMs"],"properties":{"success":{"type":"boolean","example":true},"method":{"type":"string","example":"getReservations"},"durationMs":{"type":"integer","example":1240},"result":{"description":"Method return value (shape varies by method)"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/methods/getReservationDetails":{"post":{"summary":"getReservationDetails","description":"Retrieves details for a specific reservation.","tags":["Methods"],"operationId":"run_getReservationDetails","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"params":{"type":"object","description":"Named method parameters (alternative: use `args` array)","required":["reservationIdAppLittleHotelier"],"properties":{"reservationIdAppLittleHotelier":{"type":"string","description":"The reservation ID"},"propertyIdAppLittleHotelier":{"type":"string","description":"Optional property ID"}}},"args":{"type":"array","description":"Positional arguments (alternative to `params`)","items":{},"example":[]}}}}}},"responses":{"200":{"description":"Method executed successfully","content":{"application/json":{"schema":{"type":"object","required":["success","method","durationMs"],"properties":{"success":{"type":"boolean","example":true},"method":{"type":"string","example":"getReservationDetails"},"durationMs":{"type":"integer","example":1240},"result":{"description":"Method return value (shape varies by method)"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/methods/getReservationsByDateRange":{"post":{"summary":"getReservationsByDateRange","description":"Retrieves reservations for a date range using GraphQL.","tags":["Methods"],"operationId":"run_getReservationsByDateRange","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"params":{"type":"object","description":"Named method parameters (alternative: use `args` array)","required":["startDate","endDate"],"properties":{"startDate":{"type":"string","format":"date","example":"2026-04-01","description":"Start date (YYYY-MM-DD)"},"endDate":{"type":"string","format":"date","example":"2026-04-01","description":"End date (YYYY-MM-DD)"},"uuidProperty":{"type":"string","description":"Optional property UUID"}}},"args":{"type":"array","description":"Positional arguments (alternative to `params`)","items":{},"example":[]}}}}}},"responses":{"200":{"description":"Method executed successfully","content":{"application/json":{"schema":{"type":"object","required":["success","method","durationMs"],"properties":{"success":{"type":"boolean","example":true},"method":{"type":"string","example":"getReservationsByDateRange"},"durationMs":{"type":"integer","example":1240},"result":{"description":"Method return value (shape varies by method)"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/methods/addPaymentRecord":{"post":{"summary":"addPaymentRecord","description":"Adds a payment record to a reservation.","tags":["Methods"],"operationId":"run_addPaymentRecord","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"params":{"type":"object","description":"Named method parameters (alternative: use `args` array)","required":["reservationId","payment"],"properties":{"reservationId":{"type":"string","description":"The reservation ID"},"payment":{"type":"object","description":"Payment data object"},"propertyId":{"type":"string","description":"Optional property ID"},"locale":{"type":"string","description":"Locale","default":"es"},"csrfToken":{"type":"string","description":"Optional CSRF token"}}},"args":{"type":"array","description":"Positional arguments (alternative to `params`)","items":{},"example":[]}}}}}},"responses":{"200":{"description":"Method executed successfully","content":{"application/json":{"schema":{"type":"object","required":["success","method","durationMs"],"properties":{"success":{"type":"boolean","example":true},"method":{"type":"string","example":"addPaymentRecord"},"durationMs":{"type":"integer","example":1240},"result":{"description":"Method return value (shape varies by method)"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/methods/updateReservation":{"post":{"summary":"updateReservation","description":"Updates a reservation with fixed data.","tags":["Methods"],"operationId":"run_updateReservation","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"params":{"type":"object","description":"Named method parameters (alternative: use `args` array)","required":["fixedReservationData"],"properties":{"fixedReservationData":{"type":"object","description":"Fixed reservation data object"},"propertyId":{"type":"string","description":"Optional property ID"}}},"args":{"type":"array","description":"Positional arguments (alternative to `params`)","items":{},"example":[]}}}}}},"responses":{"200":{"description":"Method executed successfully","content":{"application/json":{"schema":{"type":"object","required":["success","method","durationMs"],"properties":{"success":{"type":"boolean","example":true},"method":{"type":"string","example":"updateReservation"},"durationMs":{"type":"integer","example":1240},"result":{"description":"Method return value (shape varies by method)"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/methods/updateStatusReservation":{"post":{"summary":"updateStatusReservation","description":"Updates the status of a reservation.","tags":["Methods"],"operationId":"run_updateStatusReservation","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"params":{"type":"object","description":"Named method parameters (alternative: use `args` array)","required":["reservationId","status"],"properties":{"reservationId":{"type":"string","description":"The reservation ID"},"status":{"type":"string","enum":["confirmed","checked_in","checked_out"],"description":"New status"},"propertyId":{"type":"string","description":"Optional property ID"}}},"args":{"type":"array","description":"Positional arguments (alternative to `params`)","items":{},"example":[]}}}}}},"responses":{"200":{"description":"Method executed successfully","content":{"application/json":{"schema":{"type":"object","required":["success","method","durationMs"],"properties":{"success":{"type":"boolean","example":true},"method":{"type":"string","example":"updateStatusReservation"},"durationMs":{"type":"integer","example":1240},"result":{"description":"Method return value (shape varies by method)"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/methods/fetchReservationsCSV":{"post":{"summary":"fetchReservationsCSV","description":"Fetches reservations as a CSV file.","tags":["Methods"],"operationId":"run_fetchReservationsCSV","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"params":{"type":"object","description":"Named method parameters (alternative: use `args` array)","properties":{"dateFrom":{"type":"string","format":"date","example":"2026-04-01","description":"Start date filter"},"dateTo":{"type":"string","format":"date","example":"2026-04-01","description":"End date filter"},"idPropertyAppTheBookingButton":{"type":"string","description":"Optional property ID"}}},"args":{"type":"array","description":"Positional arguments (alternative to `params`)","items":{},"example":[]}}}}}},"responses":{"200":{"description":"Method executed successfully","content":{"application/json":{"schema":{"type":"object","required":["success","method","durationMs"],"properties":{"success":{"type":"boolean","example":true},"method":{"type":"string","example":"fetchReservationsCSV"},"durationMs":{"type":"integer","example":1240},"result":{"description":"Method return value (shape varies by method)"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/methods/getIdPropertyAppTheBookingButton":{"post":{"summary":"getIdPropertyAppTheBookingButton","description":"Gets the ID for App The Booking Button.","tags":["Methods"],"operationId":"run_getIdPropertyAppTheBookingButton","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"params":{"type":"object","description":"Named method parameters (alternative: use `args` array)","properties":{"forceUpdate":{"type":"boolean","description":"Force fresh fetch","default":false}}},"args":{"type":"array","description":"Positional arguments (alternative to `params`)","items":{},"example":[]}}}}}},"responses":{"200":{"description":"Method executed successfully","content":{"application/json":{"schema":{"type":"object","required":["success","method","durationMs"],"properties":{"success":{"type":"boolean","example":true},"method":{"type":"string","example":"getIdPropertyAppTheBookingButton"},"durationMs":{"type":"integer","example":1240},"result":{"description":"Method return value (shape varies by method)"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/methods/getUuidPropertyAppTheBookingButton":{"post":{"summary":"getUuidPropertyAppTheBookingButton","description":"Gets the UUID for App The Booking Button.","tags":["Methods"],"operationId":"run_getUuidPropertyAppTheBookingButton","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"params":{"type":"object","description":"Named method parameters (alternative: use `args` array)","properties":{"uuidPropertyLittleHotelier":{"type":"string","description":"Optional Little Hotelier UUID"}}},"args":{"type":"array","description":"Positional arguments (alternative to `params`)","items":{},"example":[]}}}}}},"responses":{"200":{"description":"Method executed successfully","content":{"application/json":{"schema":{"type":"object","required":["success","method","durationMs"],"properties":{"success":{"type":"boolean","example":true},"method":{"type":"string","example":"getUuidPropertyAppTheBookingButton"},"durationMs":{"type":"integer","example":1240},"result":{"description":"Method return value (shape varies by method)"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/methods/getUuidPropertyLittleHotelier":{"post":{"summary":"getUuidPropertyLittleHotelier","description":"Gets the UUID for Property Little Hotelier.","tags":["Methods"],"operationId":"run_getUuidPropertyLittleHotelier","responses":{"200":{"description":"Method executed successfully","content":{"application/json":{"schema":{"type":"object","required":["success","method","durationMs"],"properties":{"success":{"type":"boolean","example":true},"method":{"type":"string","example":"getUuidPropertyLittleHotelier"},"durationMs":{"type":"integer","example":1240},"result":{"description":"Method return value (shape varies by method)"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/methods/generateInvoice":{"post":{"summary":"generateInvoice","description":"Generates an invoice for a reservation.","tags":["Methods"],"operationId":"run_generateInvoice","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"params":{"type":"object","description":"Named method parameters (alternative: use `args` array)","required":["reservationId"],"properties":{"reservationId":{"type":"string","description":"The reservation ID"},"propertyId":{"type":"string","description":"Optional property ID"}}},"args":{"type":"array","description":"Positional arguments (alternative to `params`)","items":{},"example":[]}}}}}},"responses":{"200":{"description":"Method executed successfully","content":{"application/json":{"schema":{"type":"object","required":["success","method","durationMs"],"properties":{"success":{"type":"boolean","example":true},"method":{"type":"string","example":"generateInvoice"},"durationMs":{"type":"integer","example":1240},"result":{"description":"Method return value (shape varies by method)"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/methods/getInvoices":{"post":{"summary":"getInvoices","description":"Gets invoices for a reservation.","tags":["Methods"],"operationId":"run_getInvoices","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"params":{"type":"object","description":"Named method parameters (alternative: use `args` array)","required":["reservationId"],"properties":{"reservationId":{"type":"string","description":"The reservation ID"},"propertyId":{"type":"string","description":"Optional property ID"}}},"args":{"type":"array","description":"Positional arguments (alternative to `params`)","items":{},"example":[]}}}}}},"responses":{"200":{"description":"Method executed successfully","content":{"application/json":{"schema":{"type":"object","required":["success","method","durationMs"],"properties":{"success":{"type":"boolean","example":true},"method":{"type":"string","example":"getInvoices"},"durationMs":{"type":"integer","example":1240},"result":{"description":"Method return value (shape varies by method)"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/methods/getPropertyId":{"post":{"summary":"getPropertyId","description":"Gets the Property ID for App Little Hotelier.","tags":["Methods"],"operationId":"run_getPropertyId","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"params":{"type":"object","description":"Named method parameters (alternative: use `args` array)","properties":{"uuidPropertyAppTheBookingButton":{"type":"string","description":"Optional UUID"}}},"args":{"type":"array","description":"Positional arguments (alternative to `params`)","items":{},"example":[]}}}}}},"responses":{"200":{"description":"Method executed successfully","content":{"application/json":{"schema":{"type":"object","required":["success","method","durationMs"],"properties":{"success":{"type":"boolean","example":true},"method":{"type":"string","example":"getPropertyId"},"durationMs":{"type":"integer","example":1240},"result":{"description":"Method return value (shape varies by method)"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/methods/getInventory":{"post":{"summary":"getInventory","description":"Retrieves inventory data.","tags":["Methods"],"operationId":"run_getInventory","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"params":{"type":"object","description":"Named method parameters (alternative: use `args` array)","properties":{"startDate":{"type":"string","format":"date","example":"2026-04-01","description":"Start date (YYYY-MM-DD)"},"propertyId":{"type":"string","description":"Optional property ID"}}},"args":{"type":"array","description":"Positional arguments (alternative to `params`)","items":{},"example":[]}}}}}},"responses":{"200":{"description":"Method executed successfully","content":{"application/json":{"schema":{"type":"object","required":["success","method","durationMs"],"properties":{"success":{"type":"boolean","example":true},"method":{"type":"string","example":"getInventory"},"durationMs":{"type":"integer","example":1240},"result":{"description":"Method return value (shape varies by method)"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/methods/getRoomTypes":{"post":{"summary":"getRoomTypes","description":"The property's room types (id, name, how many rooms). The ids availability watches match on.","tags":["Methods"],"operationId":"run_getRoomTypes","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"params":{"type":"object","description":"Named method parameters (alternative: use `args` array)","properties":{"startDate":{"type":"string","format":"date","example":"2026-04-01","description":"Read the inventory from this date (YYYY-MM-DD). Default today."},"propertyId":{"type":"string","description":"Optional property ID"}}},"args":{"type":"array","description":"Positional arguments (alternative to `params`)","items":{},"example":[]}}}}}},"responses":{"200":{"description":"Method executed successfully","content":{"application/json":{"schema":{"type":"object","required":["success","method","durationMs"],"properties":{"success":{"type":"boolean","example":true},"method":{"type":"string","example":"getRoomTypes"},"durationMs":{"type":"integer","example":1240},"result":{"description":"Method return value (shape varies by method)"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/methods/getIdsAfterLogin":{"post":{"summary":"getIdsAfterLogin","description":"Gets all property IDs after login.","tags":["Methods"],"operationId":"run_getIdsAfterLogin","responses":{"200":{"description":"Method executed successfully","content":{"application/json":{"schema":{"type":"object","required":["success","method","durationMs"],"properties":{"success":{"type":"boolean","example":true},"method":{"type":"string","example":"getIdsAfterLogin"},"durationMs":{"type":"integer","example":1240},"result":{"description":"Method return value (shape varies by method)"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/methods/checkDirectBookingReservations":{"post":{"summary":"checkDirectBookingReservations","description":"Checks for direct booking reservations (cron job).","tags":["Methods"],"operationId":"run_checkDirectBookingReservations","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"params":{"type":"object","description":"Named method parameters (alternative: use `args` array)","properties":{"startDate":{"type":"string","format":"date","example":"2026-04-01","description":"Start date (YYYY-MM-DD)"},"endDate":{"type":"string","format":"date","example":"2026-04-01","description":"End date (YYYY-MM-DD)"}}},"args":{"type":"array","description":"Positional arguments (alternative to `params`)","items":{},"example":[]}}}}}},"responses":{"200":{"description":"Method executed successfully","content":{"application/json":{"schema":{"type":"object","required":["success","method","durationMs"],"properties":{"success":{"type":"boolean","example":true},"method":{"type":"string","example":"checkDirectBookingReservations"},"durationMs":{"type":"integer","example":1240},"result":{"description":"Method return value (shape varies by method)"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/methods/checkUpcomingCheckInReservations":{"post":{"summary":"checkUpcomingCheckInReservations","description":"Checks for upcoming check-in reservations (cron job).","tags":["Methods"],"operationId":"run_checkUpcomingCheckInReservations","responses":{"200":{"description":"Method executed successfully","content":{"application/json":{"schema":{"type":"object","required":["success","method","durationMs"],"properties":{"success":{"type":"boolean","example":true},"method":{"type":"string","example":"checkUpcomingCheckInReservations"},"durationMs":{"type":"integer","example":1240},"result":{"description":"Method return value (shape varies by method)"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/methods/checkTodayCheckedInReservations":{"post":{"summary":"checkTodayCheckedInReservations","description":"Checks for today's checked-in reservations (cron job).","tags":["Methods"],"operationId":"run_checkTodayCheckedInReservations","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"params":{"type":"object","description":"Named method parameters (alternative: use `args` array)","properties":{"options":{"type":"object","description":"Options object { from?: string; to?: string }"}}},"args":{"type":"array","description":"Positional arguments (alternative to `params`)","items":{},"example":[]}}}}}},"responses":{"200":{"description":"Method executed successfully","content":{"application/json":{"schema":{"type":"object","required":["success","method","durationMs"],"properties":{"success":{"type":"boolean","example":true},"method":{"type":"string","example":"checkTodayCheckedInReservations"},"durationMs":{"type":"integer","example":1240},"result":{"description":"Method return value (shape varies by method)"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/methods/checkTodayCheckOutReservations":{"post":{"summary":"checkTodayCheckOutReservations","description":"Checks for today's check-out reservations (cron job).","tags":["Methods"],"operationId":"run_checkTodayCheckOutReservations","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"params":{"type":"object","description":"Named method parameters (alternative: use `args` array)","properties":{"options":{"type":"object","properties":{"from":{"type":"string","format":"date","example":"2026-04-01","description":"Start date (YYYY-MM-DD)"},"to":{"type":"string","format":"date","example":"2026-04-01","description":"End date (YYYY-MM-DD)"}},"description":"Options object"}}},"args":{"type":"array","description":"Positional arguments (alternative to `params`)","items":{},"example":[]}}}}}},"responses":{"200":{"description":"Method executed successfully","content":{"application/json":{"schema":{"type":"object","required":["success","method","durationMs"],"properties":{"success":{"type":"boolean","example":true},"method":{"type":"string","example":"checkTodayCheckOutReservations"},"durationMs":{"type":"integer","example":1240},"result":{"description":"Method return value (shape varies by method)"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/methods/checkCheckOutReservationsByDate":{"post":{"summary":"checkCheckOutReservationsByDate","description":"Checks for check-out reservations for a specific date range.","tags":["Methods"],"operationId":"run_checkCheckOutReservationsByDate","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"params":{"type":"object","description":"Named method parameters (alternative: use `args` array)","required":["startDate","endDate"],"properties":{"startDate":{"type":"string","format":"date","example":"2026-04-01","description":"Start date (YYYY-MM-DD)"},"endDate":{"type":"string","format":"date","example":"2026-04-01","description":"End date (YYYY-MM-DD)"}}},"args":{"type":"array","description":"Positional arguments (alternative to `params`)","items":{},"example":[]}}}}}},"responses":{"200":{"description":"Method executed successfully","content":{"application/json":{"schema":{"type":"object","required":["success","method","durationMs"],"properties":{"success":{"type":"boolean","example":true},"method":{"type":"string","example":"checkCheckOutReservationsByDate"},"durationMs":{"type":"integer","example":1240},"result":{"description":"Method return value (shape varies by method)"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/methods/searchGuests":{"post":{"summary":"searchGuests","description":"Search guests by phone number, name, or email using the Little Hotelier REST API v2.","tags":["Methods"],"operationId":"run_searchGuests","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"params":{"type":"object","description":"Named method parameters (alternative: use `args` array)","properties":{"phoneNumber":{"type":"string","description":"Phone number to search (partial match)"},"firstName":{"type":"string","description":"Guest first name filter"},"lastName":{"type":"string","description":"Guest last name filter"},"email":{"type":"string","description":"Guest email filter"},"perPage":{"type":"number","description":"Results per page (max 50)","default":10},"page":{"type":"number","description":"Page number","default":1},"propertyId":{"type":"string","description":"Optional property ID (numeric)"}}},"args":{"type":"array","description":"Positional arguments (alternative to `params`)","items":{},"example":[]}}}}}},"responses":{"200":{"description":"Method executed successfully","content":{"application/json":{"schema":{"type":"object","required":["success","method","durationMs"],"properties":{"success":{"type":"boolean","example":true},"method":{"type":"string","example":"searchGuests"},"durationMs":{"type":"integer","example":1240},"result":{"description":"Method return value (shape varies by method)"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/methods/searchGuestsByQuery":{"post":{"summary":"searchGuestsByQuery","description":"Search guests using a single text query (guest_search_query) against the Little Hotelier REST API v2. Matches name, phone, email, reservation reference, etc.","tags":["Methods"],"operationId":"run_searchGuestsByQuery","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"params":{"type":"object","description":"Named method parameters (alternative: use `args` array)","required":["query"],"properties":{"query":{"type":"string","description":"General search text (name, phone, booking ref, etc.)"},"perPage":{"type":"number","description":"Results per page (max 50)","default":10},"page":{"type":"number","description":"Page number","default":1},"propertyId":{"type":"string","description":"Recommended: numeric property ID (e.g. 19170). When omitted, the property ID from the authenticated session is used automatically."}}},"args":{"type":"array","description":"Positional arguments (alternative to `params`)","items":{},"example":[]}}}}}},"responses":{"200":{"description":"Method executed successfully","content":{"application/json":{"schema":{"type":"object","required":["success","method","durationMs"],"properties":{"success":{"type":"boolean","example":true},"method":{"type":"string","example":"searchGuestsByQuery"},"durationMs":{"type":"integer","example":1240},"result":{"description":"Method return value (shape varies by method)"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}}},"components":{"securitySchemes":{"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"API key (htk_...) or Supabase JWT","description":"`Authorization: Bearer htk_xxxxxxxxxx`"}},"schemas":{"RoomType":{"type":"object","properties":{"id":{"type":"string","description":"Little Hotelier room type id. What a watch matches on.","example":"12345"},"name":{"type":"string","example":"Doble Exterior"},"totalRooms":{"type":"integer","nullable":true,"description":"Rooms of this type the property has, when the inventory says."}}},"AvailabilityWatch":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"guestName":{"type":"string","nullable":true,"description":"Free text. This is not a guest record."},"guestChannels":{"type":"array","items":{"type":"string","enum":["email","whatsapp"]}},"guestEmail":{"type":"string","nullable":true},"guestPhone":{"type":"string","nullable":true},"guestSituations":{"type":"array","items":{"type":"string","enum":["available","partial","expiring"]}},"checkIn":{"type":"string","format":"date","example":"2026-10-12"},"checkOut":{"type":"string","format":"date","description":"Exclusive: 12th → 14th is two nights.","example":"2026-10-14"},"roomTypeIds":{"type":"array","items":{"type":"string"},"description":"Empty means any room type will do."},"roomsRequested":{"type":"integer","description":"Needed on every night of the stay."},"notifyHotel":{"type":"boolean"},"hotelChannels":{"type":"array","items":{"type":"string","enum":["email","whatsapp"]}},"hotelEmail":{"type":"string","nullable":true,"description":"Falls back to the account's notification address."},"hotelPhone":{"type":"string","nullable":true},"hotelSituations":{"type":"array","items":{"type":"string","enum":["available","partial","expiring"]}},"source":{"type":"string","enum":["operator","mcp","public"],"description":"\"public\" is a self-service waiting-list signup."},"status":{"type":"string","enum":["active","paused","fulfilled","expired","cancelled"]},"lastResult":{"type":"string","nullable":true,"description":"What the last check found, verbatim — including a message that was composed and withheld."},"lastSituation":{"type":"string","nullable":true},"lastCheckedAt":{"type":"string","format":"date-time","nullable":true},"notifiedAt":{"type":"string","format":"date-time","nullable":true,"description":"Set once. Availability flickers, so a watch announces one time."},"checkCount":{"type":"integer"},"summary":{"type":"string"}}},"AvailabilityWatchInput":{"type":"object","required":["checkIn","checkOut"],"properties":{"id":{"type":"string","format":"uuid","description":"Omit to create; supply to update one of your own."},"checkIn":{"type":"string","format":"date","description":"First night. Must be in the future."},"checkOut":{"type":"string","format":"date","description":"Departure day, exclusive."},"roomsRequested":{"type":"integer","minimum":1,"maximum":50,"default":1},"roomTypeIds":{"type":"array","items":{"type":"string"},"description":"From /room-types. Empty means any type."},"guestName":{"type":"string"},"guestChannels":{"type":"array","items":{"type":"string","enum":["email","whatsapp"]},"description":"Empty tells the guest nothing — valid when the watch is for the property alone."},"guestEmail":{"type":"string","description":"Required when guestChannels includes email."},"guestPhone":{"type":"string","description":"Required when guestChannels includes whatsapp. International format."},"guestSituations":{"type":"array","items":{"type":"string","enum":["available","partial","expiring"]},"description":"Default [\"available\"]: a partial match is a phone call from the property, not an automatic message to a guest."},"notifyHotel":{"type":"boolean","default":true},"hotelChannels":{"type":"array","items":{"type":"string","enum":["email","whatsapp"]}},"hotelEmail":{"type":"string"},"hotelPhone":{"type":"string"},"hotelSituations":{"type":"array","items":{"type":"string","enum":["available","partial","expiring"]},"description":"Default all three — the front desk is what can act on the other two."},"status":{"type":"string","enum":["active","paused","cancelled"]}}},"ScheduledTask":{"type":"object","properties":{"function":{"type":"string","example":"checkAvailabilityWatches"},"scheduled":{"type":"boolean","description":"At least one task runs it and is enabled."},"advanced":{"type":"boolean","description":"More than one task runs it — edit them on the Cron Jobs page."},"minIntervalMinutes":{"type":"integer","description":"Tightest gap this function may be given."},"primary":{"type":"object","nullable":true,"properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"schedule":{"type":"string","example":"0 1 * * *"},"scheduleText":{"type":"string","example":"Every day at 01:00"},"isActive":{"type":"boolean"},"lastRunAt":{"type":"string","format":"date-time","nullable":true},"nextRunAt":{"type":"string","format":"date-time","nullable":true}}},"jobs":{"type":"array","items":{"type":"object"}},"presets":{"type":"array","items":{"type":"object"},"description":"Offered schedules: every 20/30 minutes, every 1/3/6/12/24 hours."}}},"HealthResponse":{"type":"object","properties":{"status":{"type":"string","example":"ok"},"version":{"type":"string","example":"1.0.0"},"timestamp":{"type":"string","format":"date-time"},"endpoints":{"type":"array","items":{"type":"string"}}}},"Reservation":{"type":"object","description":"Reservation object from Little Hotelier (shape may vary)","properties":{"id":{"type":"string"},"guestName":{"type":"string"},"status":{"type":"string","enum":["confirmed","cancelled","checked_in","checked_out","no_show"]},"checkIn":{"type":"string","format":"date"},"checkOut":{"type":"string","format":"date"},"roomType":{"type":"string"},"totalAmount":{"type":"number"}}},"Guest":{"type":"object","description":"Guest record from Little Hotelier REST API v2","properties":{"id":{"type":"integer","example":55682769},"property_id":{"type":"integer","example":19170},"first_name":{"type":"string","example":"TEODOMIRO"},"last_name":{"type":"string","example":"MAESTRO TORCA"},"name":{"type":"string","example":"TEODOMIRO MAESTRO TORCA"},"email":{"type":"string","nullable":true},"phone_number":{"type":"string","nullable":true,"example":"636667773"},"organisation":{"type":"string","nullable":true},"reservation_ids":{"type":"object","description":"Map of booking reference → numeric reservation ID","additionalProperties":{"type":"integer"},"example":{"LH24030138843905":38843905}},"reservations":{"type":"array","items":{"type":"string"},"description":"Booking reference strings","example":["LH24030138843905"]},"reservationDetails":{"type":"array","description":"Present only when includeReservations=true","items":{"type":"object","properties":{"ref":{"type":"string"},"id":{"type":"integer"},"details":{}}}},"totalReservations":{"type":"integer","description":"Total reservations for this guest (present when includeReservations=true)"},"reservationDetailsTruncated":{"type":"boolean","description":"True if more reservations exist beyond maxReservationsPerGuest"}}},"Invoice":{"type":"object","description":"Invoice object from Little Hotelier","properties":{"id":{"type":"integer"},"invoice_number":{"type":"integer"},"uploaded":{"type":"integer"},"pdf_name":{"type":"string"},"status":{"type":"string"},"custom_invoice_number":{"type":"string"},"url":{"type":"string"},"download_url":{"type":"string"},"updated_at":{"type":"string"},"outdated":{"type":"boolean"},"updated":{"type":"boolean"},"invoice_id":{"type":"integer"},"invoice_compliance_data":{"type":"object"}}},"MethodMeta":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"endpoint":{"type":"string"},"params":{"type":"array","items":{"type":"object"}}}},"ErrorResponse":{"type":"object","required":["statusCode","statusMessage"],"properties":{"statusCode":{"type":"integer"},"statusMessage":{"type":"string"}}}},"responses":{"Unauthorized":{"description":"Missing or invalid Bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"statusCode":401,"statusMessage":"Unauthorized"}}}},"NotFound":{"description":"Method not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"statusCode":404,"statusMessage":"Method \"foo\" not found"}}}},"ValidationError":{"description":"Invalid query parameters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"statusCode":422,"statusMessage":"startDate must be YYYY-MM-DD"}}}},"RateLimited":{"description":"Rate limit exceeded","headers":{"Retry-After":{"description":"Seconds until reset","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"statusCode":429,"statusMessage":"Too many requests. Try again in 42 seconds."}}}},"InternalError":{"description":"Unexpected server-side error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"statusCode":500,"statusMessage":"Internal Server Error"}}}}}}}