@molecule/api-hotels-amadeus
Provider bond · hotels · API (Node) · v1.0.2 · Apache-2.0
Amadeus self-service Hotel Search API provider bond.
npm install @molecule/api-hotels-amadeusnpm · Source on GitHub · Implements @molecule/api-hotels
How it works
@molecule/api-hotels-amadeus is a provider bond on the API (Node) side: it implements the hotels core interface (@molecule/api-hotels) with a concrete vendor or library behind it.
Your code calls the core; you wire this provider once at startup. Swapping vendors later is one line in that wiring, not a rewrite.
import { setProvider } from '@molecule/api-hotels'
import { provider } from '@molecule/api-hotels-amadeus'
setProvider(provider)Works with: @molecule/api-hotels, @molecule/api-secrets
Secrets: AMADEUS_CLIENT_ID, AMADEUS_CLIENT_SECRET
Reference
Amadeus hotels provider for molecule.dev.
Implements the HotelsProvider interface against the Amadeus
Self-Service hotels APIs. Provides hotel search (city or geo),
priced offer lookup (/v3/shopping/hotel-offers), and a booking
stub that explicitly surfaces "use the hosted checkout flow"
(Amadeus's direct hotel-booking endpoint requires PCI-compliant
card capture and is not safely callable from a generic bond).
Authentication uses Amadeus's OAuth2 client-credentials flow, with
the same AMADEUS_CLIENT_ID / AMADEUS_CLIENT_SECRET env vars
shared with @molecule/api-flights-amadeus. The bond mints and
caches tokens per-provider-instance.
The OAuth secret NEVER appears in error messages. URLs do not carry
authentication in the query string — the bearer token is sent via
the Authorization header — so URL-redaction is unnecessary.
Auto-generated, AI-first package reference for the molecule.dev ecosystem. It is written to be read by coding agents as much as by people, and is generated from this package's source — edit
src/index.tsJSDoc, not this file.
Quick Start
import { setProvider } from '@molecule/api-hotels'
import { provider } from '@molecule/api-hotels-amadeus'
setProvider(provider)
Type
provider
Installation
npm install @molecule/api-hotels-amadeus @molecule/api-hotels @molecule/api-secrets
API
Interfaces
AmadeusHotelsConfig
Configuration options for the Amadeus hotels provider.
Amadeus exposes a single OAuth2 client-credentials flow that is shared
across all of its product APIs (flights, hotels, points-of-interest,
etc.). The same AMADEUS_CLIENT_ID / AMADEUS_CLIENT_SECRET env vars
therefore drive both @molecule/api-flights-amadeus and
@molecule/api-hotels-amadeus. Token caching is per-provider-instance
— there is no cross-bond shared cache, but the OAuth pattern is
identical so credentials work interchangeably.
Amadeus exposes a free Self-Service "test" environment at
https://test.api.amadeus.com (sandbox data, generous rate limits) and a
paid production environment at https://api.amadeus.com. This bond
defaults to the TEST host — identical to @molecule/api-flights-amadeus
— so flights + hotels wired with one key pair hit the same host and a
token minted on one is accepted by the other. Set useProduction
(or the AMADEUS_USE_PRODUCTION=true env var, which both bonds honor) to
switch; a single env setting flips both together.
interface AmadeusHotelsConfig {
/**
* OAuth2 client ID, sent as the `client_id` form field when minting a
* fresh access token.
*
* If omitted, the provider falls back to the `AMADEUS_CLIENT_ID`
* environment variable. Requests fail with a sanitized error if
* neither is set.
*/
clientId?: string
/**
* OAuth2 client secret, sent as the `client_secret` form field when
* minting a fresh access token.
*
* If omitted, the provider falls back to the `AMADEUS_CLIENT_SECRET`
* environment variable. The secret is NEVER included in error
* messages — token-mint failures redact it before bubbling up.
*/
clientSecret?: string
/**
* When `true`, routes requests to the production endpoint
* (`https://api.amadeus.com`). When `false` or omitted, uses the
* Self-Service test sandbox (`https://test.api.amadeus.com`). Falls back
* to the `AMADEUS_USE_PRODUCTION=true` env var, which
* `@molecule/api-flights-amadeus` reads too — so one env setting flips
* both bonds to production together.
*/
useProduction?: boolean
/**
* Base URL override. Takes precedence over `useProduction`. When
* neither is set, defaults to the Self-Service test sandbox
* (`'https://test.api.amadeus.com'`). Pass `'https://api.amadeus.com'`
* for production, or any proxy URL for self-hosted setups.
*/
baseUrl?: string
/**
* Request timeout in milliseconds for both the token-mint call and the
* data-API calls. Defaults to `10000`.
*/
timeout?: number
/**
* Number of seconds to subtract from the upstream `expires_in` value
* before treating a cached token as stale. Defaults to `30`. Lower
* values reduce wasted token mints; higher values reduce the chance
* of using a token that expires mid-request.
*/
tokenSkewSeconds?: number
}
Functions
sanitizeErrorMessage(message)
Returns a sanitized copy of an error message body. Currently a no-op passthrough for the common "errors[].detail" shape — exposed for symmetry with the flights bond and to keep the redaction surface documented in one place.
function sanitizeErrorMessage(message: string): string
message— A free-form upstream error message.
Returns: The same message, with any future-redacted patterns scrubbed.
createProvider(config)
Creates an Amadeus hotels provider.
function createProvider(config?: AmadeusHotelsConfig): HotelsProvider
config— Provider configuration. Credentials may be supplied here directly or via theAMADEUS_CLIENT_IDandAMADEUS_CLIENT_SECRETenvironment variables. Defaults to the Self-Service TEST host; setuseProduction(orAMADEUS_USE_PRODUCTION) to route to production.
Returns: A HotelsProvider backed by Amadeus.
Constants
provider
The default provider implementation, lazily initialized on first use.
Reads AMADEUS_CLIENT_ID, AMADEUS_CLIENT_SECRET,
AMADEUS_USE_PRODUCTION, and AMADEUS_BASE_URL from environment
variables — the same set @molecule/api-flights-amadeus reads, so a
single AMADEUS_USE_PRODUCTION=true flips BOTH bonds to production
together. Defaults to the Self-Service TEST host. Use
createProvider directly if you need to supply configuration
programmatically.
const provider: HotelsProvider
hotelsAmadeusSecretDefinitions
Secret definitions required by the Amadeus hotels bond.
const hotelsAmadeusSecretDefinitions: SecretDefinition[]
MISSING_CREDENTIALS
Error code raised when a hotels-provider call is attempted with no
AMADEUS_CLIENT_ID and/or AMADEUS_CLIENT_SECRET configured (neither
via the config object nor via environment variables). Surfaced via
Error.cause so callers can distinguish it from upstream errors.
const MISSING_CREDENTIALS: 'MISSING_CREDENTIALS'
TOKEN_MINT_FAILED
Error code raised when the OAuth2 token-mint call fails (e.g. invalid
credentials, network error, non-2xx status). The error message NEVER
includes the raw client_secret.
const TOKEN_MINT_FAILED: 'TOKEN_MINT_FAILED'
UPSTREAM_ERROR
Error code raised when an Amadeus hotels data API call fails (e.g. a
non-2xx HTTP status, a structured errors[] body, etc.).
const UPSTREAM_ERROR: 'UPSTREAM_ERROR'
BOOKING_NOT_SUPPORTED
Error code raised when HotelsProvider.bookHotel is called.
Amadeus's hotel-booking endpoint requires PCI-compliant card capture
and is not safely callable from a generic backend bond — callers
should use Amadeus's hosted checkout / "price the offer" flow
instead.
const BOOKING_NOT_SUPPORTED: 'BOOKING_NOT_SUPPORTED'
Core Interface
Implements @molecule/api-hotels interface.
Bond Wiring
Setup function to register this provider with the core interface:
import { setProvider } from '@molecule/api-hotels'
import { provider } from '@molecule/api-hotels-amadeus'
export function setupHotelsAmadeus(): void {
setProvider(provider)
}
Injection Notes
Requirements
Peer dependencies:
@molecule/api-hotels^1.0.1@molecule/api-secrets^1.0.1
Environment Variables
AMADEUS_CLIENT_ID(required) — Amadeus API key- Setup: Create an app in Amadeus for Developers (Self-Service) and copy the API Key.
- Get it here: https://developers.amadeus.com/my-apps
AMADEUS_CLIENT_SECRET(required) — Amadeus API secret- Setup: Copy the API Secret from your Amadeus app page.
- Get it here: https://developers.amadeus.com/my-apps
Runtime Dependencies
-
@molecule/api-hotels -
@molecule/api-secrets -
Defaults to the TEST sandbox host (
test.api.amadeus.com) — identical to@molecule/api-flights-amadeus. Amadeus issues Self-Service TEST keys first (production needs approval), and a token is host-specific, so the safe default is TEST. SetAMADEUS_USE_PRODUCTION=true(oruseProduction/baseUrloncreateProvider()) to route to production. Because BOTH the flights and hotels bonds read the sameAMADEUS_USE_PRODUCTIONenv var, one setting flips them together — so a travel app wiring flights + hotels with one key pair never has one bond 401ing on the wrong host. -
bookHotel()ALWAYS throws (see the core's remarks) — implement checkout on the vendor's hosted flow; search and priced offers are fully supported.
E2E Tests
Integration checklist — drive the real UI (live preview, no mocks), adapt each item to this app's actual screens/flows, and check every box off one by one. A box you can't check is an integration bug to fix — not a skip:
- A search for a real location + check-in/check-out dates + guest count
(
searchHotelswith acityCodeorlocation) returns REALHotelSearchResults rendered in the UI — each with a name, its city /address, and afromPricefor the stay — never an empty list, a stuck spinner, or placeholder cards. Results match the query: the right city, and the dates / occupancy you entered are reflected in the prices shown. - Any exposed filter or sort (price, star
rating, amenities) actually narrows / reorders the rendered list — e.g. a price sort puts the lowestHotelPrice.totalfirst; a 4–5 star filter drops lower-rated properties. - Availability is respected: a sold-out or invalid-date search (e.g.
checkOutDatenot strictly aftercheckInDate) shows a visible "no availability" empty state — never a crash, a blank screen, or fabricated results. - Prices total correctly and every amount shows its currency: a shown
HotelOffer.price.totalequals nights × nightly rate + any fees, in its ISO 4217HotelPrice.currency(no bare "123" with no symbol or code). - If hotel detail / booking is exposed, opening a hotel calls
getHotelOffersand shows its real rooms / rates (roomDescription+price); selecting one records the chosenofferIdin the app. Booking itself goes out-of-band to the vendor (orbookHotelthrowsBOOKING_NOT_SUPPORTED→ a redirect) — verify the app's RECORDED selection, not a fake in-app confirmation. - A provider error (upstream down / rate-limited) surfaces as a graceful, visible message — not a blank page or an unhandled rejection — and the provider API key stays server-side: search / offers / booking all run on the server, and no key appears in network responses or page source.