6 · Book
Place a booking.
What: places the booking. Requires the quoteToken from /fare-quote; the supplier-quoted price is taken from the signed token, so a tenant can’t book a different price than was quoted.
When: after a successful /fare-quote, within the token’s validity.
Before / After: /fare-quote → here → do what the booking’s nextAction says (WAIT, ISSUE_TICKET, SUBMIT_PNR, SEARCH_AGAIN, CONTACT_SUPPLIER or NONE).
Gotchas
- Read the new names — the old ones are removed in the next release. Each new name has the same value as its old one until then, and the reference marks the old name deprecated.
supplierCodeforsourceCode;sellerforsupplier;origin/destination({ iataCode, name, city, countryCode, timezone }) fororiginCode/destCode;price(withquotedTotalAmount, the total of the quote the booking was made on) fortotalAmount/currency/fareBreakdown;policy.refund/policy.changeforpolicy.refundPolicy/changePolicy; each flight’smarketingCarrier { iataCode, name, flightNumber }forairlineCode/airlineName/flightNumber,origin/destinationfor its codes, names and time zones,departureTerminal/arrivalTerminal/cabinfororiginTerminal/destinationTerminal/cabinClass, a technical stop’sairportforairportCode/airportName; each passenger’spassengerTypefortravelerTypeandguardianPassengerIndexforguardian. - Send the offer’s trip back —
tripType,departureDateand (round trip)returnDate— exactly as on/fare-quote(the token binds them). - Idempotency is the
Idempotency-KeyHEADER (required). Persist it and reuse the same value on retries — the booking is reserved before the supplier call, so a retry returns the same booking instead of charging twice. There is no body idempotency field. passengers[].passengerTypeisADULT/CHILD/INFANT, and the array must matchadults/children/infants— which must be the party the offer was searched for (else400 PASSENGER_COUNTS_CHANGED). ACHILDorINFANTneedsdateOfBirth. The type goes by age on the date of the last flight (the return flight on a round trip):ADULT12 or older,CHILD2–11,INFANTunder 2 — so a baby who turns 2 before the return flight books as aCHILD. A date of birth that does not fit the type, or is after the departure date, →422 PASSENGER_FIELD_REQUIREDnaming the passenger by position, the date and the type to use: search again with that passenger counted as that type (the passengers you book must be the ones you searched with, so changing the type on/bookalone is refused again). EveryINFANTneedsguardianPassengerIndex: the position inpassengers(0 = the first) of theADULTit travels with, on their lap; each adult takes one infant.- Every booking shows what the offer was sold with: each flight’s
departureTerminal,arrivalTerminalandcabin, the offer’scabinandpolicy, and its includedbaggage(the supplier’s booking answer when it gave one, else the quote at booking, else Search; a later supplier answer that states baggage — a refresh, or the status check of a booking confirmed after/book— replaces the rows of the passenger types it states, the others stay;nullwhen none stated any). These describe the offer, so aFAILEDbooking shows them too: nothing was booked. Each passenger shows thepassportIssuingCountryKitionz sent. - An adult with no
dateOfBirthon a domestic trip: some suppliers refuse a booking without one, so Kitionz sends them 1 January of the year 20 years back. The booking does not show that date as the passenger’s: it answersdateOfBirth: nullwithdateOfBirthDefaulted: true. Send the real date when you have it. bookeris your record of who made the booking: Kitionz stores and returns it, and some sellers receivebooker.nameas the contact name. The seller receives your organisation’s registered email and mobile as the booking contact (your Kitionz account details), notbooker.email/booker.phone(booker.phoneis sent only when your organisation has no mobile on record). Seller and airline messages (e-ticket, schedule change, cancellation) therefore come to you: pass them on to your traveller.- Which reference to keep: the booking
id(bkg_…) — your key for every call after booking; key your orders on it, never on the PNR.pnris the airline’s booking code for the traveller (check-in, the airline’s website); it can arrive after/book.supplierReferenceis the seller’s own booking number: quote it to the seller’s support. Send your own order number asexternalReference: if you lose the/bookanswer,GET /v1/flights/bookings?externalReference=…finds the booking. Kitionz returns no e-ticket numbers or ticket document: get them from the seller named inseller. - Old names, removed in the next release:
supplierOfferRef(nowofferId),passengers[].travelerType(nowpassengerType),passengers[].guardian(the adult’s name; nowguardianPassengerIndex),bookingMode: "LIVE"(now"INSTANT"),originCodeanddestCode(not needed: Kitionz takes the route from the offer). They are still accepted until then; use the names above. An old and a new name with different values →400 VALIDATION_ERRORnaming both. Answers carry the new names beside the old ones (see the first note);bookingModestill answers"LIVE"for an instant booking until the next version. bookingMode: "INSTANT"(the default) books now, with no hold. The offer decides how: through the seller’s system, or, when the offer is a request booking, as a request you complete by sending the PNR (nextActionsays which)."HOLD"holds the seats where the offer allows it.- The body takes only the fields in the reference: any other field →
400 VALIDATION_ERROR. Seats, meals and extra baggage, payment details, corporate or tour codes and frequent-flyer numbers cannot be sent at booking (no supplier receives them). - A
201means Kitionz recorded a booking — decide bydata.statusanddata.nextAction, never by the HTTP status alone.FAILED(withfailuresaying why) means nothing was booked or charged: search again.PENDING_CONFIRMATIONmeans wait: the booking may exist; readGET /bookings/{id}afternextAction.pollAfterSeconds, and never book again meanwhile. - If
/bookis slow or your request times out, never book again with a new key. Send the identical request again with the SAMEIdempotency-Key: you get the first booking back, or409 BOOKING_IN_PROGRESS/IDEMPOTENT_REQUEST_IN_PROGRESSwhile it is still running (wait, then send it again).409 QUOTE_TOKEN_ALREADY_USEDnames the booking that quote already made indetails.existingBookingId: read it. 500 BOOKING_OUTCOME_UNKNOWN(withdetails.bookingId) and any answer with no Kitionzerror.code(for example a504from the network) mean the booking may exist: send the same request again with the same key, or read the booking. Set your HTTP timeout for/bookto at least 90 seconds; a slow supplier can take more than a minute.- A
4xxmeans nothing was booked and no supplier booking was made. - Just before booking, Kitionz asks the supplier for the fare again and checks it the same way as
/fare-quote. A broken answer →422 FARE_QUOTE_UNUSABLE. An infant price that changed since your quote →409 FARE_RECHECK_FAILED:failureClassINFANT_PRICE_WITHDRAWN(you were quoted one, the supplier gives none now; quote again: it showsNOT_GIVEN) orINFANT_PRICE_APPEARED(you were quotedNOT_GIVEN, the supplier gives one now; quote again: it showsPRICED). All are definite: nothing was booked or charged. - You never send an amount. When the quote said
infantFare.status: NOT_GIVEN, the booking is made at the supplier’s total (adults and children) and your own infant charge stays with you. The booking’sprice.infantFareis the supplier’s statement when the booking was made; since/bookrefuses any change from the quote, it is always the status you were quoted.
Example — request (with the Idempotency-Key: <your-uuid> header):
Response (trimmed) — the HTTP status is 201; decide by data.status:
seller names who sold the flight (the real seller behind a marketplace), with the support contacts Kitionz holds — on every booking from the moment it exists (supplier is its old name). returnSupplier is always null (one seller per booking); it will be removed in a later version.
Authentication
Your Kitionz API key, sent as Authorization: Bearer keyId:secret. Create a Test key at sandbox.kitionz.com → API keys, a Live key at kitionz.com → API → API keys.
Headers
A new unique value (a UUID) for every write: one booking, one cancel. Save it with your order before you call. Send the SAME value again only when you send the SAME request again after a timeout: you get the first answer back. Reusing it on any other write answers 422 IDEMPOTENCY_KEY_REUSED_WITH_DIFFERENT_PARAMS.
Request
Required (or its old name supplierOfferRef; send one, null is refused): the id of the offer you chose in /search — the offer’s offerId (floffer_…, 30 characters).
The offer’s departureDate from /search — send it back unchanged (a different date → 400 OFFER_TRIP_MISMATCH).
Leave it out: the offer id already names the supplier. Only an organisation shown suppliers before booking may send it, and it must then match the offer; for everyone else it is refused with SOURCE_CODE_NOT_ACCEPTED.
The offer’s tripType from /search. May be left out: the offer’s own trip type is used. When sent it must be the offer’s (another → 400 OFFER_TRIP_MISMATCH).
Round trip only: the offer’s returnDate from /search, sent only with tripType ROUND_TRIP. May be left out (the offer fixes it); when sent, send it on /fare-quote and /book alike.
Your record of who made the booking (for example your agent or customer): Kitionz stores it and returns it on the booking, and some sellers receive name as the contact name. The seller receives your organisation’s registered email and mobile as the booking contact (your Kitionz account details), not booker.email / booker.phone (booker.phone is sent only when your organisation has no mobile on record). Seller and airline messages — e-ticket, schedule change, cancellation — come to you: pass them on to your traveller.
INSTANT (the default): book now, with no hold. The offer decides how: through the seller’s system, or, when the offer is a request booking, as a request you complete by sending the PNR. HOLD: hold the seats where the offer allows it. LIVE is the old name of INSTANT, accepted until the next release, which removes it.
Your own order number, up to 120 characters. Kitionz stores it and returns it on the booking. Send it on every /book: if you lose the answer, GET /v1/flights/bookings?externalReference=… finds the booking. Not checked for uniqueness.
Response
A full persisted booking (passengers, segments, events, amounts, status).