Hermeseus API

A travel-booking gateway that unifies flights, hotels, and activities behind one consistent JSON API. Built for high-throughput agency platforms with multi-source aggregation and USD-normalized pricing.

v2.0 β€” Stable REST / JSON Session-based auth USD-normalized Multi-source aggregation

Introduction #

Hermeseus is a unified travel-booking API that acts as a single entry point for three product families:

  • Air β€” aggregates multiple flight inventory sources behind a single endpoint. Region-specific fare and routing rules are handled transparently by the gateway.
  • Hotel β€” hotel availability and booking with rich property metadata. All foreign-currency rates are converted to USD before the response reaches you.
  • Activity β€” a comprehensive activity / tours / attractions catalog and booking flow under /api/Activity/v2/*, with money fields normalized to USD and full booking state managed by the gateway.

The public contract is stable and language-agnostic; clients only need an HTTP/JSON library.

Architecture #

Every booking lives in our local database with its own UniqueId. Upstream write operations are kept to an absolute minimum β€” the platform fulfils each booking through its own controlled process. This gives full control of pricing, cancellation, and refund policy without depending on upstream behavior.

LayerResponsibility
Flight aggregationRoutes a search to the appropriate inventory source, normalises output, and enforces regional routing rules transparently.
Hotel aggregationRoutes hotel availability between inventory sources and merges the responses into a single shape.
Booking engineOwns the entire booking state machine, balance holds/releases, and duplicate detection.
Balance ledgerAtomic hold / release / deduct / charge operations on office wallets.
Currency normaliserRecursively rewrites any money field to USD using cached FX rates.
Activity gatewayManages the activity catalogue, bookings, vouchers, and webhook polling.

Base URL & Environments #

The environment is selected by the base URL (host) you call β€” there is no longer a header or query flag to toggle it.

EnvironmentBase URLBehaviour
Productionhttps://api.hermeseus.com/apiReal providers, real wallet.
Sandboxhttps://demo.hermeseus.com/apiReal providers up to the booking commit; seeded test wallet (see Sandbox Mode).

All endpoints in this document are relative to the base URL. Every request and response is JSON. Responses are always shaped as { "Success": boolean, ...payload..., "Error": null|object }.

πŸ”‘
One environment per user. Each set of credentials is bound by the admin to exactly one environment. A production user calling the sandbox host (or vice-versa) is rejected with 403 (Err0101007). Use the base URL that matches your credentials.

Conventions #

Currency

All monetary fields visible to the client are denominated in USD. Any upstream amounts in other currencies are converted via cached FX rates before being returned.

FareSourceCode

Every priced itinerary is identified by an opaque FareSourceCode string. Treat it as a black box β€” pass it back to the gateway exactly as received in subsequent Revalidate / Book / Cancel calls. The gateway uses it internally to route the request to the correct inventory source.

Booking lifecycle (status_category / status_code)

10 / 10
Booked
Created. Awaiting OrderTicket.
20 / 20
TicketInProcess
Order placed. Awaiting issuance.
21 / 21
Ticketed
Ticket / hotel voucher issued. Funds deducted.
21 / 25
TicketCancelled
Refunded after ticketing.
30 / 30
Cancelled
Held funds released back to the office balance.
40 / 41
PaymentError
Insufficient office balance at book time.
40 / 42
DuplicateBooking
Same (user, FareSourceCode) detected in an active state.

Coded fields & enumerations

Several fields in flight responses are integer/string codes rather than human text. The tables below list every coded field and its possible values. These are identical across AirLowFareSearch and AirLowFareSearchIncremental β€” each itinerary is normalized the same way in both.

NonRefundableType β€” itinerary & per-segment

Derived from the fare's refund-before / refund-after-departure availability. 0 means β€œunknown” (the supplier did not provide refund terms) β€” it does not mean refundable.

ValueMeaningCondition
0Unknown / not statedNo refund terms supplied by the source.
1Non-refundableRefund not allowed before and after departure.
2Non-refundable after departureRefund after departure not allowed (before may be).
3Non-refundable before departureRefund before departure not allowed (after may be).

RefundMethod

ValueMeaning
0Unknown β€” no refund terms supplied.
1Online β€” refund is permitted (handled online).
2Non-refundable.

DirectionInd β€” trip direction

ValueMeaning
1One-way (a single OriginDestinationOption).
2Round-trip / multi-leg (more than one OriginDestinationOption).

CabinClassCode β€” per flight segment

ValueCabin
1Economy
2Premium Economy
3Business
4Premium Business
5First
6Premium First

PassengerType β€” in PtcFareBreakdown and travelers

ValueMeaning
1Adult (ADT)
2Child (CHD)
3Infant (INF)

FareType β€” in AirItineraryPricingInfo

ValueMeaning
1Published fare.
2Private / negotiated fare (the value returned by the aggregated low-cost / OTA sources).

SegmentRefundRules / Refund & Change flags

Inside SegmentRefundRules[*] (and each Legs[*]), the Refund and Change objects carry BeforeFlight / AfterFlight availability as a tri-state, plus an optional penalty:

ValueMeaning
trueAllowed.
falseNot allowed.
nullUnknown β€” the source did not state this rule.

At the leg level the shape is { "Available": true|false|null, "Penalty": { "currency_code": "USD", "value": 0 } | null }. A Penalty.value of 0 means a fee-free change/refund; null means no penalty information.

Boolean itinerary flags

Fields such as IsLowcost, IsCharter, IsReturn, IsPassportMandatory, IsPassportIssueDateMandatory, IsDestinationAddressMandatory, IsClosed, HasAmenities, IsSeatServiceMandatory and IsMealServiceMandatory are plain booleans (true/false).

Request β€” TravelPreference.CabinType

On the search request, CabinType accepts these integer codes:

ValueCabin requested
1Economy
2Premium Economy
3Business
4Premium Business (mapped to Business on sources without a separate cabin)
5First
6Premium First (mapped to First on sources without a separate cabin)

Pagination

Where supported, pagination uses query parameters page (1-based) and per_page (default 50, capped per-endpoint).

Sandbox Mode πŸ§ͺ

Sandbox is selected by calling the dedicated base URL https://demo.hermeseus.com/api β€” point your client's host at it; the request/response bodies are identical to production. (The previous X-Sandbox header / ?sandbox=1 query flag is deprecated and no longer the documented mechanism.)

In sandbox the pipeline behaves exactly like production right up to the moment a booking is committed:

  • Real provider data. Every search, revalidate, rules, baggage, availability, check-rate and activity catalogue call hits the real upstream providers. Responses are byte-for-byte the same shape as production (no synthetic generator), so what you test is what you ship.
  • Real local lifecycle. AirBook / HotelBook, status transitions, cancellation and refund all run end-to-end against your database.
  • Simulated commit. The three endpoints that would otherwise create a live, money-bearing booking with a provider are simulated β€” no real booking is ever created upstream. The response is generated from the structure of real logged responses, so it is indistinguishable in shape:
    • POST /Air/AirOrderTicket
    • POST /Hotel/HotelOrder
    • POST /Activity/v2/bookings
  • Separate wallet. The first sandbox call lazily creates an office named "<your-office> (sandbox)" with a seeded balance of USD 10,000. All holds / deducts / releases hit this sandbox wallet β€” real funds are never touched.
  • Real auth. Login flow, SessionId, role permissions, and sliding TTL behave identically.
πŸ§ͺ
Switch by host, not by flag. Your credentials are bound to one environment. Keep your sandbox test build pointed at demo.hermeseus.com and your production build at api.hermeseus.com; nothing else in the request changes.
curl -X POST https://demo.hermeseus.com/api/Air/AirLowFareSearch \
  -H "Content-Type: application/json" \
  -d '{"SessionId":"...","OriginDestinationInformations":[{"OriginLocationCode":"IST","DestinationLocationCode":"DXB","DepartureDateTime":"2026-06-12T00:00:00"}],"AdultCount":1,"TravelPreference":{"CabinType":1}}'

Authentication #

Hermeseus uses session-based authentication. Call CreateSession once, then include the returned SessionId in the JSON body of every subsequent request. Sessions are valid for 15 minutes and renew on every authenticated request (sliding TTL).

⚠️
Password derivation. The Password field must be the SHA-512 hash of your account UUID, uppercased: strtoupper(hash('sha512', $userUuid)). Plain passwords are not accepted.
POST /Authenticate/CreateSession

Create a new API session. No authentication required.

Request

{
  "OfficeId": "OFFICE_NAME_OR_ID",
  "UserName": "your_username",
  "Password": "A1B2C3...HASHED_SHA512_UPPERCASE"
}

Response β€” 200 OK

{
  "Success": true,
  "SessionId": "7f3c81d2-0e8d-4f9a-9c12-4d5e2b9a8f01",
  "Error": null
}

Response β€” Error

{
  "Success": false,
  "SessionId": null,
  "Error": {
    "Id": "Err0101003",
    "Message": "Invalid credentials. Username or password is incorrect."
  }
}
POST /Authenticate/EndSession

Terminate the current session immediately. After this call the SessionId is invalid.

Request

{ "SessionId": "7f3c81d2-0e8d-4f9a-9c12-4d5e2b9a8f01" }

Response

{ "Success": true, "Error": null }

Error Codes #

CodeMeaning
Err0101001Invalid or missing SessionId β€” recreate the session.
Err0101002Session expired (idle > 15 min) β€” recreate the session.
Err0101003Wrong credentials.
Err0101004User has no access to the specified office.
Err0101005Account is not verified.
Err0101006Permission denied for this endpoint.
Err0102004Revalidation required before booking (cache miss).
Err0103003Booking not found.
Err0103004Booking not cancellable in current state.
Err0103005Booking already cancelled.
Err0103006Booking not in ticketable / confirmable state.
Err0103007Only the booking owner or an admin can perform this action.
Err0104002Hotel booking not found.
Err0106001Missing required field in request body.
Err0106002At least one of UniqueId / ClientUniqueId must be provided.

Air β€” End-to-end booking guide #

The complete flight journey, from a route the traveller enters to an issued ticket. Each step names the exact endpoint, its request parameters (taken directly from the platform's own validation rules β€” not guesswork), what comes back, and the one value you carry forward. Every Air endpoint requires the SessionId from Authenticate/CreateSession in the JSON body. All fares are USD.

🧭
The path: AirLowFareSearch β†’ (optional AirRules/AirBaggages) β†’ AirRevalidate β†’ AirBook β†’ AirOrderTicket β†’ AirBookingData, with AirCancel / AirRefund for undoing. The value you carry changes at each stage: FareSourceCode (search) β†’ UniqueId (book).

1 β€” Search for itineraries

Send the passenger mix, the cabin, and one OriginDestinationInformations entry per leg (one for one-way, two for round-trip, more for multi-city). Use AirLowFareSearch for a single JSON response, or its streaming twin AirLowFareSearchIncremental (Server-Sent Events) for progressive results.

POST /api/Air/AirLowFareSearch
{
  "SessionId": "…",
  "AdultCount": 1,
  "ChildCount": 0,
  "InfantCount": 0,
  "PricingSourceType": "All",
  "RequestOption": "All",
  "TravelPreference": { "CabinType": 3, "AirTripType": "OneWay" },
  "OriginDestinationInformations": [
    { "OriginLocationCode": "IST", "DestinationLocationCode": "DXB", "DepartureDateTime": "2026-10-12T00:00:00" }
  ]
}
ParameterTypeReq.Description & allowed values
SessionIdstringyesSession from CreateSession.
AdultCountinteger β‰₯1yesAdult passengers.
ChildCountinteger β‰₯0yesChild passengers.
InfantCountinteger β‰₯0yesInfant passengers (no seat).
PricingSourceTypestringyesPricing scope, e.g. All.
RequestOptionstringyesResult-set option, e.g. All.
NumberOfResultsinteger β‰₯0β€”Basket size. Omit / 0 / 100 = default 100; other values scale the mix; max 5000.
Providerstringβ€”Optional inventory-source hint. Omit to let the gateway aggregate all sources and pick the best; unknown values fall back to the configured default.
TravelPreference.CabinTypeintegerβ€”Cabin: 1 Economy, 2 Premium Economy, 3 Business, 4 Premium Business, 5 First, 6 Premium First. Default 1. See Conventions.
TravelPreference.AirTripTypestringβ€”e.g. OneWay / Return. Defaults from the number of legs (one leg β†’ one-way, more β†’ return).
TravelPreference.MaxStopsQuantitystring/intβ€”Cap on stops, e.g. All, 0 (non-stop).
TravelPreference.VendorPreferenceCodesarray<string>β€”Restrict to these airline codes.
TravelPreference.VendorExcludeCodesarray<string>β€”Exclude these airline codes.
OriginDestinationInformationsarray (β‰₯1)yesOne entry per leg.
…[].OriginLocationCodestringyesDeparture IATA code (e.g. IST).
…[].DestinationLocationCodestringyesArrival IATA code (e.g. DXB).
…[].DepartureDateTimestringyesDeparture date/time, e.g. 2026-10-12T00:00:00.
…[].OriginType / DestinationTypestringβ€”Optional point qualifier (airport / city).

The response is { Success, SearchId, PricedItineraries[], Error }. Each itinerary has a FareSourceCode, the OriginDestinationOptions[].FlightSegments[] (with per-segment CabinClassCode in the same integer enum as the request), and AirItineraryPricingInfo.ItinTotalFare. Full field list in Search & Pricing. Carry the FareSourceCode of the chosen itinerary β€” and note the SearchId for AirSearchData.

2 β€” (Optional) Fare rules & baggage

Before committing, show the traveller the fare conditions and baggage allowance for the selected FareSourceCode.

EndpointBodyReturns
POST /Air/AirRulesFareSourceCode (or UniqueId for a booked fare){ Success, FareType, FareRules[], Error }
POST /Air/AirBaggagesFareSourceCode{ Success, BaggageInfoes[]{Departure,Arrival,FlightNo,Baggage}, Services[], Error }

3 β€” Revalidate the fare

At the moment the traveller confirms, re-price the itinerary to confirm it is still available at the quoted fare.

POST /api/Air/AirRevalidate
{ "SessionId": "…", "FareSourceCode": "…from step 1…" }
ParameterTypeReq.Description
SessionIdstringyesSession from CreateSession.
FareSourceCodestringyesThe itinerary code from step 1.

Returns { Success, PricedItinerary, Services, MealTypeServices, SeatServices, Error }. If the fare expired or is gone, Success is false with Error.Id Err0102003/Err0102005 β€” go back to search. Otherwise proceed to book with the same FareSourceCode.

4 β€” Book (reserve & hold)

Reserve the itinerary with the passengers' details. This creates the reservation and a ticketing deadline; the ticket is not yet issued.

POST /api/Air/AirBook
{
  "SessionId": "…",
  "FareSourceCode": "…from step 3…",
  "ClientUniqueId": "your-own-ref-0001",
  "TravelerInfo": {
    "PhoneNumber": "+441234567890",
    "Email": "traveller@example.com",
    "AirTravelers": [
      {
        "PassengerType": 1,
        "Gender": 0,
        "DateOfBirth": "1990-05-14",
        "PassengerName": { "PassengerFirstName": "Jane", "PassengerLastName": "Doe" }
      }
    ]
  }
}
ParameterTypeReq.Description & allowed values
SessionIdstringyesSession from CreateSession.
FareSourceCodestringyesThe revalidated code from step 3.
TravelerInfo.PhoneNumberstringyesLead contact phone.
TravelerInfo.EmailemailyesLead contact email.
TravelerInfo.AirTravelersarray (β‰₯1)yesOne entry per passenger; the count must match the search pax mix.
…AirTravelers[].PassengerTypeintegeryesPassenger type code (adult / child / infant), forwarded to the source.
…AirTravelers[].GenderintegeryesGender code: 0 Male, 1 Female.
…AirTravelers[].DateOfBirthstring (date)yesPassenger date of birth, YYYY-MM-DD.
…AirTravelers[].PassengerName.PassengerFirstNamestringyesGiven name as on the passport.
…AirTravelers[].PassengerName.PassengerLastNamestringyesSurname as on the passport.
ClientUniqueIdstringβ€”Your own reference; later usable in AirBookingData instead of UniqueId.
MarkupForAdult / MarkupForChild / MarkupForInfantnumericβ€”Per-passenger markup added to the fare. Default 0.

Returns { Success, UniqueId, TktTimeLimit, TktTimeLimitTimeZone, Category: 10, Status: 10, PriceChange, WarningMessage, Error }. Keep the UniqueId β€” it drives every remaining step β€” and the TktTimeLimit: the reservation is held only until then.

⏳
Ticketing deadline. The reservation (Status 10) holds a seat but is not a ticket. Issue the ticket before TktTimeLimit or the booking is released.

5 β€” Issue the ticket

Once the traveller has paid you, issue the ticket. This finalizes the booking with the airline.

POST /api/Air/AirOrderTicket
{ "SessionId": "…", "UniqueId": "…from step 4…" }
ParameterTypeReq.Description
SessionIdstringyesSession from CreateSession.
UniqueIdstringyesThe booking id from AirBook (step 4).

Returns { Success, Category: 20, Status: 20, Error } β€” Status 20 means ticketed. See the status_category / status_code table for the full lifecycle.

6 β€” Track the order

Fetch the full booking β€” itinerary, passengers, pricing, e-tickets, status β€” at any time.

POST /api/Air/AirBookingData
{ "SessionId": "…", "UniqueId": "…from step 4…" }
ParameterTypeReq.Description
SessionIdstringyesSession from CreateSession.
UniqueIdstringone ofThe booking id from AirBook.
ClientUniqueIdstringone ofYour own reference from AirBook. Provide at least one of UniqueId / ClientUniqueId.

Full response shape (itinerary, pricing, passengers, services) in AirBookingData.

7 β€” Cancel or refund

EndpointBody parametersUse when
POST /Air/AirCancelSessionId, UniqueId (both required)Void a reservation before the ticket is issued. Returns cancellation policies, a new PaymentDeadline, and CanExtendPaymentDeadline.
POST /Air/AirRefundDisplayUniqueId (required)Preview refund terms after ticketing β€” per-ticket PenaltyAmount, IsNonRefundable, IsRefunded.
POST /Air/AirRefundUniqueId (required); optional RefundType (int, default 1), EticketNumbers (array), RefundPaymentMode (int)Execute the refund for an issued ticket.

Air β€” Price Calendar #

POST /Air/AirPriceCalendar air_low_fare_search

The cheapest fare for each day on a route β€” a price calendar. Give an origin, a destination and the trip type (OneWay / RoundTrip); you get one entry per available date. This endpoint is provider-agnostic: there is no Provider field and the data always comes from the same aggregated fare source. All prices are in USD by default.

Request

{
  "SessionId": "...",
  "Origin": "DXB",
  "Destination": "IST",
  "TripType": "RoundTrip",
  "DepartDate": "2026-11",
  "ReturnDate": "2026-12",
  "CalendarType": "departure_date",
  "Currency": "USD"
}
fieldrequirednotes
Origin / Destinationyes3-letter IATA city/airport code.
TripTypenoOneWay or RoundTrip. Default RoundTrip.
DepartDateyesyyyy-mm (whole month) or yyyy-mm-dd.
ReturnDateround-tripRequired when TripType=RoundTrip; ignored for one-way.
CalendarTypenodeparture_date (default) or return_date β€” which date the calendar iterates over.
CurrencynoISO code. Default USD.

Response

{
  "Success": true,
  "Origin": "DXB", "Destination": "IST", "TripType": "RoundTrip",
  "CalendarType": "departure_date", "Currency": "USD",
  "DaysCount": 50, "MinPrice": 370, "MaxPrice": 608,
  "Cheapest": { "Date": "2026-11-27", "Price": 370, ... },
  "Days": [
    {
      "Date": "2026-11-01", "Price": 412, "Currency": "USD",
      "Transfers": 1, "Airline": "PS", "FlightNumber": 576,
      "DepartureAt": "2026-11-01T06:35:00+03:00", "ReturnAt": "2026-12-01T13:30:00+02:00",
      "ExpiresAt": "2026-08-10T12:34:14Z", "Origin": "DXB", "Destination": "IST"
    }, ...
  ],
  "Error": null
}
πŸ—“οΈ
Days is sorted by date. Price is the cheapest fare found for that day and Transfers its stop count; ExpiresAt is when the cached price may no longer be valid. Use it to drive a month view, then run AirLowFareSearch for a live, bookable quote on the chosen day.
POST /Air/AirRevalidate air_revalidate

Re-check price and availability for a specific itinerary before calling AirBook. Required: the booking flow rejects book attempts that have not been revalidated within the current session.

Request

{
  "SessionId": "7f3c81d2-...",
  "FareSourceCode": "dHA6ODc0NTE6ZmwxMjM0OjMyNw=="
}

Response

{
  "Success": true,
  "PricedItinerary": { /* same shape as in AirLowFareSearch[*] */ },
  "PriceChange": false,
  "Error": null
}
POST /Air/AirRules air_rules

Fare rules (penalties, changes, refundability) for an itinerary. Accepts either FareSourceCode (pre-booking) or UniqueId (post-booking).

Request

{ "SessionId": "...", "FareSourceCode": "..." }

Air β€” Booking #

POST /Air/AirBook air_book

Reserves the previously revalidated itinerary. Funds equal to TotalFare are held on your office wallet. The reservation auto-cancels after 10 minutes if the ticket is not issued via AirOrderTicket.

Request

{
  "SessionId": "7f3c81d2-...",
  "FareSourceCode": "dHA6ODc0NTE6ZmwxMjM0OjMyNw==",
  "ClientUniqueId": "order_29013",
  "TravelerInfo": {
    "PhoneNumber": "+971501234567",
    "Email": "customer@example.com",
    "AirTravelers": [
      {
        "PassengerType": "Adt",
        "Gender": 0,            // 0=Male 1=Female
        "DateOfBirth": "1992-04-18",
        "Nationality": "AE",
        "PassengerName": {
          "PassengerTitle": "Mr",
          "PassengerFirstName": "John",
          "PassengerLastName": "Smith"
        },
        "Passport": {
          "PassportNumber": "A12345678",
          "ExpiryDate": "2030-12-01",
          "IssueDate": "2020-12-01",
          "Country": "AE"
        }
      }
    ]
  }
}

Response β€” Reserved

{
  "Success": true,
  "UniqueId": "4d2e91c8-3b71-4a55-8e9c-5f1a2d3b6c80",
  "TktTimeLimit": "2026-05-20T10:23:01+00:00",
  "TktTimeLimitTimeZone": "UTC",
  "Category": 10,
  "Status": 10,
  "PriceChange": false,
  "WarningMessage": null,
  "Error": null
}

Response β€” Insufficient balance

{ "Success": true, "Category": 40, "Status": 41, "WarningMessage": "Insufficient balance.", "Error": null }
POST /Air/AirOrderTicket air_order_ticket

Transitions the booking from 10/10 to 20/20 (TicketInProcess), queuing it for ticket issuance.

Request

{ "SessionId": "...", "UniqueId": "4d2e91c8-..." }

Response

{ "Success": true, "Category": 20, "Status": 20, "Error": null }
POST /Air/AirCancel air_cancel

Cancel a booking that has not been ticketed yet. Released funds are returned to the office's available balance immediately. Ticketed bookings (status 21) require AirRefund instead.

Request

{ "SessionId": "...", "UniqueId": "4d2e91c8-..." }
POST /Air/AirRefundDisplay air_refund_display

Returns the list of e-tickets eligible for refund and any per-ticket penalty.

POST /Air/AirRefund air_refund

Issues a refund. Booking moves to 21/25 (TicketCancelled). The refunded amount is credited back to the office wallet via a charge transaction.

Request

{
  "SessionId": "...",
  "UniqueId": "4d2e91c8-...",
  "RefundType": 1,
  "EticketNumbers": ["235-2148764520"]
}
POST /Air/AirBookingData air_booking_data

Full reservation record: travelers, e-tickets, segments, fare breakdown, booking notes, and audit timestamps. Lookup by either internal UniqueId or your ClientUniqueId.

Each segment in TravelItinerary.ItineraryInfo.ReservationItems additionally carries three fields: DepartureAirportTerminal and ArrivalAirportTerminal (plain strings) and AirlineRules (an HTML string of the fare/airline conditions). They are empty until populated for the booking and may be blank for segments left unfilled.

Request

{ "SessionId": "...", "UniqueId": "4d2e91c8-..." }

Air β€” Reference Data #

Static lookup datasets for resolving the codes returned in flight results (IATA airport/city codes, airline codes, country codes). These are plain JSON files served from this domain β€” fetch them once, cache them on your side, and refresh occasionally (they change rarely). No SessionId is required.

DatasetURLRecords
Countrieshttps://api.hermeseus.com/data/reference/countries.json~253
Citieshttps://api.hermeseus.com/data/reference/cities.json~9,600
Airportshttps://api.hermeseus.com/data/reference/airports.json~10,300
Airlineshttps://api.hermeseus.com/data/reference/airlines.json~1,150
GET/data/reference/countries.json

Array of countries. code is the 2-letter ISO country code used by city/airport records; currency is the country's ISO-4217 currency; name_translations holds localized names.

[
  {
    "code": "GM",
    "name": "Gambia",
    "currency": "GMD",
    "name_translations": { "en": "Gambia" },
    "cases": { "su": "Gambia" }
  }
]
GET/data/reference/cities.json

Array of cities. code is the 3-letter IATA city code (matches OriginLocationCode/DestinationLocationCode in search), country_code links to countries, coordinates gives lat/lon, and has_flightable_airport indicates the city is reachable by air.

[
  {
    "code": "FSZ",
    "name": "Shizuoka",
    "country_code": "JP",
    "time_zone": "Asia/Tokyo",
    "coordinates": { "lat": 34.796112, "lon": 138.18944 },
    "has_flightable_airport": true,
    "name_translations": { "en": "Shizuoka" },
    "cases": { "su": "Shizuoka" }
  }
]
GET/data/reference/airports.json

Array of airports. code is the 3-letter IATA airport code (matches segment DepartureAirportLocationCode/ArrivalAirportLocationCode), city_code links to cities, iata_type is the entity type (e.g. airport), and flightable indicates bookable flights operate there.

[
  {
    "code": "BLK",
    "name": "Blackpool Airport",
    "city_code": "BLK",
    "country_code": "GB",
    "iata_type": "airport",
    "time_zone": "Europe/London",
    "coordinates": { "lat": 53.778385, "lon": -3.041985 },
    "flightable": true,
    "name_translations": { "en": "Blackpool Airport" }
  }
]
GET/data/reference/airlines.json

Array of airlines. code is the 2-character IATA airline code (matches ValidatingAirlineCode and segment MarketingAirlineCode/OperatingAirline.Code), name is the carrier name, and is_lowcost flags low-cost carriers.

[
  {
    "code": "ZM",
    "name": "Air Manas",
    "is_lowcost": true,
    "name_translations": { "en": "Air Manas" }
  }
]
GET /logos/airline-logos/{code}

Airline logo image proxy. {code} is a 1–4 character airline code (alphanumeric). Returns a square PNG (200Γ—200, transparent background) served from this domain, cached for 24h. Use it directly as an <img> src. Returns 404 if no logo exists for that code.

<img src="https://api.hermeseus.com/logos/airline-logos/UN" alt="airline logo" width="80" height="80">

# or
curl -L 'https://api.hermeseus.com/logos/airline-logos/TK' -o tk.png
GET /logos/airline-logos-v2/{code}

Alternate logo source β€” same contract as above but served from a different image provider with a square fill crop (200Γ—200 PNG). Use whichever renders best for a given carrier; 404 if no logo exists.

<img src="https://api.hermeseus.com/logos/airline-logos-v2/UN" alt="airline logo" width="80" height="80">

Hotel β€” City Lookup #

Hotel cities are an offline dataset synced from each provider's catalog. These lookups are public β€” no session required.

GET /Hotel/cities/search?q=istanbul&provider=example-provider-1&limit=50

Fuzzy search across city name, destination name, and country. Matches starting with the query string are boosted first.

Response

{
  "data": [
    {
      "id": 1248,
      "external_id": 35487,
      "name": "Istanbul",
      "destination": "Istanbul, Turkey",
      "country": { "code": "TR", "name": "Turkey" }
    }
  ],
  "meta": { "query": "istanbul", "count": 1, "limit": 50 }
}
POST /Hotel/cities/lookup

Bulk resolve up to 500 cities by provider-side external_id.

Request

{ "external_ids": [35487, 35488], "provider": "example-provider-1" }

Hotel β€” Availability & Booking #

POST /Hotel/HotelAvailability hotel_availability

Search available hotel offers. Provider=example-provider-1 (default) returns the rich payload with full hotel metadata (images, facilities, geo); Provider=example-provider-2 uses an alternate inventory source. All prices are normalised to USD.

Request

{
  "SessionId": "7f3c81d2-...",
  "Provider": "example-provider-1",
  "CheckIn": "2026-07-10",
  "CheckOut": "2026-07-14",
  "CityId": 35487,
  "CountryCode": "TR",
  "NationalityId": "AE",
  "Occupancies": [
    { "AdultCount": 2, "ChildCount": 1, "ChildAges": [7] }
  ]
}

Response (truncated)

{
  "Success": true,
  "PricedItineraries": [
    {
      "FareSourceCode": "cGFydG86SE9URUw0Mjg3MQ==",
      "HotelId": 42871,
      "HotelName": "Grand Hyatt Istanbul",
      "NetRate": 487.20,
      "Currency": "USD",
      "NonRefundable": false,
      "Hotel": {
        "name": "Grand Hyatt Istanbul",
        "rating": 5,
        "location": { "latitude": 41.0427, "longitude": 28.9882 },
        "images": ["https://cdn.../1.jpg", ...]
      },
      "Rooms": [ ... ],
      "CancellationPolicies": [ ... ]
    }
  ],
  "Error": null
}
POST /Hotel/HotelCheckRate hotel_check_rate

Verify a hotel rate before booking. The returned itinerary is cached and is what HotelBook reads from β€” calling Book without CheckRate fails with Err0102004.

πŸ”„
FSC rotation. Some inventory sources return a different FareSourceCode on CheckRate than the one you sent. Both codes are cached at the gateway, so HotelBook accepts either.
POST /Hotel/HotelBook hotel_book

Reserve the previously rate-checked offer. Funds are held; the reservation auto-cancels after 10 minutes if not confirmed via HotelOrder.

Request

{
  "SessionId": "...",
  "FareSourceCode": "cGFydG86SE9URUw0Mjg3MQ==",
  "PhoneNumber": "+971501234567",
  "Email": "customer@example.com",
  "NationalityId": "AE",
  "ClientUniqueId": "hotel_order_88112",
  "Rooms": [
    {
      "RoomId": "R-1",
      "Passengers": [
        { "PassengerType": 1, "Title": "Mr", "FirstName": "John", "LastName": "Smith", "Nationality": "AE" }
      ]
    }
  ]
}
POST /Hotel/HotelOrder hotel_order

Confirm the reservation. Status moves 10β†’20 and the voucher is then issued.

POST /Hotel/HotelCancel hotel_cancel

Cancel a hotel booking and release held funds.

POST /Hotel/HotelBookingData hotel_booking_data

Full hotel reservation record: hotel metadata, booked rooms, voucher numbers, cancellation policies, audit timestamps.

Hotel v2 β€” Overview #

A second-generation hotel API served under /api/v2/Hotel/*. It exposes a richer, discovery-first surface: typed place search (city / region / country / hotel / landmark / airport / district), first-class filters, pagination, sorting, a map viewport, and deep hotel detail pages. All rates are quoted in USD.

🧭
Same session, same booking flow. Authenticate exactly as for v1 (Authenticate/CreateSession) and pass the returned SessionId. cities/search and cities/lookup are public. HotelBook / HotelOrder / HotelCancel / HotelBookingData under /v2/Hotel/ are identical to their v1 counterparts β€” see the Hotel section above.
πŸ”’
Public IDs. Ids are never negative. A negative source city id is encoded by dropping the sign and prefixing seven 1s (e.g. -1456928 β†’ 11111111456928); send it back verbatim as CityId. Positive ids (hotels) are unchanged.

Hotel v2 β€” End-to-end booking guide #

This is the complete journey from a destination the traveller types to a confirmed, paid booking. Each step names the exact endpoint, what to send and why, what you get back, and β€” most importantly β€” the one value you must carry into the next step. Every step below is followed by a parameter-by-parameter request table taken directly from the platform's own validation rules β€” not guesswork. All prices are USD.

πŸ”
Where the session goes. cities/search and cities/lookup need no session. Every other endpoint here, including HotelAvailability, requires your SessionId from Authenticate/CreateSession (in the JSON body). The platform manages the upstream provider session separately, so you never handle that one.

1 β€” Find the destination

The traveller types a destination into a search box; you resolve it to an external_id. The value of q is exactly what the user typed.

GET /api/v2/Hotel/cities/search?q=paris&limit=20&type=city
ParameterTypeReq.Description & allowed values
qstring (≀128)β€”The user's raw search text. Empty q returns an empty list.
limitinteger 1–100β€”Max results. Default 20.
typestringβ€”Restrict to one place kind: city, district, region, country, landmark, hotel. Omit for all kinds.
{
  "data": [
    {
      "id": 13,
      "external_id": "11111111456928",
      "type": "city",
      "name": "Paris",
      "label": "Paris, Ile de France, France",
      "destination": "Paris, Ile de France, France",
      "country": { "code": "FR", "name": "France" },
      "nr_hotels": 25708
    }
  ],
  "meta": { "query": "paris", "count": 1, "limit": 20, "type": "city" }
}

The response gives you the external_id you use in the next step. For display you can also use name/label, the country (country.name), the number of properties (nr_hotels), and the place type (city, country, district, region, landmark, hotel). type restricts results to one kind. To re-resolve ids you already stored, use POST /api/v2/Hotel/cities/lookup with { "external_ids": [ … ] }.

2 β€” List the hotels in a place

Put the external_id from step 1 into CityId, add stay dates and guests (Occupancies), and optionally Filters, Sort, Page/PageSize and a Map viewport. See Availability for the full filter/sort menus.

POST /api/v2/Hotel/HotelAvailability
{
  "SessionId": "…",
  "CheckIn": "2026-10-15",
  "CheckOut": "2026-10-16",
  "CityId": 11111111456928,
  "Occupancies": [ { "AdultCount": 2, "ChildCount": 0 } ],
  "Filters": { "class": [4,5], "mealplan": ["breakfast_included"] },
  "Sort": "price",
  "Page": 1
}
ParameterTypeReq.Description & allowed values
SessionIdstringyesYour session from CreateSession.
CheckIndate YYYY-MM-DDyesArrival date.
CheckOutdate YYYY-MM-DDyesDeparture date. Must be after CheckIn.
CityIdintegerone ofThe external_id from step 1 (city mode). Provide exactly one of CityId / HotelId.
HotelIdintegerone ofA single property (step 3). When set, send CityId as null.
Occupanciesarray (β‰₯1)yesOne entry per room requested.
Occupancies[].AdultCountinteger 1–8yesAdults in that room.
Occupancies[].ChildCountinteger 0–6β€”Children in that room.
Occupancies[].ChildAgesarray<int 0–17>β€”Age of each child at check-out; supply one age per child when ChildCount > 0.
Filtersobjectβ€”Facet filters (class, mealplan, price, …). Full menu in Availability.
Sortstring (≀64)β€”e.g. price. See the Sorting list in Availability. No default β€” omit for the supplier's natural order.
Pageinteger β‰₯1β€”1-based page number. Default 1.
PageSizeinteger 1–200β€”Results per page.
Mapobjectβ€”Optional viewport bounds to constrain results to a map area.
langstring (≀8)β€”Content language, e.g. ar.

The response is a page of properties under PricedItineraries[] β€” each with HotelId, HotelName, NetRate, Currency, NonRefundable, FreeCancellation, MealPlan and a Hotel object (name, rating, location, images) β€” plus Meta (pagination, price range, the applied/available sorts and filters). Show this as a list. Carry the HotelId of the chosen property into the next step.

🌐
Localization. Send lang (e.g. ar) and, where the supplier has translated content, hotel names and text come back in that language.

3 β€” Open a hotel: rooms, amenities, prices

Call the same endpoint with the same parameters as step 2, but set CityId to null and pass the HotelId from step 2. This switches the response from a list of properties to the full room-and-rate list for that one hotel.

POST /api/v2/Hotel/HotelAvailability
{
  "SessionId": "…",
  "CheckIn": "2026-10-15",
  "CheckOut": "2026-10-16",
  "CityId": null,
  "HotelId": 7825691,
  "Occupancies": [ { "AdultCount": 2 } ]
}

You receive the hotel's Hotel profile, Policies, Reviews, and every bookable room-rate under PricedItineraries[]: price, MealPlan/Board, NonRefundable, RefundableUntil, CancellationPolicy, AvailableRooms, and a full Room object (name, beds, size, description, highlights, amenities, amenity groups, room images). See Hotel details for every field.

πŸ”‘
Keep the FareSourceCode of the room-rate the traveller picks β€” it is what you validate and book with in the next steps.

4 β€” Validate the rate

Collect the guests' details first. At the exact moment the traveller confirms, re-price the chosen offer.

POST /api/v2/Hotel/HotelCheckRate
{ "SessionId": "…", "FareSourceCode": "…from step 3…" }
ParameterTypeReq.Description
FareSourceCodestringyesThe room-rate code from step 3.
SessionIdstringyesYour session from CreateSession. The validated fare is cached against it for 15 minutes so the following HotelBook/HotelOrder need no further upstream re-price. Omit it and the flow cannot continue.

The response may return the same FareSourceCode or a new one (the price may have moved). Either way, use the FareSourceCode returned here for the next call.

⏱️
15-minute window. After CheckRate the rate is valid and bookable for up to 15 minutes. Call at 10:00 β†’ you have until 10:15 to do step 5.

5 β€” Hold the booking

After the traveller confirms, hold the room with the validated FareSourceCode plus the guest details.

POST /api/v2/Hotel/HotelBook
{
  "SessionId": "…",
  "FareSourceCode": "…from step 4…",
  "PhoneNumber": "+441234567890",
  "Email": "traveller@example.com",
  "Nationality": "GB",
  "ClientUniqueId": "your-own-ref-0001",
  "Note": "High floor if possible",
  "Rooms": [
    {
      "Passengers": [
        {
          "FirstName": "Jane", "LastName": "Doe",
          "PassengerType": 1, "PassengerTitle": 2,
          "NationalId": null, "PassportNumber": "123456789"
        }
      ]
    }
  ],
  "HotelTransfers": []
}
ParameterTypeReq.Description & allowed values
SessionIdstringyesSame session used at CheckRate.
FareSourceCodestringyesThe code returned by HotelCheckRate (step 4), not the step-3 one.
PhoneNumberstringyesLead contact phone.
EmailemailyesLead contact email.
Roomsarray (β‰₯1)yesOne entry per booked room; the count must match the Occupancies you searched with.
Rooms[].Passengersarray (β‰₯1)yesGuests in that room.
…Passengers[].FirstName / LastNamestringyesGuest name as on ID/passport.
…Passengers[].PassengerTypeintegeryesGuest type code forwarded to the supplier (adult vs child). Match it to the Occupancies you searched.
…Passengers[].PassengerTitleintegeryesTitle/salutation code forwarded to the supplier (e.g. Mr / Mrs / Ms / child).
…Passengers[].NationalIdstringβ€”National ID where the property requires it.
…Passengers[].PassportNumberstringβ€”Passport number where required.
Nationalitystringβ€”Lead guest nationality (ISO country code); some rates are nationality-scoped.
ClientUniqueIdstringβ€”Your own reference for the booking; you can later fetch it by this instead of UniqueId.
Notestringβ€”Free-text special request passed to the property.
HotelTransfersarraypresentMust be present (send [] if none). Each entry: TransferType (int), ServiceType (int), and optional AirLine, FlightNo, ArrivalTime, DepartureTime.

Nothing is charged yet β€” this is a temporary hold, valid for up to another ~15 minutes. The response returns a UniqueId β€” keep it; it drives every remaining step. Call at 10:12 β†’ you have until ~10:27 to place the order.

6 β€” Place the order (pay & finalize)

Once you have taken payment from the traveller (your own gateway), finalize with the UniqueId.

POST /api/v2/Hotel/HotelOrder
{ "SessionId": "…", "UniqueId": "…from step 5…" }
ParameterTypeReq.Description
UniqueIdstringyesThe booking id returned by HotelBook (step 5).
SessionIdstringyesSame session as the rest of the flow.

Miss the window and the hold is released β€” you must restart from step 4. On success the booking total is deducted from your deposit with us and the reservation is confirmed.

πŸ’³
This is the charge point. Balance is deducted only at HotelOrder β€” never at Book. Always confirm the traveller has paid you before calling it.

7 β€” Track, extend, or cancel

All keyed on the booking UniqueId:

EndpointBody parametersPurpose
POST /v2/Hotel/HotelBookingDataUniqueId or ClientUniqueId (one required)Fetch the booking and its current status at any time.
POST /v2/Hotel/HotelExtendPaymentDeadlineUniqueId (required)Extend the payment deadline on a still-held (unconfirmed) booking.
POST /v2/Hotel/HotelCancelDisplayUniqueId (required)Preview cancellation terms and any penalty before cancelling.
POST /v2/Hotel/HotelCancelUniqueId (required); optional CancelActor (int, default 0), RefundPaymentMode (int, default 0), HasAnyCanceledUser (bool, default false)Cancel the booking.

Full request/response shapes for these are in Booking lifecycle.

Hotel v2 β€” Places #

GET /v2/Hotel/cities/search?q=paris&limit=20&type=city

Typed place search. Optional type restricts results to one of city, region, country, hotel, landmark, airport, district. Public β€” no session required.

Response

{
  "data": [
    {
      "id": 13,
      "external_id": 11111111456928,
      "type": "city",
      "name": "Paris",
      "label": "Paris, Ile de France, France",
      "country": { "code": "FR", "name": "France" },
      "nr_hotels": 25708
    }
  ],
  "meta": { "query": "paris", "count": 5, "limit": 20 }
}
POST /v2/Hotel/cities/lookup

Bulk resolve up to 500 places by public external_id. Public.

Request

{ "external_ids": [11111111456928, 2281] }

Hotel v2 β€” Availability #

POST /v2/Hotel/HotelAvailability hotel_availability

City search. Send CityId (from cities/search) with optional Filters, Page, Sort and Map. Provide exactly one of CityId or HotelId.

Request

{
  "SessionId": "7f3c81d2-...",
  "CheckIn": "2026-10-15",
  "CheckOut": "2026-10-16",
  "CityId": 11111111456928,
  "Occupancies": [ { "AdultCount": 2 } ],
  "Page": 1,
  "Sort": "price",
  "Filters": { "class": [4,5], "mealplan": ["breakfast_included"], "free_cancellation": [1] },
  "Map": { "Center": { "lat": 48.86, "lng": 2.35 }, "RadiusKm": 2 }
}

Response (truncated)

{
  "Success": true,
  "CheckIn": "2026-10-15", "CheckOut": "2026-10-16", "Nights": 1,
  "PricedItineraries": [
    {
      "FareSourceCode": "",
      "HotelId": 7825691,
      "HotelName": "Example Paris Hotel",
      "NetRate": 98.53, "Currency": "USD",
      "NonRefundable": true, "FreeCancellation": false, "MealPlan": "Room Only",
      "Hotel": { "external_id": 7825691, "review_score": 7.4, "location": { ... }, "images": [ ... ] }
    }
  ],
  "Meta": {
    "Pagination": { "Page": 1, "PageSize": 140, "TotalPages": 8, "HasNextPage": true },
    "PriceRange": { "min": 98.53, "max": 204.22, "currency": "USD" },
    "Sort": "price", "AvailableSorts": [ ... ], "AvailableFilters": { ... }, "Map": { ... }
  }
}
↕️
Sorting. Sort accepts price (default), popularity, review_score, stars_desc, stars_asc, distance. The full menu is echoed back in Meta.AvailableSorts. Pages are ~130–140 hotels each.

Filters

Send selections under Filters as { "key": [ ids… ] }; conditions combine with AND. For each search, every filter key, its human title, and the selectable option ids (with a live result count) are returned under Meta.AvailableFilters β€” each entry is { "title": …, "categories": [ { "id", "name", "count" } ] } β€” and the filters you sent are echoed back in Meta.AppliedFilters. The complete set of filter keys:

⚠️
Filters are not the amenity list. facility and room_facility are curated filter catalogues β€” the short, fixed set of facilities you can search by (their values are listed in full below). They are not the same as the per-hotel/per-room amenities / amenity_groups you get back in a hotel-details response β€” that is an open-ended, free-text descriptive list (Bathrobe, Slippers, Wake-up service, Socket near the bed, …) that varies by property and has no fixed value set. Filter by the catalogue ids here; display the descriptive amenities from the response.
KeyFilters byValue
classProperty star rating0 unrated Β· 1–5 stars. e.g. [4,5]
mealplanMeals includedbreakfast_included Β· breakfast_and_dinner Β· half_board Β· full_board Β· all_inclusive
free_cancellationFree-cancellation rates only1 = free cancellation
reviewscorebucketsMin guest review score50 = 5+ Β· 60 = 6+ Β· 70 = 7+ Β· 80 = 8+ Β· 90 = 9+
rshlMin location score6 = 6+ Β· 7 = 7+ Β· 8 = 8+ Β· 9 = 9+
property_typeProperty type3 Entire homes & apartments Β· 201 Apartments Β· 203 Hostels Β· 204 Hotels Β· 205 Motels Β· 206 Resorts Β· 208 Bed and breakfasts Β· 209 Ryokans Β· 210 Farm stays Β· 212 Holiday parks Β· 213 Villas Β· 214 Campsites Β· 215 Boats Β· 216 Guest houses Β· 220 Holiday homes Β· 221 Lodges Β· 222 Homestays Β· 223 Country houses Β· 224 Luxury tents Β· 225 Capsule hotels Β· 226 Love hotels Β· 228 Chalets Β· 231 Economy hotels Β· 235 Student accommodation
stay_typeTravel group2 = Adults only Β· 4 = Travel Proud (LGBTQ+ friendly)
distanceMax distance from city centre1000 = <1 km Β· 3000 = <3 km Β· 5000 = <5 km
twin_double_bedBed preference2 = 2 single beds Β· 3 = double bed
num_bedsMinimum number of beds1 = 1+ Β· 2 = 2+ Β· 3 = 3+ Β· 4 = 4+ Β· 5 = 5+
entire_place_bedroom_countMinimum bedrooms (entire places)1 = 1+ Β· 2 = 2+ Β· 3 = 3+ Β· 4 = 4+
SustainablePropertyLevelFilterSustainability certification4 = certified
facilityProperty facilities2 Parking Β· 3 Restaurant Β· 4 Pets allowed Β· 5 Room service Β· 8 24-hour front desk Β· 11 Fitness centre Β· 16 Non-smoking rooms Β· 17 Airport shuttle Β· 28 Family rooms Β· 46 Free parking Β· 54 Spa & wellness centre Β· 72 BBQ facilities Β· 107 Free WiFi Β· 139 Airport shuttle (free) Β· 182 Electric vehicle charging station Β· 185 Wheelchair accessible Β· 433 Swimming pool
room_facilityRoom facilities5 Bath Β· 11 Air conditioning Β· 16 Kitchenette Β· 17 Balcony Β· 23 Desk Β· 34 Washing machine Β· 37 Patio Β· 38 Private bathroom Β· 71 Fireplace Β· 75 Flat-screen TV Β· 79 Soundproofing Β· 81 View Β· 86 Electric kettle Β· 93 Private pool Β· 108 Sea view Β· 120 Coffee machine Β· 123 Terrace Β· 998 Coffee/tea maker Β· 999 Kitchen/kitchenette
priceBudget for the stay (USD)min–max range; the search's bounds are in Meta.PriceRange
chaincodeHotel chainchain ids β€” vary by search; e.g. 1080 Marriott Β· 1078 Hilton Β· 1053 ibis Β· 1050 Novotel Β· 8647 Radisson Β· 12902 Sheraton
districtNeighbourhood / districtcity-specific ids β€” read the options from Meta.AvailableFilters.district
popular_nearby_landmarksNear a landmarkcity-specific landmark ids β€” from Meta.AvailableFilters.popular_nearby_landmarks
popularCurated shortcut of the most-used filtersmixed ids β€” from Meta.AvailableFilters.popular

Hotel v2 β€” Hotel details #

POST /v2/Hotel/HotelAvailability hotel_availability

Deep hotel page. Send HotelId (and CityId: null) to get full property content plus one offer per room-rate.

Response (abridged)

{
  "Success": true, "Nights": 1,
  "Hotel": { "external_id": 6331862, "name": "...", "rating": 3, "description": "...", "facility_groups": [ ... ], "images": [ ... ] },
  "Policies": {
    "CheckIn": { "from": "15:00", "until": "23:30" }, "CheckOut": { "from": "08:00", "until": "11:00" },
    "AgeRestriction": { "min_age": 18, "phrase": "..." }, "Curfew": null,
    "Children": { "allowed": false }, "Pets": { "allowed": "NO", "charge": "NOT_APPLICABLE" },
    "Groups": null, "Meals": [ { "type": "BREAKFAST", "offer": "BOTH" } ],
    "AcceptedPaymentCards": [ "Visa", "Mastercard", ... ],
    "DamageDeposit": null, "ImportantInfo": null, "License": { "numbers": [ "1462850" ], "phrases": [ ... ] }
  },
  "Reviews": { "Score": 8.2, "Count": 12408, "SubScores": [ ... ], "Featured": [ ... ] },
  "PricedItineraries": [
    { "FareSourceCode": "NjMz...", "NetRate": 358.13, "MealPlan": "Breakfast",
      "Room": {
        "id": "...", "name": "Standard Twin Room", "adults": 2, "children": 0, "max_occupancy": 2, "beds": [ ... ],
        "size_m2": 32, "size_feet2": 344.4, "description": "The spacious twin room features…",
        "highlights": [ { "name": "Free WiFi", "icon": "wifi" }, { "name": "Air conditioning", "icon": "air-conditioning" } ],
        "amenities": [ "TV", "Minibar", "Private bathroom", ... ],
        "amenity_groups": [ { "group": "Bathroom", "items": [ "Toilet", "Shower", "Hairdryer" ] }, ... ],
        "images_count": 7, "images": [ { "url": "https://images.example.com/hotel/large/....jpg", "thumbnail_url": "https://images.example.com/hotel/thumb/....jpg" } ] } }
  ],
  "Meta": { "HotelId": 6331862, "Currency": "USD", "Offers": 5 }
}
πŸ–ΌοΈ
Room detail. In hotel-details each offer's Room carries the full room profile:
  • size_m2 / size_feet2 β€” room area in square metres and square feet.
  • description β€” free-text room description.
  • highlights β€” key features as { name, icon } (icon is an opaque UI icon token).
  • amenities β€” flat list of amenity names; amenity_groups groups them as { group, items[] } (Bathroom, Kitchen, Media & Technology, …). This is a descriptive, open-ended list that varies per room (many hundreds of possible values) β€” not the room_facility filter, which is the short curated set of ids you can search by.
  • beds, max_occupancy, adults, children β€” sleeping capacity.
  • images (with images_count) β€” that specific room's own photos, each a { url, thumbnail_url } pair (full-size / thumbnail). Separate from Hotel.images, the whole-property gallery.
These per-room fields appear only in hotel-details (send HotelId), not in city search.
πŸ“‹
Full hotel policies. Policies carries every house rule the supplier publishes, so you can show the complete conditions per hotel:
  • CheckIn / CheckOut β€” { from, until } time windows (either bound may be null).
  • AgeRestriction β€” { min_age, phrase } minimum check-in age.
  • Curfew β€” a curfew note, or null.
  • Children β€” { allowed }.
  • Pets β€” { allowed: "YES"|"NO", charge }.
  • Groups β€” group-booking terms string, or null.
  • Meals β€” array of { type, offer } (e.g. BREAKFAST / BOTH, LUNCH / OPTIONAL_PAID).
  • AcceptedPaymentCards β€” cards accepted at the property.
  • DamageDeposit β€” deposit terms, or null.
  • ImportantInfo β€” free-text important information, or null.
  • License β€” { numbers[], phrases[] } the property's licence/registration.
The full Policies block is returned only in hotel-details (send HotelId), not in city search.
POST /v2/Hotel/HotelCheckRate hotel_check_rate

Re-prices a FareSourceCode live and keeps it valid for at least 15 minutes. The confirmed itinerary is cached at the gateway and is what /v2/Hotel/HotelBook reads from.

Request

{ "SessionId": "...", "FareSourceCode": "NjMz..." }

Hotel v2 β€” Booking lifecycle (Book / Order / Data / Cancel) #

Booking under v2 uses the same gateway lifecycle as v1 β€” only the path prefix changes to /v2/Hotel/. Call HotelCheckRate first so the offer is cached, then HotelBook holds the reservation and HotelOrder confirms it. Every endpoint below is POST, carries the SessionId, and β€” except HotelBook β€” is keyed on the UniqueId returned by HotelBook. Responses use the standard { "Success", "Error", … } envelope and are identical in shape to the corresponding Hotel v1 endpoint.

EndpointPurposeRequest body
POST /v2/Hotel/HotelBookHold the reservation for a checked-rate offer.SessionId, FareSourceCode and guest/traveller details β€” same shape as the v1 HotelBook.
POST /v2/Hotel/HotelOrderConfirm (issue) a held booking.{ "SessionId": "…", "UniqueId": "…" }
POST /v2/Hotel/HotelBookingDataRetrieve a booking and its current status.{ "SessionId": "…", "UniqueId": "…" } (or ClientUniqueId)
POST /v2/Hotel/HotelCancelDisplayPreview the cancellation terms and any penalty before cancelling.{ "SessionId": "…", "UniqueId": "…" }
POST /v2/Hotel/HotelCancelCancel a booking.{ "SessionId": "…", "UniqueId": "…", "CancelActor": 0, "RefundPaymentMode": 0, "HasAnyCanceledUser": false } β€” the last three are optional.
POST /v2/Hotel/HotelExtendPaymentDeadlineExtend the payment deadline on a held (unconfirmed) booking.{ "SessionId": "…", "UniqueId": "…" }

UniqueId is the booking reference returned by HotelBook; keep it to drive Order, BookingData, CancelDisplay, Cancel and ExtendPaymentDeadline. Each endpoint requires its role permission (hotel_book, hotel_order, hotel_booking_data, hotel_cancel_display, hotel_cancel, hotel_extend_payment_deadline).

Activity β€” Overview #

The Activity API exposes a full catalogue of tours, attractions and experiences and a complete booking lifecycle under /api/Activity/v2/.... Resources are organized into Products, Product Types, Pricing, Bookings, Vouchers, Configuration and Webhook Events. The gateway applies exactly these transforms before handing a response back:

  • Money β†’ USD. Every monetary field (basePrice, totalAmount, amount, price, rate amounts, …) and its currency/currencyCode is converted to USD. The original amount is preserved in the human-readable format string where the inventory source provides one (e.g. "S$30.00").
  • Links β†’ this gateway. Every HATEOAS links[].href points at this gateway ({your base URL}/api/Activity/v2/...); the upstream inventory host is never exposed. CDN/image URLs are left untouched so images keep loading.
  • Nothing else changes. UUIDs, titles, dates, statuses and every other field are returned verbatim.

Booking model β€” deferred fulfilment

Creating a booking does not immediately commit it with the inventory source. POST /v2/bookings returns a booking in reserved status and mirrors it locally; the platform then finalises it asynchronously. Key consequences for your integration:

  • The data.uuid you receive at create time is your permanent reference β€” keep using it for details, status, vouchers and download. Once the platform issues the booking upstream it is transparently mapped onto the real supplier UUID; your original reference keeps working.
  • The office balance is held for the booking total. Vouchers exist only once the booking has been issued.

Typical flow

GET products β†’ GET products/{uuid} β†’ GET products/{uuid}/product-types β†’ GET product-types/{uuid}/price-lists β†’ POST bookings β†’ (PUT bookings/{uuid}/confirm) β†’ GET bookings/{uuid}/vouchers β†’ GET .../download-voucher/{voucher}.

Authentication

Every operation requires an active SessionId (obtain it from CreateSession). Send it as a query parameter on GET requests and in the JSON body on POST/PUT. It is applied to all Activity operations and omitted from the per-endpoint parameter tables for brevity.

Common parameters

NameInTypeDescription
SessionIdquery / bodystringRequired on every request.
pagequeryinteger1-based page number (list endpoints).
per_pagequeryintegerPage size (list endpoints).

Response envelope

Resource responses are wrapped as { "data": ..., "timestamp": "<ISO-8601 +08:00>" }; list responses add "meta": { "pagination": { "page", "pageCount", "perPage", "total" } }. Timestamps and booking date/time strings are in the +08:00 (Asia/Singapore) zone.

Status codes

CodeMeaning
200 OKSuccess β€” body holds the resource / list.
400 Bad RequestMalformed request or invalid parameters.
401 UnauthorizedMissing / expired SessionId.
403 ForbiddenYour role lacks permission for this operation.
404 Not FoundResource / UUID does not exist.
422 UnprocessableValidation failed (e.g. unavailable date, bad pax mix).
5xxGateway or inventory-source error.

Error responses use an error object with a machine code, a human message and the http_code:

{
  "error": {
    "code": "not_found",
    "message": "Listing was not found or expired, provided UUID: d3bfa3e1-e",
    "http_code": 404
  },
  "timestamp": "2026-06-02T16:43:33.184+08:00"
}

Activity β€” End-to-end booking guide #

The full journey from browsing the catalogue to a downloadable voucher. Each step names the exact endpoint, what to send, what you get back, and the id you carry forward. Send SessionId as a query parameter on GET and in the JSON body on POST/PUT. Prices are USD; times are in the activity's local timezone.

🧭
The path: products β†’ products/{uuid} β†’ product-types β†’ price-lists (+ timeslots) β†’ POST bookings β†’ PUT …/confirm β†’ vouchers β†’ download-voucher. Ids carried: product uuid β†’ productTypeUuid β†’ booking uuid.

1 β€” Browse the catalogue

List products, filtered and sorted. Served from the gateway's local cache, so it is fast.

GET /api/Activity/v2/products?city=Singapore&category=Attractions&min_price=20&max_price=200&sort=bestselling&page=1&per_page=50&SessionId=…
Query paramTypeDescription & allowed values
qstringName search in any stored language; matched rows carry matchedLanguage.
typestringProduct-type name substring or type UUID.
categorystringCategory name or UUID; a parent UUID expands to all descendants.
country / citystringFilter by location name.
tagstringFilter by tag.
languagestringLocalize titles, e.g. KO, AR.
min_price / max_pricefloatPrice band (USD).
sortstringOne of price, -price, date, -date, bestselling, sold, -sold. Default: catalogue order.
page / per_pageintegerPagination. per_page default 50.

Returns { data: [ product… ], meta.pagination, timestamp }. Each product carries uuid, title, basePrice, cityName, image_url, images[]. Carry the product uuid. For the whole catalogue in one call use products/all; for a countryβ†’city tree use products/by-location.

2 β€” Open the product

Fetch the full product β€” description, images, itinerary, policies β€” live from the source.

GET /api/Activity/v2/products/{uuid}?SessionId=…

See Product details for the full body. Use /products/{uuid}/restrictions for age/pax rules.

3 β€” Pick a bookable variant (product-type)

A product has one or more product-types (variants: ticket tiers, durations, packages). Each has its own uuid β€” the productTypeUuid you book.

GET /api/Activity/v2/products/{uuid}/product-types?SessionId=…

Carry the chosen productTypeUuid.

4 β€” Check available dates & times

Confirm the variant is available and priced for the traveller's date. Already-past dates and timeslots are filtered out automatically against the activity's own timezone.

EndpointReturns
GET /product-types/{uuid}/price-lists{ data: [ { date, timezone, available, price, currency } … ] } β€” the bookable-date calendar. Optional date_start/date_end query bounds.
GET /product-types/{uuid}/price-lists/{date}Pricing/availability for one YYYY-MM-DD; data is null if that date is already past.
GET /product-types/{uuid}?date=YYYY-MM-DDVariant details incl. data.timeslots[] (each with startTime) and data.timezone. Timeslots already past for the given date are dropped.

5 β€” Create the booking

Reserve with the variant, date, pax mix, optional timeslot, and the lead customer. The booking is created in reserved status and mirrored locally; the platform issues it asynchronously. The office balance is held for the total.

POST /api/Activity/v2/bookings
{
  "SessionId": "…",
  "productTypeUuid": "5f3ff236-40b1-4f0a-a780-1db8649d1f5b",
  "arrivalDate": "2026-10-15",
  "adults": 2, "children": 0, "seniors": 0,
  "timeSlot": null,
  "partnerReference": "your-ref-0001",
  "customer": {
    "salutation": "Mr.", "firstName": "Jane", "lastName": "Doe",
    "email": "traveller@example.com", "phone": "+441234567890"
  }
}
ParameterTypeReq.Description
productTypeUuidstring (uuid)yesThe variant to book (step 3).
arrivalDatestring (date)yesVisit date YYYY-MM-DD (alias date). Defaults to today+7 if omitted.
adultsintegeryesAdult tickets (alias pax). Default 1.
children / seniorsintegerβ€”Child / senior tickets. Default 0.
timeSlotstringβ€”The chosen timeslot when the variant exposes them (step 4).
customerobjectyessalutation (default "Mr."), firstName, lastName, email, phone.
partnerReferencestringβ€”Your own reference, echoed on the booking.
optionsobjectcond.Answers to the product-type's booking options β€” required when the variant has any required: true option. See below.
πŸ“
Booking options. Some activities need extra input at booking time (a flight number, a pickup zone, per-guest names, a document upload). Each variant's response (GET /product-types/{uuid} and GET /products/{uuid}/product-types) carries an options object with two arrays β€” perBooking (answered once) and perPax (answered once per guest). Every option lists uuid, name, description, required, addOn, inputType (1–14: 1=list, 4=string, 5=boolean, 6=date, 7=file, 13=phone, 14=flight-no, …), formatRegex, optional price and, for list types, an items[] array of { label, value, price }.
Submit the answers under options using the option uuid and the guest's value β€” perBooking is a flat list, perPax is an array of arrays, one per traveller (adults, then children, then seniors):
"options": {
  "perBooking": [
    { "uuid": "46db421e-5727-46fc-9f2c-10679e026582", "value": "EK202" }
  ],
  "perPax": [
    [ { "uuid": "543f0e45-bdfe-4dc7-af73-e7fd5eda8246", "value": "zone_1" } ],
    [ { "uuid": "543f0e45-bdfe-4dc7-af73-e7fd5eda8246", "value": "zone_2" } ]
  ]
}

Validation. Required options are enforced before the booking is held: if a required option is missing β€” including a perPax option not answered for every traveller (adults + children + seniors) β€” the request fails with 422 and a message naming the option (e.g. Missing required per-guest booking option: "Full name" (traveller 2)) and no balance is held. Answer every required option to book.
Add-on pricing. An option's price, and each list items[].price, is a per-choice surcharge in USD the guest pays on top of the ticket rates (the customer-facing price, markup already applied). Options with no price (e.g. a flight number) are informational and free. The submitted options are echoed on the booking and replayed to the supplier when the agent issues it.

Returns { data: { uuid, code, status: "reserved", totalAmount, amountBreakdown[], options, links[] … }, timestamp }. Carry data.uuid β€” it is your permanent reference for the whole lifecycle, even after it is mapped onto the real supplier UUID. Full field list in Bookings.

6 β€” Confirm

Confirm the reserved booking to move it toward issuance. The status transitions reserved β†’ waiting (awaiting issuance), then to approved once issued.

PUT /api/Activity/v2/bookings/{uuid}/confirm
{ "SessionId": "…" }

The {status} path segment accepts confirm (β†’ waiting) or cancel (β†’ cancelled). See Booking status for the full status set.

7 β€” Get the voucher

Vouchers exist only once the booking has been issued. Poll the booking (GET /bookings/{uuid}) until status is issued, then fetch and download.

GET  /api/Activity/v2/bookings/{uuid}/vouchers?SessionId=…
GET  /api/Activity/v2/bookings/{uuid}/download-voucher/{voucher}?SessionId=…

vouchers returns { data: [ { uuid, generatedAt, visualId, links[] } ], timestamp } (empty until issued); download-voucher streams the voucher application/pdf through this gateway. You can also subscribe to webhook events (e.g. booking_status_updated) instead of polling.

Activity β€” Products #

The product catalogue of tours, attractions, and experiences. Each list item is additionally augmented by the gateway with image_url (main image) and images (full gallery) from the local image cache.

GET /Activity/v2/products?page=1&per_page=50 activity_products_list

Paginated list of products, served from the gateway's local catalogue cache (kept fresh by the daily sync + provider webhooks) rather than a live upstream call β€” so it's fast and resilient. Each item is augmented with image_url (main image) and images (full gallery), and the response keeps the same { data, meta.pagination } envelope. For the WHOLE catalogue in one call, use GET /v2/products/all.

Query Parameters

NameTypeDescription
qstringName search in ANY stored language (default title + translated titles). Matched rows carry matchedLanguage.
typestringFilter by product type β€” typeName/tourType (name substring) or typeUuid (exact). UUIDs from /config.
categorystringFilter by category β€” name substring or UUID.
countrystringFilter by location country β€” name substring or UUID.
citystringFilter by location city β€” name substring or UUID.
tagstringBroad classification match β€” any of the product's categories, type or tour type.
min_price / max_pricenumberFilter by the product's basePrice (USD) range. Combine freely with the other filters β€” all conditions AND together.
sortstringOne of price, -price, date, -date (date = last update), or bestselling (most bookings first). Every item carries a sold_count.
languagestringReturn each product's name in the same language you searched in (auto-detected from the query script), or force it with a code like KO.
page / per_pageintegerPagination (1-based). No cap β€” per_page=1000 returns 1000. Default 50.

Responses

200 OK β€” paginated array of product summaries:

{
  "data": [
    {
      "uuid": "52d1b30f-9314-432a-9ec8-406b525a4f5c",
      "updatedAt": "2026-06-06 08:00:14",
      "title": "Hong Kong Disneyland Park Ticket",
      "titleTranslated": "Hong Kong Disneyland Park Ticket",
      "validFrom": null,
      "validThrough": null,
      "basePrice": 16.91,
      "typeName": "Attraction",
      "typeUuid": "d3c54653-dd05-598f-b193-f6683d1064ab",
      "cityName": "Hong Kong",
      "links": [
        { "method": "GET", "rel": "self",         "href": "https://api.hermeseus.com/api/Activity/v2/products/52d1b30f-..." },
        { "method": "GET", "rel": "productTypes",  "href": "https://api.hermeseus.com/api/Activity/v2/products/52d1b30f-.../product-types" }
      ],
      "image_url": "https://cdn.example.com/images/content/original/b4d63993-....jpg",
      "images": [ "https://cdn.example.com/images/content/original/b4d63993-....jpg", "..." ]
    }
  ],
  "meta": { "pagination": { "page": 1, "pageCount": 3, "perPage": 50, "total": 142 } },
  "timestamp": "2026-06-07T14:10:58.976+08:00"
}
GET /Activity/v2/products/all activity_products_list

Returns the entire product catalogue in a single response, served from the gateway's local cache β€” there is no 100-per-page limit. Use this when you need to pull every product at once (e.g. to build your own index) instead of walking the paginated GET /v2/products list. The cache is refreshed by the gateway on a daily schedule, so results reflect the most recent sync. By default each row is a lightweight summary (UUID, name, main image, and the product's top-level scalar fields); pass full=1 to receive every product's complete object. Every row is augmented with image_url (and, in full mode, images) from the local image cache.

Query Parameters

NameTypeDescription
fullbooleanWhen 1/true, return each product's full object (heavy). Default returns a summary row.
qstringCase-insensitive name search. Matches the product name in any stored language β€” the default title plus every translated title field (title, titleTranslated, …). Descriptions are not matched.
typestringFilter by product type β€” typeName/tourType (name substring) or typeUuid (exact).
categorystringFilter by a category β€” categories[].name (substring) or categories[].uuid (exact). Categories are hierarchical: passing a parent category UUID also matches products in any of its child categories.
countrystringFilter by location country β€” locations[].country (substring) or locations[].countryUuid (exact).
citystringFilter by location city β€” locations[].city (substring) or locations[].cityUuid (exact).
tagstringBroad classification match β€” hits when the value matches any of the product's categories, type or tour type (name substring or UUID).
min_price / max_pricenumberFilter by basePrice (USD) range. ANDs with the other filters.
sortstringbestselling (most bookings first), price/-price, or date/-date. Every row carries a sold_count.
languagestringReturn each product's name in this language (e.g. KO, JA) when a translation exists. If omitted, a row that matched q is returned in the language q was written in β€” so a Korean query yields Korean names. Localized rows carry matchedLanguage.
per_page / pageintegerOptional chunking with no hard cap. Omit per_page to receive the whole (filtered) catalogue in one response.

All filters are optional and AND together. The response adds total (products matching the filters) next to count (rows in this response). UUIDs for country/city come from GET /v2/products/by-location; type/category UUIDs from GET /v2/config.

Responses

200 OK β€” summary rows (default; example for ?type=Attraction):

{
  "success": true,
  "count": 142,
  "total": 142,
  "data": [
    {
      "uuid": "52d1b30f-9314-432a-9ec8-406b525a4f5c",
      "name": "Hong Kong Disneyland Park Ticket",
      "image_url": "https://cdn.example.com/images/content/original/b4d63993-....jpg",
      "title": "Hong Kong Disneyland Park Ticket",
      "basePrice": 16.91,
      "currency": "USD",
      "typeName": "Attraction"
    }
  ]
}

With ?full=1 each item in data is the complete Product object (same shape as GET /v2/products/{uuid}), plus image_url and images.

GET /Activity/v2/products/by-location public Β· no auth

Public endpoint β€” no SessionId or credentials required. A breakdown of how many products are available in each country and, within it, each city β€” computed from the local catalogue cache (no upstream calls). Each country object lists its cities with a per-city product count and a country total. A product that spans several cities is counted once per distinct city, and once toward each distinct country's total (so total is the number of products available in that country, not the sum of its cities). Countries are sorted by total descending, cities by count descending.

Responses

200 OK:

{
  "success": true,
  "countries": 37,
  "products": 2249,
  "data": [
    {
      "country": "United Arab Emirates",
      "countryUuid": "1f2e3d4c-...",
      "cities": [
        { "city": "Dubai",     "cityUuid": "aaaa-...", "count": 128 },
        { "city": "Abu Dhabi", "cityUuid": "bbbb-...", "count": 31 }
      ],
      "total": 159
    },
    {
      "country": "Singapore",
      "countryUuid": "9a8b7c6d-...",
      "cities": [
        { "city": "Singapore", "cityUuid": "cccc-...", "count": 142 }
      ],
      "total": 142
    }
  ]
}
GET /Activity/v2/products/{uuid} activity_products_details

Full product metadata. Returns a single data object with descriptive fields (description, highlights, additionalInfo, priceIncludes/priceExcludes, itinerary, warnings, safety), commercial fields (basePrice, currency, isFlatPaxPrice, minPax/maxPax, reviewCount, reviewAverageScore, typeName/typeUuid), logistics (latitude, longitude, address, hotelPickup, airportPickup, businessHoursFrom/To, language lists), media (photos, image_url, images), categories, locations, and links. *Translated twins carry the localized text.

Path Parameters

NameTypeRequiredDescription
uuidstring (uuid)yesProduct UUID (from the products list).

Responses

200 OK β€” the Product object (excerpt):

{
  "data": {
    "uuid": "4dd77d8e-043b-4edb-b32a-0776043143ad",
    "updatedAt": "2026-05-16 08:03:13",
    "title": "MINT Museum of Toys Ticket",
    "titleTranslated": "MINT Museum of Toys Ticket",
    "description": "The MINT Museum of Toys houses Asia's largest collection of vintage toys ...",
    "highlights": "Delve into nostalgia with Asia's largest collection of vintage toys ...",
    "priceIncludes": "Admission ticket",
    "priceExcludes": "Hotel pickup and drop-off",
    "basePrice": 11.66,
    "currency": { "code": "USD", "symbol": "$" },
    "isFlatPaxPrice": false,
    "minPax": 1,
    "maxPax": 99,
    "reviewCount": 0,
    "reviewAverageScore": 0,
    "typeName": "Attraction",
    "typeUuid": "d3c54653-dd05-598f-b193-f6683d1064ab",
    "latitude": "1.2931", "longitude": "103.8520",
    "address": "26 Seah Street, Singapore 188382",
    "hotelPickup": false, "airportPickup": false,
    "categories": [ { "name": "Museums", "uuid": "..." } ],
    "locations": [ { "city": "Singapore", "country": "Singapore", "cityUuid": "...", "countryUuid": "..." } ],
    "photos": [ { "uuid": "...", "paths": { "original": "...", "1280x720": "..." } } ],
    "image_url": "https://cdn.example.com/images/content/original/....jpg",
    "images": [ "https://cdn.example.com/...jpg", "..." ],
    "links": [
      { "method": "GET", "rel": "self",         "href": "https://api.hermeseus.com/api/Activity/v2/products/4dd77d8e-..." },
      { "method": "GET", "rel": "productTypes",  "href": "https://api.hermeseus.com/api/Activity/v2/products/4dd77d8e-.../product-types" }
    ]
  },
  "timestamp": "2026-06-07T14:10:58.976+08:00"
}
GET /Activity/v2/products/{uuid}/restrictions activity_products_restrictions

Nationality / country-of-origin booking restrictions for a product. allow is a whitelist and exclude a blacklist of country codes; both empty means no restriction.

Response

{
  "data": { "exclude": [], "allow": [] },
  "timestamp": "2026-06-07T14:10:59.732+08:00"
}
GET /Activity/v2/products/{uuid}/product-types activity_product_types_list

Bookable variants of a product (e.g. β€œAdmission”, β€œAdmission with Guided Tour”, adult/child bundles). Each item carries the productTypeUuid you pass to POST /v2/bookings, plus links to its price-lists.

Response

{
  "data": [
    {
      "uuid": "80590844-e665-4fdc-843c-ffc6fe893403",
      "title": "Admission with Complimentary Guided Tour",
      "links": [
        { "method": "GET", "rel": "self",       "href": "https://api.hermeseus.com/api/Activity/v2/product-types/80590844-..." },
        { "method": "GET", "rel": "priceLists",  "href": "https://api.hermeseus.com/api/Activity/v2/product-types/80590844-.../price-lists" },
        { "method": "GET", "rel": "product",     "href": "https://api.hermeseus.com/api/Activity/v2/products/4dd77d8e-..." }
      ]
    }
  ],
  "timestamp": "2026-06-07T14:10:58.976+08:00"
}

Activity β€” Product Types #

GET /Activity/v2/product-types/{uuid} activity_product_types_details

Details of a single bookable variant: capacity (minPax/maxPax), per-category ticketTypes (adult/child/senior/infant/youth) with their gateRatePrice (USD), refundability (isNonRefundable, cancellationPolicies), durationDays/durationHours, voucher instructions, time slots, timezone, and options.

Path Parameters

NameTypeRequiredDescription
uuidstring (uuid)yesProduct-type UUID.

Responses

200 OK β€” the ProductType object (excerpt):

{
  "data": {
    "uuid": "68ec3814-b54e-4e9a-ba84-7a92cfadbfb2",
    "title": "Adult Pass",
    "durationDays": 30, "durationHours": 0,
    "firstAvailabilityDate": "2026-05-31",
    "isNonRefundable": true,
    "minPax": 1, "maxPax": 10,
    "instantConfirmation": true,
    "adultGateRatePrice": 25.54,
    "ticketTypes": [
      { "type": "adult",  "label": "Person", "allowed": true,  "min": 1, "max": 10, "gateRatePrice": 20 },
      { "type": "child",  "label": "Child",  "allowed": false },
      { "type": "senior", "label": "Senior", "allowed": false }
    ],
    "cancellationPolicies": [],
    "timeslots": [],
    "timezone": "Asia/Singapore",
    "options": { "perBooking": [], "perPax": [] },
    "links": [
      { "method": "GET", "rel": "self",       "href": "https://api.hermeseus.com/api/Activity/v2/product-types/68ec3814-..." },
      { "method": "GET", "rel": "priceLists",  "href": "https://api.hermeseus.com/api/Activity/v2/product-types/68ec3814-.../price-lists" },
      { "method": "GET", "rel": "product",     "href": "https://api.hermeseus.com/api/Activity/v2/products/701cc19d-..." }
    ]
  },
  "timestamp": "2026-05-31T04:09:10.378+08:00"
}
GET /Activity/v2/product-types/{uuid}/price-lists?date_start=2026-07-01&date_end=2026-07-31 activity_price_lists

Price & availability calendar for a product-type, one entry per date in the requested range (date_start / date_end, capped at 6 months from today). Each date lists availability per category, the bookable prices[] (each with a prices[].id = the priceId), voucherValidity, timezone and any cancellationPolicy.

Parameters

NameInTypeRequiredDescription
uuidpathstring (uuid)yesProduct-type UUID.
date_startquerystring (date)noRange start (default today).
date_endquerystring (date)noRange end (capped 6 months out).

Inside prices[].rates[] each rate has a type (retailPrice = sell price, nettPrice = your cost), a category (adult/child/senior), the USD amount, and a format string showing the original price (e.g. "S$30.00").

Response (excerpt)

{
  "data": [
    {
      "date": "2026-07-01",
      "weekday": "Wednesday",
      "availability": [
        { "category": "adult",  "type": "shared", "quantity": 999 },
        { "category": "child",  "type": "shared", "quantity": 999 },
        { "category": "senior", "type": "shared", "quantity": 999 }
      ],
      "cancellationPolicy": [],
      "currency": "USD",
      "timezone": "Asia/Singapore",
      "voucherValidity": { "adult": { "date": "2026-10-01" }, "child": { "date": "2026-10-01" }, "senior": { "date": "2026-10-01" } },
      "prices": [
        {
          "id": "ffa35637-4073-4432-9093-e79c5aa33b81",
          "rates": [
            { "type": "retailPrice", "category": "adult", "currency": "USD", "amount": 23.32, "format": "S$30.00" },
            { "type": "nettPrice",   "category": "adult", "currency": "USD", "amount": 18.66, "format": "S$24.00" }
            /* ... child, senior ... */
          ]
        }
      ]
    }
    /* ... one object per date in the range ... */
  ],
  "timestamp": "2026-06-07T14:11:00.000+08:00"
}
GET /Activity/v2/product-types/{uuid}/price-lists/{date} activity_price_lists

Checks real-time availability and price for a single {date} (YYYY-MM-DD): data is one price-list object with the same availability / prices[].rates[] shape.

Path Parameters

NameTypeRequiredDescription
uuidstring (uuid)yesProduct-type UUID.
datestring (date)yesThe date to check, YYYY-MM-DD.
βœ…
It is highly recommended to call this endpoint to confirm availability immediately before creating a booking.

Activity β€” Bookings #

POST /Activity/v2/bookings activity_create_booking

Create an activity booking. Returns the booking in reserved status; the gateway mirrors it locally and finalises it asynchronously. Use data.uuid as the reference for all follow-up calls β€” it keeps working for the whole lifecycle. The office balance is held for the booking total.

πŸ’‘
Check availability first. Always confirm price & availability with GET /v2/product-types/{uuid}/price-lists/{date} before creating a booking.

Request Body application/json

PropertyTypeRequiredDescription
SessionIdstringyesActive session id.
productTypeUuidstring (uuid)yesThe bookable variant to reserve (from GET products/{uuid}/product-types).
arrivalDatestring (date)yesVisit date, YYYY-MM-DD.
adultsintegeryesNumber of adult tickets. May be 0 only when the product-type's minPax = 0.
childrenintegernoNumber of child tickets. Default 0.
seniorsintegernoNumber of senior tickets. Default 0.
customerobjectyesLead customer: salutation, firstName, lastName, email, phone.
partnerReferencestringnoYour own booking id, echoed back on the booking.
messagestringnoFree-text message to the host/supplier.
timeSlotUuidstring (uuid)noRequired only when the product-type exposes time slots.
optionsobjectnoSelected add-on options: { "perBooking": [...], "perPax": [{ "uuid": "...", "value": "1" }] }. Each entry references an option uuid and its value.

Request β€” example

{
  "productTypeUuid": "5f3ff236-40b1-4f0a-a780-1db8649d1f5b",
  "arrivalDate": "2026-06-15",
  "partnerReference": "your-ref-123",
  "adults": 2,
  "children": 0,
  "seniors": 0,
  "customer": {
    "salutation": "Mr.",
    "firstName": "Jane",
    "lastName": "Doe",
    "email": "jane@example.com",
    "phone": "+971501234567"
  },
  "options": { "perBooking": [], "perPax": [] }
}

Responses

200 OK β€” the created booking (envelope { "data": Booking, "timestamp": ... }), status reserved.

{
  "data": {
    "uuid": "1d59a17e-9726-4283-a5b7-bab7886e4709",
    "code": "Z2KB84Z",
    "partnerReference": "your-ref-123",
    "status": "reserved",
    "productTypeTitle": "Diamond Head State Monument Self-guided Hike",
    "productTypeUuid": "5f3ff236-40b1-4f0a-a780-1db8649d1f5b",
    "currencyCode": "USD",
    "totalAmount": 18.50,
    "amountBreakdown": [ { "name": "adult", "quantity": 2, "price": "9.25" } ],
    "arrivalDate": "2026-06-15",
    "adults": 2, "children": 0, "seniors": 0,
    "ticketTypes": [ { "type": "adult", "quantity": 2 } ],
    "links": [ "... self / confirm / cancel / productType / product ..." ]
  },
  "timestamp": "2026-06-15T16:24:49.648+08:00"
}
GET /Activity/v2/bookings activity_list_bookings

Filterable, paginated list of your bookings.

Query Parameters

NameTypeDescription
date_start / date_endstring (date)Filter by arrival-date range.
first_name / last_namestringFilter by customer name.
email / phonestringFilter by customer contact.
partner_referencestringFilter by your own partnerReference.
querystringFree-text search (e.g. customer name).
statusstringFilter by status β€” see the Booking statuses table (reserved, waiting, cancellation_requested, cancelled, approved, expired, rejected, refunded, refund_declined).
page / per_pageintegerPagination.

Responses

200 OK β€” data is an array of Booking objects with meta.pagination.

GET /Activity/v2/bookings/{uuid} activity_booking_details

A single booking by its reference UUID.

Path Parameters

NameTypeRequiredDescription
uuidstring (uuid)yesBooking reference returned at create time, or the upstream UUID once issued.

Responses

200 OK β€” the Booking object:

{
  "data": {
    "uuid": "ae80dd16-42c7-485c-8d7e-703d948366eb",
    "code": "VKH3YHE",
    "partnerReference": "TTA_1780405735695943",
    "status": "reserved",
    "productTypeTitle": "Professional Photo Shoot in Bali - 1 Hour",
    "productTypeUuid": "97475b54-3378-4eb2-8f19-85c8b76255d8",
    "currencyCode": "USD",
    "totalAmount": 156.45,
    "amountBreakdown": [ { "name": "adult", "quantity": 1, "price": "156.45" } ],
    "arrivalDate": "2026-06-25",
    "timeSlot": null,
    "createdAt": "2026-06-02 21:08:59",
    "updatedAt": "2026-06-02 21:08:59",
    "salutation": "Mr.", "firstName": "Jane", "lastName": "Doe",
    "email": "jane@example.com", "phone": "+9120175399",
    "adults": 1, "children": 0, "seniors": 0,
    "ticketTypes": [ { "type": "adult", "quantity": 1 }, { "type": "child", "quantity": 0 }, { "type": "senior", "quantity": 0 } ],
    "options": [],
    "completedAt": null,
    "cancellationRequestAt": null,
    "cancellationRequestStatus": "none",
    "cancellationStatus": null,
    "refundDate": null, "refundAmount": null, "refundTransaction": null,
    "links": [
      { "method": "GET", "rel": "self",    "href": "https://api.hermeseus.com/api/Activity/v2/bookings/ae80dd16-..." },
      { "method": "PUT", "rel": "confirm", "href": "https://api.hermeseus.com/api/Activity/v2/bookings/ae80dd16-.../confirm" },
      { "method": "PUT", "rel": "cancel",  "href": "https://api.hermeseus.com/api/Activity/v2/bookings/ae80dd16-.../cancel" }
    ]
  },
  "timestamp": "2026-06-02T21:09:00.000+08:00"
}

Booking status values: reserved (created, awaiting confirmation), approved/confirmed (issued), cancelled, expired, rejected.

PUT /Activity/v2/bookings/{uuid}/{status} activity_update_booking_status

Drive a lifecycle transition with {status} = confirm or cancel:

  • A new booking starts in the reserved status.
  • confirm moves it to waiting and locks the inventory; on our side, funds equal to totalAmount are held on your office balance.
  • Bookings left in waiting expire if not actioned within 5 days.
  • cancel raises a cancellation request (subject to approval) and releases any active hold.

See the full Booking statuses table below.

Path Parameters

NameTypeRequiredDescription
uuidstring (uuid)yesBooking reference.
statusstringyesTarget transition β€” typically confirm or cancel.

Request Body application/json

Only SessionId is required; the rest of the body may be empty ({ "SessionId": "..." }).

Responses

200 OK β€” the updated booking status:

{
  "data": {
    "uuid": "ae80dd16-42c7-485c-8d7e-703d948366eb",
    "status": "reserved",
    "updatedAt": "2026-06-02 21:09:31"
  },
  "timestamp": "2026-06-02T21:09:31.053+08:00"
}

Booking statuses

A booking's status field moves through the lifecycle below (applies to both instant- and non-instant-confirmation products):

StatusMeaning
reservedCreated; inventory not yet locked. Confirm it (β†’ waiting) or it will expire.
waitingConfirmed by you; inventory locked, awaiting issuance. Expires if not actioned within 5 days.
approvedConfirmed/issued by the inventory source. Vouchers are available.
cancellation_requestedA cancellation request was raised and is pending approval.
cancelledCancellation approved. Any active balance hold is released.
expiredA reserved/waiting booking that timed out.
rejectedThe inventory source rejected the booking.
refundedCancelled and refunded.
refund_declinedA refund request was declined.
GET /Activity/v2/bookings/{uuid}/vouchers activity_get_vouchers

Retrieves the issued vouchers / tickets for a confirmed booking. Each voucher carries a uuid, generatedAt, an optional visualId, and links (a download link and a booking link). All links[].href point at this gateway β€” including the download link, which targets the voucher-download endpoint below.

πŸ”’
Voucher download links require an authenticated session β€” do not send them directly to the end customer. Download the file yourself (via the download endpoint) and distribute it.

Path Parameters

NameTypeRequiredDescription
uuidstring (uuid)yesBooking reference.

Responses

200 OK β€” list of vouchers:

{
  "data": [
    {
      "uuid": "81995901-b405-4cf6-8cf8-df284f104117",
      "generatedAt": "2026-05-28 16:25:15",
      "links": [
        { "rel": "download", "href": "https://api.hermeseus.com/api/Activity/v2/bookings/{uuid}/download-voucher/81995901-..." },
        { "rel": "booking",  "href": "https://api.hermeseus.com/api/Activity/v2/bookings/{uuid}" }
      ],
      "visualId": null
    }
  ],
  "meta": { "pagination": { /* ... */ } },
  "timestamp": "2026-06-02T06:40:59.864+08:00"
}
GET /Activity/v2/bookings/{uuid}/download-voucher/{voucher} activity_get_vouchers

Downloads the actual voucher file (usually a PDF). The gateway fetches it from the supplier and streams the bytes back through this domain β€” the supplier URL is never exposed. The response is the raw file with Content-Disposition: attachment, not JSON. {uuid} accepts either the booking reference you received at create time or the upstream UUID. As with every endpoint, include your SessionId (query param) when requesting the file.

curl -L -X GET \
  'https://api.hermeseus.com/api/Activity/v2/bookings/{uuid}/download-voucher/{voucher}?SessionId=...' \
  -o voucher.pdf
GET /Activity/v2/config activity_config

Fetches essential configuration: supported languages, currencies, locations (countries, cities), product types and categories, timezones, the base URLs, and your own user profile. The UUIDs returned here are the ones you pass to the type / category / country / city / language filters elsewhere.

πŸ‘€
The user block reflects your account: name/email/uuid are your credentials and the wallet* figures are your office balance (USD) β€” walletBalance = total, walletBlockedBalance = held (shown negative), walletAvailableBalance = available. serverUrl/photosUrl point at this gateway.

Responses

200 OK β€” configuration object (excerpt):

{
  "data": {
    "now": "2026-06-07T00:05:01.740500Z",
    "version": 2,
    "serverUrl": "https://api.hermeseus.com/api/Activity",
    "photosUrl": "https://api.hermeseus.com",
    "activitiesSorting": [ "date", "-date", "price", "-price", "distance", "-distance" ],
    "user": {
      "name": "<your office name>",
      "email": "<your email>",
      "uuid": "<your user uuid>",
      "defaultPagination": 50,
      "defaultSortBy": "date",
      "defaultCurrencyCode": "USD",
      "defaultLanguageCode": "EN",
      "walletBalance": 1000.00,
      "walletBlockedBalance": -250.50,
      "walletAvailableBalance": 749.50,
      "walletAlertValue": 0
    },
    "languages":  { "data": [ { "name": "english", "code": "EN", "uuid": "..." } ] },
    "countries":  { "data": [ { "name": "Singapore", "code": "SG", "uuid": "..." } ] },
    "currencies": { "data": [ { "code": "USD", "symbol": "$", "uuid": "cd15153e-..." } ] },
    "types":      { "data": [ { "name": "Attraction", "uuid": "..." } ] },
    "categories": { "data": [ { "name": "Museums", "uuid": "..." } ] }
  },
  "timestamp": "2026-06-07T08:05:01.740+08:00"
}

Activity β€” Webhook Event Polling #

The gateway captures upstream lifecycle events and exposes them as a polling API, so you do not need to stand up your own webhook receiver. Query the endpoint below to inspect the latest known state of a product, product-type, or booking.

EventUse
product_available / product_updatedCatalogue changes.
product_type_available / _not_available / _content_updated / _pricing_updated / _availability_updatedPer-variant changes.
booking_status_updatedBooking transitioned to a new status.
booking_data_updatedBooking metadata changed.
booking_tickets_updatedVoucher / ticket issued.
GET /Activity/v2/webhook-events/{event}/{uuid} activity_webhook_events

Latest payload received for that (event, UUID). Add ?history=1&limit=20 to get up to 50 most recent payloads instead.

Response β€” found

{
  "event": "booking_status_updated",
  "primaryUuid": "b8e2f01c-...",
  "found": true,
  "receivedAt": "2026-05-21T03:11:48+00:00",
  "eventTimestamp": "2026-05-21 11:11:42 SGT",
  "signature": "a1b2c3...",
  "payload": { "bookingUuid": "...", "currentStatus": "APPROVED", ... }
}

Response β€” none yet

{ "event": "...", "primaryUuid": "...", "found": false, "message": "No webhook of this type has been received yet for this UUID." }

Activity β€” Schemas #

Reusable object models referenced by the Activity operations above. All monetary fields are in USD and all HATEOAS links[].href point at this gateway. Many text fields have a *Translated twin carrying the localized value for the requested language.

Catalogue

ProductSummary products list item

FieldTypeDescription
uuidstring (uuid)Product UUID.
title / titleTranslatedstringProduct title (English / requested language).
basePricenumberBase price (USD).
typeName / typeUuidstringProduct type name & UUID.
validFrom / validThroughstring | nullAvailability window (null = no limit).
updatedAtstringLast update timestamp.
linksLink[]HATEOAS links (self, productTypes).
image_url / imagesstring / string[]Gateway-added main image & gallery.

Product

Full product. Includes all ProductSummary fields plus:

FieldTypeDescription
description, highlights, additionalInfostringRich content (each with a *Translated twin).
priceIncludes / priceExcludesstringWhat is / isn't included.
itinerary, warnings, safetystring | nullItinerary, safety & insurance warnings.
latitude / longitude / addressstringLocation.
minPax / maxPaxintegerPax limits (adults).
isFlatPaxPricebooleanSame price regardless of pax count.
reviewCount / reviewAverageScoreinteger / numberReview stats.
hotelPickup / airportPickupbooleanPickup availability (deprecated).
businessHoursFrom / businessHoursTostringSupplier business hours.
categoriesCategory[]Listing categories.
locationsLocation[]Where the product operates.
photosPhoto[]Gallery photos with sized paths.
guideLanguages, writtenLanguages, audioHeadsetLanguagesLanguage[]Available languages.

ProductType

FieldTypeDescription
uuidstring (uuid)Product-type UUID (used to book).
title / titleTranslatedstringVariant title.
description / descriptionTranslatedstringVariant description.
durationDays / durationHours / durationMinutesintegerActivity duration.
minPax / maxPaxinteger | nullBooking pax limits.
firstAvailabilityDatestringEarliest bookable date.
daysInAdvance / cutOffTimeinteger | nullLead-time / cut-off constraints.
isNonRefundablebooleanWhether the variant is non-refundable.
cancellationPolicies / cancellationPolicySummaryarray / stringCancellation terms (refundable variants).
allowAdults / allowChildren / allowSeniors / allowInfantbooleanPermitted ticket categories (see ticketTypes).
ticketTypesTicketType[]Per-category booking rules.
instantConfirmationbooleanBooking confirms instantly.
nonInstantVoucher / directAdmission / voucherRequiresPrintingbooleanVoucher handling rules.
voucherUse, voucherRedemptionAddress, meetingAddress, meetingTimestring | nullRedemption / meeting instructions (with *Translated twins).
validityobjectE-ticket / voucher validity rule.
timeslotsarrayTime slots, when applicable.
timezonestringProduct-type timezone.
options / hasOptionsProductTypeOptions / booleanAdd-on options.

TicketType

FieldTypeDescription
typestringCategory: adult / child / senior / infant / youth.
quantityintegerTicket quantity (in bookings) or allowed flag (in product types).

Pricing & availability

AvailabilityDate

FieldTypeDescription
date / weekdaystringTravel date and weekday.
availabilityAvailability[]Remaining quantity per category.
pricesPriceDetail[]Bookable price configurations.
currencystringCurrency (USD).
timezonestringProduct-type timezone.
cancellationPolicyarrayPer-date cancellation terms.
voucherValidityobjectVoucher validity per category for this date.

Availability

FieldTypeDescription
categorystringTicket category.
typestringInventory type (e.g. shared, exclusive).
quantityintegerRemaining quantity.

PriceDetail

FieldTypeDescription
idstring (uuid)Price configuration id.
ratesRateDetail[]Per-category rates.

RateDetail

FieldTypeDescription
typestringRate type: retailPrice (sell) or nettPrice (cost).
categorystringTicket category.
amountnumberPrice in USD.
currencystringCurrency (USD).
formatstringOriginal supplier price string, e.g. "S$30.00".

Bookings

Booking BookingDetails

FieldTypeDescription
uuidstring (uuid)Booking reference (your permanent id).
codestring7-character booking code.
partnerReferencestringYour own reference.
statusstringSee Booking statuses.
productTypeTitle / productTypeUuidstringBooked variant.
totalAmountnumberTotal price (USD).
currencyCode / currencyUuidstringCurrency (USD).
amountBreakdownAmountBreakdown[]Per-category price breakdown.
arrivalDate / timeSlotstringVisit date and time slot.
adults / children / seniorsintegerPax counts.
ticketTypesTicketType[]Per-category quantities.
salutation, firstName, lastName, email, phonestringLead customer.
optionsarraySelected add-on options.
completedAt, cancellationRequestAt, cancellationRequestStatus, cancellationStatusstring | nullLifecycle timestamps / states.
refundDate / refundAmount / refundTransactionstring | number | nullRefund details.
createdAt / updatedAtstringTimestamps.
linksLink[]HATEOAS links (self, confirm, cancel).

BookingCustomer

FieldTypeDescription
salutationstringe.g. Mr., Ms..
firstName / lastNamestringLetters, spaces, hyphen and apostrophe only.
emailstring (email)Customer email.
phonestringCustomer phone.

AmountBreakdown

FieldTypeDescription
namestringCategory (e.g. adult).
quantityintegerNumber of tickets.
pricestringPer-ticket price (USD, string).

BookingOptions

Sent on POST /v2/bookings as options:

FieldTypeDescription
perBooking{ uuid, value }[]Options applied once per booking.
perPax{ uuid, value }[]Options applied per passenger.

Vouchers

BookingVoucher

FieldTypeDescription
uuidstring (uuid)Voucher UUID.
generatedAtstringWhen the voucher was generated.
downloadedAtstring | nullWhen it was last downloaded via the API.
visualIdstring | nullDisplay id, when applicable.
linksLink[]download (file) and booking links on this gateway.

Reference (config)

Compact lookup objects returned by GET /v2/config; each list is wrapped as { "data": [ ... ] }.

SchemaFields
Currencycode (e.g. USD), symbol, uuid
Languagename, code (e.g. EN), uuid
ActivityTypename, uuid
Categoryname, uuid, children (nested Category[])
Countryname, code, uuid, states (State[])
Statename, uuid, cities (City[])
Cityname, uuid
Locationcity/cityUuid, state/stateUuid, country/countryUuid
Photouuid, caption, paths ({ original, 1280x720, … })

Common

SchemaFields
Linkmethod, rel, href (always on this gateway)
Paginationpage, pageCount, perPage, total (under meta.pagination)
ErrorResponseerror.code, error.message, error.http_code

Common #

POST /Common/CreditBalance credit_balance

Returns the current office balance, held funds and available balance in USD.

Response

{
  "Success": true,
  "Balance": 12450.00,
  "HeldBalance": 340.20,
  "AvailableBalance": 12109.80,
  "Currency": "USD",
  "Error": null
}
POST /Common/ErrorDetails error_details

Human-readable explanation for any Err... code.