fulfillment.status: "completed" in the sandbox environment, paying agentically: no browser, no card, no human at a checkout page.
This walkthrough completes end to end with a sandbox key. Steps 1 to 3, 5 and 6 go to the booking API. Step 4, the test-token mint, goes to a second host: the control plane’s sandbox deployment at
https://platform-api.sandbox.gojinko.com. It takes the same sandbox key.Both hosts are REST, so every call here sends the key as X-API-Key. The MCP endpoint takes the same key as Authorization: Bearer instead; see Which header to send.get_api_key are tenant keys: jnk_t_ in production and jnk_sandbox_t_ in sandbox. Older person-bound keys with the bare jnk_ prefix (no _t_) are still accepted as legacy keys. Set your sandbox key. The example uses today’s UTC date plus 60 days for departure; replace it with the intended travel date when needed.
What you need
- A sandbox API key. Sandbox and production use separate keys; a production key will not authenticate here.
curl,jq, and Python 3 for the relative-date helper.
Sandbox is a fully isolated environment. Payments use Stripe test mode, so no real money moves, and no booking reaches a real airline’s production system. Bookings on Jinko Airline are ticketed with test ticket numbers that start with
999.How agentic payment works
The normal flow sends a human to a checkout page. The agentic flow replaces that step with a Shared Payment Token (SPT), a Stripe object that authorizes one specific charge up to one specific amount.1) Price a flight
Search mode needs a route and a date. Passcurrency: "USD" so the cart quotes in USD.
Flight coverage and empty results
Use Jinko Airline (ZZ) for sandbox flight tests. It is a fake airline that exists only in sandbox and always behaves the same way: search, booking, ticketing, extras, refund, void and exchange all work, and each flight number triggers one documented behaviour, including failures. Search with include_carriers: ["ZZ"] on one of its routes, such as JFK–LHR, from tomorrow onwards. Flight ZZ101 has no scenario, so the example below books it. See Testing in sandbox for the routes, prices and every scenario.
Real airlines also appear in sandbox, through the providers’ test accounts. Use them only when you need real airline data. Their coverage is limited by those accounts and changes without notice: a missing airline or an empty result does not, by itself, mean your integration is broken, and a booking on them can fail even after search and checkout succeeded. Jinko does not publish which real airlines sandbox can search, sell or ticket; treat the real-airline path as best effort and keep your automated tests on Jinko Airline.
Every filter that narrows the results is applied as sent: when no itinerary matches a carrier filter, the search returns no offers. The route codes may resolve to a metropolitan area, so inspect the returned departure and arrival airports before treating a real-airline result as a match.
The example below uses Jinko Airline and your chosen future date. With include_carriers: ["ZZ"] the results are Jinko Airline only. A search that finds no matching offers returns HTTP 200 with offers: [] and result: "no_matches" (details); a search that could not be completed returns an error. If offers is empty, do not extract an item token or continue to step 2. Jinko Airline does not search departures for today (UTC): choose a departure date from tomorrow onwards, since repeating the same date will not recover the search. For an error, follow the remedy for its error code; retry a 503 or 504 later with the same filters. For no_matches, check the date and filters first: even the tested route returned zero itineraries with a USD 1 price ceiling. Adjust the test search deliberately; an empty result alone does not identify its cause or establish production availability.
2) Build the trip
One call creates the trip, adds the item, and attaches the traveler and contact. Both a traveler and a contact withemail and phone are required before a trip can be booked.
3) Check out and read agent_spt_params
checkout quotes the cart and returns everything you need to pay for it.
payment_type: "intent" means nothing has been charged yet: you still have to pay, which is what the next two steps do. agent_spt_params is the mandate for the token you are about to mint. max_amount_money.value is 18865, not 188.65: it is in minor units, hundredths of a dollar, as every value in the money shape is. The response also still carries the deprecated agent_spt_params.max_amount and agent_spt_params.currency, the same limit and currency; read max_amount_money instead.
4) Mint a test Shared Payment Token
This is the step that replaces your buyer’s payment platform: it mints a Stripe test-mode token so you can authorize a booking without a real card. The sandbox endpoint mints on the Stripe account your partner is bound to (the US test account when unbound), in USD or EUR.This request goes to a different host. The mint lives on Jinko’s control plane, not on the booking API you have been calling: send it to
$JINKO_PLATFORM_API (https://platform-api.sandbox.gojinko.com) with the same sandbox key in X-API-Key. The route exists only in non-production environments, by design, since it mints test-mode tokens. There is no production counterpart, and the booking API host answers it with 404.
A 503 here means no test minter is configured for the Stripe account your partner is bound to (another account may still be configured). That is a deployment issue, not a bad request. Nothing else returns 503.
5) Submit the payment
Hand the trip and the token to Jinko. This authorizes the charge and schedules fulfillment in one call.If the response carries a
checkout_url, the agentic path stopped. The card issuer asked for a 3DS step-up, or declined. Send a human to that URL to finish. This is the designed fallback, not an error, so handle it in production code.6) Poll until it lands
get_trip is a cheap read rather than a live provider call, so polling every few seconds is fine.
completed you get one entry per item:
booking_reference is the supplier confirmation. Use the trip-level booking_ref for lookup and servicing.
That is a full agentic booking. See the fulfillment table for the fulfillment outcomes, and remember that partial means some items booked and some did not.
The polling loop also works for trips paid through the ordinary hosted checkout. When
fulfillment is absent, it reads the trip’s top-level status and stops if the trip is expired.When something goes wrong
What’s next?
- Webhooks so you learn a booking landed without polling.
- Flight booking guide for the browser-based checkout flow.
- Car rental guide: the same agentic path books a car in sandbox. Search with
"currency": "USD"there too. - Errors for the full status-code reference, including what happens when a price moves.
