Skip to main content
Most real journeys involve more than one item. Jinko’s trip is a single booking unit: you can put a flight and a hotel into the same trip, set the travelers once, and check out once. The user pays a single Stripe charge and receives a single confirmation with a single Jinko booking reference covering both bookings. This guide builds a Paris weekend (a flight from JFK to CDG and a hotel in Paris) end to end with side-by-side examples for SDK, CLI, and MCP. The one-line version of the flow:
flight discovery → flight_search → hotel_search → trip(add_item flight + travelers) → trip(add_item hotel) → book → 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) Find the flight

Discover flights for the route and dates, then confirm pricing on the candidate the user picks. This is the same first two steps as the Flight booking guide, so we keep it short.
import { createJinkoClient } from '@gojinko/api-client'

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

const flightCandidates = await client.findFlight({
  origin: 'JFK',
  destination: 'CDG',
  trip_type: 'roundtrip',
  departure_dates: ['2026-06-15'],
  return_dates: ['2026-06-22'],
})

// flight_search is flat: origin/destination/departure_date are required;
// offer_token (optional) re-prices the discovered candidate. The returned
// fare's trip_item_token is ready to add to a trip as-is.
const priced = await client.flightSearch({
  origin: 'JFK',
  destination: 'CDG',
  departure_date: '2026-06-15',
  return_date: '2026-06-22',
  trip_type: 'roundtrip',
  offer_token: flightCandidates.results[0].offer_token,
})
const flightToken = priced.offers[0].fares[0].trip_item_token

2) Find the hotel

Search hotels at the destination for the same dates. Hotel rates are already live offers, no separate price-check step is needed.
const hotels = await client.hotelSearch({
  destination: { query: 'Paris' },
  checkin: '2026-06-15',
  checkout: '2026-06-22',
  adults: 1,
})

const hotelToken = hotels.hotels[0].rooms[0].rates[0].offer_id // htl_*

3) Build the trip with both items

Now the key step. Add the flight to a new trip and set travelers in the same call. Then add the hotel to the same trip with a second trip(add_item) call. Both items now live on the same trip and will check out together.
// Create the trip with the flight + travelers in one call.
const trip = await client.trip({
  add_item: { trip_item_token: flightToken },
  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

// Append the hotel to the same trip.
await client.trip({
  trip_id: tripId,
  add_item: { trip_item_token: hotelToken },
})
The trip response now contains two items[]: the flight and the hotel. The total price is the sum of both, in the same currency.

4) Checkout

Call book once. You get one Stripe checkout_url covering both items.
const { checkout_url } = await client.book(tripId)
console.log('Open in browser:', checkout_url)

5) User pays

The user opens checkout_url, sees the flight and the hotel side by side with one total, enters payment once, and Stripe holds the authorization.

6) Fulfillment

Stripe webhooks trigger fulfillment for both items. They book in parallel and the trip is completed once both providers confirm.
StateMeaning
pendingPayment cleared, neither item booked yet
fulfillingAt least one item is still being booked
completedBoth items confirmed, booking_ref populated for each
failedA provider rejected. Refund covers the full amount.
If only one of the two providers fails, Jinko rolls back the other booking and refunds the full charge. You get a single, atomic outcome: both succeed or neither does.

7) Watch the booking land

Poll get_trip until fulfillment.status is terminal. The bookings[] array will contain entries for both the flight and the hotel.
const trip = await client.getTrip(tripId)
if (trip.fulfillment?.status === 'completed') {
  for (const booking of trip.bookings) {
    console.log(booking.type, booking.booking_ref)
  }
}

What’s next?