Flight Booking
This API allows you to integrate flight booking functionality into your application. You can search for flights, select a flight offer, make a reservation and issue flight tickets.
Overview
The Flight Booking API provides a complete workflow for:
- Searching for available flights
- Selecting a flight offer and locking price
- Creating reservations
- Issuing flight tickets
Authentication
All API endpoints require JWT authentication. Obtain a JWT token by logging in through the /api/logon endpoint.
For detailed information on authenticating and obtaining a token, see the Authentication guide.
Headers
Required:
Authorization: Bearer <JWT_TOKEN>- Your JWT authentication token
Optional:
X-External-User-Id: <user_id>- Your internal user ID for reference tracking (stored and returned but not used for business logic)Content-Language: <language_code>- Language for responses (default:en)Content-Currency: <currency_code>- Currency for pricing (default:USD)
Wallet Balance Required
Your account must have enough money in its wallet to make bookings via the API. You can add funds by topping up using the Safiri app or by manually sending money to Safiri’s bank account (contact support for details), after which your wallet will be credited. If your wallet does not have sufficient balance, your booking requests will fail with an error.
Endpoints
Get Airports List
Retrieve a list of airports along with their IATA codes, names, and locations. These airport IATA codes are required when searching for flights via the API. You can also use this list to power airport autocomplete or selection functionality in your application's UI, so users can search and select their desired airports easily.
Endpoint: GET https://gtfs-flight.safiri.app/api/stops?mini=true
Response:
[
{
"stop_id": "AAA:FLT:0", // Unique internal Safiri stop ID (contains airline IATA code; FLT (stands for flight; and 0))
"stop_lat": -17.3526, // Latitude of the airport
"stop_lon": -145.509995, // Longitude of the airport
"stop_name": "AAA Anaa Airport", // Name of the airport (with IATA code)
"stop_desc": "Anaa, French Polynesia", // City and Country that the airport is in
"stop_timezone": "Pacific/Honolulu", // Timezone of the airport
"city_geohash": "2ubt2c", // Geohash for city clustering/spatial search
"popularity": 30 // Popularity index (for ranking/search purposes)
}
]
Search for Flights
Search for available flight offers between two locations.
Endpoint: POST /api/flight/search
Request Body:
{
"originLocationCode": "JRO", // IATA code of the departure airport e.g., LUN (code for Lusaka)
"destinationLocationCode": "DAR", // IATA code of the arrival airport e.g., DAR (code for Dar es Salaam)
"departureDate": "2025-06-15", // Date of outbound flight in YYYY-MM-DD format
"adults": 1, // Number of adult passengers (age 12+)
"children": 0, // Number of child passengers (age 2-11)
"infants": 0, // Number of infant passengers (under 2 years)
"travelClass": "ECONOMY", // Travel class: ECONOMY, PREMIUM_ECONOMY, BUSINESS, FIRST
"currencyCode": "USD", // Preferred currency for prices (e.g., USD, TZS)
}
Request Body Parameters In-depth
| Key | Type | Required | Description |
|---|---|---|---|
originLocationCode | string | Yes | IATA code of the departure airport (e.g., "JRO" for Kilimanjaro) |
destinationLocationCode | string | Yes | IATA code of the arrival airport (e.g., "DAR" for Dar es Salaam) |
departureDate | string (date) | Yes | Outbound flight date in YYYY-MM-DD format |
adults | integer | Yes | Number of adult passengers (aged 12 and above) |
children | integer | Yes | Number of child passengers (ages 2-11) |
infants | integer | Yes | Number of infant passengers (under 2 years old) |
travelClass | string | Yes | Travel class: ECONOMY, PREMIUM_ECONOMY, BUSINESS, or FIRST |
currencyCode | string | Yes | Preferred currency code for prices (e.g., USD, TZS) |
returnDate | string (date) | No | Return flight date in YYYY-MM-DD format (for round trips only) |
includedAirlineCodes | string[] | No | Array of airline codes to include in the results (e.g., ["TC", "PW"]) |
excludedAirlineCodes | string[] | No | Array of airline codes to exclude from results |
nonStop | boolean | No | If true, only direct (non-stop) flights will be returned |
maxPrice | number | No | Maximum price (using currency specified in currencyCode) for filtering results |
max | integer | No | Maximum number of offers to return in the response |
Response:
The API returns a minimal, stable payload with no internal or third-party provider fields:
| Field | Type | Description |
|---|---|---|
meta | object | Contains count (number of flight offers returned). |
data | array | List of flight offer objects (itineraries, price, travelerPricings, baggage, etc.). |
Example response
{
"meta": {
"count": 1
},
"data": [
{
"type": "flight-offer",
"id": "1",
"source": "GDS",
"instantTicketingRequired": false,
"nonHomogeneous": false,
"oneWay": false,
"isUpsellOffer": false,
"lastTicketingDate": "2026-02-25",
"lastTicketingDateTime": "2026-02-25",
"numberOfBookableSeats": 9,
"itineraries": [
{
"duration": "PT2H20M",
"segments": [
{
"departure": {
"iataCode": "DAR",
"at": "2026-02-25T11:00:00",
"stop": {
"id": "DAR:FLT:0",
"iataCode": "DAR",
"name": "Julius Nyerere International Airport",
"nameWithCode": "DAR Julius Nyerere International Airport",
"description": "Dar es Salaam, Tanzania",
"city": "Dar es Salaam",
"country": "Tanzania",
"latitude": -6.87811,
"longitude": 39.202599,
"timezone": "Africa/Dar_es_Salaam",
"geohash": "kygbu5"
}
},
"arrival": {
"iataCode": "LUN",
"at": "2026-02-25T12:20:00",
"stop": {
"id": "LUN:FLT:0",
"iataCode": "LUN",
"name": "Kenneth Kaunda International Airport Lusaka",
"nameWithCode": "LUN Kenneth Kaunda International Airport Lusaka",
"description": "Lusaka, Zambia",
"city": "Lusaka",
"country": "Zambia",
"latitude": -15.3308,
"longitude": 28.4526,
"timezone": "Europe/Berlin",
"geohash": "ktk0z3"
}
},
"carrierCode": "TC",
"number": "216",
"aircraft": {
"code": "223",
"name": "AIRBUS A220-300"
},
"operating": {
"carrierCode": "TC",
"name": "Air Tanzania Company Limited",
"tradingName": "Air Tanzania",
"iataCode": "TC",
"country": "Tanzania, United Republic of",
"logo": {
"favicon": "https://bucket.safiri.app/website/flight/favicons/TC.png",
"small": "https://bucket.safiri.app/website/flight/small-logos/TC.png",
"normal": "https://bucket.safiri.app/website/flight/logos/TC.png"
},
"icaoCode": "",
"callsign": "",
"countryCode": "",
"dateFounded": "",
"status": "active",
"website": "https://www.airtanzania.co.tz"
},
"duration": "PT2H20M",
"id": "1",
"numberOfStops": 0,
"blacklistedInEU": false,
"durations": {
"time": {
"days": 0,
"hours": 2,
"minutes": 20,
"seconds": 0
},
"full": "2 hours 20 minutes",
"short": "2hrs 20mins",
"abbreviation": "2h 20m"
},
"transitAgency": {
"name": "Air Tanzania Company Limited",
"tradingName": "Air Tanzania",
"iataCode": "TC",
"logos": {
"favicon": "https://bucket.safiri.app/website/flight/favicons/TC.png",
"small": "https://bucket.safiri.app/website/flight/small-logos/TC.png",
"normal": "https://bucket.safiri.app/website/flight/logos/TC.png"
},
"website": "https://www.airtanzania.co.tz"
},
"seatId": "FREE",
"seatLabel": "FREE",
"positionX": 0,
"positionY": 0,
"cabin": "ECONOMY"
}
],
"durations": {
"time": {
"days": 0,
"hours": 2,
"minutes": 20,
"seconds": 0
},
"full": "2 hours 20 minutes",
"short": "2hrs 20mins",
"abbreviation": "2h 20m"
}
}
],
"price": {
"currency": "USD",
"total": "554.80",
"base": "264.00",
"fees": [
{
"amount": "0.00",
"type": "SUPPLIER"
},
{
"amount": "0.00",
"type": "TICKETING"
}
],
"grandTotal": "554.80"
},
"pricingOptions": {
"fareType": [
"PUBLISHED"
],
"includedCheckedBagsOnly": true
},
"validatingAirlineCodes": [
"TC"
],
"travelerPricings": [
{
"travelerId": "1",
"fareOption": "STANDARD",
"travelerType": "ADULT",
"price": {
"currency": "USD",
"total": "277.40",
"base": "132.00"
},
"fareDetailsBySegment": [
{
"segmentId": "1",
"cabin": "ECONOMY",
"fareBasis": "XOWAF",
"class": "X",
"includedCheckedBags": {
"quantity": 2
},
"includedCabinBags": {
"quantity": 1
}
}
]
},
{
"travelerId": "2",
"fareOption": "STANDARD",
"travelerType": "ADULT",
"price": {
"currency": "USD",
"total": "277.40",
"base": "132.00"
},
"fareDetailsBySegment": [
{
"segmentId": "1",
"cabin": "ECONOMY",
"fareBasis": "XOWAF",
"class": "X",
"includedCheckedBags": {
"quantity": 2
},
"includedCabinBags": {
"quantity": 1
}
}
]
}
],
"fareRules": {
"rules": [
{
"category": "EXCHANGE",
"maxPenaltyAmount": "50.00"
},
{
"category": "REFUND",
"notApplicable": true
},
{
"category": "REVALIDATION",
"notApplicable": true
}
]
},
"commission": {
"baseCurrency": "USD",
"currency": "USD",
"baseSafiriCommission": 60,
"safiriCommission": 60,
"safiriCommissionFormatted": "USD 60.00",
"baseWakalaCommission": 0,
"wakalaCommission": 0,
"wakalaCommissionFormatted": "USD 0.00"
},
"policiesAndAllowance": {
"title": "Baggage Allowance & Policies",
"bookingInformation": {
"title": "Booking Information",
"ticketIssuing": {
"title": "Ticket Issuing Time",
"content": "Once payment is confirmed, tickets will be issued within 2 hours."
}
},
"baggageAllowance": {
"title": "Baggage Allowance",
"note": "Baggage dimensions include wheels and handles.",
"departure": {
"title": "Depart",
"subtext": "DAR - LUN",
"personalItems": {
"title": "Personal Item",
"content": "1 piece per person, 3 KG per piece.",
"note": "Please check with the airline for detailed regulations."
},
"includedCabinBags": {
"title": "Carry-on Baggage",
"content": "1 piece per person, 7 KG per piece. Must be placed in the overhead compartment.",
"size": "Each piece cannot exceed 55 x 38 x 22 CM in size."
},
"includedCheckedBags": {
"title": "Checked Baggage",
"content": "2 pieces per person, 23 KG per piece.",
"size": "Total dimensions (length + width + height) of each piece cannot exceed 150 CM."
}
}
},
"policies": {
"title": "Flight Cancellation & Change Policies",
"subtitle": "The ticket policy is subject to the rules listed below, and these may differ from the airline's own policies. Any cancellation or change request must be made through Safiri platforms or our customer support.",
"cancellationFee": {
"title": "Cancellation fee",
"subtext": "Price per passenger",
"flexibility": {
"title": "Flexibility Added",
"content": "With Safiri's Flexible Fare option, you'll get up to 90% of your ticket price refunded. Refund amount depends on how many hours before departure the cancellation request is made."
},
"departure": {
"title": "Depart",
"subtext": "DAR - LUN",
"table": {
"headers": [
"Request time",
"Adult tickets"
],
"body": [
[
"24 or more hours before departure",
"90% refund or USD 499.32"
],
[
"12 – 24 hours before departure",
"60% refund or USD 332.88"
],
[
"6 – 12 hours before departure",
"30% refund or USD 166.44"
],
[
"Less than 6 hours before departure",
"Non-refundable. Tax non-refundable."
]
]
}
},
"additionalCancellationInfo": "For cancellations, an additional USD 55.48 service fee will be charged per passenger. For partially used tickets, the value of flown segments and non-refundable taxes will be deducted."
},
"changeFee": {
"title": "Change fee",
"subtext": "Within the same class",
"flexibility": {
"title": "Flexibility Added",
"content": "With Safiri's Flexible Fare option, you'll get a Free first change. Any additional changes are subject to 10% service fee per passenger + fare difference (if any)."
},
"departure": {
"title": "Depart",
"subtext": "DAR - LUN",
"table": {
"headers": [
"Request time",
"Adult tickets"
],
"body": [
[
"First change",
"Free first change"
],
[
"Subsequent changes",
"10% service fee + fare difference"
]
]
}
},
"additionalChangeInfo": [
"* If the new fare is higher, the difference must be paid. If the new fare is lower, no partial refunds are issued. According to the airline's policy, once a ticket has been changed, the change fee that has already been paid is non-refundable. A 10% service fee will be charged per passenger.",
"* If your flight is changed due to external factors (for example if your flight is delayed, rescheduled or canceled) and you need to change your flight, please refer to the latest airline policies."
]
}
}
}
}
]
}
- Each item in
datais a full flight offer. To select it pass the offer to the Lock Flight Price endpoint to lock the price.
Lock Flight Price
Confirm the availability and final price of a flight offer. This step locks in the current price and verifies that seats are still available before you proceed to create a reservation.
Endpoint: POST /api/flight/offer
Request Body:
{
"flightOffer": { /* flight offer object from search results */ },
"include": ["bags", "detailed-fare-rules"], // (OPTIONAL) Additional data to include in response
"forceClass": false // (OPTIONAL) Force usage of booking class for pricing
}
Request Body Parameters In-depth
| Parameter | Type | Required | Description |
|---|---|---|---|
flightOffer | object | Yes | The complete flight offer object from the search response (one offer for one-way or round-trip). |
originLocationCode | string | No | IATA code of departure airport. Derived from the first segment of the first itinerary when omitted. |
destinationLocationCode | string | No | IATA code of arrival airport. Derived from the last segment of the first itinerary when omitted. |
originDesc | string | No | Human-readable origin (e.g. "Kilimanjaro, Tanzania"). Used for commission; optional. |
destinationDesc | string | No | Human-readable destination. Used for commission; optional. |
numberOfPassengers | number | No | Total passengers. Derived from flightOffer.travelerPricings.length when omitted. |
returnDate | string | No | Return date YYYY-MM-DD. For round trips, derived from the second itinerary when omitted. |
include | string[] | No | Extra data: bags, credit-card-fees, other-services, detailed-fare-rules. |
forceClass | boolean | No | Force booking class for pricing (default: false). |
Response:
The response uses the same shape as search: meta plus a data array of enriched flight offers. Use the first offer (data[0]) as flightOffer when calling Create Reservation.
| Field | Type | Description |
|---|---|---|
meta | object | count (number of offers, typically 1), currency (e.g. USD). |
data | array | List of locked, enriched flight offers (itineraries, price, commission, policies, baggage, etc.). |
bookingRequirements | object | Optional. Requirements to collect from the user before creating a reservation (e.g. emailAddressRequired, mobilePhoneNumberRequired, travelerRequirements). |
Example response
{
"meta": {
"count": 1,
"currency": "USD"
},
"data": [
{
"type": "flight-offer",
"id": "1",
"source": "GDS",
"price": {
"currency": "USD",
"total": "150.00",
"base": "120.00",
"fees": [],
"grandTotal": "150.00"
},
"pricingOptions": { "fareType": ["PUBLISHED"], "includedCheckedBagsOnly": true },
"validatingAirlineCodes": ["TC"],
"travelerPricings": [ ... ],
"itineraries": [
{
"duration": "PT1H30M",
"durations": { "full": "1 hour 30 minutes", "short": "1hr 30mins", "abbreviation": "1h 30m" },
"segments": [
{
"departure": { "iataCode": "JRO", "at": "2025-06-15T10:00:00", "stop": { "iataCode": "JRO", "name": "Kilimanjaro", "city": "Kilimanjaro", "country": "Tanzania" } },
"arrival": { "iataCode": "DAR", "at": "2025-06-15T11:30:00", "stop": { "iataCode": "DAR", "name": "Julius Nyerere", "city": "Dar es Salaam", "country": "Tanzania" } },
"carrierCode": "TC",
"number": "123",
"transitAgency": { "name": "Precision Air", "iataCode": "TC", "logos": { ... } },
"durations": { "full": "1 hour 30 minutes", "short": "1hr 30mins" }
}
]
}
],
"commission": { "currency": "USD", "safiriCommissionFormatted": "12.00 USD", "wakalaCommissionFormatted": "15.00 USD", ... },
"policiesAndAllowance": { ... }
}
],
"bookingRequirements": {
"emailAddressRequired": true,
"mobilePhoneNumberRequired": true,
"travelerRequirements": [{
"travelerId": "1",
"documentRequired": true,
"genderRequired": true, "dateOfBirthRequired": true
}]
}
}
The response will include a bookingRequirements field. These are important as they specify all information you need to collect from the user in order to successfully submit the reservation. Be sure to capture every requirement listed before proceeding to the booking step.
Create Reservation
Create a flight reservation using your float account. The authenticated user (from the JWT) must be a Safiri agent; their wallet is charged. You do not send boughtByUser or display fields (from, to, fromStopId, etc.) — the agent is identified from the JWT and origin/destination are derived from the offer.
Endpoint: POST /api/flight/reserve/agent
Headers:
Authorization: Bearer <JWT_TOKEN>(required)X-External-User-Id: <your_internal_user_id>(optional) — stored and returned for your referenceContent-Language(optional): Response language (default:en)Content-Currency(optional): Currency for pricing (default:USD)
Sample Request Body:
{
"flightOffer": { /* flight offer object from search results */ },
"passengers": [
{
"firstName": "John",
"lastName": "Doe",
"dateOfBirth": "2002-01-01",
"gender": "0",
"email": "john@example.com",
"phoneNumber": "757705678",
"countryCode": "27",
"documentNumber": "7384653435",
"expiryDate": "2030-01-01",
"nationality": "ZA"
}
],
"selectedOption": "NON_REFUNDABLE_FARE",
"notifications": {
"safiriAccountCreation": false,
"ticketPurchase": true,
"allOtherTripNotification": true
},
"bookingRequirements": {}
}
Request Body Parameters In-depth
| Field | Type | Required | Description |
|---|---|---|---|
flightOffer | object | Yes | The locked offer from the Lock Flight Price response: use data[0] from that response. |
passengers | array | Yes | Array of passenger details in traveler order (index 0 = traveler 1). Each object: firstName, lastName, dateOfBirth, gender (e.g. "0" Male, "1" Female), email, phoneNumber, countryCode. When the offer requires a document, include documentNumber, expiryDate, nationality, and optionally issuanceLocation, issuanceDate, validityCountryCode, birthPlace. |
selectedOption | string | No | "FLEXIBLE_FARE" or "NON_REFUNDABLE_FARE" (default: "NON_REFUNDABLE_FARE"). |
notifications | object | No | safiriAccountCreation, ticketPurchase, allOtherTripNotification (each optional, default: true). |
bookingRequirements | object | No | From the Lock Flight Price response; use to know which passenger fields are required. |
Response: On success (201 Created), returns:
- reservation:
bookingReference,paymentReference,paymentDeadlineAt(ISO),paymentTimeLimitInSeconds,ticketIds,verifyUrl,id(flight order id),associatedRecords - tickets: Array of minimal ticket summaries (
ticketId,bookingReference,paymentReference,departureDate,departureTime,from,to,price,passengerSummary,paymentDeadlineAt)
Full ticket details are available via GET /api/flight/tickets?ticketId=<TICKET_ID>.
⚠️ Critical: You must provide every field listed in the bookingRequirements section of the priced offer for each traveler in passengerDetails (such as date of birth, document/passport, gender, etc.), or your booking will fail.
These required fields can differ between offers and must be obtained dynamically based on each price response.
For more details on each field and advanced examples, refer to the official API Swagger documentation.
Retrieve Flight Ticket
Retrieve the details of a specific booked flight ticket by its unique identifier.
Endpoint: GET /api/flight/tickets?ticketId=<TICKET_ID>
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
ticketId | string | Yes | The unique ticket ID returned when you booked the flight. |
Response:
On success (200 OK), returns a detailed array of ticket(s).
Example response
[
{
"_id": "691e0f74971390f6f3686316",
"uuid": "efa8d300-08cb-4015-9d0d-4b60948679ba",
"bookingReference": "8EIVYA",
"bookingOrderId": "eJzTd9e3cPUMi3QEAArzAlw",
"ticketId": "RVLJBT13R6IA38T",
"paymentReference": "FLTRVLJBT13R6IA38T",
"departureDate": "2025-11-21",
"departureTime": "10:50",
"reportingTime": "08:50",
"travelTime": "4h 50m",
"estimatedArrivalTime": "15:40",
"estimatedArrivalDate": "2025-11-21",
"estimatedArrivalDays": 0,
"businessUuid": null,
"from": "LUN Kenneth Kaunda International Airport Lusaka",
"fromStopId": "LUN:FLT:0",
"fromStopDesc": "Lusaka, Zambia",
"to": "CPT Cape Town International Airport",
"toStopId": "CPT:FLT:0",
"toStopDesc": "Cape Town, South Africa",
"returnTrip": false,
"isReturnTicket": false,
"connections": [
{
"departure": {
"iataCode": "LUN",
"at": "2025-11-21T10:50:00",
"stop": {
"stop_id": "LUN:FLT:0",
"stop_lat": -15.3308,
"stop_lon": 28.4526,
"stop_name": "LUN Kenneth Kaunda International Airport Lusaka",
"stop_desc": "Lusaka, Zambia",
"stop_timezone": "Europe/Berlin",
"city_geohash": "ktk0z3",
"popularity": 30
}
},
"arrival": {
"iataCode": "GBE",
"at": "2025-11-21T12:30:00",
"stop": {
"stop_id": "GBE:FLT:0",
"stop_lat": -24.555201,
"stop_lon": 25.9182,
"stop_name": "GBE Sir Seretse Khama International Airport",
"stop_desc": "Gaberone, Botswana",
"stop_timezone": "Africa/Gaborone",
"city_geohash": "kedkmc",
"popularity": 30
}
},
"carrierCode": "BP",
"number": "323",
"aircraft": { "code": "E75" },
"operating": null,
"duration": "PT1H40M",
"stops": null,
"id": "117",
"seatId": "FREE",
"seatLabel": "FREE",
"positionX": 0,
"positionY": 0,
"cabin": "X",
"transitAgency": {
"name": "Air Botswana",
"tradingName": "Air Botswana",
"iataCode": "BP",
"logos": {
"favicon": "https://bucket.safiri.app/website/flight/favicons/BP.png",
"small": "https://bucket.safiri.app/website/flight/small-logos/BP.png",
"normal": "https://bucket.safiri.app/website/flight/logos/BP.png"
},
"website": "https://www.airbotswana.co.bw"
}
},
{
"departure": {
"iataCode": "GBE",
"at": "2025-11-21T13:40:00",
"stop": {
"stop_id": "GBE:FLT:0",
"stop_lat": -24.555201,
"stop_lon": 25.9182,
"stop_name": "GBE Sir Seretse Khama International Airport",
"stop_desc": "Gaberone, Botswana",
"stop_timezone": "Africa/Gaborone",
"city_geohash": "kedkmc",
"popularity": 30
}
},
"arrival": {
"iataCode": "CPT",
"at": "2025-11-21T15:40:00",
"stop": {
"stop_id": "CPT:FLT:0",
"stop_lat": -33.964802,
"stop_lon": 18.6017,
"stop_name": "CPT Cape Town International Airport",
"stop_desc": "Cape Town, South Africa",
"stop_timezone": "Africa/Johannesburg",
"city_geohash": "k3vnz2",
"popularity": 30
}
},
"carrierCode": "BP",
"number": "233",
"aircraft": { "code": "E75" },
"operating": null,
"duration": "PT2H",
"stops": null,
"id": "118",
"seatId": "FREE",
"seatLabel": "FREE",
"positionX": 0,
"positionY": 0,
"cabin": "X",
"transitAgency": {
"name": "Air Botswana",
"tradingName": "Air Botswana",
"iataCode": "BP",
"logos": {
"favicon": "https://bucket.safiri.app/website/flight/favicons/BP.png",
"small": "https://bucket.safiri.app/website/flight/small-logos/BP.png",
"normal": "https://bucket.safiri.app/website/flight/logos/BP.png"
},
"website": "https://www.airbotswana.co.bw"
}
}
],
"numberOfStops": 2,
"price": {
"currency": "GBP",
"total": "336.00",
"base": "130.00",
"taxes": [
{ "amount": "9.50", "code": "BW" },
{ "amount": "7.60", "code": "H2" },
{ "amount": "19.00", "code": "JI" },
{ "amount": "1.40", "code": "K2" },
{ "amount": "3.80", "code": "QJ" },
{ "amount": "7.60", "code": "RM" },
{ "amount": "5.10", "code": "S8" },
{ "amount": "152.00", "code": "YQ" }
]
},
"passengerDetails": {
"firstName": "Jessie",
"lastName": "Johns",
"fullName": "Jessie Johns",
"phoneNumber": "7577056786",
"countryCode": "44",
"countryCodeName": "gb",
"email": "jessietjohns@itule.me",
"gender": "Female",
"ageCategory": "Adult",
"passportNumber": "127301797",
"passportExpiryDate": "2031-11-11T00:00:00.000Z",
"passportIssuedDate": null,
"nationality": "GB",
"nationalityCountry": null,
"nextOfKin": null,
"identificationInfo": null,
"luggagePrice": null,
"dateOfBirth": "1997-08-23T00:00:00.000Z",
"price": {
"currency": "GBP",
"total": "336.00",
"base": "130.00",
"taxes": [
{ "amount": "9.50", "code": "BW" },
{ "amount": "7.60", "code": "H2" },
{ "amount": "19.00", "code": "JI" },
{ "amount": "1.40", "code": "K2" },
{ "amount": "3.80", "code": "QJ" },
{ "amount": "7.60", "code": "RM" },
{ "amount": "5.10", "code": "S8" },
{ "amount": "152.00", "code": "YQ" }
]
},
"discountInfo": null
},
"seats": [
{
"seatId": "FREE",
"seatLabel": "FREE",
"positionX": 0,
"positionY": 0,
"cabin": "ECONOMY"
},
{
"seatId": "FREE",
"seatLabel": "FREE",
"positionX": 0,
"positionY": 0,
"cabin": "ECONOMY"
}
],
"isPassengerPaid": false,
"includedCheckedBags": 1,
"includedCheckedBagsWeight": 23,
"includedCheckedBagsWeightUnit": "KG",
"includedCabinBags": 1,
"includedCabinBagsWeight": 7,
"includedCabinBagsWeightUnit": "KG",
"additionalCheckedBags": 0,
"additionalCheckedBagsWeight": 0,
"additionalCheckedBagsWeightUnit": "KG",
"refundableFare": null,
"noRestrictionFare": null,
"noPenaltyFare": null,
"boughtByUser": {
"_id": "5f4edb1c4f07f40023883840",
"uuid": "f7a76866-4452-40e4-9686-1ccc33bcf696",
"email": "jessietjohns@icloud.com",
"fullName": "Tafadzwa Johns",
"permissionLevel": 777,
"phoneNumber": "7577056786",
"countryCode": "44",
"countryCodeName": "GB",
"companies": null,
"business": null,
"commission": null
},
"cancelled": false,
"cancelledBy": null,
"validation": null,
"dateBought": "2025-11-19T18:41:56.343Z",
"boughtOn": "2025-11-19T00:00:00.000Z",
"bookingUserType": null,
"bookingChannel": "web-app",
"bookingWebsiteOwner": null,
"commission": {
"baseCurrency": "USD",
"currency": "USD",
"baseSafiriCommission": 30,
"safiriCommission": 30,
"safiriCommissionFormatted": "USD 30.00",
"baseWakalaCommission": 0,
"wakalaCommission": 0,
"wakalaCommissionFormatted": "USD 0.00"
},
"safiriServiceFee": {
"fee": 30,
"currency": "USD",
"GBP": 22.85,
"USD": 30,
"TZS": 73794.21
},
"wakalaServiceFee": null,
"safiriVerificationUrl": "https://safiri.app/en/ticket/review?ticketUuid=efa8d300-08cb-4015-9d0d-4b60948679ba&bookingReference=8EIVYA",
"issuerDeviceInfo": null,
"workflowId": "FLIGHT_TICKET_ONLINE_BOOKING-FLTRVLJBT13R6IA38T-RVLJBT13R6IA38T-8EIVYA",
"selectedOption": {
"option": "NON_REFUNDABLE_FARE",
"currency": "GBP",
"serviceFee": 33.6,
"serviceFeePercentage": 10,
"refundablePercentage": 0,
"refundableAmount": 0,
"changeCount": 0,
"hoursUntilDeparture": null,
"canRefund": null,
"refundDeadline": null,
"refundAmountFormatted": null,
"refundDeadlineFormatted": null
},
"policiesAndAllowance": {
"title": "Baggage Allowance & Policies",
"bookingInformation": {
"title": "Booking Information",
"ticketIssuing": {
"title": "Ticket Issuing Time",
"content": "Once payment is confirmed, tickets will be issued within 2 hours."
}
},
"baggageAllowance": {
"title": "Baggage Allowance",
"note": "Baggage dimensions include wheels and handles.",
"departure": {
"title": "Depart",
"subtext": "LUN - GBE, GBE - CPT",
"personalItems": {
"title": "Personal Item",
"content": "1 piece per person, 3 KG per piece.",
"note": "Please check with the airline for detailed regulations."
},
"includedCheckedBags": {
"title": "Checked Baggage",
"content": "1 pieces per person, 23 KG per piece.",
"size": "Total dimensions (length + width + height) of each piece cannot exceed 150 CM."
}
}
},
"policies": {
"title": "Flight Cancellation & Change Policies",
"subtitle": "The ticket policy is subject to the rules listed below, and these may differ from the airline’s own policies. Any cancellation or change request must be made through Safiri platforms or our customer support.",
"cancellationFee": {
"title": "Cancellation fee",
"subtext": "Price per passenger",
"flexibility": {
"title": "Flexibility Added",
"content": "With Safiri's Flexible Fare option, you'll get up to 90% of your ticket price refunded. Refund amount depends on how many hours before departure the cancellation request is made."
},
"departure": {
"title": "Depart",
"subtext": "LUN - GBE, GBE - CPT",
"table": {
"headers": [
"Request time",
"Adult tickets"
],
"body": [
[
"24 or more hours before departure",
"90% refund or GBP 302.40"
],
[
"12 – 24 hours before departure",
"60% refund or GBP 201.60"
],
[
"6 – 12 hours before departure",
"30% refund or GBP 100.80"
],
[
"Less than 6 hours before departure",
"Non-refundable. Tax non-refundable."
]
]
}
},
"additionalCancellationInfo": "For cancellations, an additional GBP 33.60 service fee will be charged per passenger. For partially used tickets, the value of flown segments and non-refundable taxes will be deducted."
},
"changeFee": {
"title": "Change fee",
"subtext": "Within the same class",
"flexibility": {
"title": "Flexibility Added",
"content": "With Safiri's Flexible Fare option, you'll get a Free first change. Any additional changes are subject to 10% service fee per passenger + fare difference (if any)."
},
"departure": {
"title": "Depart",
"subtext": "LUN - GBE, GBE - CPT",
"table": {
"headers": [
"Request time",
"Adult tickets"
],
"body": [
[
"First change",
"Free first change"
],
[
"Subsequent changes",
"10% service fee + fare difference"
]
]
}
},
"additionalChangeInfo": [
"* If the new fare is higher, the difference must be paid. If the new fare is lower, no partial refunds are issued. According to the airline's policy, once a ticket has been changed, the change fee that has already been paid is non-refundable. A 10% service fee will be charged per passenger.",
"* If your flight is changed due to external factors (for example if your flight is delayed, rescheduled or canceled) and you need to change your flight, please refer to the latest airline policies."
]
}
}
}
}
]
Errors
400 Bad Request: Missing or invalidticketId.404 Not Found: Ticket not found for the giventicketId.401 Unauthorized: Invalid or missing JWT token.
- The
ticketIdparameter is required. It must be the same ID you received when booking the ticket. - A successful response returns the full booking details, passenger information, and comprehensive itinerary and price data.
Notification Preferences
Control which notifications are sent using the notifications object in your booking request. Example:
{
"notifications": {
"safiriAccountCreation": false, // Suppress Safiri account creation notifications
"ticketPurchase": false, // Suppress ticket purchase confirmations
"allOtherTripNotification": false // Suppress delay/cancellation notifications
}
}
Set any value to false to suppress that notification type. Omitted fields default to true (notifications sent).
Error Handling
Common error codes:
401 Unauthorized: Invalid or missing JWT token400 Bad Request: Invalid request parameters402 Payment Required: Insufficient wallet balance500 Internal Server Error: Server error
External User ID
The X-External-User-Id header allows you to pass your internal user IDs for reference tracking. This ID is stored and returned but not used for business logic.
Other Documentation
- Swagger UI: Flight Swagger Docs
- Postman Collections: Download Flight Booking Postman Collection