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

Removals ship as a new minor version, on the same path

Removing a field or an input, making an optional input required, or narrowing a type is a breaking change. It ships as a new minor contract version on the same /v1 path. The version is announced under Contract changes on this page and in the changelog, at the latest when it is released, with every field and input it removes. Read that list for every new version before you upgrade.
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 90 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 its removal date. 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

While a route is deprecated but still answering, every response it sends carries three 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. The first route to carry them is POST /v1/book, superseded by POST /v1/checkout:
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.
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. The field keeps being sent with the same meaning, its replacement is sent beside it, and the OpenAPI document marks the old field deprecated: true with a description that names the replacement (“Deprecated: use <new> instead.”). The Deprecation and Sunset headers describe whole routes, so a route that only has deprecated fields does not send them. Removing a deprecated field is a breaking change and follows the rules above. Until a removal date is announced, reading the old field keeps working; move to the replacement when you next change that code.

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.
  • 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.

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 unannounced removals. A field or an input is removed only in a version listed on this page, 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.
  • What a code means: Errors & troubleshooting carries the full enum with a remedy for each entry.