{"tools":[{"name":"search_location","description":"Searches LockTrip destinations (cities, regions, landmarks, or specific hotels) by free-text `query`. Returns `locations[]`, each with `id`, `name`, `country`, `type` (CITY/REGION/HOTEL/AIRPORT/POI), and `fullName`. Pass a chosen location's `id` to `hotel_search` as its `regionId` to search hotels in that area. No authentication required. This is the first step of the booking flow.","inputSchema":{"type":"object","properties":{"query":{"type":"string","description":"Search query (city name, region, or hotel name)"},"language":{"type":"string","description":"Language code (default: en)"}},"required":["query"]},"auth_required":false},{"name":"hotel_search","description":"Starts an asynchronous hotel-availability search and returns a `searchKey` (plus `sessionId`, `status`, `totalHotels`). Required: `startDate` and `endDate` (both YYYY-MM-DD), `rooms[]` (each `{adults, childrenAges?[]}`, 1-4 adults per room), `nationality` (2-letter ISO country code), and EITHER `regionId` (from `search_location`) OR `latitude`+`longitude`; `currency` (3-letter) is strongly recommended. Before calling, collect the missing trip details from the user (exact check-in date, number of nights to derive `endDate`, number of rooms/adults, and nationality) instead of guessing. No authentication required. Pass the returned `searchKey` to `get_search_results` (poll it), and reuse the SAME `searchKey` for `get_hotel_rooms` and `prepare_booking`.","inputSchema":{"type":"object","properties":{"regionId":{"type":"string","description":"Location ID from search_location. Required unless latitude/longitude are provided."},"startDate":{"type":"string","description":"Check-in date (YYYY-MM-DD)"},"endDate":{"type":"string","description":"Check-out date (YYYY-MM-DD)"},"currency":{"type":"string","description":"Currency code (USD, EUR, etc.)"},"rooms":{"type":"array","items":{"type":"object","properties":{"adults":{"type":"number","description":"Number of adults (1-4)"},"childrenAges":{"type":"array","items":{"type":"number"},"description":"Ages of children"}},"required":["adults"]},"description":"Room configurations"},"nationality":{"type":"string","description":"2-letter country code for guest nationality"},"latitude":{"type":"number","description":"Latitude for coordinate-based search (-90 to 90). Use with longitude instead of regionId."},"longitude":{"type":"number","description":"Longitude for coordinate-based search (-180 to 180). Use with latitude instead of regionId."},"radiusInMeters":{"type":"number","description":"Search radius in meters (default: 30000, min: 1000, max: 100000). Only with lat/lng."}},"required":["startDate","endDate","rooms","nationality"]},"auth_required":false},{"name":"get_search_results","description":"Returns the hotel results for a running search identified by `searchKey` (from `hotel_search`). Results stream in asynchronously, so POLL this tool until the returned `searchStatus` equals `COMPLETED` (it is `IN_PROGRESS` while results are still loading). Supports `page` (0-indexed on BOTH the request and the response echo — page 0 is the first page and comes back as page 0), `size` (use a large value like 5000 to get all), `filters` (minPrice/maxPrice/starRatings/amenities/mealTypes/hotelName), `sortBy` (PRICE_ASC/PRICE_DESC/RATING_DESC/DISTANCE), and `currency`. Returns `hotels[]` (each with `hotelId`, `name`, `starRating`, `minPrice`, `originalPrice`, `currency`, `discountScore`, `reviewScore`, `hasFreeCancellation`, `isRefundable`, etc.), plus `totalCount` and `hasMore`. No authentication required. Pass a chosen `hotelId` together with the SAME `searchKey` to `get_hotel_rooms`.","inputSchema":{"type":"object","properties":{"searchKey":{"type":"string","description":"Search key from hotel_search"},"page":{"type":"number","description":"Page number (0-indexed)"},"size":{"type":"number","description":"Results per page (use 5000 to get all)"},"filters":{"type":"object","properties":{"minPrice":{"type":"number"},"maxPrice":{"type":"number"},"starRatings":{"type":"array","items":{"type":"number"}},"amenities":{"type":"array","items":{"type":"string"}},"mealTypes":{"type":"array","items":{"type":"string"}},"hotelName":{"type":"string"}}},"sortBy":{"type":"string","enum":["PRICE_ASC","PRICE_DESC","RATING_DESC","DISTANCE"]},"currency":{"type":"string","description":"Currency code from hotel_search (prices are in this currency)"}},"required":["searchKey"]},"auth_required":false},{"name":"get_hotel_rooms","description":"Lists the bookable room packages for one hotel. Required: `hotelId` (from `get_search_results`), the SAME `searchKey` from `hotel_search`, plus `startDate`, `endDate`, `rooms[]`, `nationality`, and `regionId` (re-supply the same values used in `hotel_search`); `currency` is recommended. Returns `packages[]`, each with a `quoteId` (the priced, bookable offer — format `packageId_hotelId`), a `packageId` (the same string as the `quoteId`; truncate it at the underscore before sending it to `check_cancellation_policy`), `roomName`, `mealType`, `price`, `pricePerNight`, `totalNights`, `amenities`, and `isRefundable`. No authentication required. Pass a chosen `quoteId` to `prepare_booking` (and to `confirm_booking` for B2B). To inspect cancellation terms first, pass `quoteId.split('_')[0]` (the bare package UUID, WITHOUT the `_hotelId` suffix) to `check_cancellation_policy` — handing it the full `quoteId` fails with a misleading SESSION_EXPIRED even on a live session.","inputSchema":{"type":"object","properties":{"hotelId":{"type":"string","description":"Hotel ID from search results"},"searchKey":{"type":"string","description":"Search key"},"startDate":{"type":"string","description":"Check-in date (YYYY-MM-DD)"},"endDate":{"type":"string","description":"Check-out date (YYYY-MM-DD)"},"rooms":{"type":"array","items":{"type":"object","properties":{"adults":{"type":"number"},"childrenAges":{"type":"array","items":{"type":"number"}}},"required":["adults"]}},"nationality":{"type":"string","description":"2-letter country code"},"regionId":{"type":"string","description":"Region ID"},"currency":{"type":"string","description":"Currency code"}},"required":["hotelId","searchKey","startDate","endDate","rooms","nationality","regionId"]},"auth_required":false},{"name":"check_cancellation_policy","description":"Returns detailed cancellation terms for one or more room packages before booking. Required: `searchKey`, `hotelId`, and `packageIds[]` — pass the package UUID WITHOUT the `_hotelId` suffix, i.e. `quoteId.split('_')[0]`. Passing the full `quoteId` here fails with a misleading SESSION_EXPIRED even on a live session, because the policy lookup matches the bare UUID. Returns `policies[]`, each with `packageId`, `isRefundable`, `freeCancellationUntil`, and `fees[]` (`fromDate`, `amount`, `currency`). `currency` is the supplier's own denomination for that penalty and is OMITTED, never guessed, when the supplier does not state one — so read the amount as unpriced if it is absent rather than assuming your search currency. No authentication required. Informational only — use it to confirm the refund/penalty terms with the user, then continue with `prepare_booking`.","inputSchema":{"type":"object","properties":{"searchKey":{"type":"string","description":"Search key"},"hotelId":{"type":"string","description":"Hotel ID"},"packageIds":{"type":"array","items":{"type":"string"},"description":"Package IDs to check"}},"required":["searchKey","hotelId","packageIds"]},"auth_required":false},{"name":"prepare_booking","description":"Prepares a booking for a chosen room offer and returns the final price plus a `preparedBookingId` (also returned as the alias `bookingInternalId`). A prepared booking has NO fixed expiry — what expires is the search session behind its `quoteId`, which surfaces as \"This booking is unavailable\"; re-run `hotel_search` and re-quote if you see that. Required: `quoteId` (from `get_hotel_rooms`), `searchKey` (the SAME one from `hotel_search` — omitting it is a 400), `rooms[]` (each `{roomIndex (0-based), guests:[{firstName, lastName, title?}], children?}` — one guest entry per adult), and a `contactPerson` (`firstName`, `lastName`, `email`, `phone`); the `searchKey` is required (it warms the cancellation-policy cache the backend needs to book). Collect every guest's first/last name and a contact email + phone from the user before calling. REQUIRES AUTHENTICATION: if the connection is not already authorized via OAuth, first call `guest_login` (email only) for consumers or `login` (email + password) for partners, then pass the returned token as the `sessionToken` argument. After this, send `preparedBookingId` to `create_checkout` (consumer, recommended) or `get_payment_url` (consumer, Stripe alternative); B2B partners use `confirm_booking`.","inputSchema":{"type":"object","properties":{"quoteId":{"type":"string","description":"Quote ID from get_hotel_rooms"},"searchKey":{"type":"string","description":"Search key from hotel_search (needed to validate cancellation policy)"},"rooms":{"type":"array","items":{"type":"object","properties":{"roomIndex":{"type":"number","description":"Room index (0-based)"},"guests":{"type":"array","items":{"type":"object","properties":{"firstName":{"type":"string"},"lastName":{"type":"string"},"title":{"type":"string","enum":["Mr","Mrs","Ms"],"description":"Guest title (default: Mr)"},"email":{"type":"string"},"phone":{"type":"string"},"isLeadGuest":{"type":"boolean"}},"required":["firstName","lastName"]},"description":"Adult guests in this room"},"children":{"type":"array","items":{"type":"object","properties":{"firstName":{"type":"string"},"lastName":{"type":"string"},"age":{"type":"number","description":"Child age (0-17)"}},"required":["firstName","lastName","age"]},"description":"Children in this room (must match childrenAges from search)"}},"required":["roomIndex","guests"]}},"contactPerson":{"type":"object","properties":{"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string"},"phone":{"type":"string"},"title":{"type":"string","enum":["Mr","Mrs","Ms"],"description":"Title (default: Mr)"},"countryCode":{"type":"string"}},"required":["firstName","lastName","email","phone"]},"specialRequests":{"type":"string"},"sessionToken":{"type":"string","description":"Auth token to authenticate this call when you are NOT already connected via OAuth. Get it by calling guest_login (email only - creates an account and emails the user) or login (email + password) first, then pass the returned token here."}},"required":["quoteId","searchKey","rooms","contactPerson"]},"auth_required":true},{"name":"confirm_booking","description":"Confirms and finalizes a booking using a B2B credit line — PARTNER ACCOUNTS ONLY. Guests cannot use this; ordinary consumers must instead pay via `create_checkout` (recommended) or `get_payment_url`. Required: `bookingInternalId` and `quoteId` (both from `prepare_booking`); `paymentMethod` defaults to `CREDIT_LINE`. REQUIRES AUTHENTICATION via a partner account: if the connection is not already authorized via OAuth, first call `login` (email + password) — a `guest_login` session has no credit line and will be rejected — then pass the returned token as the `sessionToken` argument. Returns `accepted` and `message`, plus a `voucherUrl` on success. On `accepted:false`, READ `serviceUnavailable`: if it is absent the credit-line service gave a real verdict (insufficient balance, inactive line) and the answer will NOT change on retry — tell the user and stop; if it is `true` the credit-line service was unreachable, which is a system fault and not a decision about their credit. Even then do NOT blindly retry: the reservation may already have been applied — re-read the booking with `get_booking_details` first. Note also that `accepted:true` means the credit line was charged and the booking accepted, but the supplier reservation is completed asynchronously afterwards.","inputSchema":{"type":"object","properties":{"bookingInternalId":{"type":"string","description":"Booking ID from prepare_booking"},"quoteId":{"type":"string","description":"Quote ID used in prepare_booking"},"paymentMethod":{"type":"string","description":"Payment method (default: CREDIT_LINE for B2B)"},"sessionToken":{"type":"string","description":"Auth token to authenticate this call when you are NOT already connected via OAuth. Get it by calling guest_login (email only - creates an account and emails the user) or login (email + password) first, then pass the returned token here."}},"required":["bookingInternalId","quoteId"]},"auth_required":true},{"name":"list_bookings","description":"Lists the authenticated user's existing bookings, filtered by `type`: `UPCOMING` (default), `COMPLETED`, `CANCELLED`, or `PENDING`. REQUIRES AUTHENTICATION with a registered account: if the connection is not already authorized via OAuth, first call `login` (email + password) — guest sessions from `guest_login` cannot list bookings — then pass the returned token as the `sessionToken` argument. Returns `bookings[]` (each with `bookingId`, `bookingReferenceId`, `hotelName`, `checkIn`, `checkOut`, `status`, `roomCount`, `createdAt`) plus `totalCount`. Pass a `bookingId` to `get_booking_details` or `cancel_booking`.","inputSchema":{"type":"object","properties":{"type":{"type":"string","enum":["UPCOMING","COMPLETED","CANCELLED","PENDING"],"description":"Booking list filter"},"sessionToken":{"type":"string","description":"Auth token to authenticate this call when you are NOT already connected via OAuth. Get it by calling guest_login (email only - creates an account and emails the user) or login (email + password) first, then pass the returned token here."}},"required":[]},"auth_required":true},{"name":"get_booking_details","description":"Returns the full details of one existing booking: hotel, dates, rooms, guests, contact person, total price, payment status, and the cancellation policy (including any cancellation fees and the free-cancellation deadline). Required: `bookingId` (from `list_bookings`). REQUIRES AUTHENTICATION with a registered account: if the connection is not already authorized via OAuth, first call `login` (email + password) — guest sessions cannot view booking details — then pass the returned token as the `sessionToken` argument. Informational only. Use this to read the fee/refund terms BEFORE cancelling, since `cancel_booking` itself does not return a fee preview.","inputSchema":{"type":"object","properties":{"bookingId":{"type":"string","description":"Booking ID to retrieve"},"sessionToken":{"type":"string","description":"Auth token to authenticate this call when you are NOT already connected via OAuth. Get it by calling guest_login (email only - creates an account and emails the user) or login (email + password) first, then pass the returned token here."}},"required":["bookingId"]},"auth_required":true},{"name":"cancel_booking","description":"Cancels an existing booking. Required: `bookingId` (from `list_bookings`) and the boolean `confirmed`; optional `reason`. This is a two-step safety flow: calling with `confirmed:false` does NOT cancel and returns only a 'not confirmed' acknowledgement (it does NOT return fees or refund amounts — read those first from `get_booking_details`); call again with `confirmed:true` to actually submit the cancellation. REQUIRES AUTHENTICATION with a registered account: if the connection is not already authorized via OAuth, first call `login` (email + password) — guest sessions cannot cancel bookings — then pass the returned token as the `sessionToken` argument. Returns `success` and a `message`. This is a destructive, terminal action — confirm with the user before calling with `confirmed:true`.","inputSchema":{"type":"object","properties":{"bookingId":{"type":"string","description":"Booking ID to cancel"},"confirmed":{"type":"boolean","description":"Set true to confirm cancellation"},"reason":{"type":"string","description":"Optional cancellation reason"},"sessionToken":{"type":"string","description":"Auth token to authenticate this call when you are NOT already connected via OAuth. Get it by calling guest_login (email only - creates an account and emails the user) or login (email + password) first, then pass the returned token here."}},"required":["bookingId","confirmed"]},"auth_required":true},{"name":"get_hotel_details","description":"Fetches full descriptive details for one hotel: name, address, coordinates (lat/lng), star rating, description, phone, amenities, photos, and reviews. Required: the NUMERIC `hotelId` (the same hotel identifier returned in search results, passed as a number); optional `language`, `includeImages` (set true to fetch extra photos), and `imageLimit` (default 20, max 100). Returns a `hotel` object and optional `additionalImages`. No authentication required. Informational only — use it to enrich what you show the user before they pick a room.","inputSchema":{"type":"object","properties":{"hotelId":{"type":"number","description":"Hotel ID (numeric, from search results externalId)"},"language":{"type":"string","description":"Language code for description (default: en)"},"includeImages":{"type":"boolean","description":"Fetch additional hotel images (default: false)"},"imageLimit":{"type":"number","description":"Max additional images to fetch (default: 20, max: 100)"}},"required":["hotelId"]},"auth_required":false},{"name":"get_payment_url","description":"Creates a hosted Stripe (credit-card) checkout link for a prepared booking — an alternative consumer payment rail to `create_checkout` (Revolut). Required: `bookingId` (the `preparedBookingId` from `prepare_booking`), `currency` (3-letter), and `backUrl`; optional `successUrl`. REQUIRES AUTHENTICATION WITH A REGISTERED ACCOUNT: call `login` (email + password) — a `guest_login` session is REJECTED here (the Stripe rail is not open to guests; use `create_checkout` for a guest instead) — then pass the returned token as the `sessionToken` argument. Returns a payment `url` and a `sessionId`. Share the `url` with the guest — the booking is confirmed automatically by webhook once payment succeeds. Prefer `create_checkout` unless a Stripe card link is specifically needed.","inputSchema":{"type":"object","properties":{"bookingId":{"type":"string","description":"Booking ID from prepare_booking (preparedBookingId)"},"currency":{"type":"string","description":"Payment currency code (e.g., USD, EUR)"},"backUrl":{"type":"string","description":"URL to redirect on cancel/back"},"successUrl":{"type":"string","description":"Custom URL to redirect on success"},"sessionToken":{"type":"string","description":"Auth token to authenticate this call when you are NOT already connected via OAuth. Get it by calling guest_login (email only - creates an account and emails the user) or login (email + password) first, then pass the returned token here."}},"required":["bookingId","currency","backUrl"]},"auth_required":true},{"name":"create_checkout","description":"Creates a hosted Revolut payment link for a prepared booking — the RECOMMENDED way for ordinary consumers (no credit line) to pay. Required: `bookingId` (the `preparedBookingId` from `prepare_booking`) and `currency` (3-letter); optional `backUrl` and `successUrl`. REQUIRES AUTHENTICATION: if the connection is not already authorized via OAuth, first call `guest_login` (email only) or `login` (email + password), then pass the returned token as the `sessionToken` argument. Returns a `checkoutUrl` (valid ~20 minutes), a `checkoutToken`, and a `message`. Share the `checkoutUrl` with the guest to complete payment — the booking is confirmed automatically by webhook once payment succeeds (do not call `confirm_booking`, which is B2B-only).","inputSchema":{"type":"object","properties":{"bookingId":{"type":"string","description":"Booking ID from prepare_booking (preparedBookingId)"},"currency":{"type":"string","description":"3-letter currency code (e.g. USD, EUR, GBP)"},"backUrl":{"type":"string","description":"Cancel/back redirect URL (optional, defaults to locktrip.com)"},"successUrl":{"type":"string","description":"Success redirect URL (optional)"},"sessionToken":{"type":"string","description":"Auth token to authenticate this call when you are NOT already connected via OAuth. Get it by calling guest_login (email only - creates an account and emails the user) or login (email + password) first, then pass the returned token here."}},"required":["bookingId","currency"]},"auth_required":true},{"name":"login","description":"Authenticates a registered LockTrip user (typically a B2B/partner account) with `email` and `password`, and returns a session `token` plus `userId`. Pass the returned `token` as the `sessionToken` argument on subsequent authenticated tools, OR rely on it being set as the connection's Bearer token. Use `login` when you need partner-only capabilities: B2B credit-line confirmation via `confirm_booking`, and listing/viewing/cancelling existing bookings (`list_bookings`, `get_booking_details`, `cancel_booking`). For an ordinary consumer who only wants to search and pay, use `guest_login` instead. No authentication required to call this tool.","inputSchema":{"type":"object","properties":{"email":{"type":"string","description":"User email address"},"password":{"type":"string","description":"User password"}},"required":["email","password"]},"auth_required":false},{"name":"guest_login","description":"Creates an anonymous guest session from an `email` ONLY (no password, no other user input) and returns a session `token` plus `userId`, `email`, and `isNewUser`; new emails also receive a verification email. Collect the customer's email from the user, then call this automatically whenever a booking tool needs authentication and you don't already have a token — NEVER ask the user to supply a token. Pass the returned `token` as the `sessionToken` argument to `prepare_booking`, `create_checkout`, and `get_payment_url`. Note: if the email belongs to an already-registered/verified account, this returns no token and asks you to use the `login` tool (email + password) instead. A guest session can search, prepare a booking, and pay, but CANNOT list/view/cancel bookings or use `confirm_booking`. No authentication required to call this tool.","inputSchema":{"type":"object","properties":{"email":{"type":"string","description":"User email address for guest registration/login"}},"required":["email"]},"auth_required":false}]}