Searching and booking flights
Look up airports, search flights, pick a fare, and create a flight booking.
Flights follow the standard four-step flow: search → fares → build → book.
Search and fares stream results as Server-Sent Events (SSE); build and book
return JSON. Every request below carries the full consumer-HMAC and
traveler-bearer headers — see Authentication for how
$SIGNATURE is computed from $METHOD, the request path, $TIMESTAMP, and
the body.
1. Look up airports
Resolve free-text input to IATA codes before searching.
curl -G "$TRAVELTECH_BASE_URL/v1/flights/airports" \
--data-urlencode "q=milan" \
--data-urlencode "limit=5" \
-H "TravelTech-Consumer: $CONSUMER" \
-H "TravelTech-Timestamp: $TIMESTAMP" \
-H "TravelTech-Signature: $SIGNATURE" \
-H "Authorization: Bearer $TRAVELER_JWT"(Sign with METHOD=GET, PATH=/v1/flights/airports — the query string is
never part of the signed path — and the empty-body hash from
Authentication.)
Response — an array of FlightAirportOut:
[
{
"iata": "MXP",
"name": "Milan Malpensa",
"city": "Milan",
"country": "IT",
"timezone": "Europe/Rome",
"is_metro": false
}
]2. Search
POST /v1/flights/search initiates a search and streams normalised solutions
back as SSE. Each data: event is a JSON flight solution whose id is a
canonical travel.tech UUID. The stream ends with event: done.
curl -N -X POST "$TRAVELTECH_BASE_URL/v1/flights/search" \
-H "TravelTech-Consumer: $CONSUMER" \
-H "TravelTech-Timestamp: $TIMESTAMP" \
-H "TravelTech-Signature: $SIGNATURE" \
-H "Authorization: Bearer $TRAVELER_JWT" \
-H "Content-Type: application/json" \
-d '{
"itinerary": [
{ "origin": "MXP", "destination": "LHR", "date": "2026-02-10" },
{ "origin": "LHR", "destination": "MXP", "date": "2026-02-14" }
],
"passengers": { "adults": 1, "children": 0, "infants": 0 },
"cabinClass": "economy",
"directOnly": false
}'(-N disables curl's output buffering so SSE frames print as they arrive.)
Request body (FlightSearchRequest) — one leg per itinerary entry (two legs for
a round trip). cabinClass is one of economy, premium_economy, business,
first. Optional priceMin / priceMax bound the results. Optional
includeNearbyDeparture and includeNearbyDestination (both default to false)
ask the search to consider airports near a specific origin or destination.
Each result then includes isNearbyDeparture and isNearbyDestination. A flag is
true only when that side was requested, the traveler named a specific airport,
and the flight uses a different airport than the one named for that flight.
Searching a city — every airport in the city — is not a nearby substitution.
On a round trip the departure side is the outbound takeoff and the return
landing, and the destination side is the outbound landing and the return
takeoff. A return aimed at a different airport the traveler chose is not a
nearby substitution of the outbound pair. Grouped
results use the same two flags on the outbound and on each return option. A
trip with three or more legs ignores both choices. Signed-in and guest search
use this same request. Each FlightLeg
also accepts departureAfter / departureBefore / arrivalAfter /
arrivalBefore as HH:MM local-time windows.
A full trace of the response stream — three solutions, then done:
event: solution
data: {"id":"018f2c3e-1234-7abc-8def-000000000101","solutionKey":"6f1c9e2a...","providerName":"example-airline","totalPrice":{"amount":"149.90","currency":"EUR"},"cabinClass":"economy","stops":0,"isPolicyCompliant":true}
event: solution
data: {"id":"018f2c3e-1234-7abc-8def-000000000102","solutionKey":"a3d8b410...","providerName":"another-airline","totalPrice":{"amount":"172.40","currency":"EUR"},"cabinClass":"economy","stops":1,"isPolicyCompliant":true}
event: solution
data: {"id":"018f2c3e-1234-7abc-8def-000000000103","solutionKey":"c9027fe1...","providerName":"example-airline","totalPrice":{"amount":"210.00","currency":"EUR"},"cabinClass":"economy","stops":0,"isPolicyCompliant":false}
event: done
data: {}
Each solution arrives as an event: solution frame; the stream closes with
event: done. Keep the solution id you want to fare — it is the
searchSolutionId for the next step.
A provider failure ends the stream with a terminal event: error frame
instead of done — no partial results are re-sent:
event: error
data: {"code":"PROVIDER_ERROR","message":"Search temporarily unavailable."}
3. List fares
POST /v1/flights/fares streams the bookable fares for a chosen solution as SSE.
curl -N -X POST "$TRAVELTECH_BASE_URL/v1/flights/fares" \
-H "TravelTech-Consumer: $CONSUMER" \
-H "TravelTech-Timestamp: $TIMESTAMP" \
-H "TravelTech-Signature: $SIGNATURE" \
-H "Authorization: Bearer $TRAVELER_JWT" \
-H "Content-Type: application/json" \
-d '{ "searchSolutionId": "018f2c3e-1234-7abc-8def-000000000101", "solutionKey": "6f1c9e2a..." }'Request body (FlightFaresBody):
{ "searchSolutionId": "018f...", "solutionKey": null }Each streamed fare carries a fareId and solutionKey used to build it.
External fares are withheld by default. Pay-at-airline (external) fares
are not surfaced unless your environment enables them. Fares returned here are
prepaid through BizAway.
4. Build the fare
POST /v1/flights/fares/build locks a specific fare and returns it as JSON
(201).
curl -X POST "$TRAVELTECH_BASE_URL/v1/flights/fares/build" \
-H "TravelTech-Consumer: $CONSUMER" \
-H "TravelTech-Timestamp: $TIMESTAMP" \
-H "TravelTech-Signature: $SIGNATURE" \
-H "Authorization: Bearer $TRAVELER_JWT" \
-H "Content-Type: application/json" \
-d '{ "fareId": "018f2c3e-1234-7abc-8def-000000000201", "searchSolutionId": "018f2c3e-1234-7abc-8def-000000000101", "solutionKey": null }'Request body (FlightBuildFareBody):
{ "fareId": "018f...", "searchSolutionId": "018f...", "solutionKey": null }Response (FlightFareOut):
{
"fareId": "018f2c3e-1234-7abc-8def-000000000201",
"solutionKey": "6f1c9e2a...",
"providerName": "example-airline",
"key": "abc123",
"merchant": "bizaway",
"totalPrice": { "amount": "149.90", "currency": "EUR" },
"fareBasis": "YIE6M1",
"fareFamily": "Light",
"cabinClass": "economy",
"conditions": ["Non-refundable", "Changeable for a fee"],
"refundability": "none",
"isRefundable": false,
"isChangeable": true,
"ancillaries": [
{
"id": "018f2c3e-1234-7abc-8def-000000000301",
"kind": "ancillary",
"code": "checked_bag",
"name": "Checked bag 23kg",
"price": { "amount": "35.00", "currency": "EUR" },
"priceUnit": "per_passenger",
"mode": "selectable",
"quantity": 0,
"maxQuantity": 2
}
],
"trips": [
{
"tripId": "outbound",
"legs": [
{
"fromAirport": "MXP",
"toAirport": "LHR",
"departureAt": { "localAt": "2026-02-10T09:15:00", "timezone": "Europe/Rome", "source": "provider" },
"arrivalAt": { "localAt": "2026-02-10T10:35:00", "timezone": "Europe/London", "source": "provider" },
"carrier": "EX",
"airlineName": "Example Airline",
"flightNumber": "EX 123"
}
]
}
]
}Money is always { "amount": "149.90", "currency": "EUR" } with the amount as a
string carrying at least two decimals.
If fareId has expired (fare locks are time-limited), the build call returns
410 with the standard error envelope:
{
"error": {
"code": "OFFER_EXPIRED",
"message": "This fare is no longer available. Search again to get a current price.",
"details": [],
"recoveryAction": "CHANGE_SELECTION",
"retryAfterSeconds": null
}
}5. (Optional) Protection plans
Before booking, list available protection plans for a built fare:
GET /v1/flights/fares/{fare_id}/protection-plans
6. Book
POST /v1/flights/bookings creates the booking from the built fareId. Send a
unique Idempotency-Key header (required — see
Bookings and trips).
curl -X POST "$TRAVELTECH_BASE_URL/v1/flights/bookings" \
-H "TravelTech-Consumer: $CONSUMER" \
-H "TravelTech-Timestamp: $TIMESTAMP" \
-H "TravelTech-Signature: $SIGNATURE" \
-H "Authorization: Bearer $TRAVELER_JWT" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 018f2c3e-1234-7abc-8def-000000000401" \
-d '{
"fareId": "018f2c3e-1234-7abc-8def-000000000201",
"passengers": [
{
"firstName": "Ada",
"lastName": "Lovelace",
"type": "adult",
"birthday": "1990-12-10",
"gender": "F",
"nationality": "GB",
"isHolder": true,
"documentNumber": "P1234567"
}
],
"isPersonal": false,
"tripId": null
}'Request body (CreateBookingRequest):
{
"fareId": "018f...",
"passengers": [
{
"firstName": "Ada",
"lastName": "Lovelace",
"type": "adult",
"birthday": "1990-12-10",
"gender": "F",
"nationality": "GB",
"isHolder": true,
"documentNumber": "P1234567"
}
],
"isPersonal": false,
"tripId": null
}Pass tripId to attach the booking to an existing trip, or leave it out and
associate it later (see Bookings and trips).
Response (BookingDetailOut, 201):
{
"bookingId": "018f2c3e-1234-7abc-8def-000000000401",
"vertical": "flight",
"status": "pending",
"providerName": null,
"needsApproval": false,
"totalPrice": { "amount": "149.90", "currency": "EUR" },
"availableOperations": ["cancel_booking"],
"providerData": {},
"legs": [
{
"legId": "018f2c3e-1234-7abc-8def-000000000501",
"originAirportCode": "MXP",
"destinationAirportCode": "LHR",
"originDisplayName": "Milan Malpensa",
"destinationDisplayName": "London Heathrow",
"departureAt": { "localAt": "2026-02-10T09:15:00", "timezone": "Europe/Rome", "source": "provider" },
"arrivalAt": { "localAt": "2026-02-10T10:35:00", "timezone": "Europe/London", "source": "provider" },
"duration": "PT1H20M",
"airlineName": "Example Airline",
"flightNumber": "EX 123",
"terminal": null,
"gate": null,
"seat": null
}
],
"travelers": [
{
"travelerId": "018f2c3e-1234-7abc-8def-000000000601",
"displayName": "Ada Lovelace",
"legAssignments": []
}
],
"bizAwayCode": "BA-928374",
"providerConfirmationReferences": [],
"tripId": null
}Manage the booking lifecycle — confirm, cancel, calculate costs — with the
shared booking endpoints documented in
Bookings and trips. Once a booking is confirmed
or issued, subsequent schedule changes and disruptions push a notification
to the traveler — see Notification severity tiers
for how to interpret and render the severity field on those notifications.
See the API Reference for the complete request and
response schemas.
Reporting issues
This page is maintained by the BizAway integration team. To request changes or
report an error, contact your BizAway integration contact. Edits made directly
in the ReadMe dashboard are overwritten on the next publish.
Updated about 1 month ago