Searching and booking stays

Look up destinations, search stays, pick a fare, and create a stay booking.

Stays follow the standard four-step flow: search → fares → build → book.
Search and fares stream results as Server-Sent Events (SSE); build and book
return JSON. All requests require the consumer HMAC and a traveler bearer
token
— omitted from the snippets below for brevity.

("Stays" is Mercury's canonical name for lodging — hotels, apartments, and other
accommodation.)

1. Look up destinations

Resolve free-text input to destination and property identifiers.

GET /v1/stays/destinations?q=rome&limit=5

Queries shorter than two characters return an empty result. See the
API Reference for the response shape.

2. Search

POST /v1/stays/search streams stay solutions for a destination and date range
as SSE. If the requested stay exceeds the configured maximum nights, the stream
terminates with event: error and code MAX_NIGHTS_EXCEEDED; other provider
failures use PROVIDER_ERROR.

Request body (StaySearchRequest) — provide either a destination or a
specific property:

{
  "destination": "rome-city-id",
  "checkIn": "2026-02-10",
  "checkOut": "2026-02-12",
  "rooms": [{ "adults": 2, "childrenAges": [] }],
  "passengers": { "adults": 2 },
  "nationality": "IT"
}

Each streamed solution's id is the searchSolutionId for the next step.

catalog-item frames (flag-gated, additive)

When catalog-item streaming is enabled for your account, the stream begins
with a burst of event: catalog-item frames a few seconds before the first
priced event: solution frames: one frame per candidate hotel (relevance
order — negotiated tier first, then the curated catalog priority rank, with
static tiebreakers: review score, star rating, image presence, distance —
capped), carrying the static property subset — same field names and
casing as the solution shape, minus id and every price/fare-derived field:

event: catalog-item
data: {"name": "...", "city": "...", "countryCode": "IT", "checkIn": {...},
       "checkOut": {...}, "rating": 4, "images": [...], "propertyId": "018f...",
       "searchSolutionId": "018f...", "distanceFromSearchMeters": 420, ...}
  • Join key: the later solution frame for the same hotel carries the
    same propertyId — treat catalog-item as insert-if-absent and
    solution as upsert-by-propertyId to hydrate prices in place.
  • Tappable immediately: a catalog-item's searchSolutionId +
    propertyId are valid for POST /v1/stays/fares right away, before any
    price has streamed.
  • Additive: clients that do not recognise the event type can ignore it;
    solution frames are unchanged. Some hotels may never receive a price
    during the search — their cards simply never hydrate.

3. List fares

POST /v1/stays/fares streams the bookable room fares for a chosen solution.

Request body (StayFaresBody):

{
  "searchSolutionId": "018f...",
  "checkIn": "2026-02-10",
  "checkOut": "2026-02-12",
  "rooms": [{ "adults": 2, "childrenAges": [] }],
  "isPersonal": false
}

External fares are withheld by default. Pay-at-property (external) fares
are not surfaced unless your environment enables them. Fares returned here are
prepaid through BizAway.

4. Build the fare

POST /v1/stays/fares/build locks a specific fare and returns it as JSON
(201).

Request body (StayBuildFareBody):

{ "fareId": "018f...", "searchSolutionId": "018f...", "isPersonal": false }

The response carries a totalPrice as { "amount": "320.00", "currency": "EUR" }. See the API Reference for the full fare shape.

5. (Optional) Protection plans

GET /v1/stays/fares/{fare_id}/protection-plans

6. Book

POST /v1/stays/bookings creates the booking from the built fareId. Send a
unique Idempotency-Key header (required — see
Bookings and trips).

Request body (BookStayRequest):

{
  "fareId": "018f...",
  "passengers": [
    {
      "firstName": "Ada",
      "lastName": "Lovelace",
      "type": "adult",
      "isHolder": true
    }
  ],
  "isPersonal": false,
  "tripId": null
}

For stays, the booking holder's phone number is taken from the authenticated
traveler's profile. Manage the booking lifecycle with the shared endpoints in
Bookings and trips.

Some fares require a birthday on every adult guest — if one is missing,
Mercury returns 422 with field-level code GUEST_BIRTHDAY_REQUIRED,
naming the specific guest, before any provider call is made.

Multi-room bookings

If you searched and priced a fare across two or more rooms (step 2's
rooms array had more than one entry), send rooms on the booking too so
Mercury knows which guest sleeps in which room. Each entry lists the
zero-based indices of passengers assigned to that room:

{
  "fareId": "018f...",
  "passengers": [
    { "firstName": "Ada", "lastName": "Lovelace", "type": "adult", "isHolder": true },
    { "firstName": "Grace", "lastName": "Hopper", "type": "adult", "isHolder": false }
  ],
  "rooms": [{ "passengerIndices": [0] }, { "passengerIndices": [1] }],
  "isPersonal": false
}

Every adult in passengers must be referenced by exactly one room, and
exactly one referenced passenger must have isHolder: true — Mercury
returns 422 (with PASSENGER_COUNT_MISMATCH, STAY_ROOM_COUNT_MISMATCH,
or STAY_ROOM_OCCUPANCY_MISMATCH as the field-level code) if the room split
doesn't add up or doesn't match the fare you built. Omit rooms entirely
for a single-room booking — behavior is unchanged.

See the API Reference for the complete request and
response schemas.

Reporting issues

This page is maintained by the BizAway integration team. To request changes or
report an error, contact your BizAway integration contact. Edits made directly
in the ReadMe dashboard are overwritten on the next publish.


Did this page help you?