Blog

Hotelbeds API Integration Guide for Hotel Booking Systems

Jun 30, 2026Hardeep SinghBlog

Hotelbeds API Integration Guide for Hotel Booking Systems

Hotelbeds API Integration Guide for Hotel Booking Systems

A clean Hotelbeds API integration rarely means calling one endpoint and being done. Hotelbeds APItude exposes a family of HTTPS APIs — hotels, activities, transfers — each with its own base path, but a shared authentication model and a familiar booking funnel: search → detail → hold → confirm.

This guide walks through the architecture behind a multi-supplier CRS (Central Reservation System) booking engine that wraps Hotelbeds and other suppliers behind a single token-based B2B/B2C API, blends live Hotelbeds inventory with offline partner inventory, and supports the two-step confirmation flow required when payment happens outside Hotelbeds.

Why a wrapper layer, not a direct call

Hotelbeds delivers inventory over HTTPS with JSON. Each product line has its own hostname path:

Product Test base URL (example)
Activities https://api.test.hotelbeds.com/activity-api/3.0
Transfers https://api.test.hotelbeds.com/transfer-api/1.0
Hotels https://api.test.hotelbeds.com/hotel-api/1.0

The booking funnel stays consistent across all three:

  1. Availability / Search — real-time listing with filters
  2. Details / CheckRate — drill into one product and a set of operational dates
  3. Booking / Post-booking — confirm, cancel, modify, list

For B2C sites collecting payment through an external gateway like Stripe, Hotelbeds recommends a two-step flow: Preconfirm (locks the allotment) → payment → Reconfirm (finalizes the booking). A single confirm call books immediately — acceptable for B2B where payment is handled offline, but risky for B2C where a card charge can still fail.

Authentication: the X-Signature pattern

Every APItude request carries two headers:

  • Api-key — the API key
  • X-SignatureSHA256(apiKey + secret + unixTimestampSeconds)

This logic belongs in a single shared utility reused across every product adapter rather than duplicated per service:

sequenceDiagram participant App as Backend participant HB as Hotelbeds APItude App->>App: ts = floor(now / 1000) App->>App: sig = SHA256(apiKey + secret + ts) App->>HB: POST /activities/availability Note over App,HB: Headers: Api-key, X-Signature, Content-Type: application/json HB-->>App: 200 JSON (activities[]) or errors[]

A few production lessons worth building in from day one:

  • Signatures are time-based, so clock skew between servers can cause intermittent 401s — keep NTP synced.
  • Log every request and response body in a dedicated supplier request log; it's invaluable for admin debugging and certification.
  • Treat a JSON errors[] array in the response body as a failure even when the HTTP status is 200.

The standard APItude booking funnel

Hotelbeds documents several funnel variants. The one that maps cleanly to a token-based CRS API looks like this:

flowchart LR A[Availability / Search] --> B[Details + Rates] B --> C{Payment outside HB?} C -->|No - B2B| D[Confirm] C -->|Yes - B2C| E[Preconfirm] E --> F[External payment] F --> G[Reconfirm] D --> H[Booking created] G --> H H --> I[Order info / Cancel]
APItude step Booking engine endpoint (Activities example) What the client holds
Availability POST /activities/search Search token per activity
Details GET /activities/details Same search token
Modality & rates POST /activities/modality-rates Rate token per bookable row
Rate hold (optional) POST /activities/hold Hold token
Prebook POST /activities/bookings/prebook booking_id + prebook token
Preconfirm PUT /activities/bookings/preconfirm Locks allotment (~30 min)
Reconfirm / Confirm PUT /activities/bookings/reconfirm or POST .../confirm Confirmed order + vouchers

Hotelbeds returns a rateKey on availability and detail responses. That key should never reach API clients directly — store it server-side against an internal rate token record and issue an opaque token (something like arate_…) to the frontend instead.

Architecture: supplier factory + blender

The core design problem is multi-supplier orchestration — live Hotelbeds inventory, offline partner inventory, and (for hotels) a separate live supplier — without duplicating booking logic in every controller.

flowchart TB subgraph Client["Booking engine / B2C web"] B2B[B2B API] B2C[B2C API] end subgraph Orchestration["Orchestration layer"] BL[Blender: search merge + token mint] SF[Supplier factory: route by channel / rate token] end subgraph Suppliers["Supplier adapters"] CRS[Offline CRS service] HB[Hotelbeds adapter] OtherHotel[Other hotel supplier adapter] end subgraph Persist["Postgres CRS tables"] ST[Search tokens] RT[Rate tokens] BD[Booking drafts] OR[Orders + supplier snapshots] end B2B --> BL B2C --> BL BL --> CRS BL --> HB BL --> ST SF --> CRS SF --> HB SF --> OtherHotel SF --> RT SF --> BD SF --> OR

Supplier factory

A thin router selects the adapter based on the search channel or rate token metadata — picking the Hotelbeds adapter when a channel is explicitly Hotelbeds, or the offline CRS service otherwise, and doing the same lookup by inspecting whether a rate token carries an external rate key. The same pattern applies identically for transfers and hotels: hotel suppliers register against an api_slug map, with a slot reserved for a Hotelbeds entry alongside whichever live hotel supplier is active today.

Every supplier implements a common interface so the blender and booking controllers stay supplier-agnostic — no branching on supplier name anywhere in controller code.

Blender: parallel search + dedupe

On search, the activity blender (and the equivalent hotel blender) calls all configured suppliers in parallel, normalizes offers to a common shape, deduplicates by a stable key, and picks a winner — typically lowest price with a preference for refundable rates.

sequenceDiagram participant API as B2B/B2C controller participant BL as Activity Blender participant CRS as Offline CRS participant HB as Hotelbeds Activities API participant DB as Postgres API->>BL: searchAndMintTokens(criteria) par Parallel supplier search BL->>CRS: searchActivities() BL->>HB: POST .../activities/availability end BL->>BL: normalise + dedupe + pickWinner BL->>DB: mint search token per row BL-->>API: activities[] with opaque tokens

A few Hotelbeds Activities quirks are worth encoding directly in the adapter:

  • The GPS filter must be shaped as { "type": "gps", "latitude": …, "longitude": … } — the older comma-separated string format returns E_REQUEST_MISSINGFILTERVALUE in test and live.
  • Content enrichment: unmapped service codes should trigger a call to the Activity Content API to upsert canonical, prefixed activities, so browsing doesn't depend on a full static import.
  • Rate key TTL is roughly 30 minutes — expiring internal tokens a couple of minutes early avoids edge-case confirm failures near the boundary.

Token system: hiding supplier internals from clients

A recurring mistake in supplier integrations is leaking hotel_id, rateKey, or supplier booking references to the frontend. A well-designed CRS API stays token-only after search — the same pattern documented for hotels and reused for activities and transfers.

stateDiagram-v2 [*] --> SearchToken: POST /search SearchToken --> RateToken: modality-rates / room-list RateToken --> HoldToken: hold / block (optional) HoldToken --> PrebookToken: prebook + booking_id PrebookToken --> ConfirmedOrder: confirm / reconfirm ConfirmedOrder --> [*]: order info by booking_id only
Token Typical TTL Backend stores
Search ~60 min Filters, dates, paxes, channel, resolved product id
Rate ~28–60 min External rate key (Hotelbeds) or rate plan id (offline)
Hold / Block ~15 min Allotment lock context
Prebook ~45 min Guest details, supplier preconfirm reference

This matters for three reasons: clients can't scrape the full catalogue by iterating supplier IDs, suppliers can be swapped or remapped without breaking client integrations, and expired tokens return a stable error code that's trivial to handle in the UI as "search again."

B2C: preconfirm before payment, reconfirm after

Hotelbeds doesn't provide a payment gateway for activities. With a hosted checkout flow like Stripe, preconfirming first is mandatory — otherwise a failed card charge can leave a confirmed booking in place, or a successful charge can lose its allotment.

sequenceDiagram participant U as User participant FE as Frontend participant API as B2C API participant BL as Blender participant HB as Hotelbeds participant ST as Payment gateway U->>FE: Book experience FE->>API: POST /bookings/prebook API->>BL: preBook + supplierPreconfirm BL->>HB: PUT /bookings/preconfirm Note over HB: Allotment locked ~30 min API-->>FE: booking_id, price FE->>API: POST /bookings/:id/checkout-session API->>ST: create session U->>ST: Pay ST-->>FE: redirect success URL loop Poll sync-payment FE->>API: POST /bookings/:id/sync-payment API->>ST: verify session paid API->>BL: processBooking (reconfirm) BL->>HB: PUT /bookings/reconfirm end API-->>FE: order + vouchers
Channel Preconfirm Reconfirm / Confirm
B2B Optional; often bundled into confirm Client pays offline; single confirm step
B2C (hosted checkout) At prebook — before checkout After payment succeeds, via sync-payment

Offline CRS suppliers can simply treat the preconfirm step as a no-op, so the same B2C flow works regardless of whether a given offer resolved to Hotelbeds or to offline inventory.

Data model: canonical product + supplier maps

Whether inventory is live API or offline, it's worth keeping one canonical product row and mapping supplier codes to it, rather than letting supplier-specific identifiers leak into application logic:

erDiagram CRS_ACTIVITIES ||--o{ CRS_SUPPLIER_ACTIVITY_MAP : "mapped by" CRS_SUPPLIERS ||--o{ CRS_SUPPLIER_ACTIVITY_MAP : owns CRS_ACTIVITIES ||--o{ CRS_MODALITIES : has CRS_SEARCH_TOKEN ||--o| CRS_RATE_TOKEN : leads_to CRS_RATE_TOKEN ||--o| CRS_BOOKING_DRAFT : prebook CRS_BOOKING_DRAFT ||--o| CRS_ORDER : confirm CRS_ACTIVITIES { string code string name string default_currency } CRS_SUPPLIER_ACTIVITY_MAP { string supplier_activity_code uuid activity_id } CRS_RATE_TOKEN { string external_rate_key string token }

For Hotelbeds, codes are often prefixed after content import. Manual seed maps can bridge certification test codes to canonical activities during development, ahead of full automated mapping.

Hotel booking: same pattern, supplier-agnostic by design

The hotel CRS can follow an identical token funnel:

Search → Details → Room List → Block → Prebook → Confirm → Order Info / Cancel
flowchart TD S[POST /search GPS + dates + guests] D[GET /hotels/details?token=...] R[POST /hotels/rooms] B[POST /hotels/block 15 min hold] P[POST /bookings/prebook] C[POST /bookings/confirm] O[GET /bookings/:booking_id] S -->|search token| D D --> R R -->|rate token| B B -->|block token| P P -->|booking_id + token| C C --> O

Live hotel availability can come from any supplier today, as long as the API contract and database schema are designed to match Hotelbeds-style semantics — cancellation policy windows, meal plans, opaque tokens. Adding a Hotelbeds Hotel adapter later then becomes a matter of implementing the common hotel service interface, registering it under its supplier slug, seeding the supplier table, and mapping a search channel to that slug — the blender already merges offline and live offers the same way it does for activities.

Observability, certification, and operations

A handful of practices consistently save time during Hotelbeds certification and staging:

Practice Implementation
Request/response logging A dedicated supplier API request log, scoped per module and per supplier slug
Postman collections Reusable collections per product for end-to-end replay
Environment separation Distinct test and live base URLs driven entirely by environment variables
Scripted end-to-end tests Booking flow scripts that exercise the full funnel automatically
Error normalization Mapping Hotelbeds errors[] into stable internal codes like soldout, rate_not_found, token_invalid
flowchart LR subgraph Runtime A[Adapter HTTP client] L[Supplier API log] ADM[Admin supervision UI] end A --> L L --> ADM ADM --> CERT[Certification / support replay]

Checklist for a Hotelbeds integration

  1. Shared auth module — one signature helper reused across every APItude product.
  2. Adapter per product — implement a common interface; never branch on supplier name in controllers.
  3. Token facade — never expose rateKey or supplier references to clients.
  4. Blender for multi-source search — parallel search, normalize, dedupe, mint tokens.
  5. Two-step confirm for B2C — preconfirm before payment, reconfirm after.
  6. Canonical catalogue + maps — one product row, many supplier codes.
  7. Content API for browse — optional runtime import instead of full static dumps.
  8. Logging from day one — certification and production debugging both depend on it.
  9. Factory registration — new suppliers plug in without rewriting the booking engine.

Summary

Hotelbeds APItude isn't a single "hotel booking API" — it's a consistent integration style across hotels, activities, and transfers: signed requests, filter-driven availability, rate keys, and optional two-step confirmation for external payments.

The most durable approach is not wrapping Hotelbeds' raw endpoints directly in a public API, but building a CRS booking engine that's token-based, multi-supplier, and blender-mediated, with Hotelbeds adapters for whichever products are live today and a hotel layer ready for the same pattern tomorrow. That separation makes it possible to pass certification, add offline partners, and swap or add suppliers later without rewriting the frontend or partner integrations.

For teams starting a similar project, investing early in tokens, supplier factories, and request logging pays off across every product line APItude offers.


Planning a Hotelbeds integration or a multi-supplier CRS booking engine? Get in touch with Teenva AI & Digital Ventures at sales@teenvaai.com or +91 9572020107.

Similar Articles