TRAVEL & BOOKINGSep 5, 202612 min read

How to Integrate the Amadeus API into a B2B Travel Portal

Amadeus gives you flights, hotels and fares over a clean REST API — but a B2B portal is more than a search box. You also have sub-agents, markup, wallets and credentials to manage. Here's the flow that works, and the B2B pieces the docs don't cover.

Amadeus is where most travel portals start, and for good reason: it's the largest GDS, the content is deep, and the modern Amadeus for Developers APIs are plain REST with JSON — no arcane messaging formats to learn on day one. But there's a gap between "I can search flights from a code sample" and "I've shipped a B2B travel portal that sub-agents actually book on." This post is about closing that gap.

I run a travel reservation platform that integrates Amadeus alongside other suppliers end to end — search, pricing, booking, refunds, agent wallets and markup. Below is the integration path I'd hand to someone starting today, in the order the decisions actually come up.

First decision: Self-Service or Enterprise?

Before you write a line of code, you have to know which Amadeus you're building against, because they are not the same product.

Practical advice: build your MVP on Self-Service. Its data model and flow mirror the enterprise concepts closely enough that if you keep Amadeus behind an adapter (more on that below), moving up later is an integration change, not a rewrite. Don't block your launch on an enterprise contract you don't need yet.

Everything that follows uses the Self-Service REST flow, because that's where 90% of new B2B portals begin.

Authentication: one token, cached, refreshed

Amadeus uses OAuth2 client credentials. You exchange your API key and secret for an access token, and you send that token as a bearer header on every subsequent call. The token is short-lived — roughly 30 minutes — so you cache it and refresh before it expires rather than requesting a new one per call.

POST https://test.api.amadeus.com/v1/security/oauth2/token
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials
&client_id=YOUR_API_KEY
&client_secret=YOUR_API_SECRET

// response
{ "access_token": "abc123...", "expires_in": 1799, "token_type": "Bearer" }

Wrap this in a small token provider that stores the token in memory with its expiry and hands out a valid one on demand. Requesting a fresh token on every search is a classic early mistake — it adds a round-trip to every request and burns quota for no reason.

Two environments, two base URLs: test.api.amadeus.com serves cached, non-live data for development; api.amadeus.com is production with live pricing and real bookings. They use different credentials. Make the base URL and keys configuration, never hard-coded — you will switch between them constantly.

The core flight flow is three calls, in order

This is the single most important thing to internalise about Amadeus, and it maps directly to how travel actually works: search is indicative, price is live, and only then can you book. Three endpoints, always in this sequence:

  1. Flight Offers SearchPOST /v2/shopping/flight-offers. Send origin, destination, dates, passenger counts, cabin. Get back a list of offers, each with a full fare breakdown and an offer object you carry forward. This is what populates your results page.
  2. Flight Offers PricePOST /v1/shopping/flight-offers/pricing. Take the exact offer the customer selected and confirm it live. Amadeus revalidates availability and price and returns the current, bookable fare. This is where you catch price drift before it costs you money.
  3. Flight Create OrdersPOST /v1/booking/flight-orders. Send the confirmed offer plus traveller details, and Amadeus creates the booking (a PNR). This is the step that actually reserves seats.

Skipping the middle step is the most expensive bug in the category. If you book straight off the search result, you'll routinely try to sell fares that have already changed or sold out — and you eat the difference or fail the booking at the worst possible moment. The price call exists precisely so you re-confirm the fare the instant before you charge the customer.

Booking is two steps for the same reason. Confirm price live, then take payment, then create the order. If order creation fails after you've charged, you must automatically void or refund — never leave an agent's wallet debited for a PNR that doesn't exist.

Now the part the docs don't cover: this is a B2B portal

A consumer booking site has one customer type. A B2B portal has agents — sub-agents, sub-sub-agents, corporate clients — each of whom logs in, sees their own fares, and books against their own money. That changes the architecture in four concrete ways.

1. Multi-tenancy and roles

Your data model needs a tenant hierarchy from day one: the portal owner at the top, then agencies, then the individual agents who log in. Every booking, every wallet transaction, every markup rule is scoped to a node in that tree. Retrofitting multi-tenancy after launch is painful — bake the agency/agent relationship into your schema and every query before you have live data.

2. The markup engine

Amadeus returns net fares. Your agents must never see the net — they see the price after your markup. So between the pricing call and the response you send to the browser, you run a markup engine that adds a margin. And it's rarely a flat number: markup varies by agency, by route, by airline, by fare class, sometimes as a percentage and sometimes as a fixed amount per passenger.

// conceptual: applied server-side, never in the client
sellFare = netFare + resolveMarkup(agencyId, route, airline, fareClass);
// the agent sees sellFare; the net stays server-side only

Two rules keep this safe. First, markup is applied on the server, always — the net fare must never reach the browser, or an agent will read it in the network tab. Second, you store both the net and the sell price on the booking record, so your reconciliation and commission reports are accurate later.

3. Agent wallets and credit

B2B agents don't pay by card per booking. They top up a wallet (or get a credit line), and each booking debits the balance. That means your booking flow has an extra gate: check the agent has sufficient balance before you call Flight Create Orders, then debit atomically as part of confirming the booking.

4. Credential and quota management

On Self-Service, the whole portal typically shares one set of production Amadeus credentials, so your rate limiting and quota tracking are portal-wide — one noisy agency hammering search can throttle everyone. Enforce per-agency rate limits inside your own layer so no single tenant can exhaust the shared quota. (On enterprise, you may have per-office-ID credentials, which changes this calculus — another reason to keep Amadeus behind an adapter.)

Keep Amadeus behind an adapter — even if it's your only supplier

It's tempting, when Amadeus is your only integration, to call it directly from your booking code. Don't. Define your own internal types — a FlightOffer, a PriceBreakdown, a BookingResult — and make an Amadeus adapter translate into them. Your search page, booking flow and admin panel work only with your model and never import Amadeus specifics.

You get two payoffs. Moving from Self-Service to enterprise later becomes an adapter change instead of a rewrite. And the day you add a second supplier — a low-cost carrier aggregator, a direct hotel feed, another GDS — you write one more adapter and the rest of the system doesn't notice. That multi-supplier architecture is a topic in itself; I've written it up separately in how to integrate multiple GDS providers into one booking engine.

Caching, rate limits and the test-data trap

GDS calls cost money and are rate-limited, so treat them accordingly:

The admin panel is half the product

None of the above is visible to your operations team unless you build for them. An internal admin panel — showing every booking with its agency, net and sell fare, wallet transaction, PNR and refund state, plus a full audit log of what you sent Amadeus and what came back — is how support resolves a stuck booking at midnight without reading raw API logs. In a B2B portal it's not optional: it's where you manage agents, set markup rules, top up wallets and investigate disputes. Build it alongside the engine, not after.

Pitfalls I'd flag before you start

Get the three-step flow and the B2B plumbing — markup, wallets, multi-tenancy — right, and Amadeus becomes a dependable engine you can build a real agency business on. Rush them, and you'll be reconciling mismatched fares and disputed wallet balances by hand for months. The API is the easy part; the portal around it is the product.


Building a B2B travel portal on Amadeus?

I've integrated Amadeus and other GDS suppliers into a live travel platform — search, pricing, booking, markup, agent wallets and refunds. If you're starting a portal or your current Amadeus integration is fragile, see what my travel portal development service covers, or just tell me what you're building.

Start a project →
← Back to all posts