Back to Blogs
travel-techapi-designredisrabbitmqidempotency

Designing Airline Booking APIs for Peak Traffic

May 07, 202611 min readTravel Tech

Lessons from flight systems where price, payment, booking, and ticketing can all disagree at the same time.

Designing Airline Booking APIs for Peak Traffic cover
--

The confirmation screen hides several truths

A flight checkout appears linear: search, choose, pay, confirm. The backend sees independent systems moving on different clocks. A fare can change, a seat request can fail, a payment can be captured after our request times out, and a booking can exist before its ticket is issued.

At ShareTrip I worked across flight search, booking, and post-booking integrations spanning more than twenty airlines and multiple third-party APIs. The recurring design lesson was that a single booked flag cannot describe vendor reality. Support and operations need to know which step succeeded, which party reported it, and what action is still safe.

Model the transitions, not the screens

I separate user intent, payment, airline booking, and ticketing state. A request may move from initiated to vendor_pending, then booked, while ticketing remains pending. Payment can be authorized, captured, failed, or uncertain. The exact names vary by provider, but the separation should remain.

Transitions belong in one domain service with an audit record of the previous state, next state, reason, vendor reference, and correlation ID. Controllers should not set status fields opportunistically. Centralizing the transition rules makes invalid moves testable and gives monitoring a consistent event to observe.

The API returns the state we know, not the state the user hopes for. Pending with a useful explanation is better than confirmed followed by a support call.

Quote expiry is provider data

Hold and quote behavior varies across airlines, GDSs, and fare conditions, so I do not encode a universal window. The record stores the provider's expiry when one is supplied, the time we received the quote, and the rule used when no explicit expiry exists.

Before payment, the service decides whether the quote is still usable, needs repricing, or must be abandoned. A reprice that changes the amount returns a new decision to the user; it should not quietly charge a different fare.

Idempotency needs a lifecycle

The client creates a key for one booking attempt and reuses it on retry. On the server I treat that key as a record with the authenticated owner, operation, request fingerprint, status, and stored response. A unique constraint lets one request claim the key. Later requests either receive the completed response, wait or retry while it is in progress, or fail if the same key was reused with a different payload.

That still does not make an external vendor idempotent. If the vendor completed a booking and our process died before saving the response, recovery needs the vendor reference, a status lookup, or a reconciliation path. The database lock protects our rows; it cannot roll back an airline or payment provider.

Keep the request path small

Search latency is dominated by providers we do not control. Parallel calls, provider-specific timeouts, short-lived caching, circuit breakers, and partial results are more useful than one generous global timeout. The service should know which providers failed without discarding the suppliers that answered.

After the booking decision, email, reporting events, ticket polling, and non-critical notifications can move to workers. Critical domain events are written with the booking transaction through an outbox, then published to RabbitMQ. That closes the gap where the booking commits but the process dies before sending the message.

Reconciliation is part of the write path

Reconciliation is easier when the booking record was designed for it: provider locator, airline locator where available, ticket numbers, payment references, timestamps, raw provider status, and the normalized internal state. Without those identifiers, an operations team is left matching passengers and amounts by hand.

Scheduled checks compare pending or suspicious records with provider state. A mismatch becomes a visible work item with a reason and a safe next action, not just another log line. Some cases can retry automatically; an ambiguous charge or conflicting ticket state should be escalated.

The architecture that holds up at peak traffic is not defined by how many services it has. It is defined by whether uncertainty is represented, duplicate work is contained, and a failed transition can be found and repaired.