Skip to main content
The contract version is info.version in the OpenAPI document, and each section below is one version of it. The rules for what counts as breaking and how much notice a removal gets are on Versioning & deprecation. This page starts at 0.57.0 (2026-10-13). Earlier versions are not listed one by one; Contract changes on Versioning & deprecation describes what changed in 0.26.0, 0.33.0 and 0.39.0.

0.86.0

Not yet released. Changed Corrections to the POST /v1/flight_search document only; nothing on the wire moves (the second step of the live flight search review, JIN-2837).
  • via_airports restricts every connection of a leg to the listed airports, not “at least one”. Some providers are asked for at most the first 9 per leg; the whole list is applied to what they return, and the 200 description no longer claims a 9-airport cap.
  • The filters refundable_only, changeable_only and checked_bag_included are no longer described as the same predicate as the fare flags is_refundable, is_changeable and checked_bag_included. The filters read the before-departure rules only, and the bag filter needs a free bag on every segment; the flags are folded from every rule (an after-departure rule alone makes them true) and the bag flag reads the first segment that states an allowance.
  • max_total: conversion to currency before the comparison is the usual case, not the rule. A provider whose first priced offer is already in currency drops its other-currency offers as unverifiable.
  • offers[].offer_id is offer__ followed by 8 hex characters (the example was off_…). It is the offer_token of a re-price and the prefix of each trip_item_token, which is now described with a real-shaped example.
  • offers[].origin.code and destination.code are the city codes of the requested pair, never the airport flown, and can be an empty string when the platform cannot map the code to a city. The examples are city codes and names.
  • On a live segment, flight_number is a bare number (the cached segment keeps the either-form text), departure_city and arrival_city are IATA city codes (the examples were names), and operating_carrier is no longer promised on every segment: whether equality with airline is stated depends on the provider.
  • offers[].fares[].expires_at: the platform’s token lifetime is 75 minutes from searched_at, not 30.
  • included_baggage and carry_on_baggage are read from the first segment that states an allowance, not per leg. Their description is text the platform writes from the allowance (1 bag, 23KG each, No checked bags included, 1 carry-on bag), not the carrier’s prose; weight_unit is KG or LB, absent when no recognised unit was stated.
  • refund_policy and change_policy list all eight values each: the four base terms and their after departure variants. A cancellation rule with an unspecified penalty reads Non-refundable; a change rule with one sets no policy.
  • Fields that had no description now have one: departure_date; offer_token (a cached-itinerary token is re-shopped live and exact_match_found says whether the same flights and cabin were found; an offer_id from an earlier flight_search is answered from the cache of that search unless the passengers or currency differ, and answers 410 OFFER_EXPIRED once its fares have expired); multi_fare (at most 10 fares per offer); connection_time_min_minutes, connection_time_max_minutes and aircraft_types (an itinerary the filter cannot verify is dropped); same_origin_airport_only and same_turnaround_airport_only (round trip only, no effect on a one-way search); limit (omitted means the cheapest flight plus up to 100 alternatives); the segment’s aircraft (absent when the provider reports no equipment); and the fare’s brand_name.

0.81.0

Not yet released. Removed
  • aircraft on the segments of a cached itinerary, on POST /v1/flight_calendar, /v1/find_dates, /v1/lowest_fare and /v1/find_destination. The cache carries no equipment, so the field was never sent there; it leaves the document and no response changes. The cached itinerary’s legs are now their own components, CachedFlightLeg and CachedSegment, with the same fields and types as FlightLeg and Segment, which flight_search and the booking reads keep unchanged, aircraft included.
Changed Corrections to the document only; nothing on the wire moves.
  • On a cached leg or segment, a datetime at an airport whose timezone the platform does not know is written as UTC (…Z) with its local wall-clock digits. The shared text said the offset was omitted, which is what a live leg does. The live text now scopes its *_local remark to booked and exchanged itineraries.
  • operating_carrier on a cached segment is sent only when the cached record carries one. The shared text promised it was always sent, equality with airline included.
  • limit is described per route: on flight_calendar a global cap on the sorted itineraries; on find_dates the number of date pairs, each with its cheapest itinerary, default 20, where the route summary promised ten; on lowest_fare not applied, the route returns at most ten itineraries; on find_destination the number of destinations.
  • On POST /v1/price_monitoring, status is the enum ok | stale rather than a free string; the four *_local datetimes carry the airport’s UTC offset (RFC 3339) despite their suffix, as they always did on the wire, and their examples now show it; inbound_stops is documented as omitted on a one-way monitor and on a non-stop return, unlike outbound_stops, which is always sent.

0.80.0

Not yet released. Deprecated
  • amount on price_per_person of a POST /v1/flight_search fare leaves the OpenAPI document. The per-person fare is now published as MoneyWithoutAmount: the Money shape without its deprecated amount member — value (an integer in minor units), currency, decimal_places and display, unchanged. The platform never set amount on this figure, so no response changes; the member stays in the code for now and is listed under Deprecated fields. total_price on the same fare is unchanged.

0.79.0

Not yet released. Deprecated
  • The passenger fields on the cache-backed flight requests leave the OpenAPI document: adults, children, infants_in_lap, infants_in_seat, youth, student, ages and residency_country on POST /v1/flight_calendar, /v1/find_dates, /v1/lowest_fare and /v1/find_destination, and adults, children and infants on POST /v1/price_monitoring. Every cached fare is one adult’s whatever party is sent (priced_for: one_adult), so none of them ever changed an answer. A request that still sends one is accepted, validated and answered exactly as before, with Deprecation: true and the deprecated-inputs Link; no removal date is set yet. They are listed under Deprecated inputs. Price a party with POST /v1/flight_search, which is unchanged.
  • On those requests a passengers object is still refused with 422, and the message now says the request takes no passenger counts instead of naming the fields above.

0.78.0

Released 2026-10-19. Added
  • Seat maps: GET /v1/trip/{trip_id} returns seat_map_url, a link to a page where the traveler picks seats on a seat map. The link expires; read the trip again for a fresh one. See Seats.
  • Seat offers in GET /v1/trip/{trip_id}/ancillaries carry seat_map_status: pending while the seats are still loading, ready once they can be selected.
  • Selected and booked seats can carry not_charged: true: this seat is not charged to the traveler.

0.77.0

Released 2026-10-19. Restored Inputs and a response field removed without notice since 2026-08-29 that the platform still serves come back, deprecated, until 2027-04-30. A request that uses one of the restored inputs is answered as it was before the input was removed, with Deprecation: true, the deprecated-inputs Link and Sunset: Fri, 30 Apr 2027 23:59:59 GMT; each is listed under Deprecated inputs. The restored response field sends no header, since reading a field is invisible to the server: it is marked deprecated in the OpenAPI document and listed under Deprecated fields.
  • The two enable_* query parameters on hotel_search that turned individual hotel suppliers on or off, refused since 0.57.0, are accepted and ignored: the platform chooses which hotel inventory to search.
  • date_flexibility_days on POST /v1/flight_search, refused since 2026-08-31, is accepted within its published range (an integer from 0 to 3) and ignored: the search runs on the exact dates, as it did then.
  • On POST /v1/exchange_shop, a segments_to_exchange entry may again name its airports as flat origin / destination codes, read as airports, and an empty segments_to_exchange reshops like leaving the field out, as before 0.43.0.
  • hotel_ref is back on hotel_search hotels and nearby alternatives, hotel_details.hotel and hotel_reviews.hotel, marked deprecated and sent until 2027-04-30 (listed under Deprecated fields). hotel_id (search) and id (details, reviews) stay the identifier to use.
Unchanged
  • ticket_numbers on the refund and exchange routes, place_id on hotel_search and a child age outside 0 to 17 stay refused.

0.76.1

Released 2026-10-18. Changed
  • POST /v1/book answers until 2027-04-30 instead of 2026-12-31; its Sunset header says so. The earlier date, published on 2026-09-07, gave less than the 180 days of notice this policy promises. /v1/book stays deprecated in favour of POST /v1/checkout.

0.76.0

Released 2026-10-18. Added
  • A request that uses a deprecated input is answered with Deprecation: true and Link: <https://docs.gojinko.com/concepts/versioning#deprecated-inputs>; rel="deprecation", and with Sunset when the input has a removal date. The response is otherwise unchanged; a request refused for naming one value under both its old and new name carries the headers too. The deprecated inputs are listed on Versioning & deprecation.
  • refund_check, refund_commit, refund_status and the one-step hotel_cancel, deprecated in 0.74.0, answer with Deprecation: true and a Link to the route that replaces each (rel="successor-version"). No removal date is set, so they send no Sunset.
Restored
  • The booking handles 0.57.0 removed without notice are accepted again, deprecated, until 2027-04-30: order_id instead of booking_ref on refund_check, refund_commit, refund_status and the exchange routes; provider_reference instead of booking_ref and last_name on flight_refund_preview, flight_refund_commit, flight_refund_status, hotel_cancel_preview, hotel_cancel_commit and hotel_cancel_status; provider_booking_id the same way on hotel_cancel; and provider on the flight refund and exchange routes and on hotel_cancel, which the platform does not need. A handle sent beside booking_ref is refused with 422. booking_ref is optional on these routes again, and required when no handle names the booking. A request that sends a handle gets Sunset: Fri, 30 Apr 2027 23:59:59 GMT.

0.75.0

Released 2026-10-18. Added
  • booking.completed data lists items[]: each booked item with its item_id, kind, booked_item_id and supplier_references, as booking.partial lists its completed items. You no longer need a get_booking call after every booking to learn the airline or hotel reference. booked_item_id is the itm_ handle get_booking names the item by, and is omitted when the platform cannot name it. Events sent before this version carry no items.

0.74.2

Released 2026-10-18. Fixed
  • exchange_price and exchange_commit refuse an empty offer_id with 422 BAD_REQUEST. It used to come back as 502 UPSTREAM_ERROR.
  • get_booking booked_ancillaries[].type lists every kind that is booked: seat, bag, meal, cabin_bag_upgrade, sports_equipment, priority_boarding, assistance, insurance, lounge, frequent_flyer, other. The contract said seats only while bags and meals were already listed.
  • flight_refund_preview and hotel_cancel_preview document the 409 active_operation_exists a preview answers while another operation is in progress on the item, naming it in active_operation.
  • Payment attempt answers document that trip_id carries the trip_ prefix, which the trip answers do not.

0.74.1

Released 2026-10-18. Fixed
  • get_booking: booking.booking_ref and items[].booking_ref are always the booking’s own Jinko reference, including after an exchange and when the booking is looked up with the reference of one of its exchanges. In 0.74.0 an exchanged item named the exchange’s own reference. The deprecated booking_reference fields keep their meaning.

0.74.0

Released 2026-10-17. Breaking
  • error.code is uppercase everywhere. attempt_in_progress, attempt_terminal, credential_format_invalid, departure_date_in_past, idempotency_key_reused, invalid_exchange_request, invalid_segments_to_exchange, leg_already_departed, leg_dates_out_of_order, not_exchangeable, partial_exchange_unsupported, payment_credential_invalid, payment_type_not_enabled, temporarily_unavailable and trip_owned_by_other_payment are now sent in uppercase (for example ATTEMPT_IN_PROGRESS), and quote_expired is now QUOTE_EXPIRED.
  • payment_error.code on payment attempts and payment refusals is uppercase, with the same codes as error.code.
  • On refund and cancellation conflicts, the sibling code quote_expired → QUOTE_EXPIRED. The other sibling codes are unchanged.
  • Exchange and ancillary status values are lowercase: confirmed, pending, partial_failure and failed on the exchange_commit and exchange_status answers; pending, confirmed and failed on selected and booked ancillaries.
  • failure_reason reads offer_unavailable, where it read offer_not_available, in the booking.failed data and wherever a booking failure is reported.
  • The travel demand trip_type is oneway or roundtrip on /v1/destination, /v1/market, /v1/audience, /v1/trend, /v1/destination/trend, /v1/market/trend and /v1/audience/trend. Requests still accept One-Way and Round-Trip.
Added
  • booking_state on GET /v1/trip/{trip_id} and on the booking get_booking returns: a summary of the booking’s progress after payment, one of awaiting_payment, processing, completed, partial, failed, cancelled or needs_review. It is absent before payment. fulfillment.status, the booking’s status and booking_status stay as the detailed status.
  • POST /v1/agent_payment/submit declares 404 NOT_FOUND when the trip, or the quote_id among its quotes, is not found. Nothing is charged.
  • The answer to the legacy flat body of POST /v1/agent_payment/submit names the quote it charged as quote_id.
Changed
  • Renamed request fields. Only the new name is documented; the old name is still accepted. Sending both names with different values is refused with 422 BAD_REQUEST, or 400 CREDENTIAL_FORMAT_INVALID on a typed payment body.
    • The integer item_id → booked_item_id on refund_check, refund_commit, refund_status, flight_refund_preview, flight_refund_commit, flight_refund_status, hotel_cancel, hotel_cancel_preview, hotel_cancel_commit, hotel_cancel_status, car_cancel_preview, car_cancel_commit, car_cancel_status, exchange_shop, exchange_price, exchange_commit and exchange_status.
    • quote → quote_id on flight_refund_commit and hotel_cancel_commit; cancellation_id → quote_id on car_cancel_commit; session_reference → quote_id on exchange_commit; the deprecated quoted_cart_id → quote_id on the typed POST /v1/agent_payment/submit body.
    • max_price → max_total on flight_search and price_monitoring.
    • offer_id → trip_item_token on the Best Price Guarantee claim.
    • pax_ref_id → traveler_ref on select_ancillaries selections; passenger_type → traveler_type on upsert_travelers.
  • The webhook event_id is documented as an opaque string, unique to each occurrence. Its format is not part of the contract.
  • On the cached flight searches, max_total is documented as a cap on the cached fare’s total, and cached fares are priced for one adult. direct_only is documented as their only stop filter.
Deprecated
  • Renamed response fields. Each new field is sent beside the old one, which keeps the same value and is marked deprecated: true. No removal date is set.
    • quoted_cart_id → quote_id on the checkout answer, the trip’s quote, the trip ancillaries answer, payment attempts, payment refusals and the quote-expired answer; quote → quote_id on the flight refund and hotel cancellation previews; cancellation_id → quote_id on the rental car cancellation answers; session_reference → quote_id on exchange_price. quote_id is a string, and the deprecated names stay beside it.
    • requote → retry_quote_id on refund and cancellation conflicts.
    • booking_reference → booking_ref, the Jinko booking reference, on the get_booking booking and its items, the exchange_commit and exchange_status answers and the one-step hotel_cancel answer; fulfillment_cart_id → booking_ref on the trip’s fulfillment, payment attempts and the legacy agent payment answer. The deprecated fulfillment_cart_id is an internal number.
    • The integer item_id → booked_item_id, the itm_ handle the servicing routes take, on get_booking items and on payment attempt items[]; the integer item → booked_item_id on servicing webhook data; item → booked_item_id on the refund and cancellation previews, where item already held the same itm_ value.
    • quoted_item_id → item_id on the select_ancillaries answer.
    • pnr and airline_locators on flight details, hotel_confirmation_number on hotel details, confirmation.confirmation_number, bookings[].booking_reference, bookings[].pnr and the booking.partial item’s booking_reference → supplier_references[], one { issuer, reference } per confirmation the airline, hotel or rental company itself issued, [] when none is known. It is on each get_booking item, each bookings[] entry of GET /v1/trip/{trip_id} and each booking.partial item.
    • The single airline_locator on the exchange answers and confirmation_number on the one-step hotel_cancel answer: read the booking’s supplier_references[] from get_booking instead.
    • offer_id → trip_item_token on hotel, rental car and Best Price Guarantee offers. The offer_id echo on trip items and bookings is deprecated; hotel and rental car items now also echo trip_item_token.
    • id → offer_token on cached itineraries.
    • pax_ref_id → traveler_ref and passenger_type → traveler_type on stored travelers and on ancillaries. traveler_type is lowercase: adult, child, infant.
    • reason_code → failure_reason in the booking failure email content, in the words of the booking.failed data.
    • stripe_checkout_session_url → checkout_url on exchange_commit.
  • stripe_reference and stripe_refund_id.
  • refund_check, refund_commit and refund_status, in favour of flight_refund_preview, flight_refund_commit and flight_refund_status; the one-step hotel_cancel, in favour of hotel_cancel_preview, hotel_cancel_commit and hotel_cancel_status.
  • The webhook envelope’s status: branch on event, which already names the outcome.
  • The trip status values that describe booking progress (fulfilling, fulfilled, partially_fulfilled, failed): read booking_state.
Fixed
  • POST /v1/checkout says it waits about 55 seconds for the quote. It said 25.
  • GET /v1/trip/{trip_id}/ancillaries declares its 202 answer (status: "pricing", with Retry-After), which it already returned.
  • Idempotency-Key is documented as required on a typed payment submission. A submission without it is refused with 400 CREDENTIAL_FORMAT_INVALID.
  • origin is documented as required on /v1/destination and /v1/destination/trend, which refuse a request without it.
  • TRIP_STATE_CONFLICT no longer says a trip refuses writes while it is being quoted. A trip refuses writes once payment has started, or when it is cancelled or failed. QUOTE_PENDING lists quote_id among its extra keys.

0.73.0

Released 2026-10-17. Deprecated
  • On hotel_search, each of these is still accepted or still sent with its behaviour unchanged. No removal date is set.
    • adults, children and rooms → occupancies, one element per room, with children given by children_ages. The shorthand is ignored whenever occupancies is sent.
    • star_rating → min_star_rating and max_star_rating. star_rating only fills a bound the request leaves unset.
    • offset → next_handle. A search that answers with next_handle already refuses a non-zero offset.
    • The search_handle answer field → next_handle. No endpoint accepts search_handle.

0.72.0

Released 2026-10-17. Breaking
  • hotel_ref is removed from hotel_search hotels, from hotel on hotel_details and from hotel on hotel_reviews. One identifier remains: hotel_id on a search hotel and id on details and reviews, with the same value. /v1/hotel_details/{hotel_id} and /v1/hotel_reviews/{hotel_id} take that value, and still accept a hotel_ref kept from an earlier search.
Changed
  • hotel_id is opaque. Pass it back exactly as received to hotel_details, hotel_reviews and hotel_ids. Do not parse or build it, and do not keep it across contract versions: the value a hotel is known by can change, so read it from a fresh hotel_search.
Fixed
  • The hotel_details example id shows the same value as the hotel_search example.

0.71.0

Released 2026-10-17. Added
  • hotel_search declares destination_resolution.radius_source, which destination searches already sent beside radius_km: caller when the request’s own radius_km was searched, city when the radius came from the spread of the resolved city’s hotels (no smaller than the platform minimum), and default when coordinates came without a radius and the platform default was used. A city or default radius may be widened when the first search finds too little; radius_km is then the radius finally searched. It is absent on the hotel_name and hotel_ids shapes.
Removed
  • destination_resolution.place_id, and place_id as a value of destination_resolution.shape. No answer carried them, since a place_id destination is refused on input with 422. shape is one of city, coordinates, query, hotel_name and hotel_ids, and its description says which other fields each one brings.

0.70.0

Released 2026-10-17. Breaking
  • hotel_search no longer returns pagination, which some destination searches sent although the contract never declared it. Page with next_handle, has_more and expires_at; total still counts the hotels on the page.
  • Each entry of hotels[].offers[] and nearby_alternatives[].offers[] no longer carries semantic_matches, promotions[].code or room_rates[].rate_id. No operation accepted rate_id. promotions[].name and promotions[].remark, which must be shown to the guest, stay, as do room_id, occupancy_number, occupancy and the rest of each room rate. The deprecated rooms[].rates[] shape, including its promotion code, is unchanged.

0.69.0

Released 2026-10-17. Breaking
  • hotel_search no longer returns budget_filter, the report of how max_budget_per_night was applied. The filter itself is unchanged: max_budget_per_night stays on the request, every hotel returned fits it, empty_reason is all_dropped_by_budget when none did, and warnings still carries the budget advisories.
  • hotel_search no longer returns search_context.trip_id. trip_id on a first request is no longer declared; a request that still sends it is accepted and the field is ignored. It never selected, priced or filtered anything. Adding a hotel offer to a trip with POST /v1/trip is unchanged.

0.68.0

Released 2026-10-17. Breaking
  • hotel_search no longer returns search_scope, which described how the search ran: plan_kind, coverage, snapshot_size, candidate_count, coverage_unknown, truncated, widened_from_km and providers[]. Paging is unchanged, and destination_resolution still reports the radius a destination search ended at.

0.67.0

Released 2026-10-16. Breaking
  • hotel_search no longer returns intent_match_probability, intent_confidence, intent_evidence_status or intent_facts on hotels in hotels and nearby_alternatives, nor internal_search on the answer. distance_km and recommendation_score stay, and the order of hotels and recommendation_score still reflect intent.user_intent.

0.66.0

Released 2026-10-16. Fixed
  • The legacy flat body of POST /v1/agent_payment/submit no longer declares intent. A body that still carries it is accepted and the field is ignored. Before, such a submission was refused with CREDENTIAL_FORMAT_INVALID.

0.65.0

Released 2026-10-16. Breaking
  • The Best Price Guarantee answer no longer carries negotiator. Read status, offer and decline; offer.reference.verified still says whether Jinko confirmed the reference.

0.64.1

Released 2026-10-15. Fixed
  • The Best Price Guarantee answer’s example is one complete offered answer. It showed every money field with the same value, beside decline and poll_after_seconds, which an offered answer does not carry. No field or rule changed.

0.64.0

Released 2026-10-15. Added
  • exchange_shop says why an exchange is not automatic. When support_level is not AUTO, the answer carries reason_code (NOT_EXCHANGEABLE with UNSUPPORTED, MANUAL_REQUIRED with MANUAL_REQUIRED) and reason, a fixed sentence to show the customer: “This booking cannot be exchanged online. Contact Jinko support.” or “This request needs manual handling by Jinko support.” An AUTO answer carries neither.
  • exchange_price adds customer_amounts.residual_value, in the money shape and in the booking’s charge currency: the part of the original fare the airline keeps as a credit in the traveller’s name after an exchange to a cheaper fare. It is not refunded to the card, is not part of total_due or total_refund, and is present only when the airline reports one.
Changed
  • exchange_price customer_amounts.new_fare no longer includes the residual value, so the figures add up as original_fare + total_due - total_refund = new_fare + penalty_amount + residual_value. Before, an EVEN exchange to a cheaper fare showed a new ticket worth exactly the old one.
  • refund_check states a reason on every answer that is not AUTO. manual_reason is present with UNSUPPORTED as well as with MANUAL_REQUIRED, and an UNSUPPORTED answer always carries a reason_code: a refusal that came with no code now carries NOT_CANCELLABLE. A refusal’s manual_reason reads the same sentence as the flight_refund_preview 409 not_cancellable for that booking, and that 409 carries the same reason_code.
  • provider.references.cancellation_reference on the refund and cancellation status answers is documented as the reference the supplier issued for the cancellation or refund itself, for example the ticket number a void settles or the refund id. It is never the supplier’s identifier for the booking, and it is absent when the supplier issued none.

0.63.0

Released 2026-10-15. Added
  • Best Price Guarantee: POST /v1/best_price_guarantee and GET /v1/best_price_guarantee/{id}. The submit takes a hotel offer from hotel_search and the lower price the traveller found for the same stay on another site. Both routes answer the state of the request: pending, offered with a new offer to add to a trip, or declined with decline.reason and, where Jinko compared the reference page, decline.detail. They take a jnk_ API key, as X-API-Key or as a Bearer token. Where the feature is not enabled for your key, the answer is 200 with status: declined and decline.reason: feature_disabled.

0.62.0

Released 2026-10-15. Added
  • hotel_search empty_reason can be all_dropped_by_rating_filter: the destination matched properties, but every one fell below the requested min_rating or min_reviews, or below the default min_rating of 7.0 when the request set none. A property whose rating is unknown is removed only by a min_rating the request sets, and one whose review count is unknown only by min_reviews.
Fixed
  • The min_rating description states the default of 7.0, and that 0 turns the floor off on destination searches.
  • The hotel_name description says property filters sent with hotel_name or hotel_ids are rejected. It said they were ignored.

0.61.0

Released 2026-10-14. Added
  • Each flight fare carries refund_policy (Free cancellation, Refundable, Refundable with fee or Non-refundable) and change_policy beside is_refundable and is_changeable, stating the refund and change terms in words.

0.60.0

Released 2026-10-14. Breaking
  • Checkout of a trip with a flight requires a contact email. Without one, checkout is refused with 422 MISSING_CUSTOMER_DETAILS naming email.
  • In a trip with a hotel, a child or infant traveller requires a date_of_birth. A traveller without one is refused on that field.
  • date_of_birth and passport_expiry must be exactly YYYY-MM-DD.
Changed
  • Travellers are checked against the trip before payment. In a trip with a flight, upsert_travelers (once the trip holds the flight) and checkout answer 400 BAD_REQUEST, with error.field naming the traveller, when a date_of_birth is in the future, when traveler_type does not match the traveller’s age on the date the booking counts ages from (the trip’s first flight, or for some bookings the return flight of a round trip: adult 12 or older, child 2 to 11, infant under 2 and still under 2 on the last flight), or when passport_expiry is before the trip’s last flight. Before, such a trip reached a payable checkout or failed at checkout with 502 UPSTREAM_ERROR.
  • Each hotel in a trip is booked for the trip’s travellers, or for the room guests that hotel_room_guests names on a room_bundle hotel. They cannot include more adults or more children than its rooms were priced for, counted by age at check-in, and each child’s age at check-in must be one of the priced children_ages. Otherwise the answer is 400 BAD_REQUEST naming the first traveller over a count or with an age the room was not priced for.
  • A flight searched for a different party is re-priced at checkout for the trip’s travellers, on the same flights and fare family. When that is not possible, or the airline declines to price the fare, checkout answers 409 OFFER_UNAVAILABLE.

0.59.2

Released 2026-10-14. Changed
  • A card declined on the payment page is reported on the trip. When the issuer declines the card the payer used on the checkout_url page, or its authentication fails, payment_attempt.payment_error.code is CARD_DECLINED or AUTHENTICATION_FAILED while payment_status stays pending and recovery.action is poll: the payer can use another card on the same page until the quote expires. payment_error returns to null once a card is authorized. Before, the trip read pending with payment_error: null.
Fixed
  • On a payment attempt, booking_ref and the deprecated fulfillment_cart_id are absent until the booking and its reference exist, and retry_after_seconds is present only while recovery.action is poll. None of them is ever null. Before a booking existed, the first two were sent as null, which the published schema does not allow.

0.59.1

Released 2026-10-14. Added
  • get_booking declares the booked flight itinerary, which it already sent. Flight details carry legs[], each with the origin, the destination, departure_datetime, arrival_datetime, stops and segments[], and the older names outbound_flight and inbound_flight for legs[0] and legs[1]. Each segment carries the carriers, flight_number, aircraft, the airports, and departure_time and arrival_time (airport local time with its UTC offset) beside departure_time_local and arrival_time_local (no offset). The itinerary is the one booked and ticketed: it is not refreshed from the airline, and no webhook reports an airline schedule change or cancellation. There is no terminal and no per-segment status.
  • Booking and item status values are documented as an open set: in progress (from pending to confirming), final (completed, partial on the booking, failed, cancelled, expired_quote) and needing attention (exchange_partial_failure, needs_review). They follow Jinko’s booking and servicing steps, never an airline change.
Fixed
  • The get_booking status example reads completed, which a ticketed booking returns. It said confirmed.

0.59.0

Released 2026-10-14. Breaking
  • slices → legs on exchange_shop offers, which no longer carry metadata. The new fare on exchange_price no longer carries baggage_source. The exchange answers, exchange_commit included, carry only the fields the contract declares.
  • exchange_commit no longer returns exchange_reference. On some exchanges it held the supplier’s reference for the booking the change creates, and no route takes it as input: exchange_status reads by booking_ref and last_name.
Added
  • exchange_shop declares each offer’s itinerary and fare: legs[] with stops, duration_minutes and segments[], whose members use the names of a flight search segment (airline, operating_carrier, flight_number, departure_airport, departure_time, arrival_airport, arrival_time, duration_minutes), the fare per segment (cabin_name, fare_brand_key, fare_basis_code, rbd) and the fare per offer (fare_basis_codes, booking_classes, cabin). Exchange times are local wall-clock time with no UTC offset, unlike a flight search segment’s.
  • exchange_price declares the new fare’s terms and baggage: new_fare_details adds fare_brand_name, fare_brand_code, fare_basis_codes, endorsements, is_refundable, is_changeable, refund_policy, change_policy, checked_baggage and carry_on_baggage, and the extra bag options declare is_included, segment_id and journey_id. fare_change_notice is declared.

0.58.0

Released 2026-10-14. Added
  • GET /v1/hotel_reviews/{hotel_id}: the guest score and review count, a summary of what guests praise and criticise, a score per category (cleanliness, location, staff and others) and a page of recent reviews, newest first, each with its headline, score, pros and cons, date and language. Page with limit (1 to 50, default 10) and offset. It takes the same hotel_id as hotel_details.
  • hotel_details adds rating, review_count, child_allowed, pets_allowed, facility_items, rooms[].amenity_items, rooms[].max_adults and rooms[].max_children, and child_allowed and pets_allowed on the policy. max_adults and max_children are how many adults and children a room accepts; max_children: 0 means the room does not take children. facility_items pairs each facility with its Jinko facility dictionary id, the value facility_ids accepts on hotel_search; a facility without a dictionary id stays in facilities only. rooms[].amenity_items carries room amenity ids, a separate list from the facility dictionary.
  • 422 MISSING_CUSTOMER_DETAILS on checkout and book carries missing_fields and missing_by_domain when the platform can name what the booking still needs, such as the passport fields an airline requires: the field names as upsert_travelers takes them (passport_number and others), and the same names grouped by product (flight). Both are absent when the refusal names no field. Send the named fields with upsert_travelers.

0.57.1

Released 2026-10-13. Changed
  • A checkout quote is payable for up to one hour. expires_at on the checkout answer is at most one hour after the checkout call (it was about fifteen minutes), and earlier when an offer in the trip has a shorter deadline of its own: some airline fares last twenty minutes from their search. A payment submitted after it is still refused with 410 QUOTE_EXPIRED.

0.57.0

Released 2026-10-13. This version was first served on 2026-10-13 at 19:34 UTC under the number 1.0.0 and renumbered 0.57.0 at 20:19 UTC; the contract is the same. Breaking
  • Supplier fields are removed from responses: provider and provider_name on flight offers; provider_statuses on flight_search, in success and error bodies; provider on hotels, on search_scope and its providers[], and on hotel_details, hotel_cancel and flight_refund_preview. The raw supplier status word is removed too: provider_status on bookings[] entries and on refund status, and inside the provider object of hotel_cancel_status and flight_refund_status, which keeps state, references and documents.
  • flight_search no longer answers result: "provider_error". A search that completes without a match answers result: "no_matches" as before. A search that could not be completed at all answers an error: UPSTREAM_REJECTED, UPSTREAM_ERROR, UPSTREAM_UNAVAILABLE or UPSTREAM_TIMEOUT. Offers that were found are returned even when part of the search failed.
  • Servicing inputs that named the supplier or its references are removed: provider on the flight refund and exchange requests and on hotel_cancel, order_id, provider_reference and provider_booking_id. A request carrying one is refused with 422 BAD_REQUEST naming the field. Name the booking with booking_ref, which is now required, and the item from get_booking (booked_item_id, named item_id until 0.74.0).
  • The two enable_* query parameters on hotel_search that turned individual hotel suppliers on or off are removed. A request carrying either is refused with 422 naming the parameter.
Changed
  • payment_types on a hotel rate is a closed enum: pay_now, pay_at_property, deposit.
  • hotel_ref and an ancillary offer_id are opaque. Pass them back exactly as received; their format is not part of the contract. hotel_details takes a hotel_ref as received, or a plain hotel_id. hotel_ref was later removed in 0.72.0.
  • The supplier-facing error codes carry fixed messages. UPSTREAM_REJECTED, UPSTREAM_UNAVAILABLE, UPSTREAM_TIMEOUT, UPSTREAM_ERROR, RATE_LIMITED, OFFER_UNAVAILABLE and OFFER_EXPIRED always carry the same message and never upstream text; see Messages on supplier failures. A 429 documents Retry-After.
  • Upstream statuses follow the error categories. A 503 from the platform is answered 503 UPSTREAM_UNAVAILABLE with its Retry-After, and a 504 is answered 504 UPSTREAM_TIMEOUT; both used to be answered 502 UPSTREAM_ERROR. A 429 carries the platform’s Retry-After. A hotel refusal is 502 UPSTREAM_REJECTED, as a flight refusal already was.
  • Refusals that used to quote the supplier carry fixed sentences: 409 not_cancellable (“This booking cannot be cancelled online. Contact Jinko support.”), 409 not_exchangeable (“This booking cannot be exchanged online. Contact Jinko support.”), 400 invalid_exchange_request and partial_exchange_unsupported (uppercase since 0.74.0) from the supplier (“This exchange cannot be processed as requested. Change the selected flights or contact Jinko support.”), and manual_reason where it would be the supplier’s own words (“This request needs manual handling by Jinko support.”).
  • The other supplier refusals carry fixed sentences too: 400 on flight_search (search and price check) and on hotel_search when a supplier declined the inputs (“The request was declined for these inputs. Check the dates, places and travellers, then retry.”), 422 on car_search (“The rental request was declined. Try another vehicle, location or time.”), and a quote’s failure_message where it would carry the supplier’s words (“The booking could not be completed. Contact Jinko support.”). warnings on refund_check and on the exchange answers leave out notes the supplier sends as free text without a code.
  • flight_price_advice answers a refusal of its inputs as 400 or 422 BAD_REQUEST with the platform’s sentence. It used to answer 502 UPSTREAM_REJECTED.
  • Descriptions and examples in the contract no longer name a supplier.