---
title: "Travel Search → Offer → Reprice → Booking Reference Architecture"
description: "Design an end-to-end reference architecture for travel search, canonical offers, repricing, payment and booking across supplier integrations."
slug: "travel-search-offer-booking-reference-architecture"
translationKey: "architecture-travel-search-offer-booking-reference"
locale: "en"
type: "guide"
category: "architecture"
tags: ["travel","search","offer","reprice","booking","architecture"]
publishedAt: "2026-09-27"
updatedAt: "2026-09-27"
reviewedAt: "2026-09-27"
technicalVerifiedAt: "2026-09-27"
codeExampleStatus: "illustrative"
---

The critical boundary in travel booking architecture is recognizing that **a search result is not the same thing as a bookable offer**. Search produces options, reprice or availability check verifies that the selected option is still valid, and booking creates a new transaction at the supplier.

This page defines a canonical backbone that can be reused across travel verticals such as hotels and flights.

## Reference architecture

```mermaid
%% title: Travel search to booking reference architecture
%% description: A search request moves through supplier fan-out, normalization, offer selection, reprice, payment and booking.
flowchart LR
  A[Search Request] --> B[Canonical Search Context]
  B --> C[Supplier Eligibility]
  C --> D{Parallel Supplier Fan-out}
  D --> E[Supplier Adapter A]
  D --> F[Supplier Adapter B]
  E --> G[Normalize]
  F --> G
  G --> H[Property / Product Mapping]
  H --> I[Canonical Offers]
  I --> J[Ranking / Presentation]
  J --> K[Offer Selected]
  K --> L[Reprice / Availability Check]
  L --> M{Still valid?}
  M -->|No| N[Offer Changed / Expired]
  M -->|Yes| O[Booking Intent]
  O --> P[Payment Strategy]
  P --> Q[Provider Booking Create]
  Q --> R{Outcome}
  R -->|Confirmed| S[Booking Confirmed]
  R -->|Failed| T[Booking Failed]
  R -->|Unknown| U[Booking Unknown]
  U --> V[Lookup / Reconciliation]
  V --> S
  V --> T
```

## 1. Canonicalize the search context

Do not turn a supplier request DTO into your domain model. Create a shared `SearchContext` first:

```json
{
  "searchId": "srch_123",
  "vertical": "hotel",
  "market": "TR",
  "currency": "TRY",
  "checkIn": "2026-10-10",
  "checkOut": "2026-10-12",
  "occupancies": [
    { "adults": 2, "childrenAges": [] }
  ]
}
```

Each adapter translates that context into a provider-specific request. Supplier changes then stay outside the domain contract.

## 2. Separate search responses from canonical offers

Suppliers expose different price, tax, cancellation and availability semantics. A normalized offer should preserve at least:

- internal offer ID,
- supplier ID,
- supplier offer/rate ID,
- property/product reference,
- total price and currency,
- tax/fee breakdown,
- cancellation/refund policy,
- payment timing,
- room/fare/product attributes,
- availability/freshness timestamp,
- raw provider context or opaque booking token.

Do not lose opaque tokens. A token returned during search may be mandatory for repricing or booking.

## 3. Treat an offer as an immutable snapshot

Instead of mutating the price later on one database row, preserve snapshots:

```text
SearchOffer
  -> SelectedOfferSnapshot
  -> RepricedOffer
  -> BookingIntent
  -> ProviderBooking
```

This makes it possible to answer: what did the traveler see, what changed at reprice, and what amount was actually booked?

## 4. Reprice is a separate domain step

A search result should not move directly to booking. Reprice or availability check should detect:

- price changes,
- tax/fee changes,
- room/fare no longer available,
- cancellation-policy changes,
- payment-model changes,
- expired supplier tokens,
- passenger/occupancy validation failures.

Creating a new `OfferVersion` is safer than overwriting the original search offer.

## 5. Booking intent is not a provider booking

When the traveler clicks purchase, create an internal `BookingIntent` before calling the supplier.

It should bind:

- selected/repriced offer,
- traveler/passenger data,
- payment strategy,
- idempotency key,
- correlation ID,
- consent/terms version,
- client/session context.

Persisting this boundary before the external call makes timeout recovery much easier.

## 6. Make payment sequencing explicit

There is no single correct sequence. Depending on the supplier and commercial model, systems may use:

```text
authorize -> book -> capture
book -> authorize/capture
pay at property
virtual card / settlement later
```

Keep PaymentState and BookingState separate. A successful payment is not proof of a confirmed booking.

## 7. Model three booking outcomes

Do not reduce a provider booking call to SUCCESS/FAILED:

- `CONFIRMED`
- `FAILED`
- `UNKNOWN`

After a network timeout or connection reset, the supplier may still have created the booking. Retrying create blindly can produce a duplicate reservation.

## 8. Recover UNKNOWN outcomes

```mermaid
%% title: Unknown booking outcome recovery
%% description: Ambiguous booking outcomes are resolved with lookup and reconciliation.
stateDiagram-v2
  [*] --> BookingRequested
  BookingRequested --> Confirmed: provider confirms
  BookingRequested --> Failed: provider rejects
  BookingRequested --> Unknown: timeout / ambiguous transport failure
  Unknown --> Confirmed: lookup finds booking
  Unknown --> Failed: authoritative lookup finds no booking
  Unknown --> Unknown: evidence still inconclusive
```

Useful recovery evidence includes:

- provider booking lookup,
- client reference / idempotency-key lookup,
- webhook/event delivery,
- scheduled reconciliation,
- manual operations queue.

## 9. Canonical domain boundaries

| Entity | Responsibility |
|---|---|
| SearchContext | Canonical traveler search intent |
| SupplierExecution | Latency/outcome of each provider call |
| CanonicalOffer | Normalized search offer |
| OfferVersion | Immutable search/reprice snapshot |
| BookingIntent | User booking intent and idempotency boundary |
| PaymentAttempt | Payment lifecycle |
| BookingAttempt | Supplier booking call |
| Booking | Materialized canonical booking state |
| BookingEvent | Immutable lifecycle evidence |
| ReconciliationRecord | Recovery for unknown/divergent state |

## 10. Preserve the correlation chain

Every stage should be traceable:

```text
searchId
 -> offerId / offerVersion
 -> bookingIntentId
 -> paymentAttemptId
 -> bookingAttemptId
 -> providerReference
 -> bookingId
```

Raw provider request/response logs must not be retained without limits when they contain PII or payment data. Apply redaction and retention rules.

## Failure modes

Important failure classes include:

- stale search offer,
- reprice mismatch,
- duplicate provider booking,
- payment authorized while booking remains unknown,
- provider confirmed but local persistence failed,
- delayed or lost webhook,
- out-of-order lifecycle events,
- supplier partial outage,
- timeout amplification,
- currency/tax normalization mismatch.

## Observability

Track at minimum:

- search success rate,
- supplier p50/p95/p99 latency,
- search-to-offer-selection conversion,
- reprice success/mismatch rate,
- booking confirmation rate,
- booking UNKNOWN rate,
- duplicate-prevention hits,
- reconciliation age,
- payment/booking divergence,
- end-to-end search-to-confirmed-booking latency.

`searchId`, `bookingIntentId` and `providerReference` should be searchable in traces and operational tooling.

## Production checklist

- canonical SearchContext,
- immutable OfferVersion,
- mandatory reprice/availability gate,
- persist BookingIntent before provider writes,
- explicit payment sequencing,
- CONFIRMED / FAILED / UNKNOWN booking outcomes,
- reconciliation path,
- operation-scoped idempotency,
- end-to-end correlation IDs,
- PII-safe logging and retention.

## Design rule

The main principle is:

**Canonicalize lifecycle boundaries, not provider DTOs.**

When Search, Offer, Reprice, Payment and Booking are modeled as separate domain states, supplier differences stay inside adapters while retry, reconciliation and troubleshooting can share a common architecture.
