hotel_search is already a live offer with an htl_* token you can drop straight into a trip.
The one-line version of the flow:
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) Search hotels
Search live inventory for the destination, dates, and occupancy. Hotel rates returned here are already priced and bookable.- SDK
- CLI
- MCP
destination field accepts five shapes (free-text query, city plus country code, lat/lng with optional radius, place ID, or a list of hotel IDs). Use whichever matches what you have.
2) Build the trip
Add the chosen rate to a trip and set travelers in one call. The hoteloffer_id (the htl_* token) goes into trip(add_item) exactly the same way a flight trip_item_token does.
- SDK
- CLI
- MCP
3) Checkout
Create the Stripe checkout session:- SDK
- CLI
- MCP
checkout_url points at app.gojinko.com/checkout, a Stripe-hosted page Jinko owns. The query string carries a signed capability token (?t=<token>).
The response also carries expires_at, the deadline on the quoted price (5 minutes, the same instant as quote.expires_at on get_trip). That is a different clock from the checkout link itself, whose signed token lasts 4 hours. The link outlives the price deliberately, so a customer who opens the page later still reaches it. See the flight guide for the full envelope.
Past expires_at the price is stale and payment is refused, not re-priced: POST /v1/agent_payment/submit answers 410 QUOTE_EXPIRED and creates no payment object, and the hosted page offers the customer a Refresh price button instead of charging them. To recover from an agent, call checkout again on the same trip (this re-quotes) and mint a new token against the new agent_spt_params. Full detail in Errors, after a quote expires.
Rate terms on the quoted item: hotel_terms
Once the trip is quoted, every hotel item on the checkout response and on get_trip carries a hotel_terms object: the cancellation ladder, the tax breakdown, board, and room, as the platform holds them. It is present on hotel items only.
An empty or absent list is not a statement that there is nothing. No
taxes_breakdown means no tax is known, not that the stay is untaxed. No cancellation_schedule means no ladder could be published, either because the provider gave none or because its steps carried no timezone (see above), not that cancelling is free. Say “not available from the provider” in your interface rather than “none”.Which fields are authoritative at quote, and which are from search
This is the distinction to build against, because the two answer different questions.
The price is re-shopped with the provider when the trip is quoted. The terms are not: they are what the provider published when you searched, carried forward on the item. That is a real window, since a property can change its cancellation policy between your search and your checkout.
So: quote
price to your customer as final, and present hotel_terms as the rate’s published conditions rather than as a guarantee of what the provider will charge at booking. Branch on terms_as_of rather than assuming. It is the field that will tell you when the platform starts re-confirming terms at quote time, and a client that reads it needs no change on the day that happens.
The canonical name for the tax array is
taxes_breakdown, on hotel_terms and everywhere else in the public contract. Some older platform-internal payloads carried the same data as taxes_and_fees; that name is not part of the public contract and you should not read it.4) User pays
Send the user tocheckout_url. They:
- Confirm the hotel, dates, and room.
- Enter payment.
- Stripe holds the authorization.
5) 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):
fulfillment is absent until the customer pays. POST /v1/checkout does not create a fulfillment record. It quotes the cart and hands you a checkout_url. The record appears, at awaiting_payment, when the customer presses Pay on that page. Before then get_trip returns quote but no fulfillment and no bookings, so read trip.fulfillment?.status defensively.pending is an item-level status and never appears as a trip’s fulfillment.status. fulfilling is a value of the trip’s own top-level status field, not of fulfillment.status, despite what older versions of this page said.6) Watch the booking land
Pollget_trip until fulfillment.status is terminal:
- SDK
- CLI
- MCP
bookings[], and the field is booking_reference, not booking_ref:
pnr is optional and normally absent on hotels. bookings[] lists failed items too, so read provider_status per entry rather than assuming every entry is confirmed. (booking_ref is a real field name elsewhere, on the guest-authenticated post-booking endpoints such as get_booking and hotel_cancel. It is just not the name used inside bookings[].)
What’s next?
- Add a flight to the trip: see the Flight + Hotel guide for one trip with two items and one Stripe checkout.
- Flight-only booking: see the Flight booking guide.
- Search by location, chain, or amenities: the hotel_search tool reference covers every filter (star rating, hotel type, chain, facilities, geo radius).
- Lookup a booking after the fact: get_booking finds a booking by reference and last name without needing a login.
- Troubleshooting: Errors has the full status-code reference.
