Standalone NDC adapter connecting to the Duffel REST API for multi-airline search, pricing, and booking.
| Operation | Supported | Notes |
|---|---|---|
| Search | ✓ | POST /air/offer_requests |
| Price | ✓ | Offer price confirmation |
| Book | ✓ | Order creation with passenger details |
| Cancel | -- | Not yet implemented |
| Health Check | ✓ | GET /air/airlines connectivity test |
Channel capability manifest:
{
channelId: 'duffel',
channelType: 'ndc',
supportsNdcLevel: 3,
supportedCarriers: ['*'],
supportedFunctions: ['search', 'price', 'book_held', 'ticket', 'refund', 'exchange', 'ssr', 'seat_map'],
reliabilityScore: 0.9,
latencyScore: 0.78,
costScore: 0.65,
}Note: The capability manifest declares full NDC functions. The current adapter implements search, price, and book. Ticketing is handled automatically by Duffel where the carrier supports instant issue.
API token (Bearer header).
- Sign up at duffel.com (free for test mode)
- Copy your test token from the dashboard (starts with
duffel_test_) - Pass the token when constructing the adapter
The token is sent as Authorization: Bearer <token> on every request. The Duffel-Version header is included for API versioning.
The Duffel adapter is configured via constructor parameters (not a Zod config object like ConnectAdapter adapters):
// Constructor accepts an API key string or a config object
const adapter = new DuffelAdapter(process.env.DUFFEL_API_KEY);The adapter hits https://api.duffel.com directly. Test mode vs. live mode is determined by the token prefix (duffel_test_ vs duffel_live_).
import { DuffelAdapter } from '@otaip/duffel';
const adapter = new DuffelAdapter(process.env.DUFFEL_API_KEY);
// Check health
const available = await adapter.isAvailable();
// Search flights
const response = await adapter.search({
origin: 'LHR',
destination: 'JFK',
departureDate: '2026-06-15',
passengers: { adults: 1 },
cabinClass: 'economy',
maxResults: 10,
});
// Price a specific offer
const priced = await adapter.price({
offerId: response.offers[0].offer_id,
});
// Book
const booking = await adapter.book({
offerId: response.offers[0].offer_id,
passengers: [{
given_name: 'John',
family_name: 'Test',
born_on: '1985-01-01',
email: 'john@example.com',
phone_number: '+442080160509',
type: 'adult',
gender: 'm',
title: 'mr',
}],
});Duffel provides a free test environment with synthetic airline data:
- Go to duffel.com and create an account
- From the dashboard, navigate to API tokens
- Copy the test mode token (prefixed
duffel_test_) - Set in your
.env:DUFFEL_API_KEY=duffel_test_... - Test mode returns simulated offers from a "Duffel Airways" test airline
No credit card required for test mode.
- Implements
DistributionAdapter(from@otaip/core), notConnectAdapter(from@otaip/connect). This is a standalone adapter package. - Native fetch: Uses Node.js 24+ global
fetch-- no HTTP client dependency - Decimal math: All monetary calculations use
decimal.js - Duration parsing:
parseDurationToMinutes()converts ISO 8601 durations (e.g., "PT5H30M") to minutes - Cabin class mapping: Maps Duffel cabin classes (economy, premium_economy, business, first) to OTAIP types
- Mock adapter:
mock-duffel-adapter.tsprovides a deterministic mock for testing
32 tests across two test files:
duffel-adapter.test.ts-- 27 tests covering search, price, book, health check, error handlingduffel-e2e.test.ts-- 5 tests for end-to-end flow validation (mocked)
All tests use mocked HTTP responses.
- No cancel: Order cancellation is not yet implemented in the adapter
- No exchange/refund: Post-booking service operations are not implemented
- No seat selection: Seat map and ancillary selection are not exposed
- Test carrier only: In test mode, only Duffel's synthetic airline returns results. Live mode requires a production token with carrier agreements.
- Separate package: Lives in
packages/adapters/duffel/, not inpackages/connect/. Implements a different interface (DistributionAdaptervsConnectAdapter).
packages/adapters/duffel/src/
duffel-adapter.ts -- DuffelAdapter class (live API)
mock-duffel-adapter.ts -- Mock adapter for testing
capabilities.ts -- Channel capability manifest
index.ts -- Package exports
__tests__/
duffel-adapter.test.ts
duffel-e2e.test.ts