Skip to main content
Booking with Jinko is a staged workflow. Once you’ve picked a candidate flight or hotel, every action mutates a server-side trip until you ship the user to a Stripe checkout page. This page explains the moving parts. If you just want to ship code, jump to the Flight booking guide.

The pipeline

Each arrow is a server call. The session state lives on the trip, you never reconstruct it client-side.

Discovery vs live pricing

Two tools play here:
  • Discovery (find_destination, flight_calendar, find_dates, lowest_fare) is cached. Returns broad, fast, exploratory results. Prices may be 5-30 minutes stale.
  • Live pricing (flight_search, hotel_search) hits the upstream provider directly. Returns a trip_item_token you can add to a trip.
You always go discovery → live pricing before adding to a trip. The discovery offer_token is not bookable on its own, pass it to flight_search (price-check mode) to get a fresh trip_item_token.

Tokens and IDs

Tokens are scoped, an offer_token from yesterday won’t work today. Always go through the pipeline fresh.

The identity you submitted is echoed back

Every item the API hands you carries the handle you used to create it, so you can reconcile a trip, a checkout, or a finished booking against your own search results without keeping a side map from your ids to ours. The echo is on items[] of all four item-bearing responses, POST /v1/trip (the create-and-add receipt) as well as GET /v1/trip/{id}, POST /v1/checkout and GET /v1/trip/{id}/ancillaries, and on every entry of bookings[] once the trip is fulfilled. Which fields appear depends on what the item is: On a hotel item that is the whole identity: offer_id and hotel_id, nothing more. The htl_ token already names the room and the rate you picked, so those two fields are enough to tie the item back to the rate in your hotel_search results.
The platform’s own storage handles are deliberately not echoed. offer_token (the search-index document id) and the provider-native rate id are internal, they change independently of anything you did, and matching on them is a bug waiting to happen. There is no room_id or rate_id on the echo either, and that is on purpose rather than an omission. The values the platform holds for those are supplier-internal tokens that never matched the room and rate ids hotel_search publishes, so echoing them would have correlated nothing while looking as though it did. offer_id is the field that actually round-trips. Match on the fields above and nothing else.

Three caveats when reconciling

trip_item_token can be absent on a flight item. The fare half is filled in by the platform’s resolver, and a caller who supplies their own item snapshot skips it. When the key is missing, read it as “the platform cannot name the fare”, never as a different fare. offer_id is still there.
A hotel offer_id stays the token you submitted even if the rate moved. When a provider re-shops the occupancy, the priced rate can differ from the one your token named, while the echo keeps naming what you sent. That is the point: it is a receipt of your request, not a description of the final rate. Read price, price_changed and hotel_terms for what the rate actually became.
Flights are de-duplicated by itinerary, not by fare. Adding the same itinerary twice with two different fares leaves one item, and the echo can name the first fare added rather than the second. If you need both fares on one trip, add them as separate itineraries.

Trip vs cart vs quote

  • Trip is the canonical user-facing name across SDK / CLI / MCP / API docs. All public examples and tool names use it (trip, getTrip, trip_id).
  • Cart is a legacy internal name that surfaces in one place: some server responses still return cart_id (numeric, the storage primary key) alongside trip_id. They identify the same object. Prefer trip_id in your code.
  • A quote is the price-locked snapshot of a trip at a point in time. checkout() schedules a quote internally before creating the Stripe session, you don’t manipulate quotes directly.

Multi-domain trips

A single trip can hold flight items AND hotel items. They go through one Stripe checkout. To build a multi-domain trip:

Providers

The API aggregates multiple upstream providers behind a unified API. As of this writing:
  • Flights: TravelFusion (NDC + LCC), Sabre (GDS).
  • Hotels: Nuitée (LiteAPI).
You usually don’t need to think about providers, the API picks the cheapest / fastest match. The provider field is exposed on responses (and accepted as an override on refund/exchange) for cases where you need to target one explicitly.

Hosted checkout

Jinko owns the payment surface. You never touch credit-card data. Flow:
  1. checkout() returns a checkout_url pointing at app.gojinko.com/checkout?t=<signed token>. Open the string as returned; never assemble one yourself.
  2. Send the user there (open in browser, deep-link from a widget, etc.).
  3. They pay on Stripe-hosted UI.
  4. Stripe webhooks finalize the booking, no client confirm step needed.
  5. Poll getTrip to watch fulfillment status (awaiting_payment → preparing → prepared → processing → confirming, ending at one of six terminal states: completed, partial, failed, cancelled, expired_quote, exchange_partial_failure). fulfillment is absent until the customer pays. The flight booking guide has the full table.
The user’s money never enters your system. You don’t need PCI compliance to use Jinko.

Async fulfillment

Booking confirmation isn’t always instant, TravelFusion bookings can take up to 72 hours to confirm with the airline. The API handles this with a job queue (River + Postgres). You poll getTrip to watch state; the user gets confirmation emails directly from Jinko when their booking lands. For end-to-end mechanics, see the Flight booking guide or the Flight + Hotel guide for a multi-item trip.