← All @molecule/* packages · App templates
@molecule/api-travelCore interface · travel · API (Node) · v1.0.1 · Apache-2.0
Travel trip-planning aggregator (flights + hotels + cars + activities) core interface for molecule.dev
npm install @molecule/api-travel@molecule/api-travel is the travel core interface on the API (Node) side: the API your app calls, with no vendor inside.
Choose the implementation by bonding one of its 1 provider: @molecule/api-travel-amadeus.
import { setProvider, searchTripOptions } from '@molecule/api-travel'
import { provider as amadeus } from '@molecule/api-travel-amadeus'
setProvider(amadeus)
const trip = await searchTripOptions({
origin: 'JFK',
destination: 'PAR',
departureDate: '2026-07-15',
returnDate: '2026-07-22',
travelers: { adults: 2 },
includeFlights: true,
includeHotels: true,
})
console.log(trip.flights.length, trip.hotels.length)Providers (1): @molecule/api-travel-amadeus
Works with: @molecule/api-bond, @molecule/api-i18n
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.
Provider-agnostic travel trip-planning interface for molecule.dev.
Defines the {@link TravelProvider} interface for aggregate trip
planning across flights, hotels, cars and activities. Bond packages
(Amadeus, Travelport, Sabre, etc.) implement this interface.
Application code uses the convenience functions (searchTripOptions,
searchActivities, searchCars) which delegate to the bonded
provider.
import { setProvider, searchTripOptions } from '@molecule/api-travel'
import { provider as amadeus } from '@molecule/api-travel-amadeus'
setProvider(amadeus)
const trip = await searchTripOptions({
origin: 'JFK',
destination: 'PAR',
departureDate: '2026-07-15',
returnDate: '2026-07-22',
travelers: { adults: 2 },
includeFlights: true,
includeHotels: true,
})
console.log(trip.flights.length, trip.hotels.length)
core
npm install @molecule/api-travel @molecule/api-bond @molecule/api-i18n
ActivityOfferNormalized activity / experience offer (e.g. a museum tour, food walk, day trip) surfaced inside a {@link TripSearchResult}.
interface ActivityOffer {
/**
* Provider-specific opaque offer / activity identifier.
*/
id: OfferId
/**
* Human-readable activity name (e.g. `'Eiffel Tower skip-the-line'`).
*/
name: string
/**
* Free-form short description, if the provider exposes one.
*/
description?: string
/**
* Per-person or per-booking price (provider-defined). Treat as the
* starting "from" price unless the provider documents otherwise.
*/
price: MoneyAmount
/**
* Geographic point the activity takes place at, when known.
*/
location?: GeoLocation
/**
* URL of a representative image, if the provider exposes one.
*/
pictureUrl?: string
/**
* URL of the provider's booking / detail page, if exposed.
*/
bookingUrl?: string
/**
* Provider-supplied minimum duration string (e.g. `'PT2H'`), if any.
*/
minimumDuration?: IsoDuration
}
CarOfferNormalized car-rental offer surfaced inside a {@link TripSearchResult}.
Most major travel aggregators expose car offers as a separate vertical; the trip facade includes them so itinerary planners can present a unified "ground transport" line item alongside flights and hotels.
interface CarOffer {
/**
* Provider-specific opaque offer identifier.
*/
id: OfferId
/**
* Vendor / supplier name (e.g. `'Hertz'`, `'Avis'`).
*/
vendor: string
/**
* Free-form vehicle / category description (e.g.
* `'Compact SUV or similar'`).
*/
vehicleDescription: string
/**
* Total price for the entire rental.
*/
price: MoneyAmount
/**
* Pickup location (IATA airport / city code or free-form locality).
*/
pickupLocation: LocationCode
/**
* Pickup date / time.
*/
pickupAt: IsoDateTime
/**
* Return date / time.
*/
returnAt: IsoDateTime
/**
* Whether unlimited mileage is included. `undefined` when unknown.
*/
unlimitedMileage?: boolean
}
FlightOfferNormalized flight offer surfaced inside a {@link TripSearchResult}.
This is a minimal, travel-core-local shape — not the same TypeScript
type as @molecule/api-flights's FlightOffer. Providers that wrap
the flights core MAY convert between the two structurally.
interface FlightOffer {
/**
* Provider-specific opaque offer identifier.
*/
id: OfferId
/**
* Total grand-total price for ALL travelers.
*/
price: MoneyAmount
/**
* Flight segments in chronological order. For round-trip itineraries
* outbound segments precede return segments.
*/
segments: FlightSegment[]
/**
* Total elapsed time across all segments (including layovers).
*/
duration: IsoDuration
}
FlightSegmentOne leg of a flight offer — a single take-off / landing pair on a single operated flight.
interface FlightSegment {
/**
* Departure airport / instant.
*/
departure: FlightSegmentEndpoint
/**
* Arrival airport / instant.
*/
arrival: FlightSegmentEndpoint
/**
* Marketing carrier IATA code (e.g. `'AA'`, `'BA'`).
*/
carrier: string
/**
* Marketing flight number (e.g. `'100'`, `'1234'`).
*/
flightNumber: string
/**
* Block / total time the segment is in the air. `null` when the
* upstream does not supply per-segment duration.
*/
duration?: IsoDuration | null
}
FlightSegmentEndpointDeparture or arrival point on a {@link FlightSegment}.
interface FlightSegmentEndpoint {
/**
* IATA airport code (e.g. `'JFK'`).
*/
airport: LocationCode
/**
* Local-time instant including timezone offset.
*/
at: IsoDateTime
/**
* Terminal designator (e.g. `'4'`, `'2A'`). `null` when not supplied.
*/
terminal?: string | null
}
GeoLocationGeographic point used for radius-based search of hotels, activities or car-rental locations.
interface GeoLocation {
/**
* Latitude in decimal degrees, WGS-84.
*/
lat: number
/**
* Longitude in decimal degrees, WGS-84.
*/
lon: number
/**
* Search radius around the point. Units are provider-defined but
* SHOULD default to kilometres if not otherwise specified by the
* provider.
*/
radius?: number
}
HotelOfferNormalized hotel offer surfaced inside a {@link TripSearchResult}.
interface HotelOffer {
/**
* Provider-specific opaque offer identifier (room / rate
* combination).
*/
id: OfferId
/**
* Provider-specific hotel identifier the offer belongs to.
*/
hotelId: string
/**
* Human-readable hotel name (e.g. `'Hotel de Paris'`).
*/
name: string
/**
* Total price for the entire stay (all nights).
*/
price: MoneyAmount
/**
* Check-in date (ISO 8601 calendar date).
*/
checkInDate: IsoDate
/**
* Check-out date (ISO 8601 calendar date).
*/
checkOutDate: IsoDate
/**
* Star rating as an integer (1..5), if the provider exposes it.
*/
rating?: number
/**
* Free-form room name / type description, if supplied.
*/
roomDescription?: string
/**
* Cancellation / refundability hint. `true` = explicitly refundable,
* `false` = explicitly non-refundable, `undefined` = unknown.
*/
refundable?: boolean
}
MoneyAmountMonetary price block. Always carries an explicit currency.
interface MoneyAmount {
/**
* Total amount in major units of {@link currency} (e.g. dollars,
* not cents).
*/
total: number
/**
* ISO 4217 currency code the {@link total} is denominated in.
*/
currency: CurrencyCode
}
SearchActivitiesOptionsSearch criteria for {@link TravelProvider.searchActivities}.
Activities are typically scoped to a destination + date range and filtered by the provider's own catalogue. Most providers expose latitude / longitude search rather than IATA codes, so callers can supply either form.
interface SearchActivitiesOptions {
/**
* Destination — either an IATA airport / city code or a geographic
* point with optional radius. Providers SHOULD prefer
* {@link GeoLocation} when both are present.
*/
destination: LocationCode | GeoLocation
/**
* Date range as `[start, end]` ISO calendar dates. Providers MAY
* ignore the range and return their full catalogue if the upstream
* does not support date-filtered availability.
*/
dates?: { start: IsoDate; end: IsoDate }
/**
* Maximum number of offers to return.
*/
maxResults?: number
}
SearchCarsOptionsSearch criteria for {@link TravelProvider.searchCars}.
interface SearchCarsOptions {
/**
* IATA airport / city code or free-form locality string for the
* pickup location.
*/
pickupLocation: LocationCode
/**
* Date / time the car is collected.
*/
pickupDate: IsoDate | IsoDateTime
/**
* Date / time the car is returned.
*/
returnDate: IsoDate | IsoDateTime
/**
* Optional alternate dropoff location. Defaults to
* {@link pickupLocation} when omitted.
*/
dropoffLocation?: LocationCode
/**
* Maximum number of offers to return.
*/
maxResults?: number
}
SearchTripOptionsSearch criteria for {@link TravelProvider.searchTripOptions}.
The criteria are deliberately broad: travelers typically want to see flights + hotels + cars + activities all at once when planning a trip, so the same date / origin / destination apply to each. Per- vertical filtering (cabin, hotel rating, etc.) is left to follow-up calls against the per-vertical cores.
interface SearchTripOptions {
/**
* Origin IATA airport / city code (e.g. `'JFK'`, `'NYC'`). Used for
* the flight portion of the trip.
*/
origin: LocationCode
/**
* Destination IATA airport / city code. Used for the flight portion
* of the trip and as the catalogue lookup for hotels / activities
* when the provider supports it.
*/
destination: LocationCode
/**
* Outbound departure date (ISO 8601 calendar date). Also serves as
* the hotel check-in date.
*/
departureDate: IsoDate
/**
* Return date for round-trip searches. Also serves as the hotel
* check-out date when supplied. Omit for one-way / open-ended trips.
*/
returnDate?: IsoDate
/**
* Traveler-count breakdown. Defaults to a single adult when omitted.
*/
travelers?: TravelerCounts
/**
* Whether to include flight offers in the result. Defaults to
* `true`.
*/
includeFlights?: boolean
/**
* Whether to include hotel offers in the result. Defaults to
* `true`.
*/
includeHotels?: boolean
/**
* Whether to include car-rental offers in the result. Defaults to
* `false` (most providers do not expose a car-rental API; opt in
* explicitly when you know yours does).
*/
includeCars?: boolean
/**
* Whether to include activity offers in the result. Defaults to
* `false` for the same reason as {@link includeCars}.
*/
includeActivities?: boolean
/**
* Maximum number of offers per vertical. Implementations MAY clamp
* this to whatever upper bound their upstream API enforces.
*/
maxResultsPerCategory?: number
}
TravelerCountsTraveler-count breakdown supplied to {@link TravelProvider.searchTripOptions}.
interface TravelerCounts {
/**
* Adult travelers (>=12 years). Defaults to `1` when omitted.
*/
adults?: number
/**
* Child travelers (2-11 years). Defaults to `0` when omitted.
*/
children?: number
/**
* Infant travelers (<2 years). Defaults to `0` when omitted.
*/
infants?: number
}
TravelProviderTravel trip-planning provider interface.
All travel providers (Amadeus, Travelport, Sabre, fixtures, etc.)
implement this interface. The interface is deliberately minimal and
aggregates across the per-vertical cores (@molecule/api-flights,
@molecule/api-hotels) so callers building "search a trip"
itinerary UIs can issue a single call and render mixed results.
Providers that lack one of the vertical APIs (e.g. Amadeus does not expose a public cars API as of v22) MUST return an empty array for that vertical rather than throwing — the absence is data, not an error.
interface TravelProvider {
/**
* Searches for trip options matching the supplied itinerary. Returns
* an aggregated {@link TripSearchResult} containing flights,
* hotels, cars and activities (each opt-in via the corresponding
* `include*` flag).
*
* @param options - Trip search criteria.
* @returns Aggregated trip search result.
*/
searchTripOptions(options: SearchTripOptions): Promise<TripSearchResult>
/**
* Searches for activity / experience offers at a destination.
*
* @param options - Activity search criteria.
* @returns Array of normalized activity offers, possibly empty.
*/
searchActivities(options: SearchActivitiesOptions): Promise<ActivityOffer[]>
/**
* Searches for car-rental offers.
*
* Providers without a car-rental API MUST return an empty array
* rather than throwing.
*
* @param options - Car-rental search criteria.
* @returns Array of normalized car-rental offers, possibly empty.
*/
searchCars(options: SearchCarsOptions): Promise<CarOffer[]>
}
TripSearchResultAggregated trip-search result returned by {@link TravelProvider.searchTripOptions}.
Each per-vertical array is empty (NOT undefined) when the caller
did not opt in to that vertical or when the provider returned no
offers — this lets consumers iterate without conditional access
checks.
interface TripSearchResult {
/**
* Flight offers matching the trip search. Empty when
* {@link SearchTripOptions.includeFlights} is `false` or the
* provider returned none.
*/
flights: FlightOffer[]
/**
* Hotel offers matching the trip search. Empty when
* {@link SearchTripOptions.includeHotels} is `false` or the
* provider returned none.
*/
hotels: HotelOffer[]
/**
* Car-rental offers matching the trip search. Empty when
* {@link SearchTripOptions.includeCars} is `false` or the provider
* does not expose a car-rental API.
*/
cars: CarOffer[]
/**
* Activity offers matching the trip search. Empty when
* {@link SearchTripOptions.includeActivities} is `false` or the
* provider returned none.
*/
activities: ActivityOffer[]
}
CurrencyCodeISO 4217 three-letter currency code (e.g. 'USD', 'EUR', 'JPY').
type CurrencyCode = string
IsoDateISO 8601 calendar date (e.g. '2026-07-15'). Time-of-day MUST NOT be
included — this is a date for searching availability, not a timestamp.
type IsoDate = string
IsoDateTimeISO 8601 instant including timezone offset
(e.g. '2026-07-15T08:30:00+02:00').
type IsoDateTime = string
IsoDurationISO 8601 duration string (e.g. 'PT2H30M', 'PT11H45M'). Plain
string for the same reason as {@link IsoDate} — providers differ on
whether they expose seconds-precision, fractional minutes, etc.
type IsoDuration = string
LocationCodeIATA airport / city / metropolitan code (e.g. 'JFK', 'NYC',
'PAR'). Plain string alias — providers map onto whatever upstream
catalogue they expose.
type LocationCode = string
OfferIdProvider-specific opaque offer identifier. Identifier scheme is provider-defined and MUST NOT be parsed by consumers.
type OfferId = string
getProvider()Retrieves the bonded travel provider, throwing if none is configured.
function getProvider(): TravelProvider
Returns: The bonded travel provider.
hasProvider()Checks whether a travel provider is currently bonded.
function hasProvider(): boolean
Returns: true if a travel provider is bonded.
searchActivities(options)Searches for activity / experience offers at a destination using the bonded provider.
function searchActivities(options: SearchActivitiesOptions): Promise<ActivityOffer[]>
options — Activity search criteria.Returns: Array of normalized activity offers, possibly empty.
searchCars(options)Searches for car-rental offers using the bonded provider.
Providers without a car-rental API return an empty array rather than throwing.
function searchCars(options: SearchCarsOptions): Promise<CarOffer[]>
options — Car-rental search criteria.Returns: Array of normalized car-rental offers, possibly empty.
searchTripOptions(options)Searches for trip options (flights + hotels + cars + activities) using the bonded provider.
function searchTripOptions(options: SearchTripOptions): Promise<TripSearchResult>
options — Trip search criteria.Returns: Aggregated trip search result.
setProvider(provider)Registers a travel provider as the active singleton. Called by bond
packages (e.g. @molecule/api-travel-amadeus) during application
startup.
function setProvider(provider: TravelProvider): void
provider — The travel provider implementation to bond.| Provider | Package |
|---|---|
| Amadeus | @molecule/api-travel-amadeus |
Peer dependencies:
@molecule/api-bond ^1.0.1@molecule/api-i18n ^1.0.1@molecule/api-bond@molecule/api-i18nIntegration checklist — drive the real UI (live preview, no mocks), adapt each item to this app's actual trip-planning / results screens, and check every box off one by one. A box you can't check is an integration bug to fix — not a skip:
searchTripOptions with a real origin ->
destination, a departureDate weeks out, and returnDate for a round
trip — e.g. JFK -> PAR) returns REAL results rendered in the UI: each
opted-in vertical (includeFlights / includeHotels / includeCars /
includeActivities) shows populated flights / hotels / cars /
activities, each an actual offer — never an empty list, a stuck spinner,
or a placeholder row presented as a successful search.FlightOffer's first
segments[].departure.airport is the searched origin and its last
arrival.airport the destination, on the requested dates; each
HotelOffer is at the destination with checkInDate / checkOutDate
equal to the searched departure / return dates — not random routes,
cities, or dates.includeFlights,
includeHotels, includeCars, or includeActivities adds ONLY that
section, and a vertical not requested (or one the provider can't serve,
e.g. cars) comes back as an EMPTY array — rendered as "none", never as a
failed or blank search. maxResultsPerCategory actually caps how many
offers each section shows. Any separate activities / cars screen calls
searchActivities / searchCars and renders its own ActivityOffer[] /
CarOffer[].price.total shows its ISO 4217 price.currency
(formatted with it — never a hardcoded $) and is sane: a flight total
is the grand total for ALL travelers, a hotel total covers the whole
stay, a car total the whole rental — positive and the right order of
magnitude.departureDate, a returnDate before it, or a route with no
availability — shows a clear per-section "no results" / "invalid" state,
not a crash and not a blank list presented as a successful search.ActivityOffer.bookingUrl or
the app's own checkout) — verify the app RECORDS the SELECTED offer (its
priced total + itinerary), since opaque OfferIds are short-lived and
can't be replayed later.