Skip to main content
This guide walks you through booking a real flight from start to finish. The same flow works regardless of interface, so each step shows three variants: SDK, CLI, and MCP. The one-line version of the flow:
discovery → flight_search → trip(add_item + travelers) → checkout → user pays → get_trip

Prerequisites

  • A Jinko account and an API key (jnk_...). Get one.
  • For the SDK path: Node.js 20 or later, then npm install @gojinko/api-client.
  • For the CLI path: npm install -g @gojinko/cli && jinko auth login --key jnk_....
  • For the MCP path: any MCP client connected to https://mcp.builders.gojinko.com/mcp.

1) Discover flights

Start with discovery. It is cached, fast, and broad.
import { createJinkoClient } from '@gojinko/api-client'

const client = await createJinkoClient({ apiKey: process.env.JINKO_API_KEY })

const { destinations } = await client.findDestination({
  origins: ['JFK'],
  destinations: ['CDG'],          // filter discovery to your target airport
  origin_type: 'airport',
  destination_type: 'airport',
  trip_type: 'roundtrip',
  departure_dates: ['2026-06-15'],
  return_dates: ['2026-06-22'],
})

// Each destination comes back with itineraries that carry an offer_token
// you can re-price with flight_search.
const candidate = destinations[0].flights[0]
console.log(candidate.offer_token, candidate.total)

2) Live pricing

Discovery returns cached prices. Before booking, confirm them with flight_search. flight_search is a flat request: origin, destination, and departure_date are required (return_date for round-trips). To re-price a specific candidate from discovery, also pass its offer_token, but the route and dates are still required alongside it. The response is an offers[] list, and each offer carries a fares[] array. A fare’s trip_item_token is ready to drop straight into a trip, no assembly required.
const priced = await client.flightSearch({
  origin: 'JFK',
  destination: 'CDG',
  departure_date: '2026-06-15',
  return_date: '2026-06-22',
  trip_type: 'roundtrip',
  offer_token: candidate.offer_token, // optional: re-price the discovered offer
})

const offer = priced.offers[0]
const tripItemToken = offer.fares[0].trip_item_token
If the response status is flight_unavailable, the flight sold out between discovery and now, and the alternatives[] field has replacements. Loop back to discovery or present them to the user.

3) Build the trip

Now you have a live trip_item_token. Add it to a trip AND set travelers in one call:
const trip = await client.trip({
  add_item: { trip_item_token: tripItemToken },
  upsert_travelers: {
    travelers: [{
      first_name: 'Jane',
      last_name: 'Doe',
      date_of_birth: '1990-01-15',
      gender: 'FEMALE',
      passenger_type: 'ADULT',
    }],
    contact: {
      email: 'jane@example.com',
      phone: '+33612345678',
    },
  },
})
const tripId = trip.trip_id

4) Quote and select ancillaries (optional)

If you want to preselect bags, seats, or meals before checkout, list what’s available with getAncillaries (it surfaces the ancillaries without generating a checkout URL), then pre-select. (Make sure travelers are set first; ancillaries are priced per passenger.)
// List ancillaries: runs a price quote behind the scenes, no checkout URL.
let ancillaries = await client.getAncillaries(tripId)

// The quote may still be pricing; poll until it's ready.
while (ancillaries.status === 'pricing') {
  await new Promise(r => setTimeout(r, (ancillaries.retry_after_seconds ?? 2) * 1000))
  ancillaries = await client.getAncillaries(tripId)
}

// Pick an ancillary offer
const item = ancillaries.items[0]
const bagOffer = item.available_ancillaries.find(a => a.type === 'bag')
await client.selectAncillaries({
  trip_id: tripId,
  item_id: item.item_id,
  selections: [{ offer_id: bagOffer.offer_id }],
})
You don’t have to pre-select. Your user can pick on the Stripe checkout page. Skip this step if you want the simplest flow.

5) Checkout

Create the Stripe checkout session:
const { checkout_url } = await client.checkout(tripId)
console.log('Open in browser:', checkout_url)
The checkout_url points at app.gojinko.com/checkout, a Stripe-hosted page Jinko owns.
On REST / CLI / SDK, checkout is synchronous: it schedules the quote, polls until it’s ready, and returns the full checkout envelope in one call: { session_id, checkout_url, agent_spt_params, status: "ready", total_amount, items }. (On MCP, the booking widget drives this interactively instead of returning the envelope.) Either way, fulfillment still happens asynchronously after the user pays. See steps 7 to 8.
Paying agentically (no browser)? checkout also returns agent_spt_params. Mint a Shared Payment Token scoped to those params, then authorize the booking server-side with submit_agent_payment (jinko agent-pay submit --trip-id "$TRIP_ID" --token "$SPT" / client.submitAgentPayment(tripId, spt)). If the card issuer requires 3DS, the response falls back to a checkout_url.

6) User pays

Send the user to checkout_url. They:
  1. Confirm the itinerary.
  2. Pick ancillaries (if not pre-selected).
  3. Enter payment.
  4. Stripe holds the authorization.

7) Fulfillment is automatic

Once the user pays, Stripe webhooks trigger fulfillment on the API. No client-side confirm step is needed. Fulfillment states (get_trip → fulfillment.status):
StateMeaning
pendingPayment cleared, booking not yet attempted
fulfillingCalling the airline or provider (takes seconds to hours)
completedBooking confirmed, pnr and booking_ref populated
failedProvider rejected (rare). Refund is issued automatically.

8) Watch the booking land

Poll get_trip until fulfillment.status is terminal:
async function waitForBooking(client, tripId: string) {
  while (true) {
    const trip = await client.getTrip(tripId)
    if (trip.fulfillment?.status === 'completed') {
      console.log('Booked! Ref:', trip.bookings[0].booking_ref)
      return trip
    }
    if (trip.fulfillment?.status === 'failed') {
      throw new Error('Booking failed, refund in progress')
    }
    await new Promise(r => setTimeout(r, 5000))
  }
}

What’s next?

  • Add a hotel to the trip: see the Flight + Hotel guide for one trip with two items.
  • Hotel-only booking: see the Hotel booking guide.
  • Refund a booking: Refund flow, refund-check then refund-commit then refund-status.
  • Exchange dates: Exchange flow, a four-step variant of the booking flow.
  • Troubleshooting: Errors has the full status-code reference.