{"openapi":"3.1.0","info":{"title":"VistaLink API","description":"VistaLink hotel API v2. Authenticate with `Authorization: Bearer vl_live_…` (or `vl_test_…`). Money is a decimal string with a sibling ISO 4217 `currency`. Every response carries `request_id` and `billing` (null when free); errors are `{error: {code, message, request_id, details?}}`. Rate-limit headers `X-RateLimit-Limit/Remaining/Reset`, credit header `X-Credit-Remaining` (USD).","version":"2.1.0","summary":"Hotel resolution, search, content, rates, room reconciliation, booking and email quotes.","contact":{"name":"VistaLink","email":"contact@vistalink.com","url":"https://vistalink.com/developers/docs"}},"servers":[{"url":"https://api.vistalink.com"}],"paths":{"/v2/hotels/resolve":{"post":{"tags":["Resolve"],"summary":"Resolve hotel identifiers to hotel_id","description":"Free and synchronous; needs the `search` scope. Up to 500 identifiers (a listing URL in `booking_url`, the hotel's own `website_url`, an `expedia_url`, or `name` + `city` + `country`) come back as one result each, in request order. An input that does not resolve carries its `status` and `reason` instead of failing the call.","operationId":"resolveHotels","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResolveRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResolveResponse"}}}},"400":{"description":"bad_request (validation errors carry details.fields[])","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"forbidden (missing scope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"rate_limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"x-vistalink-kind":"sync","x-vistalink-scope":"search","x-vistalink-cli":"vistalink hotels resolve"}},"/v2/hotels/{hotel_id}":{"get":{"tags":["Hotels"],"summary":"Hotel detail","description":"Synchronous; needs the `content` scope and bills `hotel.detail` once per call. Prices appear only when `check_in` and `check_out` are both set; `rates=refresh` also starts a `rates.live` job, which needs the `rates` scope and the Build plan or higher and bills `rates.direct.live` when it completes. `sections` limits the response to the parts you ask for.","operationId":"getHotel","parameters":[{"name":"hotel_id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The hotel's `hotel_id` (uuid), as returned by resolve or search.","example":"43d0be71-5f3c-4c8e-9a52-1d7e2b6f0a19","x-vistalink-render-default":true},{"name":"sections","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sections"},"description":"Comma-separated sections to return, any of core, images, rooms, venues, policies, facts, reviews, guest_voice, rates, location; all of them when omitted.","example":"core,images,rooms,rates","x-vistalink-render-default":true},{"name":"check_in","in":"query","required":false,"schema":{"type":"string","format":"date"},"description":"Arrival date, YYYY-MM-DD; rates are only returned when both dates are set.","example":"2026-11-14","x-vistalink-render-default":true},{"name":"check_out","in":"query","required":false,"schema":{"type":"string","format":"date"},"description":"Departure date, YYYY-MM-DD, after `check_in` and sent together with it.","example":"2026-11-16","x-vistalink-render-default":true},{"name":"adults","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","maximum":16,"minimum":1},{"type":"null"}],"title":"Adults"},"description":"Adults across all rooms for the rates lookup; defaults to 2 per room.","example":2,"x-vistalink-render-default":true},{"name":"children_ages","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Children Ages"},"description":"Comma-separated ages (0-17) of the children; cached rates cover adults-only parties, so any child returns no prices and a `rates_party_unsupported` warning."},{"name":"rooms","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","maximum":9,"minimum":1},{"type":"null"}],"title":"Rooms"},"description":"Number of rooms for the rates lookup; defaults to 1."},{"name":"currency","in":"query","required":false,"schema":{"anyOf":[{"type":"string","minLength":3,"maxLength":3},{"type":"null"}],"title":"Currency"},"description":"ISO 4217 currency of the caller; prices are never converted and stay in the currency they were observed in.","example":"EUR","x-vistalink-render-default":true},{"name":"locale","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":10},{"type":"null"}],"title":"Locale"},"description":"Language for descriptions and other translated text, as a language tag (`pt-BR` reads as `pt`); English when omitted or untranslated."},{"name":"search_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Search Id"},"description":"The `search_id` of a recent search that returned this hotel, which lets the call reuse the content that search prepared; the price is the same.","example":"srch_3f9c2a7e4b1d4c8a9e6f0b2d5a7c1e93","x-vistalink-render-default":true},{"name":"rates","in":"query","required":false,"schema":{"type":"string","enum":["cached","refresh"],"default":"cached"},"description":"`cached` serves stored observations; `refresh` also starts a live rates job (needs the `rates` scope and the Build plan, billed `rates.direct.live`)."},{"name":"image_width","in":"query","required":false,"schema":{"type":"integer","enum":[640,1024,1600],"default":1024},"description":"Width in pixels of every image URL returned: 640, 1024 or 1600."},{"name":"reviews_limit","in":"query","required":false,"schema":{"type":"integer","maximum":50,"minimum":0,"default":10,"title":"Reviews Limit"},"description":"Maximum review items in `reviews.items`."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HotelDetailResponse"}}}},"400":{"description":"bad_request (validation errors carry details.fields[])","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"forbidden (missing scope, or details.reason tier_required: this needs the Build plan or higher; on Free, details.free_trial says what a test key may try)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"rate_limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"402":{"description":"insufficient_credit","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"not_found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"x-vistalink-kind":"sync","x-vistalink-scope":"content","x-vistalink-sku":["hotel.detail","rates.direct.live"],"x-vistalink-sku-rule":"`hotel.detail` per call. `rates=refresh` also starts a live rates job, billed as `rates.direct.live` when it completes.","x-vistalink-cli":"vistalink hotels get"}},"/v2/search":{"post":{"tags":["Search"],"summary":"Search hotels","description":"Synchronous; needs the `search` scope. Send a natural-language `query`, structured `filters`, or both; at least one of `query`, `filters.location`, `filters.near` or `filters.hotel_ids` is required. Billed per call: a `query` makes it loose (else structured) and `ranking` picks filters or semantic, giving `search.structured.filters`, `search.loose.filters`, `search.structured.semantic` or `search.loose.semantic`, plus `search.extra_results` for each result beyond 10. Further pages of the same `search_id` are free, and a `needs_clarification` response is free; answer it with `{search_id, answers}`.","operationId":"searchHotels","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchResponse"}}}},"400":{"description":"bad_request (validation errors carry details.fields[])","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"forbidden (missing scope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"rate_limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"402":{"description":"insufficient_credit","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"x-vistalink-kind":"sync","x-vistalink-scope":"search","x-vistalink-sku":["search.structured.filters","search.loose.filters","search.structured.semantic","search.loose.semantic","search.extra_results"],"x-vistalink-sku-rule":"`query` present → loose, else structured; `ranking` picks filters or semantic. Results beyond 10 add `search.extra_results` (5% of the base price each). Pages of the same `search_id` are free. A `needs_clarification` response is free; the answered follow-up is billed at the original price. An empty result with `status: completed` is billed; a 503 is not.","x-vistalink-cli":"vistalink search run"}},"/v2/rates":{"post":{"tags":["Rates"],"summary":"Rates for a stay","description":"Needs the `rates` scope. `freshness: cached` is synchronous and bills `rates.direct.cached` for each hotel that comes back `observed`, or `stale` within `max_age_hours`; `freshness: live` answers 202 with a `rates.live` job, needs the Build plan or higher, and bills `rates.direct.live` for each hotel it observes. `channel: third_party` is Enterprise-only (403 `forbidden`, `details.reason: enterprise_only` otherwise). Honours `Idempotency-Key` when it creates a job.","operationId":"getRates","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RatesRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RatesResponse"}}}},"400":{"description":"bad_request (validation errors carry details.fields[])","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"forbidden (missing scope, or details.reason tier_required: this needs the Build plan or higher; on Free, details.free_trial says what a test key may try)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"rate_limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"402":{"description":"insufficient_credit","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"202":{"description":"Live job accepted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Job"}}}},"409":{"description":"conflict (Idempotency-Key reused with another body)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"x-vistalink-kind":"sync","x-vistalink-kind-switch":{"kind":"job","when":"`freshness` is `live`"},"x-vistalink-scope":"rates","x-vistalink-sku":["rates.direct.cached","rates.direct.live"],"x-vistalink-sku-rule":"Billed per hotel that comes back `observed`, or `stale` within `max_age_hours`. `unknown`, `unsupported` and `unresolved` are free.","x-vistalink-cli":"vistalink rates get","parameters":[{"name":"Idempotency-Key","in":"header","description":"Any unique string. Same key and same body returns the original response; same key and a different body is a 409.","schema":{"type":"string","maxLength":255},"example":"4f1c2b8e-booking-ada-rossi","required":false}]}},"/v2/rates/batch":{"post":{"tags":["Rates"],"summary":"Rates for many hotels and stays","description":"Always a job: answers 202 with a `rates.batch` job; needs the `rates` scope and the Build plan or higher (a Free plan's test key may try `freshness: cached` with up to 25 hotels and 1 stay). Billed at the `POST /v2/rates` unit prices for each hotel and stay that comes back priced, 25% off when the batch has 1,000 hotels or more. The result is NDJSON at `result_url`, one line per hotel and stay in the `results[]` shape of `POST /v2/rates` with its `stay`, plus a `summary`. The 10 cheapest available offers of each line can be booked with `POST /v2/bookings` for 30 minutes; re-quote any other offer with `POST /v2/rates`. Honours `Idempotency-Key`.","operationId":"batchRates","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RatesBatchRequest"}}},"required":true},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Job"}}}},"400":{"description":"bad_request (validation errors carry details.fields[])","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"forbidden (missing scope, or details.reason tier_required: this needs the Build plan or higher; on Free, details.free_trial says what a test key may try)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"rate_limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"402":{"description":"insufficient_credit","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"x-vistalink-kind":"job","x-vistalink-scope":"rates","x-vistalink-sku":["rates.direct.cached","rates.direct.live","rates.batch"],"x-vistalink-sku-rule":"Same unit prices as `POST /v2/rates`, 25% off when the batch has 1,000 hotels or more.","x-vistalink-cli":"vistalink rates batch","parameters":[{"name":"Idempotency-Key","in":"header","description":"Any unique string. Same key and same body returns the original response; same key and a different body is a 409.","schema":{"type":"string","maxLength":255},"example":"4f1c2b8e-booking-ada-rossi","required":false}]}},"/v2/rooms/reconcile":{"post":{"tags":["Rooms"],"summary":"Reconcile room types across sources","description":"Needs the `reconcile` scope and the Build plan or higher (a Free plan's test key may try it with up to 2 sources). Answers 200 with the result when every source is `kind: rooms` and the hotel is known, otherwise 202 with a `rooms.reconcile` job. Billed once per request, `rooms.reconcile.urls` when any source is a URL and `rooms.reconcile.supplied` otherwise; an identical request within 30 days returns the stored result at amount `\"0\"`. Honours `Idempotency-Key`.","operationId":"reconcileRooms","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReconcileRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReconcileResult"}}}},"400":{"description":"bad_request (validation errors carry details.fields[])","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"forbidden (missing scope, or details.reason tier_required: this needs the Build plan or higher; on Free, details.free_trial says what a test key may try)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"rate_limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"402":{"description":"insufficient_credit","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"202":{"description":"Job accepted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Job"}}}},"404":{"description":"not_found (URL sources need a hotel that resolves)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"502":{"description":"upstream_error (the matcher failed; retry later)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"x-vistalink-kind":"job","x-vistalink-kind-switch":{"kind":"sync","when":"every source has `kind: rooms` and `hotel_id` is known"},"x-vistalink-scope":"reconcile","x-vistalink-sku":["rooms.reconcile.urls","rooms.reconcile.supplied"],"x-vistalink-sku-rule":"`rooms.reconcile.urls` when any source is a URL, else `rooms.reconcile.supplied`. A repeat within 30 days bills `\"0\"`.","x-vistalink-cli":"vistalink rooms reconcile","parameters":[{"name":"Idempotency-Key","in":"header","description":"Any unique string. Same key and same body returns the original response; same key and a different body is a 409.","schema":{"type":"string","maxLength":255},"example":"4f1c2b8e-booking-ada-rossi","required":false}]}},"/v2/bookings":{"post":{"tags":["Bookings"],"summary":"Create a booking","description":"Answers 202 with the Booking, which then moves on its own: poll GET /v2/bookings/{booking_id} or subscribe to `booking.updated`. Needs the `booking` scope, the Build plan or higher and an `Idempotency-Key`; bookings are free and `billing` is always null. Payment `mode` is `checkout` (a hosted checkout link in `payment.checkout_url`, where the guest pays the property directly), `handoff` (a hosted session for the guest) or `external` (the booking engine's own prefilled page). `card_entry` is rolling out and answers 403 `not_yet_available` in production: it needs `payment_method` and `mandate`, answers `requires_credential`, and nothing touches the hotel until `POST /v2/bookings/{booking_id}/confirm`.","operationId":"createBooking","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingRequest"}}},"required":true},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Booking"}}}},"400":{"description":"bad_request (validation errors carry details.fields[])","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"forbidden (missing scope, or details.reason tier_required: this needs the Build plan or higher; on Free, details.free_trial says what a test key may try)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"rate_limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"not_found (offer unknown or expired)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"x-vistalink-kind":"job","x-vistalink-scope":"booking","x-vistalink-sku":["booking.confirmed"],"x-vistalink-sku-rule":"Free. On commissionable hotels the commission is split with you; see Billing.","x-vistalink-cli":"vistalink bookings create","parameters":[{"name":"Idempotency-Key","in":"header","description":"Required. Any unique string. Same key and same body returns the original response; same key and a different body is a 409.","schema":{"type":"string","maxLength":255},"example":"4f1c2b8e-booking-ada-rossi","required":true}]}},"/v2/bookings/{booking_id}/confirm":{"post":{"tags":["Bookings"],"summary":"Confirm a card-entry booking","description":"Rolling out; answers 403 not_yet_available in production. `card_entry` only: attaches the credential (or uses the one bound at create) and starts the booking on the hotel's own checkout, where the card is typed and the hotel charges it as merchant of record. Answers 202 with the Booking in `queued`; follow it with GET /v2/bookings/{booking_id} or `booking.updated`. Accepted only in `requires_credential` and only once: any other status, including `submitted` and `unknown`, is 409 `booking_not_confirmable`. Needs the `booking` scope, the Build plan or higher and an `Idempotency-Key`. Test keys get a simulated outcome chosen by the credential's test token.","operationId":"confirmBooking","parameters":[{"name":"booking_id","in":"path","required":true,"schema":{"type":"string","title":"Booking Id"},"description":"The `booking_id` returned by POST /v2/bookings.","example":"bk_P2w9Xc4Rt1Lm8Qz3","x-vistalink-render-default":true},{"name":"Idempotency-Key","in":"header","description":"Required. Any unique string. Same key and same body returns the original response; same key and a different body is a 409.","schema":{"type":"string","maxLength":255},"example":"4f1c2b8e-booking-ada-rossi","required":true}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConfirmBookingRequest"}}}},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Booking"}}}},"400":{"description":"bad_request (validation errors carry details.fields[])","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"forbidden (missing scope, or details.reason tier_required: this needs the Build plan or higher; on Free, details.free_trial says what a test key may try)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"rate_limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"not_found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"conflict (details.reason booking_not_confirmable, mandate_expired, credential_expired, credential_consumed, credential_deleted or credential_booking_mismatch)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"x-vistalink-kind":"job","x-vistalink-scope":"booking","x-vistalink-cli":"vistalink bookings confirm"}},"/v2/payment_credentials":{"post":{"tags":["Bookings"],"summary":"Register a payment credential","description":"Rolling out; answers 403 not_yet_available in production. Turns a Basis Theory card token you created with VistaLink's public key into a one-booking `cred_` credential for `payment.mode card_entry`. The card number never reaches VistaLink: we read the token masked and keep only brand, last four and expiry. A credential lasts 30 minutes (or less, with the token), pays for one booking, and its vault token is deleted once that booking is submitted or ends. Needs the `booking` scope and the Build plan or higher; `Idempotency-Key` is optional.","operationId":"createPaymentCredential","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentCredentialRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentCredential"}}}},"400":{"description":"bad_request (validation errors carry details.fields[])","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"forbidden (missing scope, or details.reason tier_required: this needs the Build plan or higher; on Free, details.free_trial says what a test key may try)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"rate_limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"conflict (details.reason credential_reused)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"x-vistalink-kind":"sync","x-vistalink-scope":"booking","x-vistalink-cli":"vistalink credentials create","parameters":[{"name":"Idempotency-Key","in":"header","description":"Any unique string. Same key and same body returns the original response; same key and a different body is a 409.","schema":{"type":"string","maxLength":255},"example":"4f1c2b8e-booking-ada-rossi","required":false}]}},"/v2/bookings/{booking_id}":{"get":{"tags":["Bookings"],"summary":"Get a booking","description":"Synchronous and free; needs the `booking` scope. Returns the booking's current status, payment, confirmation and timeline.","operationId":"getBooking","parameters":[{"name":"booking_id","in":"path","required":true,"schema":{"type":"string","title":"Booking Id"},"description":"The `booking_id` returned by POST /v2/bookings.","example":"bk_P2w9Xc4Rt1Lm8Qz3","x-vistalink-render-default":true}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Booking"}}}},"400":{"description":"bad_request (validation errors carry details.fields[])","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"forbidden (missing scope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"rate_limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"not_found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"x-vistalink-kind":"sync","x-vistalink-scope":"booking","x-vistalink-cli":"vistalink bookings get"}},"/v2/bookings/{booking_id}/cancel":{"post":{"tags":["Bookings"],"summary":"Cancel a booking","description":"Synchronous and free; needs the `booking` scope. Allowed while the booking is `queued`, `requires_payment`, `requires_user_action` or `requires_credential`, and while `processing` only before the engine submit step; after that the cancel is refused with 409 `conflict`. Canceling after a completed checkout payment sets `payment.refund_required: true`.","operationId":"cancelBooking","parameters":[{"name":"booking_id","in":"path","required":true,"schema":{"type":"string","title":"Booking Id"},"description":"The `booking_id` returned by POST /v2/bookings.","example":"bk_P2w9Xc4Rt1Lm8Qz3","x-vistalink-render-default":true}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Booking"}}}},"400":{"description":"bad_request (validation errors carry details.fields[])","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"forbidden (missing scope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"rate_limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"not_found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"conflict (too late to cancel)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"x-vistalink-kind":"sync","x-vistalink-scope":"booking","x-vistalink-cli":"vistalink bookings cancel"}},"/v2/quotes":{"post":{"tags":["Quotes"],"summary":"Request a quote from a hotel","description":"Answers 202 with the Quote; VistaLink then emails the hotel, from its own address and on behalf of a guest it does not name, for a direct price on the stay. Most hotels reply within 6 hours, some take a day: poll GET /v2/quotes/{quote_id} or subscribe to `quote.updated`. Costs $0.25 per quote the hotel answers with a price, charged once when the first priced offer arrives (reserved from your credit now, released when the quote closes without one); nothing when the hotel declines or does not reply. Needs the `quotes` scope, the Build plan or higher and an `Idempotency-Key`. Test keys never email anyone and cost nothing, on every plan, Free included: a simulated hotel answers within seconds, as `sandbox_outcome` says.","operationId":"createQuote","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteRequest"}}},"required":true},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Quote"}}}},"400":{"description":"bad_request (validation errors carry details.fields[])","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"forbidden (missing scope, or details.reason tier_required: this needs the Build plan or higher; on Free, details.free_trial says what a test key may try)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"rate_limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"402":{"description":"insufficient_credit","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"200":{"description":"Your open quote for the same hotel and stay, returned instead of a new one (header `X-Quote-Existing: true`); no new email, no charge.","headers":{"X-Quote-Existing":{"description":"`true`: this is an existing open quote.","schema":{"type":"string","enum":["true"]}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Quote"}}}}},"x-vistalink-kind":"job","x-vistalink-scope":"quotes","x-vistalink-sku":["quote.email.received"],"x-vistalink-sku-rule":"Reserved when the quote is created and charged once, when the hotel's first priced offer arrives. Released when the quote closes without one (declined, no reply, cancelled, failed). Returning your open quote for the same hotel and stay is free.","x-vistalink-cli":"vistalink quotes create","parameters":[{"name":"Idempotency-Key","in":"header","description":"Required. Any unique string. Same key and same body returns the original response; same key and a different body is a 409.","schema":{"type":"string","maxLength":255},"example":"4f1c2b8e-booking-ada-rossi","required":true}]}},"/v2/quotes/{quote_id}":{"get":{"tags":["Quotes"],"summary":"Get a quote","description":"Synchronous and free; needs the `quotes` scope. The quote's status, the hotel's offers and `next_step`, one sentence you can show as is.","operationId":"getQuote","parameters":[{"name":"quote_id","in":"path","required":true,"schema":{"type":"string","title":"Quote Id"},"description":"The `quote_id` returned by POST /v2/quotes.","example":"qte_7c2e9a4b1d8f3e6a0c5b","x-vistalink-render-default":true}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Quote"}}}},"400":{"description":"bad_request (validation errors carry details.fields[])","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"forbidden (missing scope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"rate_limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"not_found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"x-vistalink-kind":"sync","x-vistalink-scope":"quotes","x-vistalink-cli":"vistalink quotes get"}},"/v2/quotes/{quote_id}/cancel":{"post":{"tags":["Quotes"],"summary":"Cancel a quote","description":"Synchronous and free; needs the `quotes` scope. Stops further emails to the hotel while the quote is `queued`, `contacting`, `waiting` or `offer_received`; cancelling twice returns the cancelled quote. A quote cancelled before a priced offer costs nothing; one cancelled after keeps its charge. A closed quote answers 409 `conflict`.","operationId":"cancelQuote","parameters":[{"name":"quote_id","in":"path","required":true,"schema":{"type":"string","title":"Quote Id"},"description":"The `quote_id` returned by POST /v2/quotes.","example":"qte_7c2e9a4b1d8f3e6a0c5b","x-vistalink-render-default":true}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Quote"}}}},"400":{"description":"bad_request (validation errors carry details.fields[])","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"forbidden (missing scope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"rate_limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"not_found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"conflict (the quote is already closed)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"x-vistalink-kind":"sync","x-vistalink-scope":"quotes","x-vistalink-cli":"vistalink quotes cancel"}},"/v2/quotes/{quote_id}/offers/{offer_id}/accept":{"post":{"tags":["Quotes"],"summary":"Accept a quote offer","description":"Free; needs the `quotes` scope and the Build plan or higher (test keys: any plan). Sends the hotel one introduction email with your guest copied, on the same thread as the quote, and answers with the Quote in status `accepted`; the hotel then confirms the booking with the guest directly. Allowed while the quote is `offer_received` or `completed`. Repeating the same accept returns the accepted quote. `hotel_commission_applies` says whether the hotel pays a commission on the stay (half of it is yours).","operationId":"acceptQuoteOffer","parameters":[{"name":"quote_id","in":"path","required":true,"schema":{"type":"string","title":"Quote Id"},"description":"The `quote_id` returned by POST /v2/quotes.","example":"qte_7c2e9a4b1d8f3e6a0c5b","x-vistalink-render-default":true},{"name":"offer_id","in":"path","required":true,"schema":{"type":"string","title":"Offer Id"},"description":"The `offer_id` of one of the quote's `offers`.","example":"qof_4a9d2c7e1b8f5a3d6e0c","x-vistalink-render-default":true},{"name":"Idempotency-Key","in":"header","description":"Any unique string. Same key and same body returns the original response; same key and a different body is a 409.","schema":{"type":"string","maxLength":255},"example":"4f1c2b8e-booking-ada-rossi","required":false}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteAcceptRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Quote"}}}},"400":{"description":"bad_request (validation errors carry details.fields[])","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"forbidden (missing scope, or details.reason tier_required: this needs the Build plan or higher; on Free, details.free_trial says what a test key may try)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"rate_limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"not_found (quote or offer)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"conflict (the quote is closed, or another offer was accepted)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"x-vistalink-kind":"sync","x-vistalink-scope":"quotes","x-vistalink-cli":"vistalink quotes accept"}},"/v2/jobs/{job_id}":{"get":{"tags":["Jobs"],"summary":"Get a job","description":"Synchronous and free; any key with at least one scope can read its own jobs. Poll until `status` is `succeeded`, `failed` or `canceled`; jobs expire 7 days after creation.","operationId":"getJob","parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","title":"Job Id"},"description":"The `job_id` from a 202 response.","example":"job_K8s2Lq9Wx4Np","x-vistalink-render-default":true}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Job"}}}},"400":{"description":"bad_request (validation errors carry details.fields[])","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"forbidden (missing scope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"rate_limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"not_found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"x-vistalink-kind":"sync","x-vistalink-cli":"vistalink jobs get"}},"/v2/jobs/{job_id}/result":{"get":{"tags":["Jobs"],"summary":"Download a job's result","description":"Synchronous and free; any key with at least one scope. The NDJSON result of a `rates.batch` job, one line per hotel and stay, served here when the job's `result_url` points at this path.","operationId":"getJobResult","parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","title":"Job Id"},"description":"The `job_id` of a `rates.batch` job.","example":"job_K8s2Lq9Wx4Np","x-vistalink-render-default":true}],"responses":{"200":{"description":"NDJSON lines","content":{"application/json":{"schema":{}},"application/x-ndjson":{}}},"400":{"description":"bad_request (validation errors carry details.fields[])","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"forbidden (missing scope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"rate_limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"not_found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"x-vistalink-kind":"sync","x-vistalink-cli":"vistalink jobs result"}},"/v2/jobs":{"get":{"tags":["Jobs"],"summary":"List jobs","description":"Synchronous and free; any key with at least one scope. Newest first; pass `next_cursor` back as `cursor` for the next page.","operationId":"listJobs","parameters":[{"name":"kind","in":"query","required":false,"schema":{"type":"string","enum":["rates.live","rates.batch","rooms.reconcile"]},"description":"Only jobs of this kind."},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["queued","running","succeeded","failed","canceled"]},"description":"Only jobs in this status."},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":512},{"type":"null"}],"title":"Cursor"},"description":"The `next_cursor` of the previous page; omit for the first page.","example":"MjAyNi0xMC0wN1QwOTozMDowMCswMDowMHxqb2JfSzhzMkxxOVd4NE5w","x-vistalink-render-default":true},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":20,"title":"Limit"},"description":"Jobs per page."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobList"}}}},"400":{"description":"bad_request (validation errors carry details.fields[])","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"forbidden (missing scope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"rate_limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"x-vistalink-kind":"sync","x-vistalink-cli":"vistalink jobs list"}},"/v2/jobs/{job_id}/cancel":{"post":{"tags":["Jobs"],"summary":"Cancel a job","description":"Synchronous and free; needs the scope of the job's kind (`rates` for `rates.*`, `reconcile` for `rooms.reconcile`). Only `queued` or `running` jobs can be canceled (409 otherwise), and the credit held for the job is released.","operationId":"cancelJob","parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","title":"Job Id"},"description":"The `job_id` to cancel.","example":"job_K8s2Lq9Wx4Np","x-vistalink-render-default":true}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Job"}}}},"400":{"description":"bad_request (validation errors carry details.fields[])","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"forbidden (missing scope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"rate_limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"not_found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"x-vistalink-kind":"sync","x-vistalink-cli":"vistalink jobs cancel"}},"/v2/webhooks":{"get":{"tags":["Webhooks"],"summary":"List webhooks","description":"Synchronous and free; any key with at least one scope. The tenant's active webhooks, newest first; secrets are never returned again.","operationId":"listWebhooks","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookList"}}}},"400":{"description":"bad_request (validation errors carry details.fields[])","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"forbidden (missing scope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"rate_limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"x-vistalink-kind":"sync","x-vistalink-cli":"vistalink webhooks list"},"post":{"tags":["Webhooks"],"summary":"Create a webhook","description":"Synchronous and free; each event needs its family's scope (`booking.updated` needs `booking`, `rates.*` need `rates`, `rooms.reconcile.completed` needs `reconcile`, `quote.updated` needs `quotes`). Deliveries are signed POSTs described under Webhooks, retried after 1m, 5m, 30m, 2h, 12h and 24h; the `secret` is returned only in this response.","operationId":"createWebhook","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookCreateRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookCreated"}}}},"400":{"description":"bad_request (validation errors carry details.fields[])","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"forbidden (missing scope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"rate_limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"conflict (too many webhooks)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"x-vistalink-kind":"sync","x-vistalink-cli":"vistalink webhooks create"}},"/v2/webhooks/{webhook_id}":{"delete":{"tags":["Webhooks"],"summary":"Delete a webhook","description":"Synchronous and free; needs the scope of every event the webhook subscribes to. Answers 204 with no body, and deliveries still pending for it are dropped.","operationId":"deleteWebhook","parameters":[{"name":"webhook_id","in":"path","required":true,"schema":{"type":"string","title":"Webhook Id"},"description":"The `webhook_id` returned when the webhook was created.","example":"wh_N4c7Bz1Qk2Vt","x-vistalink-render-default":true}],"responses":{"204":{"description":"Successful Response"},"400":{"description":"bad_request (validation errors carry details.fields[])","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"forbidden (missing scope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"rate_limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"not_found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"x-vistalink-kind":"sync","x-vistalink-cli":"vistalink webhooks delete"}},"/v2/webhooks/{webhook_id}/deliveries":{"get":{"tags":["Webhooks"],"summary":"List deliveries","description":"Synchronous and free; any key with at least one scope. Delivery attempts for one webhook, newest first, with the last HTTP status and error of each.","operationId":"listWebhookDeliveries","parameters":[{"name":"webhook_id","in":"path","required":true,"schema":{"type":"string","title":"Webhook Id"},"description":"The `webhook_id` whose deliveries to list.","example":"wh_N4c7Bz1Qk2Vt","x-vistalink-render-default":true},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":512},{"type":"null"}],"title":"Cursor"},"description":"The `next_cursor` of the previous page; omit for the first page.","example":"MjAyNi0xMC0wN1QwOTozMDowMCswMDowMHx3aGRfUjJ0OE14NVFwMUx6","x-vistalink-render-default":true},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":50,"title":"Limit"},"description":"Deliveries per page, newest first."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookDeliveryList"}}}},"400":{"description":"bad_request (validation errors carry details.fields[])","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"forbidden (missing scope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"rate_limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"not_found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"x-vistalink-kind":"sync","x-vistalink-cli":"vistalink webhooks deliveries"}},"/v2/chat":{"post":{"tags":["Chat"],"summary":"Conversational hotel search","description":"Synchronous; needs the `chat` scope and bills `chat.turn` per turn, except turns that only ask a clarifying question, which are free. Same request as /v1/chat plus `search_id`; `hotels[]` use the search card shape, and the conversation is remembered per `session_id`.","operationId":"chat","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatResponse"}}}},"400":{"description":"bad_request (validation errors carry details.fields[])","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"forbidden (missing scope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"rate_limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"402":{"description":"insufficient_credit","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"x-vistalink-kind":"sync","x-vistalink-scope":"chat","x-vistalink-sku":["chat.turn"],"x-vistalink-sku-rule":"Billed per turn. Clarification-only turns are free.","x-vistalink-cli":"vistalink chat send"}},"/v2/amenities":{"get":{"tags":["Catalog"],"summary":"Amenity codes","description":"Synchronous and free; any valid key. The codes `filters.amenities` accepts; a `kind: group` code matches any of its `members`.","operationId":"listAmenities","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AmenityList"}}}},"400":{"description":"bad_request (validation errors carry details.fields[])","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"forbidden (missing scope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"rate_limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"x-vistalink-kind":"sync","x-vistalink-cli":"vistalink catalog amenities"}},"/v2/engines":{"get":{"tags":["Catalog"],"summary":"Supported booking engines","description":"Synchronous and free; any valid key. Booking engines we read prices from or book on, with their capabilities and hotel counts.","operationId":"listEngines","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EngineList"}}}},"400":{"description":"bad_request (validation errors carry details.fields[])","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"forbidden (missing scope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"rate_limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"x-vistalink-kind":"sync","x-vistalink-cli":"vistalink catalog engines"}},"/v2/health":{"get":{"tags":["Catalog"],"summary":"Health","description":"Public: no key, free, synchronous. 200 when every dependency is up, 503 with the same body when one is down.","operationId":"health","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Health"}}}},"503":{"description":"degraded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Health"}}}}},"x-vistalink-kind":"sync","x-vistalink-cli":"vistalink catalog health","security":[]}},"/v2/openapi.json":{"get":{"tags":["Catalog"],"summary":"This OpenAPI document","description":"Public: no key, free, synchronous. The OpenAPI 3.1 document for /v2, the same file the docs, SDKs and CLI are generated from.","operationId":"getOpenApi","responses":{"200":{"description":"OpenAPI 3.1 document","content":{"application/json":{"schema":{"type":"object"}}}}},"x-vistalink-kind":"sync","x-vistalink-cli":"vistalink catalog openapi","security":[]}}},"components":{"schemas":{"Amenity":{"properties":{"code":{"type":"string","title":"Code","description":"Uppercase code, for example `POOL_ROOFTOP`."},"label":{"type":"string","title":"Label","description":"Readable name in English."},"category":{"type":"string","title":"Category","description":"Group the code belongs to, for example `pool`, `wellness`, `parking` or `connectivity`."},"kind":{"type":"string","enum":["amenity","group"],"title":"Kind","description":"`amenity` for a single amenity, `group` for a code that matches any of its `members`.","default":"amenity"},"members":{"items":{"type":"string"},"type":"array","title":"Members","description":"For a `group`, the codes it matches; empty for a single amenity."}},"type":"object","required":["code","label","category"],"title":"Amenity","description":"An amenity code accepted by `filters.amenities`."},"AmenityList":{"properties":{"request_id":{"type":"string","title":"Request Id","description":"Opaque id of this request, also sent as the `X-Request-Id` header; quote it when contacting support."},"billing":{"anyOf":[{"$ref":"#/components/schemas/Billing"},{"type":"null"}],"description":"What this call cost, or null when it was free."},"amenities":{"items":{"$ref":"#/components/schemas/Amenity"},"type":"array","title":"Amenities","description":"Every amenity and group code."}},"type":"object","required":["request_id","amenities"],"title":"AmenityList","description":"Response of `GET /v2/amenities`.","examples":[{"amenities":[{"category":"pool","code":"POOL","kind":"group","label":"Pool (any)","members":["POOL_OUTDOOR","POOL_INDOOR","POOL_HEATED","POOL_ROOFTOP","POOL_WITH_VIEW"]},{"category":"pool","code":"POOL_ROOFTOP","kind":"amenity","label":"Rooftop pool","members":[]}],"request_id":"req_8f2c1a9b3d4e5f60"}]},"BedConfig":{"properties":{"type":{"type":"string","title":"Type","description":"Bed type as the source names it, for example `king`, `double`, `twin` or `sofa bed`."},"count":{"type":"integer","title":"Count","description":"Number of beds of this type.","default":1}},"type":"object","required":["type"],"title":"BedConfig","description":"Beds of one type in a room."},"Billing":{"properties":{"sku":{"type":"string","title":"Sku","description":"The price-list item charged, for example `search.loose.semantic` or `hotel.detail`; see pricing.json."},"units":{"anyOf":[{"type":"integer"},{"type":"number"}],"title":"Units","description":"How many units of `sku` were charged, for example one per priced hotel on a rates call."},"amount":{"type":"string","pattern":"^-?\\d+(\\.\\d+)?$","title":"Amount","description":"Total charged for this call, a decimal string in `currency` after any plan or batch discount; `\"0\"` for a stored repeat."},"currency":{"type":"string","const":"USD","title":"Currency","description":"Always `USD`: credit is held and charged in US dollars.","default":"USD"}},"type":"object","required":["sku","units","amount"],"title":"Billing","description":"What a billed call cost. Present on every billed response; `billing` is null on free\nones."},"Booking":{"properties":{"request_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Request Id","description":"Id of the request that returned this booking; set on create, null on get, cancel and webhook payloads."},"booking_id":{"type":"string","title":"Booking Id","description":"Booking id, `bk_` followed by 16 letters and digits."},"status":{"type":"string","enum":["queued","processing","requires_payment","requires_user_action","confirmed","confirmation_pending","failed","expired","canceled","requires_credential","submitted","requires_action","unknown"],"title":"Status","description":"Where the booking is: `queued`, `processing`, `requires_payment` (show `payment.checkout_url`), `requires_user_action` (send the guest to `handoff.url`), then `confirmed`, `confirmation_pending` (submitted, confirmation not yet seen), `failed`, `expired` or `canceled`. Only `card_entry` bookings also use `requires_credential` (waiting for confirm), `submitted` (the hotel's pay button was pressed), `requires_action` (see `next_action`) and `unknown` (submitted, outcome not yet provable: tell the guest not to book again and to check their email from the hotel)."},"hotel_id":{"type":"string","title":"Hotel Id","description":"The hotel being booked.","format":"uuid"},"offer":{"anyOf":[{"$ref":"#/components/schemas/Offer"},{"type":"null"}],"description":"The offer being booked, as it was when the booking was created."},"revalidation":{"anyOf":[{"$ref":"#/components/schemas/Revalidation"},{"type":"null"}],"description":"The result of re-checking the offer on the engine; null until that check has an outcome."},"handoff":{"anyOf":[{"$ref":"#/components/schemas/Handoff"},{"type":"null"}],"description":"Where the guest finishes the booking in `handoff` and `external` modes; null in `checkout` mode and before the engine step."},"confirmation":{"anyOf":[{"$ref":"#/components/schemas/Confirmation"},{"type":"null"}],"description":"The hotel's confirmation; null until `status` is `confirmed`."},"payment":{"anyOf":[{"$ref":"#/components/schemas/BookingPayment"},{"type":"null"}],"description":"The guest's payment; null for `external` mode and for `checkout` until the link is issued."},"failure":{"anyOf":[{"$ref":"#/components/schemas/BookingFailure"},{"type":"null"}],"description":"Why the booking failed or expired; null otherwise."},"commission":{"anyOf":[{"$ref":"#/components/schemas/Commission"},{"type":"null"}],"description":"Commission terms once the booking is confirmed; null before that."},"timeline":{"items":{"$ref":"#/components/schemas/TimelineEntry"},"type":"array","title":"Timeline","description":"Every status the booking has been in, oldest first, starting with `queued` (`requires_credential` for `card_entry`)."},"billing":{"type":"null","title":"Billing","description":"Always null: bookings are free."},"version":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Version","description":"`card_entry` only: goes up by one on every change; apply a `booking.updated` event only when its `data.version` is higher than the last one you saw."},"mandate":{"anyOf":[{"$ref":"#/components/schemas/BookingMandate"},{"type":"null"}],"description":"`card_entry` only: what the guest approved."},"payment_method":{"anyOf":[{"$ref":"#/components/schemas/BookingPaymentMethod"},{"type":"null"}],"description":"`card_entry` only: the card's brand, last four and expiry."},"next_action":{"anyOf":[{"$ref":"#/components/schemas/NextAction"},{"type":"null"}],"description":"`card_entry` only: set exactly when `status` is `requires_action`."},"charges":{"anyOf":[{"$ref":"#/components/schemas/BookingCharges"},{"type":"null"}],"description":"`card_entry` only: the total the hotel's checkout showed."},"submitted_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Submitted At","description":"`card_entry` only: when the hotel's pay button was pressed. Once set, the booking is never resubmitted and cannot be canceled."},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Expires At","description":"`card_entry` only: in `requires_credential`, the deadline to confirm; null otherwise."}},"type":"object","required":["booking_id","status","hotel_id"],"title":"Booking","description":"A booking and everything that has happened to it. Returned by create, get and cancel, and\nas `data` in `booking.updated`.","examples":[{"booking_id":"bk_P2w9Xc4Rt1Lm8Qz3","handoff":{"expires_at":"2026-10-07T10:00:00Z","url":"https://bookings.vistalink.com/s/Hn4q8Zr2Wc7Lp1Xt"},"hotel_id":"43d0be71-5f3c-4c8e-9a52-1d7e2b6f0a19","offer":{"availability":"available","board":"breakfast","channel":"direct","currency":"EUR","engine":"SYNXIS","excluded_charges":[],"free_cancellation":false,"includes_taxes":true,"listing_key":"engine_room:1234567","member_rate":false,"nightly_avg":"481.00","observed_at":"2026-10-07T09:30:00Z","occupancy":{"adults":2,"children_ages":[]},"offer_id":"off_t3c8e1a9b4f2d7e6a5c0b","rate_name":"Non-refundable, breakfast included","room_id":"5b1e2f3a-0c4d-4e5f-8a9b-1c2d3e4f5a6b","room_name":"Superior Room","total":"962.00","url":"https://umilta36.com/book?checkin=2026-11-14&checkout=2026-11-16"},"revalidation":{"checked_at":"2026-10-07T09:30:00Z","currency":"EUR","price_changed":false,"total":"962.00"},"status":"requires_user_action","timeline":[{"at":"2026-10-07T09:30:00Z","state":"queued"},{"at":"2026-10-07T09:30:02Z","state":"processing"},{"at":"2026-10-07T09:30:41Z","state":"requires_user_action"}]},{"booking_id":"bk_A7m3Rq8Zt5Hn2Wd6","hotel_id":"43d0be71-5f3c-4c8e-9a52-1d7e2b6f0a19","offer":{"availability":"available","board":"breakfast","channel":"direct","currency":"EUR","engine":"SYNXIS","excluded_charges":[],"free_cancellation":false,"includes_taxes":true,"listing_key":"engine_room:1234567","member_rate":false,"nightly_avg":"481.00","observed_at":"2026-10-07T09:30:00Z","occupancy":{"adults":2,"children_ages":[]},"offer_id":"off_t3c8e1a9b4f2d7e6a5c0b","rate_name":"Non-refundable, breakfast included","room_id":"5b1e2f3a-0c4d-4e5f-8a9b-1c2d3e4f5a6b","room_name":"Superior Room","total":"962.00","url":"https://umilta36.com/book?checkin=2026-11-14&checkout=2026-11-16"},"payment":{"amount":"962.00","checkout_url":"https://checkout.stripe.com/c/pay/cs_test_a1B2c3D4","currency":"EUR","expires_at":"2026-10-07T10:01:38Z","mode":"checkout","payee":"property","status":"pending"},"revalidation":{"checked_at":"2026-10-07T09:30:00Z","currency":"EUR","price_changed":false,"total":"962.00"},"status":"requires_payment","timeline":[{"at":"2026-10-07T09:30:00Z","state":"queued"},{"at":"2026-10-07T09:30:02Z","state":"processing"},{"at":"2026-10-07T09:30:38Z","state":"requires_payment"}]},{"booking_id":"bk_P2w9Xc4Rt1Lm8Qz3","commission":{"commissionable":true,"partner_share":"0.50","rate":"0.10"},"confirmation":{"at":"2026-10-07T09:36:12Z","code":"RX48213","evidence":"engine_confirmation_signal"},"hotel_id":"43d0be71-5f3c-4c8e-9a52-1d7e2b6f0a19","offer":{"availability":"available","board":"breakfast","channel":"direct","currency":"EUR","engine":"SYNXIS","excluded_charges":[],"free_cancellation":false,"includes_taxes":true,"listing_key":"engine_room:1234567","member_rate":false,"nightly_avg":"481.00","observed_at":"2026-10-07T09:30:00Z","occupancy":{"adults":2,"children_ages":[]},"offer_id":"off_t3c8e1a9b4f2d7e6a5c0b","rate_name":"Non-refundable, breakfast included","room_id":"5b1e2f3a-0c4d-4e5f-8a9b-1c2d3e4f5a6b","room_name":"Superior Room","total":"962.00","url":"https://umilta36.com/book?checkin=2026-11-14&checkout=2026-11-16"},"revalidation":{"checked_at":"2026-10-07T09:30:00Z","currency":"EUR","price_changed":false,"total":"962.00"},"status":"confirmed","timeline":[{"at":"2026-10-07T09:30:00Z","state":"queued"},{"at":"2026-10-07T09:30:02Z","state":"processing"},{"at":"2026-10-07T09:30:41Z","state":"requires_user_action"},{"at":"2026-10-07T09:36:12Z","state":"confirmed"}]},{"booking_id":"bk_C4r9Dn2Ty7Ek5Pw1","charges":{"amount":"962.00","currency":"EUR","mismatch":false},"commission":{"commissionable":true,"partner_share":"0.50","rate":"0.10"},"confirmation":{"at":"2026-10-07T09:31:20Z","code":"RX48219","evidence":"engine_confirmation_signal"},"hotel_id":"43d0be71-5f3c-4c8e-9a52-1d7e2b6f0a19","mandate":{"accepted_total":"962.00","consent_at":"2026-10-07T09:29:12Z","currency":"EUR","expires_at":"2026-10-07T09:59:30Z","max_total":"962.00"},"offer":{"availability":"available","board":"breakfast","channel":"direct","currency":"EUR","engine":"SYNXIS","excluded_charges":[],"free_cancellation":false,"includes_taxes":true,"listing_key":"engine_room:1234567","member_rate":false,"nightly_avg":"481.00","observed_at":"2026-10-07T09:30:00Z","occupancy":{"adults":2,"children_ages":[]},"offer_id":"off_t3c8e1a9b4f2d7e6a5c0b","rate_name":"Non-refundable, breakfast included","room_id":"5b1e2f3a-0c4d-4e5f-8a9b-1c2d3e4f5a6b","room_name":"Superior Room","total":"962.00","url":"https://umilta36.com/book?checkin=2026-11-14&checkout=2026-11-16"},"payment_method":{"brand":"visa","credential_id":"cred_Hk2v9Qx0aL7mN3pZr5Tb8Wd1","credential_status":"deleted","exp_month":12,"exp_year":2028,"last4":"4242","type":"card"},"revalidation":{"checked_at":"2026-10-07T09:30:05Z","currency":"EUR","price_changed":false,"total":"962.00"},"status":"confirmed","submitted_at":"2026-10-07T09:31:02Z","timeline":[{"at":"2026-10-07T09:29:30Z","state":"requires_credential"},{"at":"2026-10-07T09:30:00Z","state":"queued"},{"at":"2026-10-07T09:30:01Z","state":"processing"},{"at":"2026-10-07T09:31:02Z","state":"submitted"},{"at":"2026-10-07T09:31:20Z","state":"confirmed"}],"version":6}]},"BookingCharges":{"properties":{"amount":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d+)?$"},{"type":"null"}],"title":"Amount","description":"The engine's total, a decimal string."},"currency":{"anyOf":[{"type":"string","pattern":"^[A-Z]{3}$"},{"type":"null"}],"title":"Currency","description":"ISO 4217 currency of `amount`."},"mismatch":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Mismatch","description":"True when the engine reported a charge above `mandate.max_total` after submit; ops is alerted."}},"type":"object","title":"BookingCharges","description":"`card_entry` only: the amount the hotel's checkout showed, read in code, never typed by a model."},"BookingFailure":{"properties":{"reason":{"type":"string","enum":["sold_out","price_changed","price_changed_at_payment","engine_blocked","handoff_expired","payment_expired","payment_failed","unsupported_engine","payment_unavailable","card_declined","three_d_secure_failed","card_entry_failed","card_entry_unavailable","mandate_expired"],"title":"Reason","description":"Stable failure code: `sold_out`, `price_changed` (revalidation, or with `card_entry` the hotel's checkout total was above the accepted total; nothing was typed), `price_changed_at_payment` (the engine charged a different total after checkout), `engine_blocked`, `handoff_expired` (the guest left the session), `payment_expired`, `payment_failed`, `unsupported_engine` (the engine or offer cannot be booked in this mode), `payment_unavailable` (hosted checkout is not available; use `handoff`). `card_entry` only: `card_declined` (the hotel's checkout declined the card; create a new booking with a new credential), `three_d_secure_failed`, `card_entry_failed` (stopped before the pay button; nothing was charged), `card_entry_unavailable` (card entry is not available for this engine right now), and `mandate_expired` on `expired` (not confirmed within 30 minutes)."},"message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Message","description":"One English sentence explaining the failure."}},"type":"object","required":["reason"],"title":"BookingFailure","description":"Why a booking ended without a reservation. Present when `status` is `failed`, and on\n`expired` with `payment_expired`."},"BookingGuests":{"properties":{"room":{"type":"integer","maximum":8.0,"minimum":0.0,"title":"Room","description":"Zero-based index into `stay.rooms`; every room needs exactly one entry."},"lead":{"$ref":"#/components/schemas/GuestContact","description":"The room's lead guest, whose name, email and phone go on the reservation."},"occupants":{"items":{"$ref":"#/components/schemas/Occupant"},"type":"array","maxItems":16,"title":"Occupants","description":"Other guests in the room, up to 16; kept with the booking but not sent to the engine."}},"additionalProperties":false,"type":"object","required":["room","lead"],"title":"BookingGuests","description":"The guests of one room of the stay."},"BookingMandate":{"properties":{"accepted_total":{"type":"string","pattern":"^-?\\d+(\\.\\d+)?$","title":"Accepted Total","description":"The total the guest accepted, in `currency`."},"currency":{"type":"string","pattern":"^[A-Z]{3}$","title":"Currency","description":"ISO 4217 currency."},"max_total":{"type":"string","pattern":"^-?\\d+(\\.\\d+)?$","title":"Max Total","description":"The most the hotel's checkout may show before the card is typed; equals `accepted_total`."},"consent_at":{"type":"string","format":"date-time","title":"Consent At","description":"When the guest approved, as you sent it."},"expires_at":{"type":"string","format":"date-time","title":"Expires At","description":"The mandate's deadline: confirm before it, 30 minutes after create."}},"type":"object","required":["accepted_total","currency","max_total","consent_at","expires_at"],"title":"BookingMandate","description":"`card_entry` only: what the guest approved."},"BookingPayment":{"properties":{"mode":{"type":"string","enum":["checkout","handoff","external"],"title":"Mode","description":"The payment mode the booking was created with."},"status":{"type":"string","enum":["pending","completed","expired","failed"],"title":"Status","description":"`pending` until the guest pays, then `completed`, `expired` (link or session timed out, or the booking was canceled first) or `failed`; it never changes after leaving `pending`."},"checkout_url":{"anyOf":[{"type":"string","format":"uri"},{"type":"null"}],"title":"Checkout Url","description":"`checkout` only: the hosted checkout link to show the guest; null in `handoff` mode."},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Expires At","description":"`checkout` only: when the checkout link stops working, about 30 minutes after it is issued; null in `handoff` mode."},"amount":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d+)?$"},{"type":"null"}],"title":"Amount","description":"What the guest pays, the offer's total as a decimal string in `currency`."},"currency":{"anyOf":[{"type":"string","pattern":"^[A-Z]{3}$"},{"type":"null"}],"title":"Currency","description":"ISO 4217 currency of `amount`."},"payee":{"type":"string","const":"property","title":"Payee","description":"Who receives the money; always `property`.","default":"property"},"completed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Completed At","description":"When the payment completed, RFC 3339 UTC; null until then."},"refund_required":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Refund Required","description":"True when the guest's checkout payment completed and the booking will not be made, so the payment must be reversed by the checkout provider: canceled before the engine step, failed with `price_changed_at_payment`, or the amount paid did not match the booking (`payment_failed`); null otherwise."}},"type":"object","required":["mode","status"],"title":"BookingPayment","description":"The guest's payment. Null for `external` mode, and for `checkout` until the link is\nissued."},"BookingPaymentMethod":{"properties":{"type":{"type":"string","const":"card","title":"Type","description":"Always `card`."},"credential_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Credential Id","description":"The `cred_` id; null until bound."},"brand":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Brand","description":"Card brand, for example `visa`."},"last4":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last4","description":"Last four digits."},"exp_month":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Exp Month","description":"Expiry month."},"exp_year":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Exp Year","description":"Expiry year."},"credential_status":{"anyOf":[{"type":"string","enum":["active","bound","consumed","deleted"]},{"type":"null"}],"title":"Credential Status","description":"The credential's status; see `PaymentCredential.status`."}},"type":"object","required":["type"],"title":"BookingPaymentMethod","description":"`card_entry` only: the card's metadata. Never the card number or CVC."},"BookingRequest":{"properties":{"hotel_id":{"type":"string","title":"Hotel Id","description":"The hotel being booked; must be the hotel the offer belongs to.","format":"uuid"},"offer_id":{"type":"string","maxLength":128,"title":"Offer Id","description":"An `offer_id` this key received from search, hotel detail, rates or chat in the last 30 minutes."},"stay":{"$ref":"#/components/schemas/Stay","description":"The stay to book; must match the dates and party the offer was priced for."},"guests":{"items":{"$ref":"#/components/schemas/BookingGuests"},"type":"array","maxItems":9,"minItems":1,"title":"Guests","description":"One entry per room in `stay.rooms`, each with its lead guest."},"payment":{"$ref":"#/components/schemas/Payment","description":"How the guest will pay."},"payment_method":{"anyOf":[{"$ref":"#/components/schemas/PaymentMethod"},{"type":"null"}],"description":"Required with `payment.mode` `card_entry`, refused otherwise."},"mandate":{"anyOf":[{"$ref":"#/components/schemas/Mandate"},{"type":"null"}],"description":"Required with `payment.mode` `card_entry`, refused otherwise."},"special_requests":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Special Requests","description":"Free-text requests from the guest, up to 2,000 characters; stored with the booking but not sent to the engine today."},"webhook_url":{"anyOf":[{"type":"string","maxLength":2048,"format":"uri"},{"type":"null"}],"title":"Webhook Url","description":"An https URL stored with the booking; `booking.updated` events are delivered only to webhooks created with `POST /v2/webhooks`, not here."}},"additionalProperties":false,"type":"object","required":["hotel_id","offer_id","stay","guests","payment"],"title":"BookingRequest","description":"Body of `POST /v2/bookings`.","examples":[{"guests":[{"lead":{"email":"ada@example.com","first_name":"Ada","last_name":"Rossi","phone":"+390600000000"},"occupants":[],"room":0}],"hotel_id":"43d0be71-5f3c-4c8e-9a52-1d7e2b6f0a19","offer_id":"off_t3c8e1a9b4f2d7e6a5c0b","payment":{"locale":"en","mode":"handoff"},"special_requests":"Late arrival","stay":{"check_in":"2026-11-14","check_out":"2026-11-16","rooms":[{"adults":2,"children_ages":[]}]}},{"guests":[{"lead":{"email":"ada@example.com","first_name":"Ada","last_name":"Rossi","phone":"+390600000000"},"occupants":[],"room":0}],"hotel_id":"43d0be71-5f3c-4c8e-9a52-1d7e2b6f0a19","offer_id":"off_t3c8e1a9b4f2d7e6a5c0b","payment":{"locale":"en","mode":"checkout","return_url":"https://partner.example/booking/done"},"special_requests":"Late arrival","stay":{"check_in":"2026-11-14","check_out":"2026-11-16","rooms":[{"adults":2,"children_ages":[]}]}},{"guests":[{"lead":{"email":"ada@example.com","first_name":"Ada","last_name":"Rossi","phone":"+390600000000"},"occupants":[],"room":0}],"hotel_id":"43d0be71-5f3c-4c8e-9a52-1d7e2b6f0a19","mandate":{"accepted_total":"962.00","consent":{"cardholder_authorized":true,"display_hash":"sha256:9b1e5c0d2a7f43e8b6c1d9a04f7e2b3c5d8a1f6e0b9c2d7a4e3f1b8c6d5a0e9f","method":"explicit_ui_confirmation","obtained_at":"2026-10-07T09:29:12Z"},"currency":"EUR"},"offer_id":"off_t3c8e1a9b4f2d7e6a5c0b","payment":{"locale":"en","mode":"card_entry"},"payment_method":{"credential_id":"cred_Hk2v9Qx0aL7mN3pZr5Tb8Wd1","type":"card"},"special_requests":"Late arrival","stay":{"check_in":"2026-11-14","check_out":"2026-11-16","rooms":[{"adults":2,"children_ages":[]}]}}]},"BudgetFilter":{"properties":{"min":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d+)?$"},{"type":"null"}],"title":"Min","description":"Lowest acceptable price per room per night, a decimal string in `currency`; values of zero or less are ignored."},"max":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d+)?$"},{"type":"null"}],"title":"Max","description":"Highest acceptable price per room per night, a decimal string in `currency`; values of zero or less are ignored."},"currency":{"type":"string","pattern":"^[A-Z]{3}$","title":"Currency","description":"ISO 4217 currency of `min` and `max`."},"basis":{"type":"string","const":"per_room_per_night","title":"Basis","description":"What the amounts cover; `per_room_per_night` is the only value.","default":"per_room_per_night"}},"additionalProperties":false,"type":"object","required":["currency"],"title":"BudgetFilter","description":"A price band, matched against each hotel's catalogued room prices rather than live rates."},"CanonicalRoom":{"properties":{"room_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Room Id","description":"Catalogue room id when the group includes a third-party channel listing; null otherwise."},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name","description":"Room name, from the third-party channel listing when there is one, else from the first listing in the group."},"size_m2":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Size M2","description":"Room size in square metres; null when no source gives it."},"occupancy":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Occupancy","description":"Most guests the room holds; null when no source gives it."},"bed_config":{"items":{"$ref":"#/components/schemas/BedConfig"},"type":"array","title":"Bed Config","description":"Beds in the room; empty when no source gives them in a structured form."}},"type":"object","title":"CanonicalRoom","description":"The room as one record, taking each fact from the most authoritative source that has it\n(your rooms, then the hotel's site, then other channels)."},"CardLocation":{"properties":{"lat":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Lat","description":"Latitude in decimal degrees."},"lng":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Lng","description":"Longitude in decimal degrees."},"city":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"City","description":"City the hotel is in."},"country":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Country","description":"ISO 3166-1 alpha-2 country code, or the country name when no code is known."},"address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Address","description":"Street address on one line, from street and number to country."},"distance_m":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Distance M","description":"Straight-line distance in metres from `filters.near` or the landmark named in the query; null when the search had neither."}},"type":"object","title":"CardLocation","description":"Where a hotel on a search card is."},"CardRate":{"properties":{"status":{"type":"string","enum":["observed","stale","unknown"],"title":"Status","description":"`observed` when seen recently (within 4 hours for stays in the next 14 days, else 24 hours), `stale` when older but within 7 days, `unknown` when there is no usable price and the other fields are null."},"total":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d+)?$"},{"type":"null"}],"title":"Total","description":"Price for the whole stay and all rooms, a decimal string in `currency` with taxes and fees included where known."},"currency":{"anyOf":[{"type":"string","pattern":"^[A-Z]{3}$"},{"type":"null"}],"title":"Currency","description":"ISO 4217 currency the price was observed in; never converted."},"nights":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Nights","description":"Number of nights the total covers."},"includes_taxes_and_fees":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Includes Taxes And Fees","description":"True when `total` includes taxes and fees, false when some are charged on top, null when unknown."},"source":{"anyOf":[{"type":"string","enum":["direct","third_party"]},{"type":"null"}],"title":"Source","description":"Where the price was observed: `direct` for the hotel's own booking engine, `third_party` for the Enterprise third-party channel."},"observed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Observed At","description":"When the price was observed, RFC 3339 UTC."},"offer_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Offer Id","description":"The offer behind this price, bookable with `POST /v2/bookings` for 30 minutes."}},"type":"object","required":["status"],"title":"CardRate","description":"The cheapest stored price for the stay on a search card. Never fetched live."},"ChatRequest":{"properties":{"message":{"type":"string","maxLength":4000,"minLength":1,"title":"Message","description":"What the traveller said, up to 4,000 characters."},"session_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Session Id","description":"Conversation to continue, a uuid from an earlier response; omit to start a new conversation."},"guest_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Guest Id","description":"Your id for the traveller, a uuid, recorded on a new conversation; ignored when `session_id` is set."},"currency":{"anyOf":[{"type":"string","pattern":"^[A-Z]{3}$"},{"type":"null"}],"title":"Currency","description":"ISO 4217 currency the assistant should talk in; card prices stay in the currency they were observed in."},"locale":{"anyOf":[{"type":"string","maxLength":10},{"type":"null"}],"title":"Locale","description":"Language of the conversation as a language tag; `en` when omitted."},"context":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Context","description":"Structured context passed to the assistant, for example a stay as `check_in`, `check_out`, `rooms` and `adults`; with a stay, hotel cards carry cached rates."},"clarification_id":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Clarification Id","description":"The `clarification_pending.clarification_id` being answered."},"clarification_option_id":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Clarification Option Id","description":"The chosen option of that clarification."},"user_location":{"anyOf":[{"$ref":"#/components/schemas/UserLocation"},{"type":"null"}],"description":"The traveller's current position, for questions like 'near me'."},"search_id":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Search Id","description":"Accepted for symmetry with search; not used as input."}},"additionalProperties":false,"type":"object","required":["message"],"title":"ChatRequest","description":"Body of `POST /v2/chat`: one traveller turn.","examples":[{"currency":"EUR","locale":"en","message":"Somewhere quiet near the Pantheon for two nights in November"}]},"ChatResponse":{"properties":{"request_id":{"type":"string","title":"Request Id","description":"Opaque id of this request, also sent as the `X-Request-Id` header; quote it when contacting support."},"billing":{"anyOf":[{"$ref":"#/components/schemas/Billing"},{"type":"null"}],"description":"What this call cost, or null when it was free."},"session_id":{"type":"string","title":"Session Id","description":"Conversation id; send it back to continue the conversation."},"message":{"type":"string","title":"Message","description":"The assistant's reply in plain text."},"hotels":{"items":{"$ref":"#/components/schemas/SearchHotel"},"type":"array","title":"Hotels","description":"Hotels the reply refers to, up to 20, in the search card shape."},"pois":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Pois","description":"A place the turn located, as `{name, lat, lng, ...}`; at most one per turn."},"routes":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Routes","description":"Travel routes computed in the turn; always empty today."},"references":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"References","description":"Web sources cited in the turn as `{url, title, domain, index}`; always empty today."},"clarification_pending":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Clarification Pending","description":"A question the assistant needs answered, with `clarification_id`, `question` and `options[]`; null otherwise. Answer it with `clarification_id` and `clarification_option_id`."},"segments":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Segments","description":"The reply split for rendering, each `{type, id}` plus `content` for `text`, `hotel_id` for `hotel_ref` or `hotel_ids` for `trip_suggestion`."},"search_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Search Id","description":"A `search_id` for the hotels in this turn, valid for 30 minutes with `GET /v2/hotels/{hotel_id}?search_id=`; null when the turn returned no hotels."},"warnings":{"items":{"$ref":"#/components/schemas/Warning_"},"type":"array","title":"Warnings","description":"Things the turn could not fully do, such as `rates_unavailable` or `channel_unavailable`."}},"type":"object","required":["request_id","session_id","message"],"title":"ChatResponse","description":"Response of `POST /v2/chat`: the assistant's turn.","examples":[{"billing":{"amount":"0.03","currency":"USD","sku":"chat.turn","units":1},"hotels":[],"message":"Here are three quiet options a short walk from the Pantheon.","request_id":"req_8f2c1a9b3d4e5f60","search_id":"srch_3f9c2a7e4b1d4c8a9e6f0b2d5a7c1e93","session_id":"9c1d2e3f-4a5b-4c6d-8e7f-0a1b2c3d4e5f"}]},"Clarification":{"properties":{"id":{"type":"string","title":"Id","description":"Clarification id to send back as `answers[].clarification_id`."},"field":{"type":"string","title":"Field","description":"The criterion that is ambiguous or missing; `location` (a city or landmark with several matches, or no place at all) is the only one asked today."},"question":{"type":"string","title":"Question","description":"The question to show the traveller, in English."},"answer_type":{"type":"string","enum":["single_choice","free_text"],"title":"Answer Type","description":"How to answer: `single_choice` (send one of `options` as `option_id`) or `free_text` (no options; send the traveller's answer as `value`, for example a city when the search named no place).","default":"single_choice"},"options":{"items":{"$ref":"#/components/schemas/ClarificationOption"},"type":"array","title":"Options","description":"The possible answers: at least two for `single_choice`, none for `free_text`."}},"type":"object","required":["id","field","question"],"title":"Clarification","description":"A question the search needs answered before it can run."},"ClarificationAnswer":{"properties":{"clarification_id":{"type":"string","maxLength":64,"title":"Clarification Id","description":"The `clarifications[].id` being answered."},"option_id":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Option Id","description":"The chosen `clarifications[].options[].id`, for a `single_choice` question."},"value":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Value","description":"The answer in the traveller's words, for a `free_text` question, for example `Lisbon`."}},"additionalProperties":false,"type":"object","required":["clarification_id"],"title":"ClarificationAnswer","description":"The answer to one clarification question."},"ClarificationOption":{"properties":{"id":{"type":"string","title":"Id","description":"Option id to send back as `answers[].option_id`, for example `opt_gb`."},"label":{"type":"string","title":"Label","description":"What to show the traveller, for example `Cambridge, England`."}},"type":"object","required":["id","label"],"title":"ClarificationOption","description":"One possible answer to a clarification."},"ClarificationPolicy":{"properties":{"policy":{"type":"string","enum":["auto","never"],"title":"Policy","description":"`auto` returns `needs_clarification` with a question (free); `never` returns `partial` with no hotels and the ambiguous field in `resolved.unresolved` (also free).","default":"auto"}},"additionalProperties":false,"type":"object","title":"ClarificationPolicy","description":"What to do when the request is ambiguous, for example a city name shared by several\nplaces."},"Commission":{"properties":{"commissionable":{"type":"boolean","title":"Commissionable","description":"True when the hotel has an active commission contract covering today."},"rate":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d+)?$"},{"type":"null"}],"title":"Rate","description":"Commission as a decimal fraction string of the booking total, for example `\"0.10\"` for 10%; null when not commissionable or the contract has no rate."},"partner_share":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d+)?$"},{"type":"null"}],"title":"Partner Share","description":"Your share of the commission as a decimal fraction string, for example `\"0.50\"`; null when not commissionable."}},"type":"object","required":["commissionable"],"title":"Commission","description":"Commission on a confirmed booking. Null until the booking is confirmed."},"ConfirmBookingRequest":{"properties":{"credential_id":{"anyOf":[{"type":"string","pattern":"^cred_[A-Za-z0-9]{24}$"},{"type":"null"}],"title":"Credential Id","description":"The `cred_` id to charge; omit it when it was sent as `payment_method.credential_id` at create. Must be active, unexpired, yours and not bound to another booking."}},"additionalProperties":false,"type":"object","title":"ConfirmBookingRequest","description":"Body of `POST /v2/bookings/{booking_id}/confirm`.","examples":[{"credential_id":"cred_Hk2v9Qx0aL7mN3pZr5Tb8Wd1"}]},"Confirmation":{"properties":{"code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Code","description":"The hotel's confirmation number; null when the engine confirmed without showing one."},"evidence":{"type":"string","enum":["engine_confirmation_signal","email","manual"],"title":"Evidence","description":"How the confirmation was established; `engine_confirmation_signal` (the engine's own confirmation page or code) is the only value produced today."},"at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"At","description":"When the booking was confirmed, RFC 3339 UTC."}},"type":"object","required":["evidence"],"title":"Confirmation","description":"Proof that the hotel's engine accepted the reservation. Present once `status` is\n`confirmed`."},"Consent":{"properties":{"obtained_at":{"type":"string","format":"date-time","title":"Obtained At","description":"When the guest approved the summary (hotel, room, rate, dates, party, total and cancellation terms), RFC 3339; at most 15 minutes before the create call."},"method":{"type":"string","enum":["explicit_ui_confirmation","explicit_chat_confirmation"],"title":"Method","description":"`explicit_ui_confirmation` (a button on a screen showing the summary) or `explicit_chat_confirmation` (an agent turn that showed the summary and got a yes in that same turn)."},"cardholder_authorized":{"type":"boolean","const":true,"title":"Cardholder Authorized","description":"Must be `true`: you attest that the guest is the cardholder, or is authorised to use the card, for this booking."},"display_hash":{"anyOf":[{"type":"string","maxLength":128},{"type":"null"}],"title":"Display Hash","description":"Optional `sha256:` hash of the canonical JSON you showed the guest; stored as evidence."},"user_ref":{"anyOf":[{"type":"string","maxLength":128},{"type":"null"}],"title":"User Ref","description":"Optional opaque id of the guest in your system; stored as evidence, never echoed."}},"additionalProperties":false,"type":"object","required":["obtained_at","method","cardholder_authorized"],"title":"Consent","description":"`card_entry` only: your record that the cardholder approved this exact booking."},"DetailCore":{"properties":{"name":{"type":"string","title":"Name","description":"Hotel name."},"hotel_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Hotel Type","description":"Kind of property as catalogued, free text such as `hotel`; null when unknown."},"star_rating":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Star Rating","description":"Official star rating, 1 to 5; null when unrated."},"review_score":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Review Score","description":"Average guest review score on a 0 to 10 scale; null when there are no reviews."},"review_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Review Count","description":"Number of guest reviews behind `review_score`, which can differ from `reviews.count` (the reviews we hold)."},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description","description":"Long description of the hotel in `locale` when translated, else English; null when there is none."},"short_character":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Short Character","description":"One line on what the hotel is like, from its published profile; null without a profile."},"chain":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Chain","description":"Hotel chain the property belongs to; null when independent or unknown."},"brand":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Brand","description":"Brand within the chain; null when none or unknown."},"operating_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Operating Status","description":"Whether the hotel is trading, for example `open`, `closed_temporarily`, `closed`, `opening_soon` or `unknown`; null without a profile."},"check_in_time":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Check In Time","description":"Earliest check-in time, usually `HH:MM` in local time; null when unknown."},"check_out_time":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Check Out Time","description":"Latest check-out time, usually `HH:MM` in local time; null when unknown."},"phone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Phone","description":"Hotel phone number as published."},"website_url":{"anyOf":[{"type":"string","format":"uri"},{"type":"null"}],"title":"Website Url","description":"The hotel's own website."},"booking_url":{"anyOf":[{"type":"string","format":"uri"},{"type":"null"}],"title":"Booking Url","description":"The hotel's listing URL on the third-party channel; null when it has none."},"amenities":{"items":{"type":"string"},"type":"array","title":"Amenities","description":"Uppercase amenity codes, as listed by `GET /v2/amenities`."},"languages":{"items":{"type":"string"},"type":"array","title":"Languages","description":"Languages the staff speak, as listed in the hotel profile, for example `en`; empty when unknown."}},"type":"object","required":["name"],"title":"DetailCore","description":"The hotel's identity, ratings and contact details."},"DetailImage":{"properties":{"image_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Image Id","description":"Image id; the same image keeps the same id across responses."},"url":{"type":"string","title":"Url","description":"CDN URL of the image at `image_width` pixels wide (never upscaled).","format":"uri"},"label":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Label","description":"What the image shows, for example `building`, `room`, `bathroom` or `swimming pool outdoor`; null when unlabelled."},"room_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Room Id","description":"The room this image shows, matching `rooms[].room_id`; null for images of the hotel as a whole."},"width":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Width","description":"Width in pixels of the image at `url`; null when unknown."},"height":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Height","description":"Height in pixels of the image at `url`; null when unknown."},"position":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Position","description":"1-based position in gallery order; null on `hero` and venue images."}},"type":"object","required":["url"],"title":"DetailImage","description":"An image on hotel detail, resized to `image_width`."},"DetailImages":{"properties":{"hero":{"anyOf":[{"$ref":"#/components/schemas/DetailImage"},{"type":"null"}],"description":"The lead image; null when the hotel has no images."},"count":{"type":"integer","title":"Count","description":"How many distinct images the hotel has, which can exceed the 50 in `items`.","default":0},"items":{"items":{"$ref":"#/components/schemas/DetailImage"},"type":"array","maxItems":50,"title":"Items","description":"Up to 50 images in gallery order."}},"type":"object","title":"DetailImages","description":"The hotel's gallery in curated order, without duplicates."},"DetailLocation":{"properties":{"lat":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Lat","description":"Latitude in decimal degrees."},"lng":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Lng","description":"Longitude in decimal degrees."},"address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Address","description":"Street address on one line: street and number, postcode, city."},"city":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"City","description":"City the hotel is in."},"country":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Country","description":"ISO 3166-1 alpha-2 country code, or the country name when no code is known."},"neighbourhood_text":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Neighbourhood Text","description":"A paragraph on the neighbourhood, in `locale` when translated; null without a profile."},"transport_notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Transport Notes","description":"How to get there by public transport, in `locale` when translated; null without a profile."}},"type":"object","title":"DetailLocation","description":"Where the hotel is and how to get there."},"DetailRates":{"properties":{"stay":{"anyOf":[{"$ref":"#/components/schemas/ResolvedStay"},{"type":"null"}],"description":"The stay the prices are for; null when no dates were given."},"channels":{"items":{"$ref":"#/components/schemas/RateChannel"},"type":"array","title":"Channels","description":"Prices per channel, direct first; empty without dates, for parties with children, or when nothing was observed."},"refresh_job":{"anyOf":[{"$ref":"#/components/schemas/JobRef"},{"type":"null"}],"description":"The live rates job started by `rates=refresh`; its result arrives through `GET /v2/jobs/{job_id}` or the `rates.completed` webhook. Null otherwise."}},"type":"object","title":"DetailRates","description":"Stored prices for the requested stay. Without `check_in` and `check_out` this is `{stay:\nnull, channels: []}`."},"DetailRoom":{"properties":{"room_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Room Id","description":"Room id, stable across responses and used by `Offer.room_id`."},"name":{"type":"string","title":"Name","description":"Room name."},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description","description":"Room description in `locale` when translated, else English; null when there is none."},"size_m2":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Size M2","description":"Room size in square metres; null when unknown."},"occupancy":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Occupancy","description":"Standard number of guests the room is sold for; null when unknown."},"max_occupancy":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Max Occupancy","description":"Most guests the room can hold, including extra beds; equals `occupancy` when no maximum is known."},"bed_config":{"items":{"$ref":"#/components/schemas/BedConfig"},"type":"array","title":"Bed Config","description":"Beds in the room; empty when unknown."},"views":{"items":{"type":"string"},"type":"array","title":"Views","description":"Views from the room, for example `city` or `sea`."},"balcony":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Balcony","description":"True when the room has a balcony or terrace, false when it does not, null when unknown."},"accessible":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Accessible","description":"True when the room is wheelchair accessible, false when it is not, null when unknown."},"kitchen":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Kitchen","description":"True when the room has a kitchen or kitchenette, false when it does not, null when unknown."},"amenities":{"items":{"type":"string"},"type":"array","title":"Amenities","description":"Uppercase amenity codes of the room."},"images":{"items":{"$ref":"#/components/schemas/DetailImage"},"type":"array","maxItems":6,"title":"Images","description":"Up to 6 images of the room."},"listings":{"items":{"$ref":"#/components/schemas/RoomListing"},"type":"array","title":"Listings","description":"The same room on other channels, matched to this room."},"offers":{"items":{"$ref":"#/components/schemas/Offer"},"type":"array","title":"Offers","description":"Prices for this room for the requested stay; empty without dates or when none were observed."}},"type":"object","required":["name"],"title":"DetailRoom","description":"One physical room type, with its variants on other channels folded in."},"DetailWarning":{"properties":{"code":{"type":"string","enum":["rates_party_unsupported","channel_unavailable","refresh_requires_dates","refresh_unavailable"],"title":"Code","description":"`rates_party_unsupported` (stored rates cover adults-only parties), `channel_unavailable` (the third-party channel was left out), `refresh_requires_dates` or `refresh_unavailable` (no live job was started)."},"message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Message","description":"Explanation in English."},"channel":{"anyOf":[{"type":"string","const":"third_party"},{"type":"null"}],"title":"Channel","description":"The channel left out, `third_party`; set only on `channel_unavailable`."}},"type":"object","required":["code"],"title":"DetailWarning","description":"Something hotel detail could not fully do, without failing the call."},"Engine":{"properties":{"engine":{"type":"string","title":"Engine","description":"Engine code, as `Offer.engine` and `RateChannel.engine` report it, for example `SYNXIS`."},"name":{"type":"string","title":"Name","description":"Engine name."},"live_rates":{"type":"boolean","title":"Live Rates","description":"True when direct prices can be read live for hotels on this engine."},"book":{"type":"boolean","title":"Book","description":"True when bookings can be made on this engine."},"success_class":{"type":"string","enum":["high","medium","low","unmeasured"],"title":"Success Class","description":"Measured booking success rate band: `high`, `medium`, `low`, or `unmeasured` (the value for every engine today).","default":"unmeasured"},"hotel_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Hotel Count","description":"Indexed hotels on this engine; null when none or not counted."}},"type":"object","required":["engine","name","live_rates","book"],"title":"Engine","description":"A hotel booking engine we read prices from or book on."},"EngineList":{"properties":{"request_id":{"type":"string","title":"Request Id","description":"Opaque id of this request, also sent as the `X-Request-Id` header; quote it when contacting support."},"billing":{"anyOf":[{"$ref":"#/components/schemas/Billing"},{"type":"null"}],"description":"What this call cost, or null when it was free."},"engines":{"items":{"$ref":"#/components/schemas/Engine"},"type":"array","title":"Engines","description":"Supported engines, most hotels first."}},"type":"object","required":["request_id","engines"],"title":"EngineList","description":"Response of `GET /v2/engines`.","examples":[{"engines":[{"book":true,"engine":"SYNXIS","hotel_count":1830,"live_rates":true,"name":"SynXis","success_class":"unmeasured"}],"request_id":"req_8f2c1a9b3d4e5f60"}]},"ErrorBody":{"properties":{"code":{"type":"string","enum":["bad_request","unauthorized","forbidden","not_found","conflict","rate_limited","insufficient_credit","upstream_error","unavailable"],"title":"Code","description":"Stable error code; the HTTP status follows from it (`bad_request` 400, `unauthorized` 401, `insufficient_credit` 402, `forbidden` 403, `not_found` 404, `conflict` 409, `rate_limited` 429, `upstream_error` 502, `unavailable` 503). A method the route does not accept is `bad_request` with status 405."},"message":{"type":"string","title":"Message","description":"Human-readable explanation in English; do not parse it, branch on `code` and `details`."},"request_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Request Id","description":"Id of the failed request, the same value as the `X-Request-Id` header."},"details":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Details","description":"Machine-readable context when there is some: `fields[]` on validation errors, `required_scope` on a missing scope, and `reason` elsewhere, for example `tier_required` (with `required_tier`) or `enterprise_only` on a 403, `balance` or `spend_cap` on a 402, `idempotency_mismatch` on a 409, `search_expired` on a 400."}},"type":"object","required":["code","message"],"title":"ErrorBody","description":"The error itself: a stable `code`, a readable `message` and optional `details`."},"ErrorEnvelope":{"properties":{"error":{"$ref":"#/components/schemas/ErrorBody","description":"The error."}},"type":"object","required":["error"],"title":"ErrorEnvelope","description":"Body of every error response.","examples":[{"error":{"code":"forbidden","details":{"required_scope":"rates"},"message":"This key does not have the 'rates' scope.","request_id":"req_8f2c1a9b3d4e5f60"}}]},"ExcludedCharge":{"properties":{"kind":{"type":"string","title":"Kind","description":"What the charge is; `taxes_and_fees` (taxes and fees the source quoted separately) is the only kind produced today."},"amount":{"type":"string","pattern":"^-?\\d+(\\.\\d+)?$","title":"Amount","description":"Amount of the charge, a decimal string in `currency`."},"currency":{"type":"string","pattern":"^[A-Z]{3}$","title":"Currency","description":"ISO 4217 currency of `amount`."},"per":{"anyOf":[{"type":"string","enum":["night","stay","person","person_night"]},{"type":"null"}],"title":"Per","description":"What `amount` is charged per: `stay` (once for the whole stay, the only basis produced today), `night`, `person` or `person_night`."},"note":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Note","description":"Short note on how the charge is applied, for example `quoted on top of the total`."}},"type":"object","required":["kind","amount","currency"],"title":"ExcludedCharge","description":"A charge quoted on top of an offer's `total`."},"Fact":{"properties":{"kind":{"type":"string","title":"Kind","description":"Fact topic, for example `history_moment`, `brand`, `loyalty`, `language`, `trivia`, `transport_distance`, `sustainability_practice` or `other`."},"label":{"type":"string","title":"Label","description":"Short heading for the fact."},"value":{"type":"string","title":"Value","description":"The fact itself as text, in `locale` when translated."}},"type":"object","required":["kind","label","value"],"title":"Fact","description":"One fact about the hotel, such as its history or sustainability practices."},"GuestContact":{"properties":{"first_name":{"type":"string","maxLength":100,"minLength":1,"title":"First Name","description":"Lead guest's first name as it should appear on the reservation."},"last_name":{"type":"string","maxLength":100,"minLength":1,"title":"Last Name","description":"Lead guest's last name as it should appear on the reservation."},"email":{"type":"string","maxLength":254,"minLength":3,"title":"Email","description":"Lead guest's email address; the hotel's engine needs it for every reservation.","format":"email"},"phone":{"type":"string","maxLength":40,"minLength":4,"title":"Phone","description":"Lead guest's phone number, international format recommended; the hotel's engine needs it for every reservation."}},"additionalProperties":false,"type":"object","required":["first_name","last_name","email","phone"],"title":"GuestContact","description":"The lead guest of one room, sent to the hotel's booking engine."},"GuestVoice":{"properties":{"praised":{"items":{"$ref":"#/components/schemas/VoiceTheme"},"type":"array","title":"Praised","description":"Up to 6 themes with sentiment 0.5 or higher, most positive first."},"watch_outs":{"items":{"$ref":"#/components/schemas/VoiceTheme"},"type":"array","title":"Watch Outs","description":"Up to 4 themes with sentiment 0.4 or lower, most negative first."}},"type":"object","title":"GuestVoice","description":"What guests praise and complain about, summarised from reviews."},"Handoff":{"properties":{"url":{"type":"string","title":"Url","description":"In `handoff` mode, the hosted browser session for the guest; in `external` mode, the booking engine's own prefilled page.","format":"uri"},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Expires At","description":"When the hosted session stops accepting the guest (20 minutes after it is issued); null in `external` mode."}},"type":"object","required":["url"],"title":"Handoff","description":"The page where the guest finishes the booking, for `handoff` and `external` payment\nmodes."},"Health":{"properties":{"status":{"type":"string","enum":["ok","degraded"],"title":"Status","description":"`ok` when every dependency is up (HTTP 200), `degraded` when one is down (HTTP 503)."},"version":{"type":"string","title":"Version","description":"API version, `2.1.0`; the OpenAPI document's `info.version`.","default":"2.1.0"},"checks":{"additionalProperties":{"type":"string","enum":["ok","unavailable"]},"type":"object","title":"Checks","description":"State of each dependency checked, keyed by name (`database`): `ok` or `unavailable`."},"request_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Request Id","description":"Id of this request."}},"type":"object","required":["status"],"title":"Health","description":"Response of `GET /v2/health`.","examples":[{"checks":{"database":"ok"},"request_id":"req_8f2c1a9b3d4e5f60","status":"ok","version":"2.1.0"}]},"HotelDetailResponse":{"properties":{"request_id":{"type":"string","title":"Request Id","description":"Opaque id of this request, also sent as the `X-Request-Id` header; quote it when contacting support."},"billing":{"anyOf":[{"$ref":"#/components/schemas/Billing"},{"type":"null"}],"description":"What this call cost, or null when it was free."},"hotel_id":{"type":"string","title":"Hotel Id","description":"The hotel's id.","format":"uuid"},"sections_present":{"items":{"type":"string"},"type":"array","title":"Sections Present","description":"Requested sections that have data for this hotel, in a fixed order; `rates` is listed only when some channel has prices."},"core":{"anyOf":[{"$ref":"#/components/schemas/DetailCore"},{"type":"null"}],"description":"Identity, ratings and contact details."},"location":{"anyOf":[{"$ref":"#/components/schemas/DetailLocation"},{"type":"null"}],"description":"Position, address and getting there."},"images":{"anyOf":[{"$ref":"#/components/schemas/DetailImages"},{"type":"null"}],"description":"Gallery in curated order."},"rooms":{"anyOf":[{"items":{"$ref":"#/components/schemas/DetailRoom"},"type":"array"},{"type":"null"}],"title":"Rooms","description":"Physical room types with their listings and prices."},"venues":{"anyOf":[{"items":{"$ref":"#/components/schemas/Venue"},"type":"array"},{"type":"null"}],"title":"Venues","description":"Restaurants, bars, spa and other places at the hotel."},"policies":{"anyOf":[{"items":{"$ref":"#/components/schemas/Policy"},"type":"array"},{"type":"null"}],"title":"Policies","description":"House rules and terms."},"facts":{"anyOf":[{"items":{"$ref":"#/components/schemas/Fact"},"type":"array"},{"type":"null"}],"title":"Facts","description":"Other facts about the hotel."},"notices":{"anyOf":[{"items":{"$ref":"#/components/schemas/Notice"},"type":"array"},{"type":"null"}],"title":"Notices","description":"Temporary notices; returned with `core`."},"reviews":{"anyOf":[{"$ref":"#/components/schemas/Reviews"},{"type":"null"}],"description":"Guest reviews."},"guest_voice":{"anyOf":[{"$ref":"#/components/schemas/GuestVoice"},{"type":"null"}],"description":"Themes from guest reviews."},"rates":{"anyOf":[{"$ref":"#/components/schemas/DetailRates"},{"type":"null"}],"description":"Stored prices for the stay."},"provenance":{"anyOf":[{"$ref":"#/components/schemas/Provenance"},{"type":"null"}],"description":"Where the content comes from."},"warnings":{"items":{"$ref":"#/components/schemas/DetailWarning"},"type":"array","title":"Warnings","description":"Things the call could not fully do."}},"type":"object","required":["request_id","hotel_id"],"title":"HotelDetailResponse","description":"Response of `GET /v2/hotels/{hotel_id}`. Sections not requested are null.","examples":[{"billing":{"amount":"0.005","currency":"USD","sku":"hotel.detail","units":1},"core":{"amenities":["POOL"],"check_in_time":"15:00","check_out_time":"11:00","description":"Boutique hotel near the Trevi Fountain.","hotel_type":"hotel","languages":["en","it"],"name":"Umiltà 36","operating_status":"open","phone":"+39 06 000000","review_count":2056,"review_score":9.1,"short_character":"quiet, central","star_rating":5,"website_url":"https://umilta36.com"},"hotel_id":"43d0be71-5f3c-4c8e-9a52-1d7e2b6f0a19","images":{"count":182,"hero":{"image_id":"a1f3c5e7-9b2d-4f6a-8c0e-2d4f6a8c0e1b","label":"building","url":"https://images.vistalink.com/h/43d0be71/ext.jpg?w=1024"},"items":[{"height":683,"image_id":"b2e4d6f8-0a1c-4e3a-9d5b-7f9a1c3e5d2c","label":"room","position":1,"url":"https://images.vistalink.com/h/43d0be71/room.jpg?w=1024","width":1024}]},"location":{"address":"Via dell'Umiltà 36","city":"Rome","country":"IT","lat":41.9,"lng":12.48,"neighbourhood_text":"Trevi","transport_notes":"Metro A Barberini, 600 m"},"provenance":{"images_release":"7c1e9a3f-2b4d-4e6a-8f0c-5d9b1a3e7f24","profile_source":"site","profile_updated_at":"2026-10-07T09:30:00Z"},"rates":{"channels":[{"channel":"direct","engine":"SYNXIS","observed_at":"2026-10-07T09:30:00Z","offers":[{"availability":"available","board":"breakfast","channel":"direct","currency":"EUR","engine":"SYNXIS","excluded_charges":[],"free_cancellation":false,"includes_taxes":true,"listing_key":"engine_room:1234567","member_rate":false,"nightly_avg":"481.00","observed_at":"2026-10-07T09:30:00Z","occupancy":{"adults":2,"children_ages":[]},"offer_id":"off_t3c8e1a9b4f2d7e6a5c0b","rate_name":"Non-refundable, breakfast included","room_id":"5b1e2f3a-0c4d-4e5f-8a9b-1c2d3e4f5a6b","room_name":"Superior Room","total":"962.00","url":"https://umilta36.com/book?checkin=2026-11-14&checkout=2026-11-16"}],"status":"observed"}],"stay":{"check_in":"2026-11-14","check_out":"2026-11-16","rooms":[{"adults":2,"children_ages":[]}]}},"request_id":"req_8f2c1a9b3d4e5f60","rooms":[{"accessible":false,"amenities":["AIR_CONDITIONING"],"balcony":false,"bed_config":[{"count":1,"type":"king"}],"description":"City view.","images":[],"kitchen":false,"listings":[{"listing_key":"site:superior","match_confidence":"high","name":"Superior Double Room","provider":"umilta36.com","source":"site","url":"https://umilta36.com/rooms/superior"}],"max_occupancy":3,"name":"Superior Room","occupancy":2,"offers":[{"availability":"available","board":"breakfast","channel":"direct","currency":"EUR","engine":"SYNXIS","excluded_charges":[],"free_cancellation":false,"includes_taxes":true,"listing_key":"engine_room:1234567","member_rate":false,"nightly_avg":"481.00","observed_at":"2026-10-07T09:30:00Z","occupancy":{"adults":2,"children_ages":[]},"offer_id":"off_t3c8e1a9b4f2d7e6a5c0b","rate_name":"Non-refundable, breakfast included","room_id":"5b1e2f3a-0c4d-4e5f-8a9b-1c2d3e4f5a6b","room_name":"Superior Room","total":"962.00","url":"https://umilta36.com/book?checkin=2026-11-14&checkout=2026-11-16"}],"room_id":"5b1e2f3a-0c4d-4e5f-8a9b-1c2d3e4f5a6b","size_m2":28,"views":["city"]}],"sections_present":["core","location","images","rooms","rates"]}]},"HotelIdentifier":{"properties":{"booking_url":{"anyOf":[{"type":"string","maxLength":2048,"format":"uri"},{"type":"null"}],"title":"Booking Url","description":"An OTA listing URL of the form `https://www.booking.com/hotel/{cc}/{slug}.html`; matched on the listing slug."},"website_url":{"anyOf":[{"type":"string","maxLength":2048,"format":"uri"},{"type":"null"}],"title":"Website Url","description":"The hotel's own website; only the host is matched, so a bare domain works too."},"expedia_url":{"anyOf":[{"type":"string","maxLength":2048,"format":"uri"},{"type":"null"}],"title":"Expedia Url","description":"An Expedia listing URL carrying `.h{id}.`; answered `unsupported` until Expedia listings are indexed."},"name":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Name","description":"Hotel name, matched against indexed hotels in `city`; needs `city`."},"city":{"anyOf":[{"type":"string","maxLength":120},{"type":"null"}],"title":"City","description":"City of the hotel named in `name`; required with `name`, not allowed without it."},"country":{"anyOf":[{"type":"string","pattern":"^[A-Za-z]{2}$"},{"type":"null"}],"title":"Country","description":"ISO 3166-1 alpha-2 country of the hotel named in `name`; narrows the name match, and a wrong value makes it miss."}},"additionalProperties":false,"type":"object","title":"HotelIdentifier","description":"A hotel named by something other than its `hotel_id`. Send exactly one form:\n`booking_url`, `website_url`, `expedia_url`, or `name` with `city` (and ideally `country`)."},"ImageRef":{"properties":{"image_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Image Id","description":"Image id; the same image keeps the same id across responses."},"url":{"type":"string","title":"Url","description":"CDN URL of the image at 640 pixels wide.","format":"uri"},"label":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Label","description":"What the image shows, for example `building`, `room`, `bathroom` or `swimming pool outdoor`; null when unlabelled."},"width":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Width","description":"Width in pixels of the image at `url`; null on `thumbnail` and when unknown."},"height":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Height","description":"Height in pixels of the image at `url`; null on `thumbnail` and when unknown."}},"type":"object","required":["url"],"title":"ImageRef","description":"An image on a search card, resized to 640 pixels wide."},"Job":{"properties":{"request_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Request Id","description":"Id of the request that returned this job; null in lists and webhook payloads."},"job_id":{"type":"string","title":"Job Id","description":"Job id, `job_` plus 12 letters and digits."},"kind":{"type":"string","enum":["rates.live","rates.batch","rooms.reconcile"],"title":"Kind","description":"`rates.live` (from `POST /v2/rates` or `rates=refresh`), `rates.batch` or `rooms.reconcile`."},"status":{"type":"string","enum":["queued","running","succeeded","failed","canceled"],"title":"Status","description":"`queued`, `running`, then `succeeded`, `failed` or `canceled`."},"progress":{"$ref":"#/components/schemas/JobProgress","description":"Units of work done out of the total."},"created_at":{"type":"string","format":"date-time","title":"Created At","description":"When the job was created, RFC 3339 UTC; jobs expire 7 days later."},"updated_at":{"type":"string","format":"date-time","title":"Updated At","description":"When the job last changed, RFC 3339 UTC."},"estimated_seconds":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Estimated Seconds","description":"Rough time the job was expected to take when it was created, in seconds."},"result":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Result","description":"The outcome once `succeeded`: the `POST /v2/rates` response for `rates.live`, `{summary, lines, format, warnings}` for `rates.batch`, the reconcile result for `rooms.reconcile`. Null otherwise."},"result_url":{"anyOf":[{"type":"string","format":"uri"},{"type":"null"}],"title":"Result Url","description":"`rates.batch` only: where to download the NDJSON result, a signed link or `GET /v2/jobs/{job_id}/result`; null for other kinds and until the job succeeds."},"error":{"anyOf":[{"$ref":"#/components/schemas/JobError"},{"type":"null"}],"description":"Why the job failed; null unless `status` is `failed`."},"billing":{"anyOf":[{"$ref":"#/components/schemas/Billing"},{"type":"null"}],"description":"What the job cost; null until it succeeds, and when nothing billable came back."}},"type":"object","required":["job_id","kind","status","created_at","updated_at"],"title":"Job","description":"Background work started by a 202 response. Also the `data` of the `*.completed` webhooks.","examples":[{"created_at":"2026-10-07T09:30:00Z","estimated_seconds":25,"job_id":"job_K8s2Lq9Wx4Np","kind":"rates.live","progress":{"done":0,"total":1},"status":"running","updated_at":"2026-10-07T09:30:00Z"}]},"JobError":{"properties":{"code":{"type":"string","title":"Code","description":"Error code, one of the API error codes, usually `upstream_error`, `unavailable` (the job stopped and its held credit was returned) or `bad_request`."},"message":{"type":"string","title":"Message","description":"Explanation in English."}},"type":"object","required":["code","message"],"title":"JobError","description":"Why a job failed."},"JobList":{"properties":{"request_id":{"type":"string","title":"Request Id","description":"Opaque id of this request, also sent as the `X-Request-Id` header; quote it when contacting support."},"billing":{"anyOf":[{"$ref":"#/components/schemas/Billing"},{"type":"null"}],"description":"What this call cost, or null when it was free."},"jobs":{"items":{"$ref":"#/components/schemas/Job"},"type":"array","title":"Jobs","description":"Jobs on this page, newest first."},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor","description":"Pass as `cursor` for the next page; null on the last page."}},"type":"object","required":["request_id","jobs"],"title":"JobList","description":"Response of `GET /v2/jobs`.","examples":[{"jobs":[{"billing":{"amount":"0.04","currency":"USD","sku":"rates.direct.live","units":1},"created_at":"2026-10-07T09:30:00Z","estimated_seconds":25,"job_id":"job_K8s2Lq9Wx4Np","kind":"rates.live","progress":{"done":1,"total":1},"result":{"billing":{"amount":"0.002","currency":"USD","sku":"rates.direct.cached","units":1},"request_id":"req_8f2c1a9b3d4e5f60","results":[{"channel":"direct","engine":"SYNXIS","hotel_id":"43d0be71-5f3c-4c8e-9a52-1d7e2b6f0a19","input":0,"observed_at":"2026-10-07T09:30:00Z","rooms":[{"listing_key":"engine_room:1234567","name":"Superior Room","offers":[{"availability":"available","board":"breakfast","channel":"direct","currency":"EUR","engine":"SYNXIS","excluded_charges":[],"free_cancellation":false,"includes_taxes":true,"listing_key":"engine_room:1234567","member_rate":false,"nightly_avg":"481.00","observed_at":"2026-10-07T09:30:00Z","occupancy":{"adults":2,"children_ages":[]},"offer_id":"off_t3c8e1a9b4f2d7e6a5c0b","rate_name":"Non-refundable, breakfast included","room_id":"5b1e2f3a-0c4d-4e5f-8a9b-1c2d3e4f5a6b","room_name":"Superior Room","total":"962.00","url":"https://umilta36.com/book?checkin=2026-11-14&checkout=2026-11-16"}],"room_id":"5b1e2f3a-0c4d-4e5f-8a9b-1c2d3e4f5a6b"}],"status":"observed"}],"summary":{"observed":1,"stale":0,"unknown":0,"unresolved":0,"unsupported":0}},"status":"succeeded","updated_at":"2026-10-07T09:30:00Z"}],"request_id":"req_8f2c1a9b3d4e5f60"}]},"JobProgress":{"properties":{"done":{"type":"integer","title":"Done","description":"Units of work finished.","default":0},"total":{"type":"integer","title":"Total","description":"Units of work in the job: hotels for `rates.live`, hotel and stay pairs for `rates.batch`, 1 for `rooms.reconcile`.","default":1}},"type":"object","title":"JobProgress","description":"How far a job has got."},"JobRef":{"properties":{"job_id":{"type":"string","title":"Job Id","description":"Job id; poll it with `GET /v2/jobs/{job_id}`."}},"type":"object","required":["job_id"],"title":"JobRef","description":"A reference to a job."},"LocationFilter":{"properties":{"city":{"anyOf":[{"type":"string","maxLength":120},{"type":"null"}],"title":"City","description":"City name, for example `Rome`; with no `country` an ambiguous city may come back as a clarification."},"country":{"anyOf":[{"type":"string","pattern":"^[A-Za-z]{2}$"},{"type":"null"}],"title":"Country","description":"ISO 3166-1 alpha-2 country code of the city, for example `IT`."},"place_id":{"anyOf":[{"type":"string","maxLength":256},{"type":"null"}],"title":"Place Id","description":"A `resolved.location.place_id` from an earlier search, a slug of city and country such as `rome-it`."}},"additionalProperties":false,"type":"object","title":"LocationFilter","description":"Where to search: either `city` with `country`, or a `place_id` from an earlier response,\nnever both."},"Mandate":{"properties":{"accepted_total":{"type":"string","pattern":"^-?\\d+(\\.\\d+)?$","title":"Accepted Total","description":"The total the guest saw and accepted, a decimal string; must equal the offer's `total` to the cent. A higher engine total fails the booking with `price_changed` before the card is typed; a lower one goes ahead."},"currency":{"type":"string","pattern":"^[A-Z]{3}$","title":"Currency","description":"ISO 4217 currency of `accepted_total`; must be the offer's."},"consent":{"$ref":"#/components/schemas/Consent","description":"The guest's approval of this booking."}},"additionalProperties":false,"type":"object","required":["accepted_total","currency","consent"],"title":"Mandate","description":"`card_entry` only: the total the guest accepted. We never press the hotel's pay button above\nit."},"NearFilter":{"properties":{"lat":{"type":"number","maximum":90.0,"minimum":-90.0,"title":"Lat","description":"Latitude of the centre in decimal degrees."},"lng":{"type":"number","maximum":180.0,"minimum":-180.0,"title":"Lng","description":"Longitude of the centre in decimal degrees."},"radius_m":{"type":"integer","maximum":50000.0,"minimum":50.0,"title":"Radius M","description":"Search radius in metres, 50 to 50,000; also the anchor for each card's `distance_m`.","default":2000}},"additionalProperties":false,"type":"object","required":["lat","lng"],"title":"NearFilter","description":"A point and radius to search around; replaces any place named in `query`."},"NearestListing":{"properties":{"source":{"type":"string","title":"Source","description":"Source label of the candidate."},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name","description":"Room name of the candidate."},"confidence":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Confidence","description":"Match score from 0 to 1, below `min_confidence`."},"ambiguous":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Ambiguous","description":"True when another candidate scored as well, so neither could be chosen."}},"type":"object","required":["source"],"title":"NearestListing","description":"The closest candidate an unmatched listing had, below `min_confidence`."},"NextAction":{"properties":{"type":{"type":"string","enum":["three_d_secure_app","three_d_secure_otp"],"title":"Type","description":"`three_d_secure_app`: tell the guest to approve the payment in their banking app; call nothing. `three_d_secure_otp`: the bank's challenge asks for a code the guest received; the challenge is watched until `expires_at` (a relay endpoint is not available yet)."},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Expires At","description":"The bank's deadline, RFC 3339 UTC."},"cancelable":{"type":"boolean","title":"Cancelable","description":"Always false: the card was already submitted.","default":false}},"type":"object","required":["type"],"title":"NextAction","description":"`card_entry` only, non-null exactly when `status` is `requires_action`: what the guest must do."},"Notice":{"properties":{"severity":{"type":"string","enum":["info","warning","critical"],"title":"Severity","description":"`info`, `warning` or `critical`.","default":"info"},"title":{"type":"string","title":"Title","description":"Notice heading, in `locale` when translated."},"body":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Body","description":"Notice text, in `locale` when translated."},"valid_to":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Valid To","description":"Last day the notice applies, at midnight with no time zone; null when open-ended."}},"type":"object","required":["title"],"title":"Notice","description":"A temporary notice from the hotel, such as renovation work or a closed facility. Returned\nwith `core`."},"Occupancy":{"properties":{"adults":{"type":"integer","title":"Adults","description":"Adults in the room."},"children_ages":{"items":{"type":"integer"},"type":"array","title":"Children Ages","description":"Age of each child in the room; empty for adults only."}},"type":"object","required":["adults"],"title":"Occupancy","description":"The party a price or stay is for, as returned."},"Occupant":{"properties":{"first_name":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"First Name","description":"Guest's first name."},"last_name":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"Last Name","description":"Guest's last name."},"age":{"anyOf":[{"type":"integer","maximum":120.0,"minimum":0.0},{"type":"null"}],"title":"Age","description":"Guest's age in years, 0 to 120."}},"additionalProperties":false,"type":"object","title":"Occupant","description":"Another guest in a room. Stored with the booking but not sent to the booking engine\ntoday."},"Offer":{"properties":{"offer_id":{"type":"string","title":"Offer Id","description":"Offer id, `off_t` plus 20 hex characters, specific to your account; book it with `POST /v2/bookings` within 30 minutes of receiving it."},"channel":{"type":"string","enum":["direct","third_party","expedia"],"title":"Channel","description":"Where the price was observed: `direct` for the hotel's own booking engine, `third_party` for the Enterprise third-party channel."},"engine":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Engine","description":"Code of the hotel's booking engine for direct offers, for example `SYNXIS`; null on the third-party channel."},"room_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Room Id","description":"Catalogue room this offer is for, matching `rooms[].room_id` on hotel detail; null when the source names the room only by its own code or name."},"room_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Room Name","description":"Room name as the source shows it; null when the source does not name the room."},"listing_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Listing Key","description":"The source's own key for the room, such as `engine_room:SUP` or `room_type:1234567`; null when the source has none."},"rate_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rate Name","description":"Rate plan name as the source shows it, or one built from the cancellation terms and board, such as `Free cancellation, breakfast included`."},"total":{"type":"string","pattern":"^-?\\d+(\\.\\d+)?$","title":"Total","description":"Price for the whole stay and the whole party, a decimal string in `currency`; taxes the source quoted separately are added in when their amount is known. `\"0\"` when sold out."},"currency":{"type":"string","pattern":"^[A-Z]{3}$","title":"Currency","description":"ISO 4217 currency the price was observed in; never converted."},"nightly_avg":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d+)?$"},{"type":"null"}],"title":"Nightly Avg","description":"`total` divided by the number of nights, for the whole party rather than per room; null when sold out."},"includes_taxes":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Includes Taxes","description":"True when `total` includes taxes and fees, false when some are listed in `excluded_charges`, null when the source does not say."},"excluded_charges":{"items":{"$ref":"#/components/schemas/ExcludedCharge"},"type":"array","title":"Excluded Charges","description":"Charges payable on top of `total`; usually empty."},"board":{"anyOf":[{"type":"string","enum":["none","breakfast","half","full","all_inclusive"]},{"type":"null"}],"title":"Board","description":"Meals included: `none`, `breakfast`, `half` (breakfast and dinner), `full` (all meals) or `all_inclusive`; null when the source does not say."},"free_cancellation":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Free Cancellation","description":"True when the rate can be canceled free of charge, false when it is non-refundable, null when unknown."},"member_rate":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Member Rate","description":"True when the price is a member or loyalty rate that needs the guest signed in to the hotel's programme, false for a public rate, null when the source does not say. Member rates are listed so you see the hotel's full offer list; within a room they sort after public rates, and `POST /v2/bookings` accepts public rates only."},"cancellation_deadline":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Cancellation Deadline","description":"Last moment to cancel free of charge, RFC 3339; set only on offers read live, null otherwise."},"payment":{"anyOf":[{"type":"string","enum":["prepay","pay_at_hotel"]},{"type":"null"}],"title":"Payment","description":"When the guest pays: `prepay` at booking or `pay_at_hotel` at the property; null when the source does not say."},"occupancy":{"anyOf":[{"$ref":"#/components/schemas/Occupancy"},{"type":"null"}],"description":"The party this price is for, totalled across all rooms."},"availability":{"type":"string","enum":["available","sold_out","unknown"],"title":"Availability","description":"`available` when the room can be booked at this price, `sold_out` when the source showed it as unavailable, `unknown` when it did not say.","default":"unknown"},"observed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Observed At","description":"When this price was observed, RFC 3339 UTC."},"url":{"anyOf":[{"type":"string","format":"uri"},{"type":"null"}],"title":"Url","description":"Page on the source where this price was observed; null when the source has no linkable page."}},"type":"object","required":["offer_id","channel","total","currency"],"title":"Offer","description":"One bookable price for one room and rate, as observed on a channel.","examples":[{"availability":"available","board":"breakfast","channel":"direct","currency":"EUR","engine":"SYNXIS","excluded_charges":[],"free_cancellation":false,"includes_taxes":true,"listing_key":"engine_room:1234567","member_rate":false,"nightly_avg":"481.00","observed_at":"2026-10-07T09:30:00Z","occupancy":{"adults":2,"children_ages":[]},"offer_id":"off_t3c8e1a9b4f2d7e6a5c0b","rate_name":"Non-refundable, breakfast included","room_id":"5b1e2f3a-0c4d-4e5f-8a9b-1c2d3e4f5a6b","room_name":"Superior Room","total":"962.00","url":"https://umilta36.com/book?checkin=2026-11-14&checkout=2026-11-16"}]},"Override":{"properties":{"field":{"type":"string","enum":["location","stay","budget","hotel_name"],"title":"Field","description":"The criterion in conflict: `location`, `stay`, `budget` or `hotel_name`."},"query_said":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Query Said","description":"The value read from the query, for example `Paris` or `2026-11-14/2026-11-16`."},"filters_said":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Filters Said","description":"The value from `filters`, which was used."},"used":{"type":"string","enum":["query","filters"],"title":"Used","description":"Which value the search used; always `filters`."}},"type":"object","required":["field","used"],"title":"Override","description":"A conflict between the query text and `filters`, settled in favour of the filters."},"Payment":{"properties":{"mode":{"type":"string","enum":["checkout","handoff","external","card_entry"],"title":"Mode","description":"`checkout` returns a hosted checkout link for pay-now offers, `handoff` returns a hosted browser session in which the guest completes the booking, `external` returns the booking engine's own prefilled page. `card_entry` (rolling out; answers 403 `not_yet_available` in production) types the guest's card into the hotel's own checkout from a one-booking credential: send `payment_method` and `mandate`, then call `POST /v2/bookings/{booking_id}/confirm`. The hotel stays merchant of record."},"return_url":{"anyOf":[{"type":"string","maxLength":2048,"format":"uri"},{"type":"null"}],"title":"Return Url","description":"`checkout` only: the https page the guest lands on after paying (with `booking_id` appended) or abandoning; a VistaLink page when null."},"locale":{"anyOf":[{"type":"string","maxLength":10},{"type":"null"}],"title":"Locale","description":"`checkout` only: language of the hosted checkout page as a language tag; `pt-BR` reads as `pt`, and unsupported languages fall back to the guest's browser.","default":"en"}},"additionalProperties":false,"type":"object","required":["mode"],"title":"Payment","description":"How the guest pays. The guest always pays the property directly; VistaLink never holds\nthe funds."},"PaymentCredential":{"properties":{"request_id":{"type":"string","title":"Request Id","description":"Opaque id of this request, also sent as the `X-Request-Id` header; quote it when contacting support."},"billing":{"anyOf":[{"$ref":"#/components/schemas/Billing"},{"type":"null"}],"description":"What this call cost, or null when it was free."},"credential_id":{"type":"string","title":"Credential Id","description":"Credential id, `cred_` plus 24 letters and digits."},"status":{"type":"string","enum":["active","bound","consumed","deleted"],"title":"Status","description":"`active` (unused), `bound` (attached to `booking_id`), `consumed` (handed to the hotel's checkout) or `deleted` (the vault token was deleted)."},"booking_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Booking Id","description":"The booking it is bound to; null until then."},"brand":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Brand","description":"Card brand, for example `visa`; null when unknown."},"last4":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last4","description":"Last four digits of the card."},"exp_month":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Exp Month","description":"Card expiry month, 1 to 12."},"exp_year":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Exp Year","description":"Card expiry year, four digits."},"expires_at":{"type":"string","format":"date-time","title":"Expires At","description":"When the credential stops being usable: 30 minutes after creation, or the token's own expiry if sooner."},"created_at":{"type":"string","format":"date-time","title":"Created At","description":"When the credential was created, RFC 3339 UTC."}},"type":"object","required":["request_id","credential_id","status","expires_at","created_at"],"title":"PaymentCredential","description":"A one-booking card credential. It never carries the card number or CVC.","examples":[{"brand":"visa","created_at":"2026-10-07T09:29:00Z","credential_id":"cred_Hk2v9Qx0aL7mN3pZr5Tb8Wd1","exp_month":12,"exp_year":2028,"expires_at":"2026-10-07T09:59:00Z","last4":"4242","request_id":"req_8f2c1a9b3d4e5f60","status":"active"}]},"PaymentCredentialRequest":{"properties":{"bt_token_id":{"type":"string","maxLength":64,"minLength":1,"pattern":"^[A-Za-z0-9_-]+$","title":"Bt Token Id","description":"The id of a Basis Theory `card` token you created with VistaLink's public key (type `card`, `expires_at` at most 60 minutes ahead, a fresh token per booking). The card number never reaches VistaLink. Test keys accept only `tok_test_confirmed`, `tok_test_requires_action`, `tok_test_card_declined`, `tok_test_unknown` and `tok_test_price_changed`."}},"additionalProperties":false,"type":"object","required":["bt_token_id"],"title":"PaymentCredentialRequest","description":"Body of `POST /v2/payment_credentials`.","examples":[{"bt_token_id":"2b8f4c1e-6d3a-4e7b-9c0f-5a1d8e2b7c64"}]},"PaymentMethod":{"properties":{"type":{"type":"string","const":"card","title":"Type","description":"Always `card` in this version."},"credential_id":{"anyOf":[{"type":"string","pattern":"^cred_[A-Za-z0-9]{24}$"},{"type":"null"}],"title":"Credential Id","description":"A `cred_` id from `POST /v2/payment_credentials`, bound to this booking at create. Omit it to send it with `POST /v2/bookings/{booking_id}/confirm` instead."}},"additionalProperties":false,"type":"object","required":["type"],"title":"PaymentMethod","description":"`card_entry` only: the card the hotel's engine charges, as a credential from\n`POST /v2/payment_credentials`."},"Policy":{"properties":{"kind":{"type":"string","title":"Kind","description":"Policy topic, for example `checkin`, `checkout`, `children`, `extra_bed`, `pets`, `smoking`, `cancellation`, `deposit`, `payment`, `city_tax`, `breakfast`, `parking` or `other`."},"summary":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Summary","description":"The policy in one or two sentences, in `locale` when translated."},"structured":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Structured","description":"The policy's terms as key-value data when the source gives them, for example times or age limits; shape depends on `kind`."}},"type":"object","required":["kind"],"title":"Policy","description":"One hotel policy, such as children, pets or cancellation."},"Preferences":{"properties":{"text":{"anyOf":[{"type":"string","maxLength":1000},{"type":"null"}],"title":"Text","description":"Free-text taste, for example `romantic, great breakfast`, added to the semantic query; ignored when `ranking` is `filters`."},"tags":{"items":{"type":"string"},"type":"array","maxItems":20,"title":"Tags","description":"Short free-form tags such as `ROMANTIC`, up to 20, added to the semantic query and matched against review aspects."}},"additionalProperties":false,"type":"object","title":"Preferences","description":"Soft signals that shape semantic ranking without excluding hotels."},"PriceToBeat":{"properties":{"total":{"type":"string","pattern":"^-?\\d+(\\.\\d+)?$","title":"Total","description":"Price for the whole stay and party, a positive decimal string in `currency`."},"currency":{"type":"string","pattern":"^[A-Z]{3}$","title":"Currency","description":"ISO 4217 currency of `total`."}},"additionalProperties":false,"type":"object","required":["total","currency"],"title":"PriceToBeat","description":"A price you already hold for the same stay and party, which the hotel is asked to beat."},"Provenance":{"properties":{"profile_source":{"anyOf":[{"type":"string","enum":["site","booking"]},{"type":"null"}],"title":"Profile Source","description":"Main source of the published profile: `site` (the hotel's website) or `booking` (the third-party channel); null without a profile."},"profile_updated_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Profile Updated At","description":"When the profile was last updated, RFC 3339 UTC."},"images_release":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Images Release","description":"Id of the gallery release the image order comes from; null when the images section was not requested or the hotel is not in a release."}},"type":"object","title":"Provenance","description":"Where the hotel's content comes from."},"Quote":{"properties":{"request_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Request Id","description":"Id of the request that returned this quote; null in webhook payloads."},"quote_id":{"type":"string","title":"Quote Id","description":"Quote id, `qte_` plus 20 hex characters."},"status":{"type":"string","enum":["queued","contacting","waiting","offer_received","completed","no_response","declined","cancelled","failed","accepted"],"title":"Status","description":"Where the quote is: `queued`, `contacting` (the email is going out), `waiting` (sent, awaiting the reply), `offer_received` (see `offers`), then `completed` (the deadline passed with offers), `accepted` (your guest was introduced), `no_response`, `declined`, `cancelled` or `failed`."},"hotel_id":{"type":"string","title":"Hotel Id","description":"The hotel asked.","format":"uuid"},"stay":{"$ref":"#/components/schemas/QuoteStay","description":"The stay asked about."},"wants":{"anyOf":[{"$ref":"#/components/schemas/QuoteWantsOut"},{"type":"null"}],"description":"The preferences sent, or null."},"anchor":{"anyOf":[{"$ref":"#/components/schemas/QuoteAnchor"},{"type":"null"}],"description":"The price the hotel was asked to beat, or null when there was none."},"deadline_at":{"type":"string","format":"date-time","title":"Deadline At","description":"When the hotel's time to reply ends, RFC 3339 UTC."},"next_step":{"type":"string","title":"Next Step","description":"One plain sentence on what happens next, suitable to show in your own interface."},"created_at":{"type":"string","format":"date-time","title":"Created At","description":"When the quote was requested, RFC 3339 UTC."},"updated_at":{"type":"string","format":"date-time","title":"Updated At","description":"When the quote last changed, RFC 3339 UTC."},"offers":{"items":{"$ref":"#/components/schemas/QuoteOffer"},"type":"array","title":"Offers","description":"Priced offers from the hotel's replies, oldest first."},"accepted_offer_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Accepted Offer Id","description":"The offer you accepted; null until `status` is `accepted`."},"hotel_commission_applies":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Hotel Commission Applies","description":"After acceptance: true when the hotel pays a commission on the booking (half of it is yours), false when the offer was a special rate it pays no commission on; null before acceptance or when not yet known."},"failure_reason":{"anyOf":[{"type":"string","enum":["no_contact_email","hotel_suppressed","hotel_daily_ceiling","invalid_stay","invalid_request","opted_out","unreachable","rejected"]},{"type":"null"}],"title":"Failure Reason","description":"Why the quote ended without offers when `status` is `failed` or `declined`: `no_contact_email`, `hotel_suppressed`, `hotel_daily_ceiling` (try again tomorrow), `invalid_stay`, `opted_out`, `unreachable` or `rejected`; null otherwise."},"billing":{"type":"null","title":"Billing","description":"Always null on responses: the quote fee is charged once, when the hotel's first priced offer arrives, and shows in usage."}},"type":"object","required":["quote_id","status","hotel_id","stay","deadline_at","next_step","created_at","updated_at"],"title":"Quote","description":"A request for a price quote, emailed to the hotel by VistaLink. Returned by create, get,\ncancel and accept, and as `data` in `quote.updated`.","examples":[{"anchor":{"currency":"EUR","nightly":"481.00","source":"third_party","total":"962.00"},"created_at":"2026-10-07T09:30:00Z","deadline_at":"2026-10-09T09:30:00Z","hotel_id":"43d0be71-5f3c-4c8e-9a52-1d7e2b6f0a19","next_step":"Email sent to the hotel; most hotels reply within 6 hours, some take a day.","offers":[],"quote_id":"qte_7c2e9a4b1d8f3e6a0c5b","status":"waiting","stay":{"check_in":"2026-11-14","check_out":"2026-11-16","rooms":[{"adults":2,"children_ages":[]}]},"updated_at":"2026-10-07T09:30:04Z","wants":{"board":"breakfast","refundable":true,"room_type":"double"}},{"anchor":{"currency":"EUR","nightly":"481.00","source":"third_party","total":"962.00"},"created_at":"2026-10-07T09:30:00Z","deadline_at":"2026-10-09T09:30:00Z","hotel_id":"43d0be71-5f3c-4c8e-9a52-1d7e2b6f0a19","next_step":"The hotel replied with an offer. Accept it to introduce your guest.","offers":[{"beats_by":"82.00","board":"breakfast","cancellation_deadline":"2026-11-12T23:59:00Z","confidence":0.93,"currency":"EUR","evidence":"We can offer the Superior Double with breakfast for EUR 880 for both nights, free cancellation until 12 November.","free_cancellation":true,"includes_taxes":true,"needs_review":false,"nightly_avg":"440.00","offer_id":"qof_4a9d2c7e1b8f5a3d6e0c","rate_name":"Breakfast included, free cancellation","room_name":"Superior Double","source":"email","total":"880.00","valid_until":"2026-10-10"}],"quote_id":"qte_7c2e9a4b1d8f3e6a0c5b","status":"offer_received","stay":{"check_in":"2026-11-14","check_out":"2026-11-16","rooms":[{"adults":2,"children_ages":[]}]},"updated_at":"2026-10-07T14:52:10Z","wants":{"board":"breakfast","refundable":true,"room_type":"double"}},{"accepted_offer_id":"qof_4a9d2c7e1b8f5a3d6e0c","anchor":{"currency":"EUR","nightly":"481.00","source":"third_party","total":"962.00"},"created_at":"2026-10-07T09:30:00Z","deadline_at":"2026-10-09T09:30:00Z","hotel_commission_applies":false,"hotel_id":"43d0be71-5f3c-4c8e-9a52-1d7e2b6f0a19","next_step":"Your guest has been introduced to the hotel by email; the hotel replies to them directly to confirm the booking.","offers":[{"beats_by":"82.00","board":"breakfast","cancellation_deadline":"2026-11-12T23:59:00Z","confidence":0.93,"currency":"EUR","evidence":"We can offer the Superior Double with breakfast for EUR 880 for both nights, free cancellation until 12 November.","free_cancellation":true,"includes_taxes":true,"needs_review":false,"nightly_avg":"440.00","offer_id":"qof_4a9d2c7e1b8f5a3d6e0c","rate_name":"Breakfast included, free cancellation","room_name":"Superior Double","source":"email","total":"880.00","valid_until":"2026-10-10"}],"quote_id":"qte_7c2e9a4b1d8f3e6a0c5b","status":"accepted","stay":{"check_in":"2026-11-14","check_out":"2026-11-16","rooms":[{"adults":2,"children_ages":[]}]},"updated_at":"2026-10-07T15:10:31Z","wants":{"board":"breakfast","refundable":true,"room_type":"double"}}]},"QuoteAcceptRequest":{"properties":{"guest":{"$ref":"#/components/schemas/QuoteGuest","description":"The guest to introduce to the hotel."}},"additionalProperties":false,"type":"object","required":["guest"],"title":"QuoteAcceptRequest","description":"Body of `POST /v2/quotes/{quote_id}/offers/{offer_id}/accept`.","examples":[{"guest":{"email":"ada@example.com","first_name":"Ada","last_name":"Rossi"}}]},"QuoteAnchor":{"properties":{"total":{"type":"string","pattern":"^-?\\d+(\\.\\d+)?$","title":"Total","description":"Price for the whole stay and party, a decimal string in `currency`."},"nightly":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d+)?$"},{"type":"null"}],"title":"Nightly","description":"`total` divided by the number of nights."},"currency":{"type":"string","pattern":"^[A-Z]{3}$","title":"Currency","description":"ISO 4217 currency of `total`."},"source":{"type":"string","enum":["third_party","partner"],"title":"Source","description":"`third_party` when it is the best public online price we know, `partner` when you sent it."}},"type":"object","required":["total","currency","source"],"title":"QuoteAnchor","description":"The price the hotel was asked to beat."},"QuoteGuest":{"properties":{"first_name":{"type":"string","maxLength":80,"minLength":1,"title":"First Name","description":"Guest's first name, up to 80 characters."},"last_name":{"type":"string","maxLength":80,"minLength":1,"title":"Last Name","description":"Guest's last name, up to 80 characters."},"email":{"type":"string","maxLength":254,"minLength":6,"title":"Email","description":"Guest's email address; the guest is copied on the introduction email and the hotel replies to it.","format":"email"}},"additionalProperties":false,"type":"object","required":["first_name","last_name","email"],"title":"QuoteGuest","description":"The guest introduced to the hotel when you accept an offer."},"QuoteOffer":{"properties":{"offer_id":{"type":"string","title":"Offer Id","description":"Offer id, `qof_` plus 20 hex characters; pass it to accept."},"source":{"type":"string","const":"email","title":"Source","description":"Always `email`: the hotel's emailed reply.","default":"email"},"room_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Room Name","description":"Room the hotel offered, as it named it."},"rate_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rate Name","description":"Short summary of the terms, for example `Breakfast included, free cancellation`."},"total":{"type":"string","pattern":"^-?\\d+(\\.\\d+)?$","title":"Total","description":"Price for the whole stay and party, a decimal string in `currency`."},"currency":{"type":"string","pattern":"^[A-Z]{3}$","title":"Currency","description":"ISO 4217 currency the hotel quoted in; never converted."},"nightly_avg":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d+)?$"},{"type":"null"}],"title":"Nightly Avg","description":"`total` divided by the number of nights."},"includes_taxes":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Includes Taxes","description":"True when the hotel said taxes are included, false when not, null when it did not say."},"board":{"anyOf":[{"type":"string","enum":["none","breakfast","half","full","all_inclusive"]},{"type":"null"}],"title":"Board","description":"Meals included, or null when the hotel did not say."},"free_cancellation":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Free Cancellation","description":"True when the offer can be canceled free of charge, false when not, null when unknown."},"cancellation_deadline":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Cancellation Deadline","description":"Last moment to cancel free of charge, RFC 3339, when the hotel gave one."},"valid_until":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Valid Until","description":"Last day the hotel holds this price, YYYY-MM-DD, when it said so."},"offer_link":{"anyOf":[{"type":"string","format":"uri"},{"type":"null"}],"title":"Offer Link","description":"A booking link the hotel included in its reply, or null."},"beats_by":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d+)?$"},{"type":"null"}],"title":"Beats By","description":"How much cheaper than the `anchor` price this offer is, in `currency`; null without a comparable anchor."},"evidence":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Evidence","description":"The sentence of the hotel's reply the price was read from, up to 280 characters."},"confidence":{"anyOf":[{"type":"number","maximum":1.0,"minimum":0.0},{"type":"null"}],"title":"Confidence","description":"How sure the reading of the reply is, 0 to 1."},"needs_review":{"type":"boolean","title":"Needs Review","description":"True when the reply was ambiguous and the price is being checked: show it with care, it cannot be accepted yet (409 `offer_needs_review`) and it is not charged for. The hotel's first offer with `needs_review` false is the one the quote fee is charged on.","default":false}},"type":"object","required":["offer_id","total","currency"],"title":"QuoteOffer","description":"One priced offer the hotel sent by email, read from its reply."},"QuoteRequest":{"properties":{"hotel_id":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$","title":"Hotel Id","description":"The hotel to ask, as returned by resolve or search.","format":"uuid"},"stay":{"$ref":"#/components/schemas/Stay","description":"Dates and party to ask about; `check_in` must be today or later."},"wants":{"anyOf":[{"$ref":"#/components/schemas/QuoteWants"},{"type":"null"}],"description":"Room, board and cancellation preferences passed on to the hotel; omit to ask for the best rate."},"price_to_beat":{"anyOf":[{"type":"string","const":"auto"},{"$ref":"#/components/schemas/PriceToBeat"}],"title":"Price To Beat","description":"`auto` (the default) quotes the best public price we know for the stay as the price to beat, when there is one; `{total, currency}` uses your own price instead.","default":"auto"},"deadline_hours":{"type":"integer","maximum":168.0,"minimum":1.0,"title":"Deadline Hours","description":"Hours the hotel has to reply, 1 to 168; default 48. A quote with no priced reply by then closes as `no_response` and costs nothing.","default":48},"locale":{"anyOf":[{"type":"string","pattern":"^[a-z]{2}(-[A-Za-z]{2})?$"},{"type":"null"}],"title":"Locale","description":"Language of the email to the hotel, as a language tag such as `en` or `it`; the hotel's own language when omitted."},"sandbox_outcome":{"anyOf":[{"type":"string","enum":["offer","declined","no_response"]},{"type":"null"}],"title":"Sandbox Outcome","description":"Test keys only: how the simulated hotel answers, `offer` (the default), `declined` or `no_response`. Rejected with 400 on a live key."}},"additionalProperties":false,"type":"object","required":["hotel_id","stay"],"title":"QuoteRequest","description":"Body of `POST /v2/quotes`.","examples":[{"deadline_hours":48,"hotel_id":"43d0be71-5f3c-4c8e-9a52-1d7e2b6f0a19","locale":"en","price_to_beat":"auto","stay":{"check_in":"2026-11-14","check_out":"2026-11-16","rooms":[{"adults":2,"children_ages":[]}]},"wants":{"board":"breakfast","refundable":true,"room_type":"double"}},{"deadline_hours":48,"hotel_id":"43d0be71-5f3c-4c8e-9a52-1d7e2b6f0a19","locale":"en","price_to_beat":{"currency":"EUR","total":"962.00"},"stay":{"check_in":"2026-11-14","check_out":"2026-11-16","rooms":[{"adults":2,"children_ages":[]}]},"wants":{"board":"breakfast","refundable":true,"room_type":"double"}}]},"QuoteStay":{"properties":{"check_in":{"type":"string","format":"date","title":"Check In","description":"Arrival date, YYYY-MM-DD."},"check_out":{"type":"string","format":"date","title":"Check Out","description":"Departure date, YYYY-MM-DD."},"rooms":{"items":{"$ref":"#/components/schemas/Occupancy"},"type":"array","title":"Rooms","description":"One entry per room."}},"type":"object","required":["check_in","check_out"],"title":"QuoteStay","description":"The stay a quote asks about."},"QuoteWants":{"properties":{"refundable":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Refundable","description":"True to ask for a rate with free cancellation, false for the cheapest non-refundable one."},"board":{"anyOf":[{"type":"string","enum":["none","breakfast","half","full","all_inclusive"]},{"type":"null"}],"title":"Board","description":"Meals to ask for: `none`, `breakfast`, `half` (breakfast and dinner), `full` (all meals) or `all_inclusive`."},"room_type":{"anyOf":[{"type":"string","maxLength":60,"minLength":1},{"type":"null"}],"title":"Room Type","description":"Room type to ask for in plain words, for example `double` or `junior suite`; up to 60 characters, no links, email addresses or angle brackets (it is quoted to the hotel)."}},"additionalProperties":false,"type":"object","title":"QuoteWants","description":"What the guest would like; every field is optional and is passed on to the hotel as a\npreference, not a filter."},"QuoteWantsOut":{"properties":{"refundable":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Refundable","description":"Free cancellation asked for, or null."},"board":{"anyOf":[{"type":"string","enum":["none","breakfast","half","full","all_inclusive"]},{"type":"null"}],"title":"Board","description":"Meals asked for, or null."},"room_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Room Type","description":"Room type asked for, or null."}},"type":"object","title":"QuoteWantsOut","description":"The preferences sent with the quote request."},"RateChannel":{"properties":{"channel":{"type":"string","enum":["direct","third_party","expedia"],"title":"Channel","description":"`direct` for the hotel's own booking engine, `third_party` for the Enterprise third-party channel."},"engine":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Engine","description":"Code of the hotel's booking engine on the direct channel, for example `SYNXIS`; null on the third-party channel."},"status":{"type":"string","enum":["observed","stale","unknown","unsupported"],"title":"Status","description":"`observed` when seen recently (within 4 hours for stays in the next 14 days, else 24 hours) or `stale` when older but within 7 days; channels with no prices are left out."},"observed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Observed At","description":"When the newest price on this channel was observed, RFC 3339 UTC."},"offers":{"items":{"$ref":"#/components/schemas/Offer"},"type":"array","title":"Offers","description":"Prices for each room and rate, cheapest first, sold-out last."}},"type":"object","required":["channel","status"],"title":"RateChannel","description":"Prices from one channel for the requested stay."},"RateResult":{"properties":{"hotel_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Hotel Id","description":"The hotel's id; null when the input did not resolve."},"input":{"type":"integer","title":"Input","description":"Index of the hotel in the request's `hotels[]`."},"status":{"type":"string","enum":["observed","stale","unknown","unsupported","unresolved"],"title":"Status","description":"`observed` (recent prices), `stale` (older but within `max_age_hours`), `unknown` (no prices for this stay), `unsupported` (no readable booking engine, or the channel is not enabled) or `unresolved` (the input did not resolve to an indexed hotel)."},"channel":{"anyOf":[{"type":"string","enum":["direct","third_party"]},{"type":"null"}],"title":"Channel","description":"The channel read, as requested."},"engine":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Engine","description":"Code of the hotel's booking engine on the direct channel; null on the third-party channel and when unknown."},"observed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Observed At","description":"When the newest price was observed, RFC 3339 UTC; null unless `observed` or `stale`."},"rooms":{"items":{"$ref":"#/components/schemas/RateRoom"},"type":"array","title":"Rooms","description":"Prices per room, cheapest room first; empty unless `observed` or `stale`."}},"type":"object","required":["input","status"],"title":"RateResult","description":"Prices for one hotel from `hotels[]`."},"RateRoom":{"properties":{"room_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Room Id","description":"Catalogue room id when the source's room is matched to one; null otherwise."},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name","description":"Room name as the source shows it."},"listing_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Listing Key","description":"The source's own key for the room."},"offers":{"items":{"$ref":"#/components/schemas/Offer"},"type":"array","title":"Offers","description":"The cheapest offer per rate plan, cheapest first, sold-out last."}},"type":"object","title":"RateRoom","description":"Prices for one room at a hotel."},"RatesBatchRequest":{"properties":{"hotels":{"items":{"anyOf":[{"type":"string"},{"$ref":"#/components/schemas/HotelIdentifier"}]},"type":"array","maxItems":10000,"minItems":1,"title":"Hotels","description":"Up to 10,000 hotels, each a `hotel_id` or an inline identifier."},"stays":{"items":{"$ref":"#/components/schemas/Stay"},"type":"array","maxItems":10,"minItems":1,"title":"Stays","description":"Up to 10 stays; every hotel is priced for every stay."},"channel":{"type":"string","enum":["direct","third_party"],"title":"Channel","description":"`direct` (the default) or `third_party` (Enterprise only).","default":"direct"},"freshness":{"type":"string","enum":["cached","live"],"title":"Freshness","description":"`cached` reads stored prices; `live` reads the booking engines.","default":"cached"},"max_age_hours":{"type":"integer","maximum":720.0,"minimum":1.0,"title":"Max Age Hours","description":"Oldest stored price to accept on `cached`, in hours; values above 168 are treated as 168.","default":24},"currency":{"anyOf":[{"type":"string","pattern":"^[A-Z]{3}$"},{"type":"null"}],"title":"Currency","description":"Preferred ISO 4217 currency; prices are never converted."}},"additionalProperties":false,"type":"object","required":["hotels","stays"],"title":"RatesBatchRequest","description":"Body of `POST /v2/rates/batch`.","examples":[{"channel":"direct","currency":"EUR","freshness":"cached","hotels":["43d0be71-5f3c-4c8e-9a52-1d7e2b6f0a19"],"stays":[{"check_in":"2026-11-14","check_out":"2026-11-16","rooms":[{"adults":2,"children_ages":[]}]}]}]},"RatesRequest":{"properties":{"hotels":{"items":{"anyOf":[{"type":"string"},{"$ref":"#/components/schemas/HotelIdentifier"}]},"type":"array","maxItems":100,"minItems":1,"title":"Hotels","description":"Up to 100 hotels, each a `hotel_id` or an inline identifier resolved as `POST /v2/hotels/resolve` would; a hotel listed twice is billed once."},"stay":{"$ref":"#/components/schemas/Stay","description":"The stay to price."},"channel":{"type":"string","enum":["direct","third_party"],"title":"Channel","description":"`direct` (the hotel's own booking engine, the default) or `third_party` (Enterprise only).","default":"direct"},"freshness":{"type":"string","enum":["cached","live"],"title":"Freshness","description":"`cached` answers now from stored prices; `live` reads the booking engines and answers 202 with a job (Build plan or higher).","default":"cached"},"max_age_hours":{"type":"integer","maximum":720.0,"minimum":1.0,"title":"Max Age Hours","description":"Oldest stored price to accept on `cached`, in hours; values above 168 (7 days) are treated as 168.","default":24},"currency":{"anyOf":[{"type":"string","pattern":"^[A-Z]{3}$"},{"type":"null"}],"title":"Currency","description":"Preferred ISO 4217 currency; prices are never converted, and a `currency_not_converted` warning says when some differ."}},"additionalProperties":false,"type":"object","required":["hotels","stay"],"title":"RatesRequest","description":"Body of `POST /v2/rates`.","examples":[{"channel":"direct","currency":"EUR","freshness":"cached","hotels":["43d0be71-5f3c-4c8e-9a52-1d7e2b6f0a19",{"website_url":"https://umilta36.com"}],"max_age_hours":24,"stay":{"check_in":"2026-11-14","check_out":"2026-11-16","rooms":[{"adults":2,"children_ages":[]}]}}]},"RatesResponse":{"properties":{"request_id":{"type":"string","title":"Request Id","description":"Opaque id of this request, also sent as the `X-Request-Id` header; quote it when contacting support."},"billing":{"anyOf":[{"$ref":"#/components/schemas/Billing"},{"type":"null"}],"description":"What this call cost, or null when it was free."},"results":{"items":{"$ref":"#/components/schemas/RateResult"},"type":"array","title":"Results","description":"One result per entry in `hotels[]`, in request order."},"summary":{"$ref":"#/components/schemas/RatesSummary","description":"Count of results per status."},"warnings":{"items":{"$ref":"#/components/schemas/Warning_"},"type":"array","title":"Warnings","description":"Things the call could not fully do, such as `currency_not_converted`, `channel_unavailable`, `live_unavailable` or `live_partial`."}},"type":"object","required":["request_id","results"],"title":"RatesResponse","description":"Response of `POST /v2/rates` with `freshness: cached`, and the `result` of a `rates.live`\njob.","examples":[{"billing":{"amount":"0.002","currency":"USD","sku":"rates.direct.cached","units":1},"request_id":"req_8f2c1a9b3d4e5f60","results":[{"channel":"direct","engine":"SYNXIS","hotel_id":"43d0be71-5f3c-4c8e-9a52-1d7e2b6f0a19","input":0,"observed_at":"2026-10-07T09:30:00Z","rooms":[{"listing_key":"engine_room:1234567","name":"Superior Room","offers":[{"availability":"available","board":"breakfast","channel":"direct","currency":"EUR","engine":"SYNXIS","excluded_charges":[],"free_cancellation":false,"includes_taxes":true,"listing_key":"engine_room:1234567","member_rate":false,"nightly_avg":"481.00","observed_at":"2026-10-07T09:30:00Z","occupancy":{"adults":2,"children_ages":[]},"offer_id":"off_t3c8e1a9b4f2d7e6a5c0b","rate_name":"Non-refundable, breakfast included","room_id":"5b1e2f3a-0c4d-4e5f-8a9b-1c2d3e4f5a6b","room_name":"Superior Room","total":"962.00","url":"https://umilta36.com/book?checkin=2026-11-14&checkout=2026-11-16"}],"room_id":"5b1e2f3a-0c4d-4e5f-8a9b-1c2d3e4f5a6b"}],"status":"observed"}],"summary":{"observed":1,"stale":0,"unknown":0,"unresolved":0,"unsupported":0}}]},"RatesSummary":{"properties":{"observed":{"type":"integer","title":"Observed","description":"Results with recent prices.","default":0},"stale":{"type":"integer","title":"Stale","description":"Results with older prices within `max_age_hours`.","default":0},"unknown":{"type":"integer","title":"Unknown","description":"Results with no prices for the stay.","default":0},"unsupported":{"type":"integer","title":"Unsupported","description":"Results whose hotel cannot be read on this channel.","default":0},"unresolved":{"type":"integer","title":"Unresolved","description":"Inputs that did not resolve to an indexed hotel.","default":0}},"type":"object","title":"RatesSummary","description":"How many results ended in each status."},"ReconcileConflict":{"properties":{"field":{"type":"string","title":"Field","description":"The fact in dispute: `size_m2`, `max_occupancy` or `bed_config`."},"values":{"additionalProperties":true,"type":"object","title":"Values","description":"Each source's value, keyed by source label; bed values are lists of bed types."}},"type":"object","required":["field","values"],"title":"ReconcileConflict","description":"A fact the matched listings disagree on."},"ReconcileListing":{"properties":{"source":{"type":"string","title":"Source","description":"Source label: `booking`, `site`, `expedia`, or the `label` of rooms you supplied."},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name","description":"Room name on that source."},"listing_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Listing Key","description":"The listing's key: `room_type:<id>` or `room:<id>` on channels, the site's room key, or your `key` for supplied rooms."},"url":{"anyOf":[{"type":"string","format":"uri"},{"type":"null"}],"title":"Url","description":"Page of the listing, or the source URL you sent; null when neither is known."}},"type":"object","required":["source"],"title":"ReconcileListing","description":"One listing of a room on one source."},"ReconcileRequest":{"properties":{"hotel_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Hotel Id","description":"The hotel whose rooms to match; wins over `identifiers` when both are set."},"identifiers":{"anyOf":[{"$ref":"#/components/schemas/HotelIdentifier"},{"type":"null"}],"description":"The hotel as an inline identifier, resolved as `POST /v2/hotels/resolve` would."},"sources":{"items":{"oneOf":[{"$ref":"#/components/schemas/UrlSource"},{"$ref":"#/components/schemas/RoomsSource"}],"discriminator":{"propertyName":"kind","mapping":{"booking_url":"#/components/schemas/UrlSource","expedia_url":"#/components/schemas/UrlSource","rooms":"#/components/schemas/RoomsSource","website_url":"#/components/schemas/UrlSource"}}},"type":"array","maxItems":8,"minItems":1,"title":"Sources","description":"Between 1 and 8 sources to match: channels named by URL, or rooms you supply."},"min_confidence":{"type":"number","maximum":1.0,"exclusiveMinimum":0.0,"title":"Min Confidence","description":"Lowest match confidence, above 0 and at most 1, for two listings to be grouped as one room; weaker pairs stay in `unmatched`.","default":0.5}},"additionalProperties":false,"type":"object","required":["sources"],"title":"ReconcileRequest","description":"Body of `POST /v2/rooms/reconcile`. Name the hotel by `hotel_id` or `identifiers`.","examples":[{"hotel_id":"43d0be71-5f3c-4c8e-9a52-1d7e2b6f0a19","min_confidence":0.5,"sources":[{"kind":"website_url","url":"https://umilta36.com/rooms"},{"kind":"rooms","label":"partner_pms","rooms":[{"bed_config":[{"count":1,"type":"king"}],"description":"City view","images":["https://partner.example/sup-k.jpg"],"key":"SUP-K","max_occupancy":2,"name":"Superior King","size_m2":28}]}]}]},"ReconcileResult":{"properties":{"request_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Request Id","description":"Id of the request; null inside a job result."},"hotel_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Hotel Id","description":"The hotel matched; null only when supplied-rooms-only sources named no known hotel."},"reconciliation_id":{"type":"string","title":"Reconciliation Id","description":"Id of this reconciliation, `rec_` plus hex; the same inputs give the same id."},"version":{"type":"string","title":"Version","description":"Matcher version, for example `reconcile-v1.0`."},"rooms":{"items":{"$ref":"#/components/schemas/ReconciledRoom"},"type":"array","title":"Rooms","description":"Rooms matched across sources."},"unmatched":{"additionalProperties":{"items":{"$ref":"#/components/schemas/UnmatchedListing"},"type":"array"},"type":"object","title":"Unmatched","description":"Listings that matched nothing, keyed by source label."},"shared":{"items":{"$ref":"#/components/schemas/SharedListing"},"type":"array","title":"Shared","description":"Listings that sell several rooms under one name."},"billing":{"anyOf":[{"$ref":"#/components/schemas/Billing"},{"type":"null"}],"description":"What the reconciliation cost; amount `\"0\"` for an identical request repeated within 30 days."}},"type":"object","required":["reconciliation_id","version"],"title":"ReconcileResult","description":"Result of `POST /v2/rooms/reconcile`: the response body when synchronous, the job\n`result` otherwise.","examples":[{"billing":{"amount":"0.25","currency":"USD","sku":"rooms.reconcile.urls","units":1},"hotel_id":"43d0be71-5f3c-4c8e-9a52-1d7e2b6f0a19","reconciliation_id":"rec_9b2e4c7a1f3d5e8b0c6a2d4f","rooms":[{"basis":["name_similarity:0.81","size_match","image_similarity:0.88"],"canonical":{"bed_config":[{"count":1,"type":"king"}],"name":"Superior Room","occupancy":2,"room_id":"5b1e2f3a-0c4d-4e5f-8a9b-1c2d3e4f5a6b","size_m2":28},"confidence":0.93,"conflicts":[{"field":"size_m2","values":{"partner_pms":28,"site":30}}],"listings":[{"listing_key":"site:superior","name":"Superior Double Room","source":"site","url":"https://umilta36.com/rooms/superior"},{"listing_key":"SUP-K","name":"Superior King","source":"partner_pms"}]}],"shared":[],"unmatched":{"partner_pms":[],"site":[]},"version":"reconcile-v1.0"}]},"ReconciledRoom":{"properties":{"canonical":{"$ref":"#/components/schemas/CanonicalRoom","description":"The room as one record."},"listings":{"items":{"$ref":"#/components/schemas/ReconcileListing"},"type":"array","title":"Listings","description":"Every listing of this room, one or more per source."},"confidence":{"type":"number","title":"Confidence","description":"Confidence of the match from 0 to 1."},"basis":{"items":{"type":"string"},"type":"array","title":"Basis","description":"Evidence the match rests on, for example `name_exact`, `name_similarity:0.81`, `size_match`, `occupancy_match`, `beds_overlap:1.00` or `image_similarity:0.88`."},"conflicts":{"items":{"$ref":"#/components/schemas/ReconcileConflict"},"type":"array","title":"Conflicts","description":"Facts the listings disagree on."}},"type":"object","required":["canonical","confidence"],"title":"ReconciledRoom","description":"One room matched across sources."},"ResolveRequest":{"properties":{"identifiers":{"items":{"$ref":"#/components/schemas/HotelIdentifier"},"type":"array","maxItems":500,"minItems":1,"title":"Identifiers","description":"Between 1 and 500 hotels to resolve, each in one identifier form; results come back in the same order."}},"additionalProperties":false,"type":"object","required":["identifiers"],"title":"ResolveRequest","description":"Body of `POST /v2/hotels/resolve`.","examples":[{"identifiers":[{"booking_url":"https://www.booking.com/hotel/it/umilta-36.en-gb.html"},{"website_url":"https://umilta36.com"},{"expedia_url":"https://www.expedia.com/Rome-Hotels-Umilta-36.h12345.Hotel-Information"},{"city":"Rome","country":"IT","name":"Umiltà 36"}]}]},"ResolveResponse":{"properties":{"request_id":{"type":"string","title":"Request Id","description":"Opaque id of this request, also sent as the `X-Request-Id` header; quote it when contacting support."},"billing":{"anyOf":[{"$ref":"#/components/schemas/Billing"},{"type":"null"}],"description":"What this call cost, or null when it was free."},"results":{"items":{"$ref":"#/components/schemas/ResolveResult"},"type":"array","title":"Results","description":"One result per identifier, in request order."}},"type":"object","required":["request_id","results"],"title":"ResolveResponse","description":"Response of `POST /v2/hotels/resolve`. Resolution is free, so `billing` is null.","examples":[{"request_id":"req_8f2c1a9b3d4e5f60","results":[{"basis":"booking_slug","hotel_id":"43d0be71-5f3c-4c8e-9a52-1d7e2b6f0a19","input":0,"match":"exact","status":"resolved"},{"basis":"trusted_website_identity","hotel_id":"43d0be71-5f3c-4c8e-9a52-1d7e2b6f0a19","input":1,"match":"verified","status":"resolved"},{"input":2,"reason":"expedia_not_ingested","status":"unsupported"},{"basis":"name_city","confidence":0.91,"hotel_id":"43d0be71-5f3c-4c8e-9a52-1d7e2b6f0a19","input":3,"match":"probable","status":"resolved"}]}]},"ResolveResult":{"properties":{"input":{"type":"integer","title":"Input","description":"Index of the identifier in the request's `identifiers[]` this result answers."},"status":{"type":"string","enum":["resolved","known_unindexed","unresolved","unsupported"],"title":"Status","description":"`resolved` when a hotel was found, `known_unindexed` when the listing exists but the hotel is not indexed yet, `unresolved` when nothing matched, `unsupported` when that identifier kind is not served yet."},"hotel_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Hotel Id","description":"The matched hotel's id; present only when `status` is `resolved`."},"match":{"anyOf":[{"type":"string","enum":["exact","verified","probable"]},{"type":"null"}],"title":"Match","description":"How certain the match is, present only when resolved: `exact` for a listing slug or id, `verified` for a confirmed website identity, `probable` for a website host or name match."},"basis":{"anyOf":[{"type":"string","enum":["booking_slug","trusted_website_identity","website_host","expedia_id","name_city","hotel_id"]},{"type":"null"}],"title":"Basis","description":"What the match rests on, present only when resolved: `booking_slug`, `trusted_website_identity`, `website_host`, `expedia_id`, `name_city`, or `hotel_id` when a uuid was passed through."},"confidence":{"anyOf":[{"type":"number","maximum":1.0,"minimum":0.0},{"type":"null"}],"title":"Confidence","description":"Score from 0 to 1 on `probable` matches: 0.8 for a website host, 0.5 to 0.95 for a name match by strength (lower when a second candidate is as strong); absent on `exact` and `verified`."},"reason":{"anyOf":[{"type":"string","enum":["not_found","ambiguous_website","unparseable_booking_url","unparseable_website_url","unparseable_expedia_url","invalid_hotel_id","expedia_not_ingested","not_indexed"]},{"type":"null"}],"title":"Reason","description":"Why the input did not resolve; absent when `resolved`. `not_found`, `ambiguous_website` (the host belongs to several hotels), `unparseable_booking_url`, `unparseable_website_url`, `unparseable_expedia_url` or `invalid_hotel_id` with `unresolved`; `expedia_not_ingested` with `unsupported`; `not_indexed` with `known_unindexed`."},"booking_slug":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Booking Slug","description":"The listing slug as `{cc}/{slug}` in lower case, for example `it/umilta-36`; present only on `known_unindexed`."}},"type":"object","required":["input","status"],"title":"ResolveResult","description":"The outcome for one identifier."},"ResolvedAmenity":{"properties":{"code":{"type":"string","title":"Code","description":"Amenity code, a group code such as `POOL` when the term named a group."},"origin":{"anyOf":[{"type":"string","enum":["query","filters"]},{"type":"null"}],"title":"Origin","description":"`query` when the amenity was read from the query text, `filters` when it came from `filters.amenities`."}},"type":"object","required":["code"],"title":"ResolvedAmenity","description":"An amenity requirement the search applied."},"ResolvedBudget":{"properties":{"min":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d+)?$"},{"type":"null"}],"title":"Min","description":"Lowest price per room per night, a decimal string in `currency`."},"max":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d+)?$"},{"type":"null"}],"title":"Max","description":"Highest price per room per night, a decimal string in `currency`."},"currency":{"anyOf":[{"type":"string","pattern":"^[A-Z]{3}$"},{"type":"null"}],"title":"Currency","description":"ISO 4217 currency of the band; null when the query named an amount without a currency and none was sent."},"basis":{"anyOf":[{"type":"string","const":"per_room_per_night"},{"type":"null"}],"title":"Basis","description":"What the amounts cover; always `per_room_per_night`."},"origin":{"anyOf":[{"type":"string","enum":["query","filters"]},{"type":"null"}],"title":"Origin","description":"`query` when the band was read from the query text, `filters` when it came from `filters.budget`."}},"type":"object","title":"ResolvedBudget","description":"The price band applied. Present only when a minimum or maximum was given."},"ResolvedCriteria":{"properties":{"location":{"anyOf":[{"$ref":"#/components/schemas/ResolvedLocation"},{"type":"null"}],"description":"The place searched; null when the search had no location (for example `hotel_ids` only)."},"stay":{"anyOf":[{"$ref":"#/components/schemas/ResolvedStay"},{"type":"null"}],"description":"The stay used for rates; null when none was given or read."},"budget":{"anyOf":[{"$ref":"#/components/schemas/ResolvedBudget"},{"type":"null"}],"description":"The price band applied; null when none."},"amenities":{"items":{"$ref":"#/components/schemas/ResolvedAmenity"},"type":"array","title":"Amenities","description":"Amenity requirements applied, all of which a hotel must have."},"overrides":{"items":{"$ref":"#/components/schemas/Override"},"type":"array","title":"Overrides","description":"Conflicts between the query and `filters`, which the filters won."},"unresolved":{"items":{"type":"string","enum":["location","stay","budget_currency","amenities"]},"type":"array","title":"Unresolved","description":"Criteria the search could not apply: `location`, `stay` (dates in the query could not be read), `budget_currency` (a budget without a currency), `amenities` (a term with no amenity code)."}},"type":"object","title":"ResolvedCriteria","description":"What the search understood and applied, with where each criterion came from."},"ResolvedLocation":{"properties":{"place_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Place Id","description":"Slug of city and country, such as `rome-it`, reusable as `filters.location.place_id`; null for a `near` search."},"label":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Label","description":"Readable name of the place, `City, CC`; null for a `near` search."},"lat":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Lat","description":"Latitude of the city centre or the `near` point; null when only the city name is known."},"lng":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Lng","description":"Longitude of the city centre or the `near` point; null when only the city name is known."},"origin":{"anyOf":[{"type":"string","enum":["query","filters"]},{"type":"null"}],"title":"Origin","description":"`query` when the place was read from the query text, `filters` when it came from `filters`."}},"type":"object","title":"ResolvedLocation","description":"The place the search ran in."},"ResolvedStay":{"properties":{"check_in":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Check In","description":"Arrival date, YYYY-MM-DD."},"check_out":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Check Out","description":"Departure date, YYYY-MM-DD."},"rooms":{"items":{"$ref":"#/components/schemas/Occupancy"},"type":"array","title":"Rooms","description":"The party per room; a party read from the query is split evenly over its rooms and has no children."},"origin":{"anyOf":[{"type":"string","enum":["query","filters"]},{"type":"null"}],"title":"Origin","description":"`query` when the stay was read from the query text, `filters` when it came from `filters.stay`; null on hotel detail."}},"type":"object","title":"ResolvedStay","description":"The dates and party used for rates."},"Revalidation":{"properties":{"price_changed":{"type":"boolean","title":"Price Changed","description":"True when the engine's total or currency differs from the offer by any amount, in which case the booking fails with `price_changed`; false otherwise, including when the room sold out."},"total":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d+)?$"},{"type":"null"}],"title":"Total","description":"Total the engine quoted for the same room and rate, a decimal string in `currency`; null when the room or rate is no longer offered."},"currency":{"anyOf":[{"type":"string","pattern":"^[A-Z]{3}$"},{"type":"null"}],"title":"Currency","description":"ISO 4217 currency of `total`."},"checked_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Checked At","description":"When the engine was checked, RFC 3339 UTC."}},"type":"object","required":["price_changed"],"title":"Revalidation","description":"The offer re-checked on the hotel's engine just before booking."},"ReviewItem":{"properties":{"review_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Review Id","description":"Review id."},"rating":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Rating","description":"The guest's score on a 0 to 10 scale."},"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title","description":"Review headline."},"positive":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Positive","description":"What the guest liked."},"negative":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Negative","description":"What the guest disliked."},"traveller_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Traveller Type","description":"Kind of trip as the source records it, for example `Couple`, `Family`, `Solo traveller`, `Business` or `Group`."},"date":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Date","description":"Date we collected the review, YYYY-MM-DD; not the date of the stay."}},"type":"object","title":"ReviewItem","description":"One guest review, in the language it was written in."},"Reviews":{"properties":{"count":{"type":"integer","title":"Count","description":"Number of reviews we hold, which can differ from `core.review_count`.","default":0},"category_scores":{"additionalProperties":{"type":"number"},"type":"object","title":"Category Scores","description":"Average score per category on a 0 to 10 scale, keyed by the category's display name, for example `Cleanliness`, `Staff` or `Location`."},"items":{"items":{"$ref":"#/components/schemas/ReviewItem"},"type":"array","title":"Items","description":"Up to `reviews_limit` reviews, newest first."}},"type":"object","title":"Reviews","description":"Guest reviews we hold for the hotel, newest first."},"RoomListing":{"properties":{"source":{"type":"string","enum":["booking","site","engine","expedia"],"title":"Source","description":"Where the listing is: `site` (the hotel's website), `engine` (its booking engine), `booking` (the third-party channel) or `expedia`."},"provider":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Provider","description":"Who publishes the listing, for example `official_site` or the booking engine's name."},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name","description":"Room name as that listing shows it."},"listing_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Listing Key","description":"The listing's own key, such as `room_type:1234567`, matching `Offer.listing_key` where the channel has one."},"url":{"anyOf":[{"type":"string","format":"uri"},{"type":"null"}],"title":"Url","description":"Page of the listing; null when there is none."},"match_confidence":{"anyOf":[{"type":"string","enum":["high","medium","low"]},{"type":"null"}],"title":"Match Confidence","description":"How sure the match between this listing and the room is: `high` or `medium` (low-confidence candidates are not shown); null when not scored."}},"type":"object","required":["source"],"title":"RoomListing","description":"The same room as listed on one channel."},"RoomOccupancy":{"properties":{"adults":{"type":"integer","maximum":16.0,"minimum":1.0,"title":"Adults","description":"Adults in this room, 1 to 16.","default":2},"children_ages":{"items":{"type":"integer","maximum":17.0,"minimum":0.0},"type":"array","maxItems":8,"title":"Children Ages","description":"Age of each child in this room, 0 to 17, one entry per child; empty for adults only."}},"additionalProperties":false,"type":"object","title":"RoomOccupancy","description":"Who stays in one room."},"RoomsSource":{"properties":{"kind":{"type":"string","const":"rooms","title":"Kind","description":"Always `rooms`."},"label":{"type":"string","maxLength":64,"minLength":1,"pattern":"^[a-z0-9_]+$","title":"Label","description":"Name for this source in the result, lower case letters, digits and underscores, for example `partner_pms`."},"rooms":{"items":{"$ref":"#/components/schemas/SuppliedRoom"},"type":"array","maxItems":500,"minItems":1,"title":"Rooms","description":"Up to 500 rooms (1,000 across all sources)."}},"additionalProperties":false,"type":"object","required":["kind","label","rooms"],"title":"RoomsSource","description":"Rooms you supply directly."},"SearchFilters":{"properties":{"location":{"anyOf":[{"$ref":"#/components/schemas/LocationFilter"},{"type":"null"}],"description":"City or place to search in."},"near":{"anyOf":[{"$ref":"#/components/schemas/NearFilter"},{"type":"null"}],"description":"Point and radius to search around."},"hotel_ids":{"anyOf":[{"items":{"type":"string","format":"uuid"},"type":"array","maxItems":10000},{"type":"null"}],"title":"Hotel Ids","description":"Limit the search to these hotels, up to 10,000 uuids, for example your own inventory; results never include other hotels."},"stay":{"anyOf":[{"$ref":"#/components/schemas/Stay"},{"type":"null"}],"description":"Dates and party; with a stay, cards carry cached rates by default."},"budget":{"anyOf":[{"$ref":"#/components/schemas/BudgetFilter"},{"type":"null"}],"description":"Price band per room per night."},"min_stars":{"anyOf":[{"type":"integer","maximum":5.0,"minimum":1.0},{"type":"null"}],"title":"Min Stars","description":"Minimum official star rating, 1 to 5."},"amenities":{"anyOf":[{"items":{"type":"string"},"type":"array","maxItems":30},{"type":"null"}],"title":"Amenities","description":"Uppercase amenity codes from `GET /v2/amenities`, up to 30; a hotel must have all of them, and a group code matches any of its members."},"hotel_name":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Hotel Name","description":"Part of the hotel's name to match."}},"additionalProperties":false,"type":"object","title":"SearchFilters","description":"Hard constraints. Each one set here wins over the same constraint read from `query`, and\nthe override is reported in `resolved.overrides`."},"SearchHotel":{"properties":{"hotel_id":{"type":"string","title":"Hotel Id","description":"The hotel's id, for `GET /v2/hotels/{hotel_id}` and the other endpoints.","format":"uuid"},"rank":{"type":"integer","title":"Rank","description":"Position in the full result list, starting at 1 and continuing across pages."},"name":{"type":"string","title":"Name","description":"Hotel name."},"hotel_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Hotel Type","description":"Kind of property as catalogued, free text such as `hotel`; null when unknown."},"thumbnail":{"anyOf":[{"$ref":"#/components/schemas/ImageRef"},{"type":"null"}],"description":"The hotel's lead image; present even when `images` is 0."},"images":{"items":{"$ref":"#/components/schemas/ImageRef"},"type":"array","title":"Images","description":"Up to `images` gallery images in gallery order, at 640 pixels wide."},"star_rating":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Star Rating","description":"Official star rating, 1 to 5; null when unrated."},"review_score":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Review Score","description":"Average guest review score on a 0 to 10 scale; null when there are no reviews."},"review_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Review Count","description":"Number of guest reviews behind `review_score`; null when none."},"location":{"anyOf":[{"$ref":"#/components/schemas/CardLocation"},{"type":"null"}],"description":"Where the hotel is."},"amenities":{"items":{"type":"string"},"type":"array","title":"Amenities","description":"Up to 20 uppercase amenity codes, group codes such as `POOL` first."},"summary":{"anyOf":[{"type":"string","maxLength":400},{"type":"null"}],"title":"Summary","description":"The start of the hotel's description, at most 400 characters, cut at a sentence or word boundary; null when there is no description."},"rate":{"anyOf":[{"$ref":"#/components/schemas/CardRate"},{"type":"null"}],"description":"The cheapest stored price for the stay; null when `rates` is `none` or no stay was given."},"why":{"items":{"$ref":"#/components/schemas/WhyItem"},"type":"array","title":"Why","description":"Evidence for the hotel on each criterion of the query, up to 6 items; empty unless `ranking` is `semantic` (chat fills it from its own reasoning)."}},"type":"object","required":["hotel_id","rank","name"],"title":"SearchHotel","description":"One hotel in search or chat results."},"SearchRequest":{"properties":{"query":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Query","description":"What the traveller asked for in their own words; dates, budget, place and amenities are read from it, and it makes the search bill as loose."},"filters":{"anyOf":[{"$ref":"#/components/schemas/SearchFilters"},{"type":"null"}],"description":"Hard constraints, which win over anything read from `query`."},"preferences":{"anyOf":[{"$ref":"#/components/schemas/Preferences"},{"type":"null"}],"description":"Soft signals for semantic ranking."},"ranking":{"anyOf":[{"type":"string","enum":["filters","semantic"]},{"type":"null"}],"title":"Ranking","description":"`semantic` ranks by fit to the query and preferences using reviews and images and fills `why[]`; `filters` ranks matches by review score. Defaults to `semantic` when `query` is set, else `filters`."},"limit":{"type":"integer","maximum":50.0,"minimum":1.0,"title":"Limit","description":"Hotels per page, 1 to 50; each result beyond 10 bills `search.extra_results`, and at most 50 results can be paged in total.","default":10},"cursor":{"anyOf":[{"type":"string","maxLength":512},{"type":"null"}],"title":"Cursor","description":"The `next_cursor` of an earlier response, valid for 30 minutes; the page is served free from that search and other fields are ignored."},"rates":{"anyOf":[{"type":"string","enum":["none","cached"]},{"type":"null"}],"title":"Rates","description":"`cached` adds the latest stored rate to each card and needs `filters.stay`; `none` skips rates. Defaults to `cached` when a stay is set, else `none`. Search never fetches live prices."},"images":{"type":"integer","maximum":10.0,"minimum":0.0,"title":"Images","description":"Images per card, 0 to 10; `thumbnail` is returned even at 0.","default":6},"currency":{"anyOf":[{"type":"string","pattern":"^[A-Z]{3}$"},{"type":"null"}],"title":"Currency","description":"ISO 4217 currency assumed for a budget written in `query` without one; card prices stay in the currency they were observed in."},"locale":{"anyOf":[{"type":"string","maxLength":10},{"type":"null"}],"title":"Locale","description":"Language tag of the caller; it does not change the search response and only selects the language of the hotel detail prepared for a follow-up `GET /v2/hotels/{hotel_id}`."},"clarification":{"anyOf":[{"$ref":"#/components/schemas/ClarificationPolicy"},{"type":"null"}],"description":"What to do when the request is ambiguous; `auto` when omitted."},"search_id":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Search Id","description":"With `answers`: the `search_id` of a `needs_clarification` response being answered, within 30 minutes."},"answers":{"anyOf":[{"items":{"$ref":"#/components/schemas/ClarificationAnswer"},"type":"array","maxItems":10},{"type":"null"}],"title":"Answers","description":"With `search_id`: the chosen option for the pending clarification; the original search then runs and is billed at its original price."}},"additionalProperties":false,"type":"object","title":"SearchRequest","description":"Body of `POST /v2/search`. A new search needs at least one of `query`,\n`filters.location`, `filters.near` or `filters.hotel_ids`; a next page needs only `cursor`;\na clarification answer needs only `search_id` and `answers`.","examples":[{"clarification":{"policy":"auto"},"currency":"EUR","filters":{"amenities":["POOL","SPA"],"budget":{"basis":"per_room_per_night","currency":"EUR","max":"500","min":"100"},"location":{"city":"Rome","country":"IT"},"min_stars":4,"stay":{"check_in":"2026-11-14","check_out":"2026-11-16","rooms":[{"adults":2,"children_ages":[]}]}},"images":6,"limit":10,"locale":"en","preferences":{"tags":["ROMANTIC"],"text":"romantic, great breakfast"},"query":"quiet place near the Trevi Fountain with a rooftop, under 500 a night","ranking":"semantic","rates":"cached"}]},"SearchResponse":{"properties":{"request_id":{"type":"string","title":"Request Id","description":"Opaque id of this request, also sent as the `X-Request-Id` header; quote it when contacting support."},"billing":{"anyOf":[{"$ref":"#/components/schemas/Billing"},{"type":"null"}],"description":"What this call cost, or null when it was free."},"search_id":{"type":"string","title":"Search Id","description":"Id of this search, `srch_` plus 32 hex characters, valid for 30 minutes for paging, answering a clarification and `GET /v2/hotels/{hotel_id}?search_id=`."},"status":{"type":"string","enum":["completed","needs_clarification","partial"],"title":"Status","description":"`completed` when the search ran (even with no hotels), `needs_clarification` when a question must be answered first (free), `partial` when policy `never` stopped an ambiguous search (free)."},"resolved":{"$ref":"#/components/schemas/ResolvedCriteria","description":"What the search understood and applied."},"clarifications":{"items":{"$ref":"#/components/schemas/Clarification"},"type":"array","title":"Clarifications","description":"The question to answer when `status` is `needs_clarification`; empty otherwise."},"hotels":{"items":{"$ref":"#/components/schemas/SearchHotel"},"type":"array","title":"Hotels","description":"This page of results, best first; empty unless `status` is `completed`."},"total_estimate":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Total Estimate","description":"Size of the ranked candidate pool behind the results, not a catalogue-wide count; at most 50 can be paged. Null unless `status` is `completed`."},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor","description":"Pass as `cursor` for the next page, free of charge; null on the last page."},"warnings":{"items":{"$ref":"#/components/schemas/Warning_"},"type":"array","title":"Warnings","description":"Things the search could not fully do."},"versions":{"$ref":"#/components/schemas/Versions","description":"Contract and ranking versions."}},"type":"object","required":["request_id","search_id","status"],"title":"SearchResponse","description":"Response of `POST /v2/search`.","examples":[{"billing":{"amount":"0.012","currency":"USD","sku":"search.loose.semantic","units":1},"clarifications":[],"hotels":[{"amenities":["POOL","SPA"],"hotel_id":"43d0be71-5f3c-4c8e-9a52-1d7e2b6f0a19","hotel_type":"hotel","images":[{"height":427,"image_id":"b2e4d6f8-0a1c-4e3a-9d5b-7f9a1c3e5d2c","label":"room","url":"https://images.vistalink.com/h/43d0be71/room.jpg?w=640","width":640}],"location":{"address":"Via dell'Umiltà 36","city":"Rome","country":"IT","distance_m":420,"lat":41.9,"lng":12.48},"name":"Umiltà 36","rank":1,"rate":{"currency":"EUR","includes_taxes_and_fees":true,"nights":2,"observed_at":"2026-10-07T09:30:00Z","offer_id":"off_t3c8e1a9b4f2d7e6a5c0b","source":"direct","status":"observed","total":"962.00"},"review_count":2056,"review_score":9.1,"star_rating":5,"summary":"A quiet boutique hotel a few minutes from the Trevi Fountain.","thumbnail":{"image_id":"a1f3c5e7-9b2d-4f6a-8c0e-2d4f6a8c0e1b","label":"building","url":"https://images.vistalink.com/h/43d0be71/ext.jpg?w=640"},"why":[{"assessment":"supported","criterion":"quiet","evidence":"review"}]}],"next_cursor":"c_eyJzIjoic3JjaF8zZjljMmE3ZTRiMWQ0YzhhOWU2ZjBiMmQ1YTdjMWU5MyIsIm8iOjEwfQ","request_id":"req_8f2c1a9b3d4e5f60","resolved":{"amenities":[{"code":"POOL","origin":"filters"}],"budget":{"basis":"per_room_per_night","currency":"EUR","max":"500","origin":"query"},"location":{"label":"Rome, IT","lat":41.9,"lng":12.5,"origin":"filters","place_id":"rome-it"},"overrides":[],"stay":{"check_in":"2026-11-14","check_out":"2026-11-16","origin":"filters","rooms":[{"adults":2,"children_ages":[]}]},"unresolved":[]},"search_id":"srch_3f9c2a7e4b1d4c8a9e6f0b2d5a7c1e93","status":"completed","total_estimate":143,"versions":{"contract":"2.1","ranking":"r2026.10"},"warnings":[{"code":"rates_unavailable","message":"Some hotels have no observed rate for these dates."}]}]},"SharedListing":{"properties":{"source":{"type":"string","title":"Source","description":"Source label of the listing."},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name","description":"Room name on that source."},"listing_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Listing Key","description":"The listing's key."},"covers":{"items":{"type":"string"},"type":"array","title":"Covers","description":"Names of the rooms it covers."}},"type":"object","required":["source"],"title":"SharedListing","description":"A listing that sells several rooms of another source under one name, such as `Double or\nTwin`."},"Stay":{"properties":{"check_in":{"type":"string","format":"date","title":"Check In","description":"Arrival date, YYYY-MM-DD."},"check_out":{"type":"string","format":"date","title":"Check Out","description":"Departure date, YYYY-MM-DD, after `check_in` and at most 30 nights later."},"rooms":{"items":{"$ref":"#/components/schemas/RoomOccupancy"},"type":"array","maxItems":9,"minItems":1,"title":"Rooms","description":"One entry per room, 1 to 9; defaults to one room for two adults."}},"additionalProperties":false,"type":"object","required":["check_in","check_out"],"title":"Stay","description":"Dates and rooms of a stay, up to 30 nights and 9 rooms."},"SuppliedBed":{"properties":{"type":{"type":"string","maxLength":60,"minLength":1,"title":"Type","description":"Bed type, for example `king`, `double`, `twin` or `sofa bed`."},"count":{"type":"integer","maximum":20.0,"minimum":1.0,"title":"Count","description":"Number of beds of this type, 1 to 20.","default":1}},"additionalProperties":false,"type":"object","required":["type"],"title":"SuppliedBed","description":"Beds of one type in a room you supply."},"SuppliedRoom":{"properties":{"key":{"type":"string","maxLength":200,"title":"Key","description":"Your key for the room; it comes back as the `listing_key` of this room's listing."},"name":{"type":"string","maxLength":300,"title":"Name","description":"Room name in your system."},"size_m2":{"anyOf":[{"type":"number","maximum":10000.0,"minimum":0.0},{"type":"null"}],"title":"Size M2","description":"Room size in square metres."},"description":{"anyOf":[{"type":"string","maxLength":5000},{"type":"null"}],"title":"Description","description":"Room description, up to 5,000 characters."},"max_occupancy":{"anyOf":[{"type":"integer","maximum":50.0,"minimum":1.0},{"type":"null"}],"title":"Max Occupancy","description":"Most guests the room can hold."},"bed_config":{"items":{"$ref":"#/components/schemas/SuppliedBed"},"type":"array","maxItems":10,"title":"Bed Config","description":"Beds in the room."},"images":{"items":{"type":"string","format":"uri"},"type":"array","maxItems":30,"title":"Images","description":"Up to 30 public http(s) image URLs of the room, used for image matching."}},"additionalProperties":false,"type":"object","required":["key","name"],"title":"SuppliedRoom","description":"A room from your own system, such as a PMS or channel manager."},"TimelineEntry":{"properties":{"at":{"type":"string","format":"date-time","title":"At","description":"When the booking entered `state`, RFC 3339 UTC."},"state":{"type":"string","enum":["queued","processing","requires_payment","requires_user_action","confirmed","confirmation_pending","failed","expired","canceled","requires_credential","submitted","requires_action","unknown"],"title":"State","description":"The status the booking moved to."}},"type":"object","required":["at","state"],"title":"TimelineEntry","description":"One status change of a booking."},"UnmatchedListing":{"properties":{"source":{"type":"string","title":"Source","description":"Source label: `booking`, `site`, `expedia`, or the `label` of rooms you supplied."},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name","description":"Room name on that source."},"listing_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Listing Key","description":"The listing's key: `room_type:<id>` or `room:<id>` on channels, the site's room key, or your `key` for supplied rooms."},"url":{"anyOf":[{"type":"string","format":"uri"},{"type":"null"}],"title":"Url","description":"Page of the listing, or the source URL you sent; null when neither is known."},"nearest":{"anyOf":[{"$ref":"#/components/schemas/NearestListing"},{"type":"null"}],"description":"The closest candidate it had; null when there was none."}},"type":"object","required":["source"],"title":"UnmatchedListing","description":"A listing that matched no room on another source."},"UrlSource":{"properties":{"kind":{"type":"string","enum":["booking_url","website_url","expedia_url"],"title":"Kind","description":"`booking_url` (third-party channel listing), `website_url` (the hotel's website) or `expedia_url`; their listings appear under the labels `booking`, `site` and `expedia`."},"url":{"type":"string","maxLength":2048,"title":"Url","description":"Public http(s) URL of the hotel on that channel.","format":"uri"}},"additionalProperties":false,"type":"object","required":["kind","url"],"title":"UrlSource","description":"A channel whose rooms to include, named by a URL. Rooms are read from our catalogue for\nthe hotel; the URL is kept as provenance."},"UserLocation":{"properties":{"lat":{"type":"number","maximum":90.0,"minimum":-90.0,"title":"Lat","description":"Latitude in decimal degrees."},"lng":{"type":"number","maximum":180.0,"minimum":-180.0,"title":"Lng","description":"Longitude in decimal degrees."},"accuracy":{"anyOf":[{"type":"number","minimum":0.0},{"type":"null"}],"title":"Accuracy","description":"Accuracy radius of the position in metres."}},"additionalProperties":false,"type":"object","required":["lat","lng"],"title":"UserLocation","description":"The traveller's current position."},"Venue":{"properties":{"venue_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Venue Id","description":"Venue id, stable across responses."},"kind":{"type":"string","title":"Kind","description":"What the venue is, for example `dining`, `bar`, `cafe`, `spa`, `pool`, `gym`, `beach` or `kids`."},"section":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Section","description":"Group the venue is shown under: `dining`, `wellness`, `activities`, `families`, `business`, `parking`, `transport`, `shops` or `spaces`."},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name","description":"Venue name, in `locale` when translated."},"summary":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Summary","description":"A short description, in `locale` when translated."},"cuisine":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cuisine","description":"Type of cuisine for dining venues, free text such as `seafood`; null otherwise."},"opening_hours":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Opening Hours","description":"Opening hours as one line of free text; null when not published."},"price_point":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Price Point","description":"Price level, free text such as `$$$`; null when unknown."},"access":{"items":{"$ref":"#/components/schemas/VenueAccess"},"type":"array","title":"Access","description":"Who may use the venue and on what terms."},"items":{"items":{"$ref":"#/components/schemas/VenueItem"},"type":"array","title":"Items","description":"Menu items or services offered."},"images":{"items":{"$ref":"#/components/schemas/DetailImage"},"type":"array","title":"Images","description":"Images of the venue."}},"type":"object","required":["kind"],"title":"Venue","description":"A place at the hotel: a restaurant, bar, spa, pool, gym and so on."},"VenueAccess":{"properties":{"subject":{"type":"string","title":"Subject","description":"Who the rule is for: `hotel` (all guests), or `room:<room_id>`, `board_basis:<key>` or `audience:<key>` for a subset."},"access":{"type":"string","title":"Access","description":"Terms of use, for example `included`, `surcharge`, `members`, `restricted` or `adults_only`."}},"type":"object","required":["subject","access"],"title":"VenueAccess","description":"Who may use a venue and on what terms."},"VenueItem":{"properties":{"name":{"type":"string","title":"Name","description":"Item name, in `locale` when translated."},"price_text":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Price Text","description":"Price as published, free text such as `EUR 18`; null when not published."}},"type":"object","required":["name"],"title":"VenueItem","description":"One item on a venue's menu or list of services."},"Versions":{"properties":{"contract":{"type":"string","title":"Contract","description":"API contract version, `2.1`.","default":"2.1"},"ranking":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Ranking","description":"Version of the ranking model, for example `r2026.10`; results from the same version are comparable."}},"type":"object","title":"Versions","description":"Versions of the contract and ranking that produced the response."},"VoiceTheme":{"properties":{"type":{"type":"string","title":"Type","description":"Theme, for example `location`, `service`, `cleanliness`, `rooms`, `food`, `value`, `noise` or `views`."},"summary":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Summary","description":"What guests say about the theme, in one or two sentences."},"sentiment":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Sentiment","description":"How positive guests are on the theme, from 0 (negative) to 1 (positive)."},"quotes":{"items":{"type":"string"},"type":"array","title":"Quotes","description":"Up to 3 verbatim quotes from this hotel's reviews."}},"type":"object","required":["type"],"title":"VoiceTheme","description":"A theme that comes up in guest reviews."},"Warning_":{"properties":{"code":{"type":"string","title":"Code","description":"Stable warning code: `rates_unavailable` (some hotels have no stored rate), `rates_need_stay` (rates asked for without a stay), `channel_unavailable` (the third-party channel was left out), `constraints_relaxed` (no exact match, so some constraints were loosened), `semantic_unavailable`, `unresolved` (with clarification policy `never`), `search_id_unavailable` (the search cannot be paged or referenced), on chat `price_redacted` (a price in the reply that is not a direct rate was replaced with \"a rate on request\"), and on rates `currency_unavailable`."},"message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Message","description":"Explanation in English."},"channel":{"anyOf":[{"type":"string","const":"third_party"},{"type":"null"}],"title":"Channel","description":"The channel left out, `third_party`; set only on `channel_unavailable`."}},"type":"object","required":["code"],"title":"Warning","description":"Something the response could not fully do, without failing it."},"Webhook":{"properties":{"webhook_id":{"type":"string","title":"Webhook Id","description":"Webhook id, `wh_` plus 12 letters and digits."},"url":{"type":"string","title":"Url","description":"The https endpoint deliveries are POSTed to.","format":"uri"},"events":{"items":{"type":"string","enum":["rates.completed","rates.batch.completed","rooms.reconcile.completed","booking.updated","quote.updated"]},"type":"array","title":"Events","description":"Events delivered to it."},"created_at":{"type":"string","format":"date-time","title":"Created At","description":"When the webhook was created, RFC 3339 UTC."},"is_active":{"type":"boolean","title":"Is Active","description":"Always true; deleted webhooks are not listed.","default":true}},"type":"object","required":["webhook_id","url","events","created_at"],"title":"Webhook","description":"A webhook subscription."},"WebhookCreateRequest":{"properties":{"url":{"type":"string","maxLength":2048,"title":"Url","description":"The https endpoint deliveries are POSTed to; it must resolve to a public address.","format":"uri"},"events":{"items":{"type":"string","enum":["rates.completed","rates.batch.completed","rooms.reconcile.completed","booking.updated","quote.updated"]},"type":"array","maxItems":5,"minItems":1,"title":"Events","description":"Events to deliver, each needing its family's scope on the key: `booking.updated` (booking), `rates.completed` and `rates.batch.completed` (rates), `rooms.reconcile.completed` (reconcile), `quote.updated` (quotes)."},"secret":{"anyOf":[{"type":"string","maxLength":128,"minLength":24},{"type":"null"}],"title":"Secret","description":"Your own signing secret, 24 to 128 characters; one is generated when omitted."}},"additionalProperties":false,"type":"object","required":["url","events"],"title":"WebhookCreateRequest","description":"Body of `POST /v2/webhooks`.","examples":[{"events":["rates.completed","booking.updated"],"url":"https://partner.example/hooks/vistalink"}]},"WebhookCreated":{"properties":{"request_id":{"type":"string","title":"Request Id","description":"Opaque id of this request, also sent as the `X-Request-Id` header; quote it when contacting support."},"billing":{"anyOf":[{"$ref":"#/components/schemas/Billing"},{"type":"null"}],"description":"What this call cost, or null when it was free."},"webhook_id":{"type":"string","title":"Webhook Id","description":"Webhook id, `wh_` plus 12 letters and digits."},"secret":{"type":"string","title":"Secret","description":"Signing secret for `X-VistaLink-Signature`, `whsec_` plus hex when generated; shown only in this response."},"url":{"type":"string","title":"Url","description":"The https endpoint deliveries are POSTed to.","format":"uri"},"events":{"items":{"type":"string","enum":["rates.completed","rates.batch.completed","rooms.reconcile.completed","booking.updated","quote.updated"]},"type":"array","title":"Events","description":"Events delivered to it."},"created_at":{"type":"string","format":"date-time","title":"Created At","description":"When the webhook was created, RFC 3339 UTC."}},"type":"object","required":["request_id","webhook_id","secret","url","events","created_at"],"title":"WebhookCreated","description":"Response of `POST /v2/webhooks`.","examples":[{"created_at":"2026-10-07T09:30:00Z","events":["rates.completed","booking.updated"],"request_id":"req_8f2c1a9b3d4e5f60","secret":"whsec_3f9a0c2e7b5d4a1f8e6c9b2a7d4f1e0c","url":"https://partner.example/hooks/vistalink","webhook_id":"wh_N4c7Bz1Qk2Vt"}]},"WebhookDelivery":{"properties":{"delivery_id":{"type":"string","title":"Delivery Id","description":"Delivery id, `whd_` plus 12 letters and digits."},"event":{"type":"string","enum":["rates.completed","rates.batch.completed","rooms.reconcile.completed","booking.updated","quote.updated"],"title":"Event","description":"The event delivered."},"event_id":{"type":"string","title":"Event Id","description":"Id of the event, `evt_` plus 16 letters and digits, the same for every webhook that receives it and sent as `id` in the payload."},"status":{"type":"string","enum":["pending","delivered","failed"],"title":"Status","description":"`pending` (waiting for its next attempt), `delivered` (your endpoint answered 2xx) or `failed` (all attempts used, or the webhook was deleted)."},"attempts":{"type":"integer","title":"Attempts","description":"Attempts made so far, at most 7."},"last_status_code":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Last Status Code","description":"HTTP status your endpoint gave on the last attempt; null when it could not be reached."},"last_error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Error","description":"Why the last attempt failed, for example `http_500` or `webhook_deleted`; null after success."},"next_attempt_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Next Attempt At","description":"When the next attempt is due, RFC 3339 UTC; null once delivered or failed."},"created_at":{"type":"string","format":"date-time","title":"Created At","description":"When the event was queued for this webhook, RFC 3339 UTC."},"delivered_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Delivered At","description":"When your endpoint accepted it, RFC 3339 UTC; null until then."}},"type":"object","required":["delivery_id","event","event_id","status","attempts","created_at"],"title":"WebhookDelivery","description":"One event sent, or being sent, to a webhook."},"WebhookDeliveryList":{"properties":{"request_id":{"type":"string","title":"Request Id","description":"Opaque id of this request, also sent as the `X-Request-Id` header; quote it when contacting support."},"billing":{"anyOf":[{"$ref":"#/components/schemas/Billing"},{"type":"null"}],"description":"What this call cost, or null when it was free."},"deliveries":{"items":{"$ref":"#/components/schemas/WebhookDelivery"},"type":"array","title":"Deliveries","description":"Deliveries on this page, newest first."},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor","description":"Pass as `cursor` for the next page; null on the last page."}},"type":"object","required":["request_id","deliveries"],"title":"WebhookDeliveryList","description":"Response of `GET /v2/webhooks/{webhook_id}/deliveries`.","examples":[{"deliveries":[{"attempts":1,"created_at":"2026-10-07T09:30:00Z","delivered_at":"2026-10-07T09:30:01Z","delivery_id":"whd_R2t8Mx5Qp1Lz","event":"booking.updated","event_id":"evt_9Hd2Kq7Lm4Xp1Zr8","last_status_code":200,"status":"delivered"}],"request_id":"req_8f2c1a9b3d4e5f60"}]},"WebhookList":{"properties":{"request_id":{"type":"string","title":"Request Id","description":"Opaque id of this request, also sent as the `X-Request-Id` header; quote it when contacting support."},"billing":{"anyOf":[{"$ref":"#/components/schemas/Billing"},{"type":"null"}],"description":"What this call cost, or null when it was free."},"webhooks":{"items":{"$ref":"#/components/schemas/Webhook"},"type":"array","title":"Webhooks","description":"Active webhooks, newest first."}},"type":"object","required":["request_id","webhooks"],"title":"WebhookList","description":"Response of `GET /v2/webhooks`.","examples":[{"request_id":"req_8f2c1a9b3d4e5f60","webhooks":[{"created_at":"2026-10-07T09:30:00Z","events":["rates.completed","booking.updated"],"is_active":true,"url":"https://partner.example/hooks/vistalink","webhook_id":"wh_N4c7Bz1Qk2Vt"}]}]},"WhyItem":{"properties":{"criterion":{"type":"string","title":"Criterion","description":"The traveller's own word or requirement being checked, for example `quiet` or `pool`."},"assessment":{"type":"string","enum":["supported","contradicted","unknown"],"title":"Assessment","description":"`supported` when the evidence backs the criterion, `contradicted` when it goes against it (reviews only), `unknown` when it is inconclusive."},"evidence":{"anyOf":[{"type":"string","enum":["review","image","amenity","description"]},{"type":"null"}],"title":"Evidence","description":"What was checked: `review` (guest review sentiment), `image` (photos), `amenity` (the amenity list) or `description` (chat only)."}},"type":"object","required":["criterion","assessment"],"title":"WhyItem","description":"One piece of evidence for or against a hotel on a search criterion."},"WebhookEventPayload":{"description":"The JSON body POSTed to your webhook URL, signed in `X-VistaLink-Signature`.","examples":[{"created_at":"2026-10-07T09:30:00Z","data":{"billing":{"amount":"0.04","currency":"USD","sku":"rates.direct.live","units":1},"created_at":"2026-10-07T09:30:00Z","error":null,"estimated_seconds":25,"job_id":"job_K8s2Lq9Wx4Np","kind":"rates.live","progress":{"done":1,"total":1},"result":{"billing":{"amount":"0.002","currency":"USD","sku":"rates.direct.cached","units":1},"request_id":"req_8f2c1a9b3d4e5f60","results":[{"channel":"direct","engine":"SYNXIS","hotel_id":"43d0be71-5f3c-4c8e-9a52-1d7e2b6f0a19","input":0,"observed_at":"2026-10-07T09:30:00Z","rooms":[{"listing_key":"engine_room:1234567","name":"Superior Room","offers":[{"availability":"available","board":"breakfast","cancellation_deadline":null,"channel":"direct","currency":"EUR","engine":"SYNXIS","excluded_charges":[],"free_cancellation":false,"includes_taxes":true,"listing_key":"engine_room:1234567","member_rate":false,"nightly_avg":"481.00","observed_at":"2026-10-07T09:30:00Z","occupancy":{"adults":2,"children_ages":[]},"offer_id":"off_t3c8e1a9b4f2d7e6a5c0b","payment":null,"rate_name":"Non-refundable, breakfast included","room_id":"5b1e2f3a-0c4d-4e5f-8a9b-1c2d3e4f5a6b","room_name":"Superior Room","total":"962.00","url":"https://umilta36.com/book?checkin=2026-11-14&checkout=2026-11-16"}],"room_id":"5b1e2f3a-0c4d-4e5f-8a9b-1c2d3e4f5a6b"}],"status":"observed"}],"summary":{"observed":1,"stale":0,"unknown":0,"unresolved":0,"unsupported":0}},"result_url":null,"status":"succeeded","updated_at":"2026-10-07T09:30:00Z"},"event":"rates.completed","id":"evt_9Hd2Kq7Lm4Xp1Zr8"}],"properties":{"event":{"description":"The event: `rates.completed`, `rates.batch.completed` or `rooms.reconcile.completed` when a job finishes (succeeded or failed), `booking.updated` when a booking's status or payment status changes, `quote.updated` when a quote's status changes or the hotel sends an offer.","enum":["rates.completed","rates.batch.completed","rooms.reconcile.completed","booking.updated","quote.updated"],"title":"Event","type":"string"},"id":{"description":"Event id, `evt_` plus 16 characters; use it to discard repeats, since a retried delivery carries the same id.","title":"Id","type":"string"},"created_at":{"description":"When the event happened, RFC 3339 UTC.","format":"date-time","title":"Created At","type":"string"},"data":{"additionalProperties":true,"description":"The Job for job events, the Booking for `booking.updated`, the Quote for `quote.updated`.","title":"Data","type":"object"}},"required":["event","id","created_at","data"],"title":"WebhookEventPayload","type":"object"}},"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"`Authorization: Bearer vl_live_…` or `vl_test_…`. Keys carry scopes: `search`, `content`, `rates`, `reconcile`, `booking`, `chat`, `quotes`."}}},"tags":[{"name":"Resolve","description":"Listing URLs, hotel websites and names in; `hotel_id`s out."},{"name":"Search","description":"One endpoint, structured or loose input, filter or semantic ranking."},{"name":"Hotels","description":"Everything we hold about one hotel."},{"name":"Rates","description":"Direct prices from the hotel's own booking engine, cached or read live."},{"name":"Rooms","description":"One hotel's rooms matched across channels."},{"name":"Bookings","description":"Book an offer on the hotel's own engine."},{"name":"Quotes","description":"Ask a hotel by email for a direct price, then introduce your guest."},{"name":"Jobs","description":"Background work: poll or cancel."},{"name":"Webhooks","description":"Get told when a job, booking or quote moves."},{"name":"Chat","description":"Conversational search with memory."},{"name":"Catalog","description":"Amenity codes, booking engines, health and this document."}],"security":[{"bearerAuth":[]}],"webhooks":{"rates.completed":{"post":{"summary":"rates.completed","description":"A `rates.live` job finished. `data` is the Job.","operationId":"onRatesCompleted","parameters":[{"name":"X-VistaLink-Signature","in":"header","required":true,"description":"`t=<unix>,v1=<hex>`. v1 is HMAC-SHA256 of `<t>.<raw body>` with your secret.","schema":{"type":"string"},"example":"t=1791364462,v1=5f2c…"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEventPayload"},{"type":"object","properties":{"event":{"const":"rates.completed"},"data":{"$ref":"#/components/schemas/Job"}}}]}}}},"responses":{"200":{"description":"Any 2xx acknowledges. Anything else is retried: 1m, 5m, 30m, 2h, 12h, 24h."}}}},"rates.batch.completed":{"post":{"summary":"rates.batch.completed","description":"A `rates.batch` job finished. `data` is the Job.","operationId":"onRatesBatchCompleted","parameters":[{"name":"X-VistaLink-Signature","in":"header","required":true,"description":"`t=<unix>,v1=<hex>`. v1 is HMAC-SHA256 of `<t>.<raw body>` with your secret.","schema":{"type":"string"},"example":"t=1791364462,v1=5f2c…"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEventPayload"},{"type":"object","properties":{"event":{"const":"rates.batch.completed"},"data":{"$ref":"#/components/schemas/Job"}}}]}}}},"responses":{"200":{"description":"Any 2xx acknowledges. Anything else is retried: 1m, 5m, 30m, 2h, 12h, 24h."}}}},"rooms.reconcile.completed":{"post":{"summary":"rooms.reconcile.completed","description":"A `rooms.reconcile` job finished. `data` is the Job.","operationId":"onRoomsReconcileCompleted","parameters":[{"name":"X-VistaLink-Signature","in":"header","required":true,"description":"`t=<unix>,v1=<hex>`. v1 is HMAC-SHA256 of `<t>.<raw body>` with your secret.","schema":{"type":"string"},"example":"t=1791364462,v1=5f2c…"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEventPayload"},{"type":"object","properties":{"event":{"const":"rooms.reconcile.completed"},"data":{"$ref":"#/components/schemas/Job"}}}]}}}},"responses":{"200":{"description":"Any 2xx acknowledges. Anything else is retried: 1m, 5m, 30m, 2h, 12h, 24h."}}}},"booking.updated":{"post":{"summary":"booking.updated","description":"Every change to a booking's `status` or `payment.status`, and for `card_entry` to `next_action`. `data` is the Booking; its `payment_method` carries no card data (only `type`, `credential_id`, `credential_status`), and a `card_entry` booking's `version` orders events.","operationId":"onBookingUpdated","parameters":[{"name":"X-VistaLink-Signature","in":"header","required":true,"description":"`t=<unix>,v1=<hex>`. v1 is HMAC-SHA256 of `<t>.<raw body>` with your secret.","schema":{"type":"string"},"example":"t=1791364462,v1=5f2c…"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEventPayload"},{"type":"object","properties":{"event":{"const":"booking.updated"},"data":{"$ref":"#/components/schemas/Booking"}}}]}}}},"responses":{"200":{"description":"Any 2xx acknowledges. Anything else is retried: 1m, 5m, 30m, 2h, 12h, 24h."}}}},"quote.updated":{"post":{"summary":"quote.updated","description":"Every change to a quote's `status`, each new offer from the hotel and the acceptance. `data` is the Quote.","operationId":"onQuoteUpdated","parameters":[{"name":"X-VistaLink-Signature","in":"header","required":true,"description":"`t=<unix>,v1=<hex>`. v1 is HMAC-SHA256 of `<t>.<raw body>` with your secret.","schema":{"type":"string"},"example":"t=1791364462,v1=5f2c…"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEventPayload"},{"type":"object","properties":{"event":{"const":"quote.updated"},"data":{"$ref":"#/components/schemas/Quote"}}}]}}}},"responses":{"200":{"description":"Any 2xx acknowledges. Anything else is retried: 1m, 5m, 30m, 2h, 12h, 24h."}}}}}}