<major>.<minor>.<patch>. Every change, including a removal, moves the minor or patch number; the major number does not move. The number lives in info.version of the OpenAPI document, and it is the one signal that the contract moved.
The rules
1
Additive changes bump the minor version, with no notice
A new optional field, a new endpoint, a new event type, a new error code, a new enum value on a field you send us: all of these ship in a minor bump with no advance notice. Your integration keeps working through them, which is why they need none.
2
A removal is announced at least 180 days ahead
Removing a field or an input, making an optional input required, or narrowing a type is a breaking change. Before it ships, the field or input is deprecated for at least 180 days: it keeps working and it is listed on this page with its removal date. Where the OpenAPI document declares it (for example
direct_only on flight_search, or adults, star_rating and offset on hotel_search), the document marks it deprecated: true and names its replacement. The old names and values of the 0.74.0 renames, and the inputs accepted again after a removal, are not declared there, so this page is where they are listed. A deprecated input is also announced on every response to a request that uses it (see Deprecation headers). The removal then ships as a new minor contract version on the same /v1 path, listed under Contract changes and in the changelog. Read that list for every new version before you upgrade. This applies to every removal from contract 0.76.0 (2026-10-18) on. Earlier versions removed some fields and inputs without notice; each is listed in the changelog, and the inputs the platform still serves are accepted again since 0.76.0 and 0.77.0.3
A change of meaning ships on a new path
We do not repurpose a field or change what an existing request does under
/v1. A change that alters the meaning of existing requests or responses goes to a new path (/v2), and the two run side by side for at least 180 days so you can move at your own pace.4
Deprecations are announced on the wire, where you cannot miss them
A route on its way out keeps answering, and every one of its responses carries headers that name its replacement and, once it is set, its removal date. A request that still sends a deprecated input name or value is answered with a
Deprecation header too. You do not have to be reading announcements to find out: your own logs tell you, well before anything stops working.What counts as breaking
Deprecation headers
On a deprecated route
While a route is deprecated but still answering, every response it sends carries these headers:
They are set before anything else runs, so a 401 carries them just as a 200 does. That is deliberate: a caller whose credentials are stale is exactly the caller most likely to be running old code.
POST /v1/book is superseded by POST /v1/checkout and stops answering at its Sunset:
/v1/book, switch to /v1/checkout. It is the same operation under the name the rest of the platform uses, and it is where new behaviour lands.
These routes are deprecated with no removal date set yet, so they send Deprecation and Link but no Sunset:
On a request that uses a deprecated input
A request that names an input by a deprecated name, or sends a deprecated value, is still served. Its response carries:
The response is otherwise unchanged. A request refused because it names one value under both the old and the new name carries the headers too.
Deprecated fields
A response field can be deprecated while its route stays, and keeps being sent with the same meaning. A renamed field has its replacement sent beside it, and the OpenAPI document marks the old fielddeprecated: true with a description that names the replacement (“Deprecated: use <new> instead.”). A field deprecated with no replacement (for example stripe_reference and stripe_refund_id) is marked deprecated: true the same way, with nothing sent beside it: stop reading it. A response cannot tell which fields you read, so a deprecated response field sends no header; the OpenAPI document and this page are where it is announced.
Removing a deprecated field is a breaking change and follows the rules above: its removal date is announced here at least 180 days ahead. Until then, reading the old field keeps working; move to the replacement, or stop reading a field that has none, when you next change that code.
With a removal date
Without a removal date yet
A field in this table has left the OpenAPI document but not the code: a client generated from an earlier document keeps working, and the field is removed from the code in a later version, announced here with a date.Deprecated inputs
These request inputs are deprecated and still accepted. A request that uses one is answered with theDeprecation header, and with Sunset when the input has a removal date.
With a removal date
The inputs removed without notice since 2026-08-29 that the platform still serves are accepted again until 2027-04-30, each answered as it was before it was removed. The booking handles keep the rules they had before0.57.0: sending one beside booking_ref is refused with 422.
Without a removal date yet
Contract changes
Each contract version that deprecates, removes or changes something is described here, oldest first. A version that removes something lists every field and input it removes.Changes in 0.26.0
Contract 0.26.0 gives each concept one field name and every money figure one shape. Nothing is removed and no removal date is set.
- Money. Every figure is available in the money shape
{ value, currency, decimal_places, display }, withvaluein minor units: in a*_moneyfield beside the older field (total_amount_money,price_money,max_amount_money, …), asvalueinside objects that carried the same integer asamount, or asamount_moneyinside objects that hold money beside other members (a cancellation preview’srefund, a hotel tax line). The older fields, including acurrencyor*_displaysibling that only described them, are deprecated. Hotel cancellation steps addpercentandnightsfor steps that are not a sum. Money & prices maps every field. get_bookingbooking.total_paid. Now sent: the gross amount captured for the booking, refunds not subtracted. Absent when nothing was captured or when the booking was charged in more than one currency.- Cancellation and refund commits.
acknowledgedcan be copied from the preview’srefund.amount_money. The older inline figure is still accepted. - Webhooks.
booking.partialaddscaptured_amount_moneyandcaptured_total_money(the major-unitcaptured_amountandcaptured_totalare deprecated).servicing.completedis documented, withrefundandpenaltyin the money shape. See Webhooks. POST /v1/tripitems.kindis added besidetype, which is deprecated.kindis the nameGET /v1/trip/{trip_id}and checkout already use.- Hotel rooms.
room_capacityis added besidemax_occupancyonhotel_searchandhotel_detailsrooms;max_occupancyis deprecated. Onhotel_searchit repeated the requested occupancy and never was the room’s capacity. - A live flight search with no matches answers 200 with
offers: [],result: "no_matches"and aninstruction. TheX-Jinko-ResultandX-Jinko-Instructionheaders are still sent and are deprecated.
Changes in 0.33.0
- A live flight search with provider failures and no offers answers 200 with
result: "provider_error"and an instruction to retry without changing filters;provider_statuseserror rows can carry an optionalreason: { code, message }, even when other sources return offers.
Changes in 0.39.0
Contract 0.39.0 states a flight exchange in the customer’s own figures. Nothing is removed and no removal date is set.
POST /v1/exchange_pricecustomer_amounts. Added:original_fare(what the customer paid for the ticket),new_fare(the new ticket’s value),penalty_amount(the airline’s change fee, absent when there is none) andtotal_dueortotal_refund(exactly what is charged or refunded), each in the money shape. They add up:original_fare + total_due − total_refund = new_fare + penalty_amount.customer_amountsis absent when the platform cannot state the customer’s figures.POST /v1/exchange_pricefare_difference,original_fare,new_fareandpenalty_amount. Deprecated: usecustomer_amounts.*instead. They are the supplier’s figures and do not add up to what is charged or refunded. They keep their values.total_due,total_refundandpayment_outcomekeep their meaning: what the customer pays or gets back. On a booking whose supplier adds its own service fees, they now leave those fees out, since the customer is not charged them, andpayment_outcomefollows what the customer pays or gets back.POST /v1/exchange_shopestimated_amountfollows the same rule. It now leaves out the supplier’s own service fees, so the estimate states what the customer would pay./v1/exchange_pricestays the binding figure.- Exchange price and flight cancellation can now answer
manual_required: an operator completes them when the supplier’s figures cannot be stated safely. This covers components that cannot be classified or do not add up, an airline change fee charged outside the exchange total, inconsistent supplier totals, or a cancellation whose supplier booking fee cannot be established or that follows a completed exchange.
Changes in 0.57.0
Contract 0.57.0 is a minor version on the same /v1 paths that removes fields and inputs: responses, identifiers and error messages no longer say which supplier an offer, a booking or an error came from. The removals are breaking changes; read the list below before you upgrade.
Removed:
- Supplier fields on responses.
providerandprovider_nameon flight offers;provider_statusesonflight_search, in success and error bodies;provideron hotels, onsearch_scopeand itsproviders[], and onhotel_details,hotel_cancelandflight_refund_preview. The raw supplier status word is gone too:provider_statusonbookings[]entries and on refund status, and inside theproviderobject ofhotel_cancel_statusandflight_refund_status, which keepsstate,referencesanddocuments. result: "provider_error"onflight_search. A search that completes without a match answersresult: "no_matches"as before. A search that could not be completed at all answers an error (UPSTREAM_REJECTED,UPSTREAM_ERROR,UPSTREAM_UNAVAILABLEorUPSTREAM_TIMEOUT). Offers that were found are returned even when part of the search failed.- Servicing inputs that named the supplier or its references.
provideron the flight refund and exchange requests and onhotel_cancel,order_id,provider_referenceandprovider_booking_id. A request carrying one is refused with 422BAD_REQUESTnaming the field; name the booking withbooking_ref(now required) anditem_idfromget_booking. This removal shipped without notice;0.76.0accepts them again as deprecated inputs until 2027-04-30. - The two
hotel_searchquery switches that turned individual hotel suppliers on or off. A request carrying either is refused with 422 naming the parameter.
payment_typeson a hotel rate is a closed enum:pay_now,pay_at_property,deposit.hotel_refand an ancillaryoffer_idare opaque. Pass them back exactly as received; their format is not part of the contract.hotel_detailstakes ahotel_refas received, or a plainhotel_id.- Fixed messages for the supplier-facing error codes.
UPSTREAM_REJECTED,UPSTREAM_UNAVAILABLE,UPSTREAM_TIMEOUT,UPSTREAM_ERROR,RATE_LIMITED,OFFER_UNAVAILABLEandOFFER_EXPIREDalways carry the same message and never upstream text; see Messages on supplier failures. A 429 documentsRetry-After. - Upstream statuses follow the error categories. A 503 from the platform is answered 503
UPSTREAM_UNAVAILABLEwith itsRetry-After, and a 504 is answered 504UPSTREAM_TIMEOUT; both used to be answered as 502UPSTREAM_ERROR. A 429 carries the platform’sRetry-After. A hotel refusal is 502UPSTREAM_REJECTED, as a flight refusal already was. - Refusals that used to quote the supplier carry fixed sentences. 409
not_cancellable, 409not_exchangeable, 400invalid_exchange_requestandpartial_exchange_unsupportedfrom the supplier, andmanual_reasonwhere it would be the supplier’s own words, each carry one fixed sentence; see Messages on supplier failures. flight_price_adviceanswers a refusal of its inputs as 400 or 422BAD_REQUESTwith the platform’s sentence. It used to answer 502UPSTREAM_REJECTED; price advice reaches no supplier, so the refusal is the caller’s to fix.
Changes in 0.74.0
Contract 0.74.0 gives every concept one public name across REST, MCP and the CLI. Nothing is removed and no removal date is set. A renamed response field is sent beside its replacement with the same value and marked deprecated: true. A renamed request field is still accepted under its old name, but only the new name is documented; sending both names with different values is refused with 422 BAD_REQUEST (400 CREDENTIAL_FORMAT_INVALID on a typed payment body, which answers every format refusal that way). A value whose spelling changed is changed in place on responses, and requests still accept the old spelling.
Values changed in place:
error.codeis 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_unavailableandtrip_owned_by_other_paymentare now uppercase;quote_expiredisQUOTE_EXPIRED.payment_error.codeis uppercase, the same codes aserror.code.- Refund and cancellation conflicts: the sibling
codequote_expiredisQUOTE_EXPIRED. The other conflict codes are unchanged. - Exchange and ancillary
statusare lowercase: exchange commit and status (confirmed,pending,partial_failure,failed); selected and booked ancillaries (pending,confirmed,failed). failure_reasonsaysoffer_unavailable, notoffer_not_available, onbooking.failedand wherever a booking failure is published.- Travel demand
trip_typeisoneway/roundtrip.
Added:
booking_state on GET /v1/trip/{trip_id} and on get_booking, the summary of the booking’s progress after payment. POST /v1/agent_payment/submit declares 404 NOT_FOUND for a trip, or a quote_id, it cannot find.
Deprecated: refund_check, refund_commit, refund_status and the one-step hotel_cancel, in favour of the preview, commit and status routes; the webhook envelope’s status (branch on event); the booking values of the trip status (fulfilling, fulfilled, partially_fulfilled, failed), in favour of booking_state; stripe_reference and stripe_refund_id.
Changes in 0.76.0
Contract 0.76.0 announces deprecations on the response and gives the inputs 0.57.0 removed the notice they did not get.
Added: a request that uses a deprecated input is answered with Deprecation: true and a Link to Deprecated inputs, and with Sunset when the input has a removal date. refund_check, refund_commit, refund_status and the one-step hotel_cancel, deprecated in 0.74.0, answer with Deprecation and a Link to the route that replaces each.
Restored: order_id, provider_reference, provider_booking_id and provider are accepted again where they were before 0.57.0, deprecated until 2027-04-30; see Deprecated inputs. booking_ref is optional on those routes again and required when no handle names the booking.
Changes in 0.77.0
Contract 0.77.0 gives the other inputs and the response field removed without notice since 2026-08-29, that the platform still serves, the notice they did not get.
Restored: the two enable_* query parameters on hotel_search that turned a hotel supplier on or off, date_flexibility_days on flight_search, the flat origin / destination codes and an empty list in segments_to_exchange on exchange_shop, each accepted again until 2027-04-30; see Deprecated inputs. hotel_ref is sent again on the hotel answers until 2027-04-30; see Deprecated fields.
Unchanged: ticket_numbers on the refund and exchange routes, place_id on hotel_search and a child age outside 0 to 17 stay refused.
Changes in 0.79.0
Contract 0.79.0 takes the passenger fields out of the cache-backed flight requests, whose fares are one adult’s whatever party is sent.
Deprecated: 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. They are no longer in the OpenAPI document; a request that sends one is accepted and answered as before, with Deprecation: true and a Link to Deprecated inputs. No removal date is set. priced_for: one_adult on every answer says whose fare a cached total is; price a party with POST /v1/flight_search, which is unchanged.
Changes in 0.80.0
Contract 0.80.0 takes the deprecated amount member out of the per-person fare of a POST /v1/flight_search offer.
Deprecated: amount on offers[].fares[].price_per_person, a Money member the platform never set on this figure, is no longer in the OpenAPI document; the fare is published as MoneyWithoutAmount (value, currency, decimal_places and display, unchanged). A client generated from an earlier document keeps working: the member stays in the code, and no response changes. See Deprecated fields. total_price on the same fare is unchanged.
Changes in 0.81.0
Contract 0.81.0 makes the document of the five cache-backed flight routes say what the cache sends.
Removed: aircraft on the segments of a cached itinerary (POST /v1/flight_calendar, /v1/find_dates, /v1/lowest_fare, /v1/find_destination). The cache carries no equipment, so the field was never sent on those routes; it leaves the document without a deprecation period because no response changes. The cached legs are published as CachedFlightLeg / CachedSegment, with the same fields and types as FlightLeg / Segment, which flight_search and the booking reads keep unchanged.
Corrected: the datetime, operating_carrier and limit descriptions on those routes, and status, the *_local datetimes and inbound_stops on POST /v1/price_monitoring; the changelog names each.
The error-code enum is closed
error.code is a fixed set, published as an enum in the spec. The platform sends no code outside it, and every code in it has a producer. The spec and the errors page are kept in step with the platform’s own table, so the list you read is the list you can receive.
Adding a code is additive and lands in a minor bump, so keep a default branch for a code you do not recognise. A code can also stop being sent: it leaves the set in a minor version, named under Contract changes, and a branch you wrote for it goes unreached rather than wrong.
What we deliberately do not do
- No dated versions in a request header. There is no
Jinko-Versionheader to pin. The contract version is a property of the document you read, not of the call you make. - No silent behaviour changes inside a version. If the answer to the same request changes in a way you can observe, the minor version moves, so
info.versionis enough to tell you something did. - No route removals without the sunset window. A route that carries no
Sunsetheader is not going anywhere. - No removal without notice. A field or an input is removed only after it has been deprecated for at least 180 days, in a version listed on this page and in the changelog, with the removal named.
Where to look
- The served contract:
info.versionin the OpenAPI document. It is the version of the environment you fetched it from. - What changed: the contract version itself is the signal. Compare
info.versionagainst the one you last integrated against, and read a deprecated route’sDeprecationandSunsetheaders straight off your own responses. Deprecated fields are marked in the OpenAPI document. Deprecations, removals and other changes are listed by version under Contract changes, and every contract version since 0.57.0 is in the changelog. Log theDeprecationheader in your client: it names the requests that still use something on its way out. - What a code means: Errors & troubleshooting carries the full enum with a remedy for each entry.
