mishor6 API (0.1.0)

Download OpenAPI specification:

Travel product aggregator for hotels, transfers, and other services.

Downloads: this specification as openapi.yaml, and a Postman collection made from it — set its apiKey and accessToken variables, and every /api request carries them.

Hotels

Search and aggregate accommodation offers

Initiate a hotel search

Initializes a search session and returns a session event. Offers are NOT returned here — fetch them from POST /api/hotels/results/poll[/{cursor}] or POST /api/hotels/results/stream[/{cursor}]. Send the same body to those routes; the server re-derives the session ID from it. The session string returned here is informational (debug/logging) — clients do not have to pass it back.

  • Identical searches (same account + parameters) within the validity window reuse the same session: the provider fan-out runs only once.
  • The search runs in a background orchestrator, decoupled from this request, and writes offers to a per-session Redis stream as providers respond.
  • expiresAt marks the end of the result-validity (bookability) window.
Authorizations:
(ApiKeyAuthAccessTokenAuth)
Request Body schema: application/json
required
required
object (HotelSearchTarget)

Search target. Exactly one of destinationId, hotelIds, or geo must be provided.

required
object
guestCountry
required
string = 2 characters
Enum: "IL" "US" "AE" "TR" "GB" "DE" "FR"

Guest nationality — ISO 3166-1 alpha-2

currency
required
string = 3 characters

Preferred currency — ISO 4217, canonicalized (trimmed, upper-cased, aliases resolved) before anything else reads it. On /search it must be one of the currencies the requesting account trades in (its configured account currencies; every known currency for an account with none configured), otherwise the search is refused with V019.

object

Internal/service key-value fields. Any JSON value is accepted.

timeout
integer [ 5 .. 300 ]
Default: 30

Maximum search duration in seconds

Responses

Request samples

Content type
application/json
{
  • "search": {
    },
  • "stay": {
    },
  • "guestCountry": "IL",
  • "currency": "USD",
  • "timeout": 30
}

Response samples

Content type
application/json
{
  • "meta": {
    },
  • "status": "initialized",
  • "data": {
    }
}

Poll one batch of search results

Returns one enveloped JSON batch of offers for the session derived from the body. The body is identical to the search request (the server re-derives the session ID from it); read-time filters and streaming sub-objects may also be supplied. Call again with the cursor from the prior response while hasMore is true.

Authorizations:
(ApiKeyAuthAccessTokenAuth)
Request Body schema: application/json
required

The original search-request body (so the server can re-derive the session ID) plus the read-time filters and streaming blocks.

required
object (HotelSearchTarget)

Search target. Exactly one of destinationId, hotelIds, or geo must be provided.

required
object
guestCountry
required
string = 2 characters
Enum: "IL" "US" "AE" "TR" "GB" "DE" "FR"

Guest nationality — ISO 3166-1 alpha-2

currency
required
string = 3 characters

Preferred currency — ISO 4217, canonicalized (trimmed, upper-cased, aliases resolved) before anything else reads it. On /search it must be one of the currencies the requesting account trades in (its configured account currencies; every known currency for an account with none configured), otherwise the search is refused with V019.

object

Internal/service key-value fields. Any JSON value is accepted.

timeout
integer [ 5 .. 300 ]
Default: 30

Maximum search duration in seconds

object

Post-hoc filters applied to already-fetched offers (no effect on what providers return). Both keys are include booleans, so only an explicit false narrows the results; the default (everything included) means omitting filters, sending {}, and sending {onRequest:true, payAtHotel:true} all behave identically — they return every offer. Known keys:

  • onRequest (boolean) — include on-request bookings; false returns only immediately-confirmed offers
  • payAtHotel (boolean) — include pay-at-hotel options; false returns only prepaid offers

Unknown filter keys are ignored. For example, stars is reserved for a future hotel-metadata filter but currently has no effect.

Changing the filter requires restarting from the beginning (call /poll or /stream with no cursor). A cursor is bound to the filter it was issued under; resuming it with a different filter is rejected with H009, because the entries before the cursor were already delivered under the previous filter and would otherwise interleave two filter sets.

On /poll, a filtered batch may return fewer than resultsPerPush offers — even zero — while hasMore is still true: the offers that matched lie further down the stream. Keep polling with the returned cursor; it always advances, so nothing is rescanned.

On /stream, long filtered-out stretches also emit cursor-advancing empty offers frames after a bounded scan, then the stream immediately continues looking for later matching offers.

object

Controls how results are delivered. pushInterval is stream-only; resultsPerPush acts as batch size on stream and as limit on poll.

Responses

Request samples

Content type
application/json
{
  • "search": {
    },
  • "stay": {
    },
  • "guestCountry": "IL",
  • "currency": "USD",
  • "timeout": 30,
  • "filters": {
    },
  • "streaming": {
    }
}

Response samples

Content type
application/json
{
  • "meta": {
    },
  • "status": "working",
  • "data": {
    }
}

Poll the next batch of search results from a cursor

Same as POST /api/hotels/results/poll but resumes strictly after the offer identified by the given cursor token, so polls never return duplicates and never skip entries across disconnects.

Authorizations:
(ApiKeyAuthAccessTokenAuth)
path Parameters
cursor
required
string [ 8 .. 32 ] characters ^[0-9A-Za-z]+$
Example: Gk7Pq2mXwz

Opaque cursor token marking the last entry the client already consumed. It is the obfuscated form of the order-stream entry id (resume-only, and distinct from an offer's code); treat it as an opaque string. Results resume strictly after this cursor (no duplicates, no gaps). A token that cannot be decoded is rejected with H008.

Request Body schema: application/json
required

The original search-request body (so the server can re-derive the session ID) plus the read-time filters and streaming blocks.

required
object (HotelSearchTarget)

Search target. Exactly one of destinationId, hotelIds, or geo must be provided.

required
object
guestCountry
required
string = 2 characters
Enum: "IL" "US" "AE" "TR" "GB" "DE" "FR"

Guest nationality — ISO 3166-1 alpha-2

currency
required
string = 3 characters

Preferred currency — ISO 4217, canonicalized (trimmed, upper-cased, aliases resolved) before anything else reads it. On /search it must be one of the currencies the requesting account trades in (its configured account currencies; every known currency for an account with none configured), otherwise the search is refused with V019.

object

Internal/service key-value fields. Any JSON value is accepted.

timeout
integer [ 5 .. 300 ]
Default: 30

Maximum search duration in seconds

object

Post-hoc filters applied to already-fetched offers (no effect on what providers return). Both keys are include booleans, so only an explicit false narrows the results; the default (everything included) means omitting filters, sending {}, and sending {onRequest:true, payAtHotel:true} all behave identically — they return every offer. Known keys:

  • onRequest (boolean) — include on-request bookings; false returns only immediately-confirmed offers
  • payAtHotel (boolean) — include pay-at-hotel options; false returns only prepaid offers

Unknown filter keys are ignored. For example, stars is reserved for a future hotel-metadata filter but currently has no effect.

Changing the filter requires restarting from the beginning (call /poll or /stream with no cursor). A cursor is bound to the filter it was issued under; resuming it with a different filter is rejected with H009, because the entries before the cursor were already delivered under the previous filter and would otherwise interleave two filter sets.

On /poll, a filtered batch may return fewer than resultsPerPush offers — even zero — while hasMore is still true: the offers that matched lie further down the stream. Keep polling with the returned cursor; it always advances, so nothing is rescanned.

On /stream, long filtered-out stretches also emit cursor-advancing empty offers frames after a bounded scan, then the stream immediately continues looking for later matching offers.

object

Controls how results are delivered. pushInterval is stream-only; resultsPerPush acts as batch size on stream and as limit on poll.

Responses

Request samples

Content type
application/json
{
  • "search": {
    },
  • "stay": {
    },
  • "guestCountry": "IL",
  • "currency": "USD",
  • "timeout": 30,
  • "filters": {
    },
  • "streaming": {
    }
}

Response samples

Content type
application/json
{
  • "meta": {
    },
  • "status": "working",
  • "data": {
    }
}

Stream search results as SSE

Opens a Server-Sent-Events (text/event-stream) stream of session/offers/done frames. The only deviation from textbook SSE is that this is a POST (so it carries the auth headers and the search body); the response is otherwise canonical SSE, and each offers/done frame carries the resume cursor in the SSE id: line. A batch is flushed whenever streaming.resultsPerPush offers accumulate or streaming.pushInterval seconds elapse — whichever happens first. When the search is idle the server emits empty offers frames as keepalives so dead clients surface within seconds.

Authorizations:
(ApiKeyAuthAccessTokenAuth)
Request Body schema: application/json
required

The original search-request body (so the server can re-derive the session ID) plus the read-time filters and streaming blocks.

required
object (HotelSearchTarget)

Search target. Exactly one of destinationId, hotelIds, or geo must be provided.

required
object
guestCountry
required
string = 2 characters
Enum: "IL" "US" "AE" "TR" "GB" "DE" "FR"

Guest nationality — ISO 3166-1 alpha-2

currency
required
string = 3 characters

Preferred currency — ISO 4217, canonicalized (trimmed, upper-cased, aliases resolved) before anything else reads it. On /search it must be one of the currencies the requesting account trades in (its configured account currencies; every known currency for an account with none configured), otherwise the search is refused with V019.

object

Internal/service key-value fields. Any JSON value is accepted.

timeout
integer [ 5 .. 300 ]
Default: 30

Maximum search duration in seconds

object

Post-hoc filters applied to already-fetched offers (no effect on what providers return). Both keys are include booleans, so only an explicit false narrows the results; the default (everything included) means omitting filters, sending {}, and sending {onRequest:true, payAtHotel:true} all behave identically — they return every offer. Known keys:

  • onRequest (boolean) — include on-request bookings; false returns only immediately-confirmed offers
  • payAtHotel (boolean) — include pay-at-hotel options; false returns only prepaid offers

Unknown filter keys are ignored. For example, stars is reserved for a future hotel-metadata filter but currently has no effect.

Changing the filter requires restarting from the beginning (call /poll or /stream with no cursor). A cursor is bound to the filter it was issued under; resuming it with a different filter is rejected with H009, because the entries before the cursor were already delivered under the previous filter and would otherwise interleave two filter sets.

On /poll, a filtered batch may return fewer than resultsPerPush offers — even zero — while hasMore is still true: the offers that matched lie further down the stream. Keep polling with the returned cursor; it always advances, so nothing is rescanned.

On /stream, long filtered-out stretches also emit cursor-advancing empty offers frames after a bounded scan, then the stream immediately continues looking for later matching offers.

object

Controls how results are delivered. pushInterval is stream-only; resultsPerPush acts as batch size on stream and as limit on poll.

Responses

Request samples

Content type
application/json
{
  • "search": {
    },
  • "stay": {
    },
  • "guestCountry": "IL",
  • "currency": "USD",
  • "timeout": 30,
  • "filters": {
    },
  • "streaming": {
    }
}

Response samples

Content type
text/event-stream
event: session
data: {"event":"session","session":"h2:1:d123:2026-06-21:2026-06-28:IL:2+5,9|1:USD","expiresAt":"2026-06-21T21:45:00Z","status":"working"}

id: Gk7Pq2mXwz
event: offers
data: {"event":"offers","cursor":"Gk7Pq2mXwz","status":"working","progress":42,"offers":[{"code":"4Zo386UWxlL3SADqSHZuAV","hotelId":215134,"pricing":{"total":{"amount":523.61,"currency":"EUR"},"taxes":[{"type":"VAT","included":true,"percent":10},{"type":"City Tax","included":false,"amount":6.00,"currency":"EUR"}]},"conditions":{"confirmation":"immediate","payment":"pre","packageRate":true},"providerID":6192,"cancellation":{"policy":"NR","frames":[{"from":"2026-06-07T23:59:00+02:00","penalty":100}]},"board":{"code":"DB","name":"BED AND BREAKFAST"},"promotions":[{"name":"Exclusive discount","percent":9},{"name":"3rd night half price"}],"rooms":[{"name":"Family Room","type":"room","category":"family","bedding":"","id":0},{"name":"Single Room","type":"room","category":"","bedding":"single","id":1}]}]}

id: Lp3Zr8nYtd
event: done
data: {"event":"done","cursor":"Lp3Zr8nYtd","status":"done","progress":100,"total":2847}

Resume an SSE stream from a cursor

Same as POST /api/hotels/results/stream but resumes strictly after the offer identified by the given cursor token. Use this when a streaming connection was interrupted — the client passes back the cursor (the SSE id: / the cursor field of the last frame it processed) and continues without dupes or gaps.

Authorizations:
(ApiKeyAuthAccessTokenAuth)
path Parameters
cursor
required
string [ 8 .. 32 ] characters ^[0-9A-Za-z]+$
Example: Gk7Pq2mXwz

Opaque cursor token marking the last entry the client already consumed. It is the obfuscated form of the order-stream entry id (resume-only, and distinct from an offer's code); treat it as an opaque string. Results resume strictly after this cursor (no duplicates, no gaps). A token that cannot be decoded is rejected with H008.

Request Body schema: application/json
required

The original search-request body (so the server can re-derive the session ID) plus the read-time filters and streaming blocks.

required
object (HotelSearchTarget)

Search target. Exactly one of destinationId, hotelIds, or geo must be provided.

required
object
guestCountry
required
string = 2 characters
Enum: "IL" "US" "AE" "TR" "GB" "DE" "FR"

Guest nationality — ISO 3166-1 alpha-2

currency
required
string = 3 characters

Preferred currency — ISO 4217, canonicalized (trimmed, upper-cased, aliases resolved) before anything else reads it. On /search it must be one of the currencies the requesting account trades in (its configured account currencies; every known currency for an account with none configured), otherwise the search is refused with V019.

object

Internal/service key-value fields. Any JSON value is accepted.

timeout
integer [ 5 .. 300 ]
Default: 30

Maximum search duration in seconds

object

Post-hoc filters applied to already-fetched offers (no effect on what providers return). Both keys are include booleans, so only an explicit false narrows the results; the default (everything included) means omitting filters, sending {}, and sending {onRequest:true, payAtHotel:true} all behave identically — they return every offer. Known keys:

  • onRequest (boolean) — include on-request bookings; false returns only immediately-confirmed offers
  • payAtHotel (boolean) — include pay-at-hotel options; false returns only prepaid offers

Unknown filter keys are ignored. For example, stars is reserved for a future hotel-metadata filter but currently has no effect.

Changing the filter requires restarting from the beginning (call /poll or /stream with no cursor). A cursor is bound to the filter it was issued under; resuming it with a different filter is rejected with H009, because the entries before the cursor were already delivered under the previous filter and would otherwise interleave two filter sets.

On /poll, a filtered batch may return fewer than resultsPerPush offers — even zero — while hasMore is still true: the offers that matched lie further down the stream. Keep polling with the returned cursor; it always advances, so nothing is rescanned.

On /stream, long filtered-out stretches also emit cursor-advancing empty offers frames after a bounded scan, then the stream immediately continues looking for later matching offers.

object

Controls how results are delivered. pushInterval is stream-only; resultsPerPush acts as batch size on stream and as limit on poll.

Responses

Request samples

Content type
application/json
{
  • "search": {
    },
  • "stay": {
    },
  • "guestCountry": "IL",
  • "currency": "USD",
  • "timeout": 30,
  • "filters": {
    },
  • "streaming": {
    }
}

Response samples

Content type
text/event-stream
event: session
data: {"event":"session","session":"h2:1:d123:2026-06-21:2026-06-28:IL:2+5,9|1:USD","expiresAt":"2026-06-21T21:45:00Z","status":"working"}

id: Gk7Pq2mXwz
event: offers
data: {"event":"offers","cursor":"Gk7Pq2mXwz","status":"working","progress":42,"offers":[{"code":"4Zo386UWxlL3SADqSHZuAV","hotelId":215134,"pricing":{"total":{"amount":523.61,"currency":"EUR"},"taxes":[{"type":"VAT","included":true,"percent":10},{"type":"City Tax","included":false,"amount":6.00,"currency":"EUR"}]},"conditions":{"confirmation":"immediate","payment":"pre","packageRate":true},"providerID":6192,"cancellation":{"policy":"NR","frames":[{"from":"2026-06-07T23:59:00+02:00","penalty":100}]},"board":{"code":"DB","name":"BED AND BREAKFAST"},"promotions":[{"name":"Exclusive discount","percent":9},{"name":"3rd night half price"}],"rooms":[{"name":"Family Room","type":"room","category":"family","bedding":"","id":0},{"name":"Single Room","type":"room","category":"","bedding":"single","id":1}]}]}

id: Lp3Zr8nYtd
event: done
data: {"event":"done","cursor":"Lp3Zr8nYtd","status":"done","progress":100,"total":2847}

Resolve the cancellation policy of one offer

Fills in a cancellation policy the search left unknown (policy: "NA") by asking the owning provider, synchronously. For a provider that already returned the policy during search — or has no separate service for it — this is a no-op and the stored offer comes back as it was.

The body is the same search request sent to /search (the session ID is re-derived from it) and access is bound to the account that created the session. The result is not written back to the cache, so the stored offer keeps its code and stays exactly as delivered.

It checks no price: the offer comes back at the price it was delivered at. A price that moved since the search is reported by /check-availability/{code} and /pre-book/{code}.

Authorizations:
(ApiKeyAuthAccessTokenAuth)
path Parameters
code
required
string^[0-9A-Za-z]+$
Example: 4Zo386UWxlL3SADqSHZuAV

The offer's code as delivered by poll/stream — a content-derived hash and the bookable identifier. It is not a cursor and is used verbatim (no decoding). An unknown, expired, or superseded code is rejected with H010.

Request Body schema: application/json
required

The original search-request body, so the server can re-derive the session ID the offer belongs to. Read-time filters/streaming blocks have no meaning here and are ignored.

required
object (HotelSearchTarget)

Search target. Exactly one of destinationId, hotelIds, or geo must be provided.

required
object
guestCountry
required
string = 2 characters
Enum: "IL" "US" "AE" "TR" "GB" "DE" "FR"

Guest nationality — ISO 3166-1 alpha-2

currency
required
string = 3 characters

Preferred currency — ISO 4217, canonicalized (trimmed, upper-cased, aliases resolved) before anything else reads it. On /search it must be one of the currencies the requesting account trades in (its configured account currencies; every known currency for an account with none configured), otherwise the search is refused with V019.

object

Internal/service key-value fields. Any JSON value is accepted.

timeout
integer [ 5 .. 300 ]
Default: 30

Maximum search duration in seconds

Responses

Request samples

Content type
application/json
{
  • "search": {
    },
  • "stay": {
    },
  • "guestCountry": "IL",
  • "currency": "USD",
  • "timeout": 30
}

Response samples

Content type
application/json
{
  • "meta": {
    },
  • "status": "done",
  • "data": {
    }
}

Re-check one offer against the provider's live source

Confirms that an offer is still bookable as delivered. Resellers commonly serve search results from their own cache, so a room may already be sold — or repriced by a dynamic rate — by the time the user picks it.

Body and access rules are identical to /cancellation-policy/{code}. Three outcomes:

  • Confirmed — 200 with the offer and no advisories.
  • Changed — 200 with the current offer under a new code, plus warnings and/or notices describing each difference (the CH codes of Notice: price and fee, cancellation policy, board, room name, confirmation, charges at the property, payment terms). status stays done: a change is not a failure. The superseded code is removed, so book the code returned here.
  • Gone — 409 with error H013. The offer is dropped from the session; a repeat request for that code returns H010.

Any other failure — 502 with error H012, or 422 with H017 when the answer cannot be priced — also drops the offer: one we could not confirm is not bookable, and the reason does not change that. Start a new /search to obtain a confirmable offer.

The prices compared are your account's (see Pricing), so a pricing rule edited or a rate moved since the search raises CH001 too, and CH008 for a pay-at-hotel offer's fee.

A provider that does not implement the operation returns the offer as it was — priced afresh like any answer — so the endpoint is always safe to call.

Authorizations:
(ApiKeyAuthAccessTokenAuth)
path Parameters
code
required
string^[0-9A-Za-z]+$
Example: 4Zo386UWxlL3SADqSHZuAV

The offer's code as delivered by poll/stream — a content-derived hash and the bookable identifier. It is not a cursor and is used verbatim (no decoding). An unknown, expired, or superseded code is rejected with H010.

Request Body schema: application/json
required

The original search-request body, so the server can re-derive the session ID the offer belongs to. Read-time filters/streaming blocks have no meaning here and are ignored.

required
object (HotelSearchTarget)

Search target. Exactly one of destinationId, hotelIds, or geo must be provided.

required
object
guestCountry
required
string = 2 characters
Enum: "IL" "US" "AE" "TR" "GB" "DE" "FR"

Guest nationality — ISO 3166-1 alpha-2

currency
required
string = 3 characters

Preferred currency — ISO 4217, canonicalized (trimmed, upper-cased, aliases resolved) before anything else reads it. On /search it must be one of the currencies the requesting account trades in (its configured account currencies; every known currency for an account with none configured), otherwise the search is refused with V019.

object

Internal/service key-value fields. Any JSON value is accepted.

timeout
integer [ 5 .. 300 ]
Default: 30

Maximum search duration in seconds

Responses

Request samples

Content type
application/json
{
  • "search": {
    },
  • "stay": {
    },
  • "guestCountry": "IL",
  • "currency": "USD",
  • "timeout": 30
}

Response samples

Content type
application/json
{
  • "meta": {
    },
  • "status": "done",
  • "warnings": [
    ],
  • "notices": [
    ],
  • "data": {
    }
}

Run the provider's pre-book step for one offer

The pre-book / pre-reservation step, taken immediately before booking. Behaviourally identical to /check-availability/{code} — same body, same outcomes, same advisories — and differs only in which provider operation is dispatched; for most providers both map onto the same upstream call.

Use it as the final confirmation in a booking flow: it revalidates price and conditions, and it lets the provider return a pre-book reference that the booking step will use (kept server-side, never delivered).

Unlike /check-availability/{code} it also delivers pricing.flow — how the price was built, account by account — and, when your own account's pricing rules could not be applied to the offer (your account sold at its cost), the PR007 warning.

Authorizations:
(ApiKeyAuthAccessTokenAuth)
path Parameters
code
required
string^[0-9A-Za-z]+$
Example: 4Zo386UWxlL3SADqSHZuAV

The offer's code as delivered by poll/stream — a content-derived hash and the bookable identifier. It is not a cursor and is used verbatim (no decoding). An unknown, expired, or superseded code is rejected with H010.

Request Body schema: application/json
required

The original search-request body, so the server can re-derive the session ID the offer belongs to. Read-time filters/streaming blocks have no meaning here and are ignored.

required
object (HotelSearchTarget)

Search target. Exactly one of destinationId, hotelIds, or geo must be provided.

required
object
guestCountry
required
string = 2 characters
Enum: "IL" "US" "AE" "TR" "GB" "DE" "FR"

Guest nationality — ISO 3166-1 alpha-2

currency
required
string = 3 characters

Preferred currency — ISO 4217, canonicalized (trimmed, upper-cased, aliases resolved) before anything else reads it. On /search it must be one of the currencies the requesting account trades in (its configured account currencies; every known currency for an account with none configured), otherwise the search is refused with V019.

object

Internal/service key-value fields. Any JSON value is accepted.

timeout
integer [ 5 .. 300 ]
Default: 30

Maximum search duration in seconds

Responses

Request samples

Content type
application/json
{
  • "search": {
    },
  • "stay": {
    },
  • "guestCountry": "IL",
  • "currency": "USD",
  • "timeout": 30
}

Response samples

Content type
application/json
{
  • "meta": {
    },
  • "status": "done",
  • "warnings": [
    ],
  • "notices": [
    ],
  • "data": {
    }
}

Create the reservation for one offer

Creates a real reservation with the provider that produced this offer.

It revalidates like /pre-book/{code} — same live call, same CH advisories when the provider's answer differs — and then goes one step further: an offer that reached the provider is consumed, booked or not, so one code can never produce two reservations and a repeat request returns H010.

A 200 does not necessarily mean a reservation exists, and it can mean that for two different reasons.

Some providers book in two upstream steps — they block the rate first, which is when their real price appears, and confirm it second. If the offer changed in between, the booking stops rather than buying something the customer did not agree to, and the reply carries the current offer under a new code instead of a booking.

Separately, a request can reach the provider and its answer be unreadable — a timeout, a 5xx, a truncated body. Whether a reservation was created is then genuinely unknown, and the reply says so: status: initialized, with no provider.reference. That is not a failure and not a reservation; it is an attempt, and resolving it is yours (see the Unresolved example).

So reading a 200 is two steps:

  1. data.status — a booking has one, an offer has no status field at all.
  2. data.provider.reference — present if and only if a reservation exists.

See the response examples.

Two things differ from the other single-offer routes:

  • The body carries personal data. stay.rooms[] is expanded from occupancy counts into named guests, plus an optional holder, credit card, client reference and free text for the hotel. The session ID is derived from the occupancy those guests imply, so a party that does not reproduce the search resolves to a session that does not exist (H006). Room order matters; child-age order does not. A provider that cannot represent a non-guest holder rejects one with V038 before the booking lock, hold or booking request; the offer remains usable. Provider-specific wire restrictions are also checked there: GoGlobal rejects non-ASCII guest names with V039, a guest last name not beginning with two letters with V041, and card details with V040, without contacting upstream. A provider that accepts only specific card networks rejects an unsupported request card with V045; a missing card required by its payment method is V046.
  • The result is not stored here. The booking is returned to the caller, who is the system of record for it — which is why provider.data is delivered (see the schema).

A provider that has no booking implementation answers 501 (H014) and the offer is kept: nothing was asked upstream, so nothing was learned about it.

Pricing. The searched path is booked while it is still current; only when it is gone is it replaced, by the cheapest current path to the same provider contract at Hold's net. The path is settled before Hold and fixed through Book. One pricing view serves the whole request: rules and rates stay fixed, while each fresh provider net is recalculated along that path. If a path, rule or rate change since the search changes your total or fee, it stops the gate (CH001 or CH008, respectively). A gate abort answers the offer at the held price it compared. The booking's offer and a gate-abort offer carry pricing.flow, and PR007 when your own account's rules could not be applied. A held offer that cannot be priced is 422 (H017): nothing is booked yet, so the hold is released and the offer dropped. After a successful booking there is no H017: the booked offer is priced from the provider's booking answer (a change from what you agreed to raises its CH advisory), and only if that answer cannot be priced is the booking answered with the total and fee the gate accepted — its taxes and every other term being what the booking confirmed.

A concurrent attempt on the same offer is refused with 409 (H015) rather than booking twice.

If the response never arrives, a reservation may still exist upstream, and the offer is dropped either way — it cannot be known whether the booking went through, so it must not be bookable again. How that is reported depends on the provider, and a client should handle both: one that distinguishes a delivered-but-unanswered request answers 200 with status: initialized and no provider.reference (resolving the attempt is then yours — ask the provider whether the booking exists, and record its real status and locator); one that does not answers 502 (H012). The reference you sent is the key either lookup takes, and on the initialized reply the provider's echo of it comes back in provider.data.

Authorizations:
(ApiKeyAuthAccessTokenAuth)
path Parameters
code
required
string^[0-9A-Za-z]+$
Example: 4Zo386UWxlL3SADqSHZuAV

The offer's code as delivered by poll/stream — a content-derived hash and the bookable identifier. It is not a cursor and is used verbatim (no decoding). An unknown, expired, or superseded code is rejected with H010.

Request Body schema: application/json
required

The search-request body with stay.rooms expanded from occupancy counts into named guests, plus the fields a reservation needs. The occupancy the guests imply must match the search exactly, or the session cannot be found.

required
object (HotelSearchTarget)

Search target. Exactly one of destinationId, hotelIds, or geo must be provided.

required
object
guestCountry
required
string = 2 characters
Enum: "IL" "US" "AE" "TR" "GB" "DE" "FR"
currency
required
string = 3 characters

ISO 4217 code, canonicalized (trimmed, upper-cased, aliases resolved) before anything else reads it. On /search it must be one of the currencies the requesting account trades in (its configured account currencies; every known currency for an account with none configured), otherwise the search is refused with V019.

object
timeout
integer [ 5 .. 300 ]
Default: 30
object (HotelHolder)

The person responsible for the booking and the one billing documents are issued to. They generally need not be a guest. A provider that cannot represent a non-guest holder rejects that request with V038 before any booking-side effect; GoGlobal requires the holder to match the first adult of the first room. When omitted entirely, the provider uses that first adult; when present, the name fields are required.

reference
string <= 20 characters ^[A-Za-z0-9._~-]+$

Required. The booking's identifier in your system. It is the key every later operation names this reservation by — it goes in the path of /api/hotels/cancel/{reference} and /api/hotels/status/{reference} — and, if the response to this request is lost in transit, the only key that can match the attempt to a reservation that may exist upstream.

It is passed to the provider untouched apart from trimming, and echoed back in data.reference exactly as sent. Nothing is ever generated in its place: a value of ours would be indistinguishable from one you chose while matching nothing you hold.

Two constraints follow from what it is used for, and both bind whatever generates it: at most 20 characters (the tightest provider limit on the field it is forwarded into), and only RFC 3986 unreserved characters (A-Z a-z 0-9 - . _ ~), so that it survives a URL path unchanged. Uniqueness is yours to own.

object (HotelCreditCard)

Payment card, needed only by rates whose provider requires card payment. Every field except name is required once the object is present. A selected provider integration which cannot accept card details rejects the request with V040 before the booking lock or any upstream operation.

The card is never stored, never returned, and never written to an application log — the access log masks it. One gap is known and documented: the outbound provider exchange log still records the request verbatim.

specialRequests
string <= 1024 characters

Free text you want passed to the hotel — the classic name for it, and deliberately not remarks. A Remark in a response is the opposite direction: information a provider insists we show the customer. Using one word for both is how a guest's own request ends up coming back looking like a notice from the hotel.

Responses

Request samples

Content type
application/json
{
  • "search": {
    },
  • "stay": {
    },
  • "guestCountry": "IL",
  • "currency": "USD",
  • "holder": {
    },
  • "reference": "AG-99123",
  • "specialRequests": "Late arrival, twin beds preferred"
}

Response samples

Content type
application/json
Example

data.provider.reference is present, so a reservation exists.

{
  • "meta": {
    },
  • "status": "done",
  • "warnings": [
    ],
  • "data": {
    }
}

Cancel a reservation

Cancels a reservation with the provider that made it. Unlike every other hotel operation this one takes no search body: a booking outlives the search session that produced it, and this service does not persist bookings — the caller is the system of record and hands back the provider node returned by /api/hotels/book/{code}.

The reservation is named twice, and the two are different identifiers. The path carries your reference, the one /book required; the body carries the provider's node, including their own locator. Both are needed because this service stores nothing: it cannot translate your reference into a vendor locator, so you supply the locator, and it echoes your reference back so the answer says which reservation it is about.

The operation is idempotent by contract, which is what makes retrying the correct response to a failure whose answer was lost.

Authorizations:
(ApiKeyAuthAccessTokenAuth)
path Parameters
reference
required
string <= 20 characters ^[A-Za-z0-9._~-]+$
Example: AG-99123

The booking's reference — the identifier you supplied to /api/hotels/book/{code} and received back in data.reference. It is used verbatim.

It is your reference, not the provider's. The vendor's own locator travels in the body, inside the provider node, and comes back under provider.reference.

Because it appears here in a URL path, it is restricted to RFC 3986 unreserved characters (A-Z a-z 0-9 - . _ ~) and to 20 characters — the same rules /book applied when it accepted the value, and reported with the same codes (V043, V033).

Request Body schema: application/json
required
required
object or object

The provider node from the booking response, echoed verbatim — the whole node, id and data included.

At least one of reference and data must be present, and neither is required on its own. reference is the usual identity, but a booking whose outcome could not be read is delivered as initialized with no reference at all — its absence is how this API states that no reservation has been evidenced — and a status check is exactly how such a booking is resolved, so requiring the field would make the one operation that can answer the question impossible to ask. What such a handle carries instead is data, where the provider kept whatever its own post-booking calls are addressed by (a RateHawk order is addressed by data.partner_order_id and by nothing else).

Neither is permission to drop the other: where a provider keeps its locator in data (a Restel reservation is addressed by data.localizador, which appears nowhere else), a node stripped of data names a reservation that provider cannot act on. The attempt then fails as an upstream error (H012) rather than as a validation error, because only the provider knows which of its own keys it needed.

Hand back what arrived, unchanged.

Responses

Request samples

Content type
application/json
{
  • "provider": {
    }
}

Response samples

Content type
application/json
{
  • "meta": {
    },
  • "status": "done",
  • "data": {
    }
}

Read a reservation's status

Reports what one reservation currently looks like at the provider that made it. It takes the same path parameter and body as /api/hotels/cancel/{reference} and identifies the reservation the same way — a booking outlives the search session that produced it, and this service does not persist bookings, so the caller is the system of record and hands back the provider node returned by /api/hotels/book/{code}.

Unlike every other hotel operation this one only reads. Nothing is consumed by a failure, no lock is taken, and there is no such thing as an ambiguous outcome: a failed status check leaves the reservation exactly as it was, so retrying is always safe.

Two vendor limits are worth knowing. H016 ("no such booking") is raised only where the vendor states absence by name — RateHawk does, both for an unknown identifier and for a booking it reports as never created, while Hotelbeds, Restel and GoGlobal publish no such status — so on those providers an unknown reference comes back as an ordinary H012, because a wrong 404 would send the caller hunting for a typo in a correct reference. A handle for a booking reported as initialized is answerable here for the same reason it is accepted at all: the provider knows from the handle that the attempt was never resolved, and asks the upstream call that can settle it. And a provider may be unable to distinguish a reservation cancelled with fees from a live one, in which case it reports confirmed; this endpoint is therefore not proof that a booking was never cancelled — the caller's own record is.

Authorizations:
(ApiKeyAuthAccessTokenAuth)
path Parameters
reference
required
string <= 20 characters ^[A-Za-z0-9._~-]+$
Example: AG-99123

The booking's reference — the identifier you supplied to /api/hotels/book/{code} and received back in data.reference. It is used verbatim.

It is your reference, not the provider's. The vendor's own locator travels in the body, inside the provider node, and comes back under provider.reference.

Because it appears here in a URL path, it is restricted to RFC 3986 unreserved characters (A-Z a-z 0-9 - . _ ~) and to 20 characters — the same rules /book applied when it accepted the value, and reported with the same codes (V043, V033).

Request Body schema: application/json
required
required
object or object

The provider node from the booking response, echoed verbatim — the whole node, id and data included.

At least one of reference and data must be present, and neither is required on its own. reference is the usual identity, but a booking whose outcome could not be read is delivered as initialized with no reference at all — its absence is how this API states that no reservation has been evidenced — and a status check is exactly how such a booking is resolved, so requiring the field would make the one operation that can answer the question impossible to ask. What such a handle carries instead is data, where the provider kept whatever its own post-booking calls are addressed by (a RateHawk order is addressed by data.partner_order_id and by nothing else).

Neither is permission to drop the other: where a provider keeps its locator in data (a Restel reservation is addressed by data.localizador, which appears nowhere else), a node stripped of data names a reservation that provider cannot act on. The attempt then fails as an upstream error (H012) rather than as a validation error, because only the provider knows which of its own keys it needed.

Hand back what arrived, unchanged.

Responses

Request samples

Content type
application/json
{
  • "provider": {
    }
}

Response samples

Content type
application/json
{
  • "meta": {
    },
  • "status": "done",
  • "data": {
    }
}

Static

Our own geography and hotel content: countries, destinations, hotels, images and timezones. A search returns hotel ids; these endpoints say what those ids mean, so a client can fetch the content once and keep it.

Everything here is a GET with no body. Cacheable successful responses carry an ETag (plus Last-Modified where the data has a modification time); the change feed and every error use Cache-Control: no-store.

List every country

A few hundred rows that change almost never — the endpoint most worth caching.

Authorizations:
(ApiKeyAuthAccessTokenAuth)

Responses

Response samples

Content type
application/json
{
  • "status": "done",
  • "data": [
    ]
}

List a country's destination ids

All ids in one response — fetch detail from /api/static/destinations/{ids}.

Authorizations:
(ApiKeyAuthAccessTokenAuth)
path Parameters
countryId
required
string^[A-Za-z]{2}$
Example: IT

ISO 3166-1 alpha-2 code, case-insensitive.

Responses

Response samples

Content type
application/json
{
  • "status": "done",
  • "data": [
    ]
}

Get destination detail

Only active destinations are returned, so an id that is unknown or retired is simply absent from the response rather than an error.

Authorizations:
(ApiKeyAuthAccessTokenAuth)
path Parameters
ids
required
string^[0-9]+(,[0-9]+)*$
Example: 12,24,51

Comma-separated positive integers. Every operation has an independent configured cap; all currently default to 100.

Ids are deduplicated and sorted server-side, so 15,12,15 and 12,15 are the same request and share one ETag.

Responses

Response samples

Content type
application/json
{
  • "status": "done",
  • "data": [
    ]
}

List the hotel ids of one or more destinations

Grouped by destination. surroundings holds hotels attached to a destination as nearby rather than inside it.

Every active relationship for the requested destinations is returned in one response.

Authorizations:
(ApiKeyAuthAccessTokenAuth)
path Parameters
ids
required
string^[0-9]+(,[0-9]+)*$
Example: 12,24,51

Comma-separated positive integers. Every operation has an independent configured cap; all currently default to 100.

Ids are deduplicated and sorted server-side, so 15,12,15 and 12,15 are the same request and share one ETag.

Responses

Response samples

Content type
application/json
{
  • "status": "done",
  • "data": [
    ]
}

Get hotel detail

Everything needed to render a property — name, address, stars, description, facilities, remark — plus mainImage, the cover.

The cover is here so a client painting a list of search results can show a picture per hotel without fetching the whole gallery. mainImage is absent when no cover is assigned or the assigned one has been withdrawn; it is never replaced by another picture from the gallery.

The full gallery lives at /api/static/hotels/{ids}/images.

These records are the only ones carrying a modification time, so this is the only endpoint that also sends Last-Modified.

Authorizations:
(ApiKeyAuthAccessTokenAuth)
path Parameters
ids
required
string^[0-9]+(,[0-9]+)*$
Example: 12,24,51

Comma-separated positive integers. Every operation has an independent configured cap; all currently default to 100.

Ids are deduplicated and sorted server-side, so 15,12,15 and 12,15 are the same request and share one ETag.

Responses

Response samples

Content type
application/json
{
  • "status": "done",
  • "data": [
    ]
}

Get hotel image galleries

Images in catalogue order, with the cover flagged main: true. The cover is a member of the gallery, not an alternative to it.

A hotel with no images is omitted from the response rather than returned empty.

Its id cap is configured independently from the other operations and currently defaults to 100. A hotel can carry over a hundred pictures, so this cap can be tightened without reducing the hotel-detail batch size.

Authorizations:
(ApiKeyAuthAccessTokenAuth)
path Parameters
ids
required
string^[0-9]+(,[0-9]+)*$
Example: 12,24,51

Comma-separated positive integers. Every operation has an independent configured cap; all currently default to 100.

Ids are deduplicated and sorted server-side, so 15,12,15 and 12,15 are the same request and share one ETag.

Responses

Response samples

Content type
application/json
{
  • "status": "done",
  • "data": [
    ]
}

List hotels changed since a timestamp

Refresh a local copy of the catalogue without pulling all of it. active contains currently active hotels to fetch from /api/static/hotels/{ids}; deactivated contains every changed hotel whose current status is not 1, so clients can remove or explain it.

The lower bound is inclusive (updated >= timestamp) so second-resolution timestamps cannot lose an update at the boundary. Safe duplicates are therefore possible. The scan is bounded by updated <= asOf, which freezes the result while its id-ordered pages are consumed.

Store the asOf from the response and use it as the next sync's timestamp. It is the moment the scan started, and it is carried inside the cursor so every page of one scan reports the same value. A hotel modified while the scan was running has an updated past that mark, so the next sync picks it up — nothing is lost even though pages are ordered by id.

This is the one endpoint that is never cached: every distinct timestamp is a distinct answer. It sends Cache-Control: no-store and no ETag.

The timestamp must be within the last 180 days and must not be in the future. Older clients must bootstrap a fresh catalogue copy.

Authorizations:
(ApiKeyAuthAccessTokenAuth)
path Parameters
timestamp
required
integer <int64> >= 0
Example: 1785595091

Unix seconds. Hotels modified at or after this instant are returned. Use the asOf from the previous sync; the inclusive boundary can repeat ids but cannot lose same-second updates. Must be within the configured 180-day lookback and not in the future.

query Parameters
limit
integer >= 1
Example: limit=100

Change-feed page size. Defaults to static.change_page_limit_default (100) and is clamped to static.change_page_limit_max (100).

after
string

The nextCursor from the previous page. Opaque — do not construct or parse it.

Responses

Response samples

Content type
application/json
{
  • "status": "done",
  • "data": {
    }
}

Resolve the timezones a set of hotels spans

Returns one group per distinct timezone, because a list of hotels can legitimately cover several. Hotels with no resolvable zone — unknown, inactive, or holding no coordinates — are reported in unresolved rather than dropped.

The answer is an IANA name and needs no dates: the name determines the offset for any instant, summer or winter.

Authorizations:
(ApiKeyAuthAccessTokenAuth)
path Parameters
ids
required
string^[0-9]+(,[0-9]+)*$
Example: 12,24,51

Comma-separated positive integers. Every operation has an independent configured cap; all currently default to 100.

Ids are deduplicated and sorted server-side, so 15,12,15 and 12,15 are the same request and share one ETag.

Responses

Response samples

Content type
application/json
Example
{
  • "status": "done",
  • "data": {
    }
}

Resolve the timezones of one or more destinations

One group per distinct zone across the destinations asked about; groups carry no hotelIds, because a destination names no hotel. Destinations that resolve to nothing are listed in unresolved.

Authorizations:
(ApiKeyAuthAccessTokenAuth)
path Parameters
ids
required
string^[0-9]+(,[0-9]+)*$
Example: 12,24,51

Comma-separated positive integers. Every operation has an independent configured cap; all currently default to 100.

Ids are deduplicated and sorted server-side, so 15,12,15 and 12,15 are the same request and share one ETag.

Responses

Response samples

Content type
application/json
Example
{
  • "status": "done",
  • "data": {
    }
}

Resolve the timezone at a point

One group, with no hotelIds. Ocean positions resolve to a nautical Etc/GMT±N zone, so in practice any valid coordinate has an answer; a point the resolver knows nothing about returns an empty groups list rather than a default.

Authorizations:
(ApiKeyAuthAccessTokenAuth)
path Parameters
lat
required
number <double> [ -90 .. 90 ]
Example: 41.9028
lon
required
number <double> [ -180 .. 180 ]
Example: 12.4964

Responses

Response samples

Content type
application/json
Example
{
  • "status": "done",
  • "data": {
    }
}