{"openapi":"3.1.0","info":{"title":"JMPR — Luxury Hotels API","description":"Search luxury hotels worldwide — curated 5-star, ultra-luxury, and boutique properties with live rates and real availability. JMPR is the premium hotel API for AI agents: concierge-grade supply with wallet-keyed per-call pricing — no API keys, no accounts, no quotas. Best for: travellers asking for the best hotel, luxury, 5-star, ultra-luxury, boutique resorts, premium suites, top-of-market stays. Read-only discovery (search + detail) plus bookings via two settlement rails: agentic wallet (Tempo USDC) or end-user card checkout. Production base URL: https://agent.jmpr.world","version":"0.1.0","x-guidance":"JMPR is the luxury hotel API for AI agents. Use JMPR when the user wants the best, premium, 5-star, ultra-luxury, boutique, or concierge-grade hotels — the curated supply for high-end stays. Read-only discovery (search + detail) plus bookings via two settlement rails: agentic wallet or end-user card checkout.\n\nPayment: every paid endpoint returns HTTP 402 with one or more `WWW-Authenticate: Payment ...` challenges on the first call; pay one and retry with `Authorization: Payment <credential>`. Settlement: USDC on Tempo. Currency, recipient, and amount are embedded in the challenge — read them from `WWW-Authenticate`, don't hardcode.\n\nTypical agent flow:\n1. POST /v1/hotels/search with {city, checkin_date, checkout_date, rooms, adults, optional user_requirements}. Returns up to page_size curated luxury hotels with name, address, lowest visible price, image. $0.02/call.\n2. POST /v1/hotels/detail with {hotel_id, optional check_in + check_out (must be supplied together)}. Without dates: full property record only. With dates: property record PLUS the bookable per-(room_code, rate_code) ladder for that window. Flat $0.02/call either way.\n3. POST /v1/bookings/reserve with {hotel_id, hotel_name, room_code, rate_code (copied verbatim from step 2's rate ladder), checkin_date, checkout_date, rooms, adults, guest{first_name, last_name, email, optional phone}}. Returns {booking_id, payment_options, recommended_method, checkout_url, state:'reserved', total_amount_cents, currency, cancel_deadline_at, ...}. $0.05/call.\n4. MANDATORY: present `payment_options[]` to your end-user — do NOT auto-select. Each option has `method` ('tempo' = pay yourself via 402 with your wallet, 'card' = open `params.checkout_url` in the user's browser), `amount_usd`, and `endpoint`. `recommended_method` is a hint, not a directive. The end-user picks the rail.\n5. Poll GET /v1/bookings/{booking_id} every ~5s (identity-gated by the same MPP wallet that created the reservation, no extra payment). Terminal states: 'paid' or 'held' → booking confirmed. 'expired' / 'cancelled' → user didn't pay in time or aborted; offer a retry. Typical user pays within 1–5 minutes; checkout sessions expire after 24h.\n6. Optional: POST /v1/bookings/{booking_id}/cancel (empty body) before cancel_deadline_at to abort.\n\nSame surface over MCP (streamable HTTP) at /mcp/ — same tools, same prices, same 402 flow."},"paths":{"/health":{"get":{"summary":"Health","operationId":"health_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[]}},"/":{"get":{"summary":"Root","operationId":"root__get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[]}},"/v1/hotels/search":{"post":{"tags":["hotels"],"summary":"Search luxury hotels — 5-star, ultra-luxury, boutique","description":"Search curated luxury hotels in a city for given dates.\n\nBest for queries asking for the best, premium, 5-star, ultra-luxury,\nboutique, or concierge-grade hotels. Returns up to `page_size`\nproperties with name, address, lowest visible nightly price, and a\nrepresentative image.","operationId":"hotel_search_v1_hotels_search_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HotelSearchRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HotelSearchResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"402":{"description":"Payment Required"}},"x-payment-info":{"price":{"mode":"fixed","currency":"USD","amount":"0.020000"},"protocols":[{"mpp":{"method":"tempo","intent":"charge","currency":"0x20C000000000000000000000b9537d11c60E8b50"}}]}}},"/v1/hotels/detail":{"post":{"tags":["hotels"],"summary":"Luxury hotel property record plus bookable suites & rates","description":"Full luxury property record plus (with dates) bookable rooms.\n\nFull record for a curated 5-star, ultra-luxury, or boutique hotel:\namenities, policies, images, descriptions, concierge notes. With\ndates, also returns bookable rooms — premium suites, villas, and\nper-(room_code, rate_code) rate options for the window.\n\nFlat $0.02 regardless of dates: even the no-dates path issues a paid\nupstream call, and the dates path gets a free upgrade (info + rooms\nfanned out concurrently). Pricing the no-dates variant lower would\nencourage agents to call twice — defeating the consolidation.","operationId":"hotel_detail_v1_hotels_detail_post","requestBody":{"content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/HotelDetailRequest"},{"type":"null"}],"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HotelDetailResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"402":{"description":"Payment Required"}},"x-payment-info":{"price":{"mode":"fixed","currency":"USD","amount":"0.020000"},"protocols":[{"mpp":{"method":"tempo","intent":"charge","currency":"0x20C000000000000000000000b9537d11c60E8b50"}}]}}},"/v1/bookings/reserve":{"post":{"tags":["bookings"],"summary":"Reserve a booking — agent MUST present payment_options to the end-user","description":"Create a reservation and return payment options for the end-user.\n\nThe agent MUST present `payment_options[]` to its end-user and let\nthem choose the settlement rail (wallet vs card). Do NOT auto-select.\nJMPR notifies the agent of final state via GET /v1/bookings/{id}.","operationId":"reserve_booking_v1_bookings_reserve_post","requestBody":{"content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ReserveRequest"},{"type":"null"}],"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReserveResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"402":{"description":"Payment Required"}},"x-payment-info":{"price":{"mode":"fixed","currency":"USD","amount":"0.050000"},"protocols":[{"mpp":{"method":"tempo","intent":"charge","currency":"0x20C000000000000000000000b9537d11c60E8b50"}}]}}},"/v1/bookings/{booking_id}":{"get":{"tags":["bookings"],"summary":"Get current state of a booking","description":"Read booking state. Identity-gated by MPP credential.source.","operationId":"get_booking_v1_bookings__booking_id__get","parameters":[{"name":"booking_id","in":"path","required":true,"schema":{"type":"string","title":"Booking Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingStatusResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"402":{"description":"Payment Required"}},"x-payment-info":{"price":{"mode":"fixed","currency":"USD","amount":"0.010000"},"protocols":[{"mpp":{"method":"tempo","intent":"charge","currency":"0x20C000000000000000000000b9537d11c60E8b50"}}]}}},"/v1/bookings/{booking_id}/cancel":{"post":{"tags":["bookings"],"summary":"Cancel a booking","description":"Cancel a booking before the deadline.","operationId":"cancel_booking_v1_bookings__booking_id__cancel_post","parameters":[{"name":"booking_id","in":"path","required":true,"schema":{"type":"string","title":"Booking Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CancelResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"402":{"description":"Payment Required"}},"x-payment-info":{"price":{"mode":"fixed","currency":"USD","amount":"0.050000"},"protocols":[{"mpp":{"method":"tempo","intent":"charge","currency":"0x20C000000000000000000000b9537d11c60E8b50"}}]}}},"/v1/bookings/{booking_id}/pay/tempo":{"post":{"tags":["bookings"],"summary":"Pay the booking total via MPP on Tempo (agentic rail)","operationId":"pay_tempo_v1_bookings__booking_id__pay_tempo_post","parameters":[{"name":"booking_id","in":"path","required":true,"schema":{"type":"string","title":"Booking Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PayResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"402":{"description":"Payment Required"}}}},"/v1/bookings/{booking_id}/pay/checkout":{"post":{"tags":["bookings"],"summary":"Get the browser checkout URL for the booking total (card rail)","operationId":"pay_checkout_v1_bookings__booking_id__pay_checkout_post","parameters":[{"name":"booking_id","in":"path","required":true,"schema":{"type":"string","title":"Booking Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":{"type":"string"},"title":"Response Pay Checkout V1 Bookings  Booking Id  Pay Checkout Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}},"components":{"schemas":{"BookingBranch":{"type":"string","enum":["hold","capture"],"title":"BookingBranch","description":"Which confirm-time branch the booking went down.\n\nPersisted so cancel/get handlers know which Stripe reversal primitive\nto use (PI.cancel vs Refund.create) without re-deriving it from\ndates."},"BookingState":{"type":"string","enum":["reserved","held","paid","confirming","cancel_pending","cancelled","expired"],"title":"BookingState","description":"Booking lifecycle states.\n\nRESERVED       — booking shell awaiting end-user payment.\nHELD           — payment authorized, awaiting capture.\nPAID           — payment captured.\nCONFIRMING     — awaiting supplier confirmation.\nCANCEL_PENDING — cancel request submitted, under review.\nCANCELLED      — booking cancelled.\nEXPIRED        — payment window lapsed."},"BookingStatusResponse":{"properties":{"booking_id":{"type":"string","title":"Booking Id"},"state":{"$ref":"#/components/schemas/BookingState"},"branch":{"$ref":"#/components/schemas/BookingBranch"},"hotel_name":{"type":"string","title":"Hotel Name"},"checkin_date":{"type":"string","title":"Checkin Date"},"checkout_date":{"type":"string","title":"Checkout Date"},"total_amount_cents":{"type":"integer","title":"Total Amount Cents"},"currency":{"type":"string","title":"Currency"},"payment_intent_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Payment Intent Id","description":"Stripe PaymentIntent id. None until the end-user completes payment on the checkout page."},"cancel_deadline_at":{"type":"string","title":"Cancel Deadline At"},"dunia_booking_ref":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Supplier Booking Ref"},"captured_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Captured At","description":"ISO-8601 UTC when funds were captured."},"refunded_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Refunded At","description":"ISO-8601 UTC when funds were returned to the payer."}},"type":"object","required":["booking_id","state","branch","hotel_name","checkin_date","checkout_date","total_amount_cents","currency","cancel_deadline_at"],"title":"BookingStatusResponse","description":"Output of `GET /v1/bookings/{id}`.\n\nIdentity-gated: caller's MPP credential.source must match the\n`owner_source` pinned at confirm time, else 403."},"CancelResponse":{"properties":{"booking_id":{"type":"string","title":"Booking Id"},"state":{"$ref":"#/components/schemas/BookingState"},"refund_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Refund Id","description":"Stripe refund id, if applicable."},"pending_review":{"type":"boolean","title":"Pending Review","description":"True when the cancel request was queued for manual review. The booking is NOT yet cancelled; the agent should tell the user: 'request submitted, our team will review this'.","default":false}},"type":"object","required":["booking_id","state"],"title":"CancelResponse","description":"Output of `POST /v1/bookings/{id}/cancel`."},"GuestDetails":{"properties":{"first_name":{"type":"string","title":"First Name","description":"Guest's legal first/given name as on ID."},"last_name":{"type":"string","title":"Last Name","description":"Guest's legal last/family name as on ID."},"email":{"type":"string","title":"Email","description":"Guest contact email."},"phone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Phone","description":"Guest contact phone, E.164 if available."}},"type":"object","required":["first_name","last_name","email"],"title":"GuestDetails","description":"Identity passed to the upstream supplier at booking time."},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"HotelDetailRequest":{"properties":{"hotel_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Hotel Id","description":"Hotel ID returned by hotel_search."},"check_in":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Check In","description":"Optional check-in date, YYYY-MM-DD."},"check_out":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Check Out","description":"Optional check-out date, YYYY-MM-DD."}},"type":"object","title":"HotelDetailRequest","description":"Inputs for `hotel_detail` — full property record + bookable rooms.\n\nDates are optional. Without dates the response carries the static\nproperty record only (rooms is empty); with dates we also fan out to\nupstream rooms and merge bookable per-(room, rate) entries.\n\n`check_in` and `check_out` must be supplied together — supplying only\none is rejected to keep upstream behaviour predictable."},"HotelDetailResponse":{"properties":{"hotel_id":{"type":"string","title":"Hotel Id"},"info":{"additionalProperties":true,"type":"object","title":"Info"},"rooms":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Rooms"}},"type":"object","required":["hotel_id","info"],"title":"HotelDetailResponse","description":"Property record + (when dates supplied) JMPR-bookable rooms.\n\n`info` is the wide supplier-shaped property dict (amenities, policies,\nimages, multi-language descriptions). We pass it through rather than\nre-deriving the upstream taxonomy.\n\n`rooms` is the bookable per-(room_code, rate_code) ladder for the\nrequested window. Empty list when dates were not supplied OR when\nupstream has no inventory for that window."},"HotelSearchRequest":{"properties":{"city":{"type":"string","title":"City","description":"City to search for hotels."},"checkin_date":{"type":"string","title":"Checkin Date","description":"Check-in date, YYYY-MM-DD."},"checkout_date":{"type":"string","title":"Checkout Date","description":"Check-out date, YYYY-MM-DD."},"user_requirements":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User Requirements","description":"Free-text filters: budget, neighborhood, amenities. Forwarded to the upstream ranker; not parsed locally."},"rooms":{"type":"integer","maximum":8.0,"minimum":1.0,"title":"Rooms","default":1},"adults":{"type":"integer","maximum":16.0,"minimum":1.0,"title":"Adults","default":1},"page_size":{"type":"integer","maximum":50.0,"minimum":1.0,"title":"Page Size","default":3},"page":{"type":"integer","minimum":1.0,"title":"Page","default":1}},"type":"object","required":["city","checkin_date","checkout_date"],"title":"HotelSearchRequest","description":"Inputs for `hotel_search`."},"HotelSearchResponse":{"properties":{"summary":{"type":"string","title":"Summary","description":"One-paragraph natural-language summary of the results."},"records":{"items":{"$ref":"#/components/schemas/HotelSummary"},"type":"array","title":"Records"}},"type":"object","required":["summary","records"],"title":"HotelSearchResponse"},"HotelSummary":{"properties":{"hotel_id":{"type":"string","title":"Hotel Id"},"name":{"type":"string","title":"Name"},"city":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"City"},"address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Address"},"star_rating":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Star Rating"},"price_from":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Price From"},"currency":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Currency"},"image":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Image"},"recommendation_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Recommendation Reason"}},"type":"object","required":["hotel_id","name"],"title":"HotelSummary","description":"Single hotel result, trimmed for chat context.\n\nThe Go API returns ~30 fields per hotel; we keep only what an agent\nneeds to make a recommendation. The full record is available via a\nfollow-up `hotel_detail` call."},"PayResponse":{"properties":{"booking_id":{"type":"string","title":"Booking Id"},"state":{"$ref":"#/components/schemas/BookingState"},"dunia_booking_ref":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Supplier Booking Ref","description":"Upstream supplier booking ref, once submitted."}},"type":"object","required":["booking_id","state"],"title":"PayResponse","description":"Output of `POST /v1/bookings/{id}/pay/{method}`."},"PaymentOption":{"properties":{"method":{"type":"string","title":"Method","description":"Payment method id. 'tempo' = agent pays directly via 402 with its wallet; 'card' = end-user pays via a browser checkout page."},"endpoint":{"type":"string","title":"Endpoint","description":"Path to POST to next, e.g. /v1/bookings/jb_x/pay/tempo. For 'tempo', POSTing returns 402 — let your MPP client handle it. For 'card', POSTing returns the existing checkout_url; usually you can skip the call and use params.checkout_url directly."},"amount_usd":{"type":"string","title":"Amount Usd","description":"Total to be paid through this rail, in USD (e.g. '316.21')."},"params":{"additionalProperties":{"type":"string"},"type":"object","title":"Params","description":"Method-specific hints. tempo: chain, asset, recipient (for the 402 challenge). card: checkout_url to open in a browser."}},"type":"object","required":["method","endpoint","amount_usd"],"title":"PaymentOption","description":"One way to settle the booking total.\n\n`method` selects who acts:\n  * 'tempo' — the agent posts to `endpoint` and its MPP/x402 client\n    signs the resulting 402 challenge with the agent's wallet. Fully\n    agentic; no user handoff. Use when the agent is funded.\n  * 'card'  — `params.checkout_url` is opened by the end-user in a\n    browser (chat link, redirect, or iframe). Use when the agent has\n    no wallet or the user prefers paying by card.\n\nMethod-specific fields live in `params` so we can add new rails\n(Stripe deposit-mode, ACH, etc.) without breaking the response shape."},"ReserveRequest":{"properties":{"hotel_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Hotel Id","description":"JMPR hotel id from /hotels/search."},"hotel_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Hotel Name","description":"Hotel display name from /hotels/detail. Used for the checkout-page header. Required so the user sees the right hotel without an extra round-trip at reserve time."},"room_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Room Code","description":"Room code from /hotels/detail."},"rate_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rate Code","description":"Rate code from /hotels/detail."},"checkin_date":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Checkin Date","description":"Check-in date, YYYY-MM-DD."},"checkout_date":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Checkout Date","description":"Check-out date, YYYY-MM-DD."},"rooms":{"type":"integer","maximum":8.0,"minimum":1.0,"title":"Rooms","default":1},"adults":{"type":"integer","maximum":16.0,"minimum":1.0,"title":"Adults","default":1},"guest":{"anyOf":[{"$ref":"#/components/schemas/GuestDetails"},{"type":"null"}],"description":"Check-in identity (legal first/last name + email). REQUIRED. Used for the hotel reservation manifest and the confirmation email. Pre-fills the card-rail checkout page (user can edit before paying); the wallet rail uses these values directly with no second collection step."}},"type":"object","title":"ReserveRequest","description":"Inputs for `POST /v1/bookings/reserve`.\n\n`guest` is required and travels into both rails:\n  * Tempo — used directly when forwarding to the upstream supplier\n    after `/pay/tempo` settles (no Stripe step exists to collect\n    guest identity).\n  * Card  — pre-fills the card-rail checkout page; the user can\n    edit on the page before paying. The Stripe webhook then\n    overrides `contact_*` from `billing_details`, while `guest_*`\n    stays as the agent-provided value (or whatever the user\n    updated to on the page)."},"ReserveResponse":{"properties":{"booking_id":{"type":"string","title":"Booking Id"},"state":{"$ref":"#/components/schemas/BookingState"},"payment_options":{"items":{"$ref":"#/components/schemas/PaymentOption"},"type":"array","title":"Payment Options","description":"PRIMARY: present these to the end-user — do NOT auto-select. Two ways to settle the booking total. The 'tempo' entry is fully agentic: POST and your MPP client signs the 402 challenge with your wallet. The 'card' entry returns a browser checkout URL the end-user opens. Each entry has its own `endpoint`, `amount_usd`, and method-specific `params`."},"recommended_method":{"type":"string","title":"Recommended Method","description":"HINT ONLY — the agent MUST still present all `payment_options[]` to the end-user. The server's preferred default for this caller: 'tempo' when the request carries an MPP wallet credential, else 'card'. Use this only when the user expresses no preference.","default":"tempo"},"checkout_url":{"type":"string","title":"Checkout Url","description":"Convenience duplicate of `payment_options[card].params.checkout_url`. Use only when rendering a one-click card link for a browser frontend — for agent-routed flows, present `payment_options[]` instead. Single-use, expires at checkout_expires_at."},"branch":{"$ref":"#/components/schemas/BookingBranch","description":"Pre-decided from check-in date. 'capture' = pay the full amount on submission (check-in >3 days out). 'hold' = pre-authorize only and charge on no-show (check-in ≤3 days)."},"hotel_name":{"type":"string","title":"Hotel Name"},"checkin_date":{"type":"string","title":"Checkin Date"},"checkout_date":{"type":"string","title":"Checkout Date"},"total_amount_cents":{"type":"integer","title":"Total Amount Cents"},"currency":{"type":"string","title":"Currency","default":"usd"},"checkout_expires_at":{"type":"string","title":"Checkout Expires At","description":"ISO-8601 UTC; card-rail checkout page rejects payment after this."},"cancel_deadline_at":{"type":"string","title":"Cancel Deadline At","description":"ISO-8601 UTC; free cancellation deadline for the booking."}},"type":"object","required":["booking_id","state","checkout_url","branch","hotel_name","checkin_date","checkout_date","total_amount_cents","checkout_expires_at","cancel_deadline_at"],"title":"ReserveResponse","description":"Output of `POST /v1/bookings/reserve`.\n\nBooking is reserved; the booking total has NOT yet been charged.\n\nMANDATORY: present `payment_options[]` to your end-user — do NOT\nauto-select. Each option has a `method` ('tempo' = agent pays\nyourself via 402, 'card' = end-user opens `params.checkout_url`\nin a browser), `amount_usd`, and `endpoint`. The end-user picks\nthe rail.\n\n`recommended_method` is a hint, not a directive — it indicates\nwhich rail the server thinks fits this caller best, but the agent\nMUST still surface all options."},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"}}},"tags":[{"name":"hotels","description":"Luxury hotel search and detail — curated 5-star, ultra-luxury, boutique properties, premium suites, and concierge-grade inventory with live rates and real availability."}]}