Skip to main content
The Jinko public API is versioned on the published contract, as <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.
Read it from the environment you are actually calling. A sandbox and production can be serving different contract versions during a rollout, and the served document is the truth about which.

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

“Ignore what you do not recognise” is load-bearing. A client that rejects an unknown response field, an unknown enum value, an unknown error code, or an unknown webhook event turns every additive release into an outage for itself. Nothing in the additive column above is announced ahead of time, because a correct client does not need the warning.

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:
If you are calling /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.
Log the Deprecation header, do not just read it. One line in your client that warns when a response carries Deprecation: true costs nothing and turns every future sunset into something you find out about on the day it is announced rather than the day it takes effect.

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 field deprecated: 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 the Deprecation 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 before 0.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 }, with value in minor units: in a *_money field beside the older field (total_amount_money, price_money, max_amount_money, …), as value inside objects that carried the same integer as amount, or as amount_money inside objects that hold money beside other members (a cancellation preview’s refund, a hotel tax line). The older fields, including a currency or *_display sibling that only described them, are deprecated. Hotel cancellation steps add percent and nights for steps that are not a sum. Money & prices maps every field.
  • get_booking booking.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. acknowledged can be copied from the preview’s refund.amount_money. The older inline figure is still accepted.
  • Webhooks. booking.partial adds captured_amount_money and captured_total_money (the major-unit captured_amount and captured_total are deprecated). servicing.completed is documented, with refund and penalty in the money shape. See Webhooks.
  • POST /v1/trip items. kind is added beside type, which is deprecated. kind is the name GET /v1/trip/{trip_id} and checkout already use.
  • Hotel rooms. room_capacity is added beside max_occupancy on hotel_search and hotel_details rooms; max_occupancy is deprecated. On hotel_search it 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 an instruction. The X-Jinko-Result and X-Jinko-Instruction headers 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_statuses error rows can carry an optional reason: { 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_price customer_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) and total_due or total_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_amounts is absent when the platform cannot state the customer’s figures.
  • POST /v1/exchange_price fare_difference, original_fare, new_fare and penalty_amount. Deprecated: use customer_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_refund and payment_outcome keep 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, and payment_outcome follows what the customer pays or gets back.
  • POST /v1/exchange_shop estimated_amount follows the same rule. It now leaves out the supplier’s own service fees, so the estimate states what the customer would pay. /v1/exchange_price stays 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. 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 gone 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.
  • result: "provider_error" on flight_search. 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. 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 (now required) and item_id from get_booking. This removal shipped without notice; 0.76.0 accepts them again as deprecated inputs until 2027-04-30.
  • The two hotel_search query switches that turned individual hotel suppliers on or off. 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.
  • Fixed messages for the supplier-facing error codes. 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 as 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, 409 not_exchangeable, 400 invalid_exchange_request and partial_exchange_unsupported from the supplier, and manual_reason where it would be the supplier’s own words, each carry one fixed sentence; see Messages on supplier failures.
  • 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; 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.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 uppercase; quote_expired is QUOTE_EXPIRED.
  • payment_error.code is uppercase, the same codes as error.code.
  • Refund and cancellation conflicts: the sibling code quote_expired is QUOTE_EXPIRED. The other conflict codes are unchanged.
  • Exchange and ancillary status are lowercase: exchange commit and status (confirmed, pending, partial_failure, failed); selected and booked ancillaries (pending, confirmed, failed).
  • failure_reason says offer_unavailable, not offer_not_available, on booking.failed and wherever a booking failure is published.
  • Travel demand trip_type is oneway / roundtrip.
Renamed (the old name stays, deprecated): 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-Version header 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.version is enough to tell you something did.
  • No route removals without the sunset window. A route that carries no Sunset header 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.version in 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.version against the one you last integrated against, and read a deprecated route’s Deprecation and Sunset headers 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 the Deprecation header 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.