{"openapi":"3.0.0","info":{"title":"Jinko Public API","version":"0.87.0","description":"Curated public REST surface for Jinko. Authenticated with jnk_ API keys. See https://docs.gojinko.com for guides.\n\n### Per-end-user attribution\n\nOn booking calls you may send an optional `X-End-User-Id` request header to attribute the booking to one of your own end users (for per-end-user attribution and rate-limiting). The value is an **opaque, tenant-scoped** identifier that you choose — not a Jinko account id. Omit it to book as the tenant. Values prefixed `user_` or `org_` are rejected."},"servers":[{"url":"https://api.gojinko.com","description":"Production"},{"url":"https://api.sandbox.gojinko.com","description":"Sandbox"}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer"}},"schemas":{"MoneyValue":{"type":"object","properties":{"value":{"type":"integer","description":"Integer amount in MINOR units at the ISO 4217 digits of `currency`: divide by 10 ** decimal_places to get the amount. Signed where the field says so (a price change delta).","example":41250},"currency":{"type":"string","pattern":"^[A-Z]{3}$","description":"ISO 4217 currency code.","example":"USD"},"decimal_places":{"type":"integer","description":"The ISO 4217 digits of `currency` (2 for USD/EUR, 0 for JPY/KRW, 3 for BHD/KWD), and the scale of `value`.","example":2},"display":{"type":"string","description":"The same figure as a string ready to show: the ISO 4217 code, a space, then the amount at `decimal_places`, e.g. \"USD 412.50\". Show this; compute with `value`.","example":"USD 412.50"}},"required":["value","currency","decimal_places","display"],"description":"Money as every new field carries it: `value` is an INTEGER in minor units at the ISO 4217 digits of `currency` (`decimal_places`), and `display` is the same figure ready to show. All four members are always present; a figure the platform does not know is absent, never `{value: 0}`. New integrations read the `*_money` fields (and `value` on the objects that carry it); the two-scale rule described on `Money` applies only to the deprecated fields."},"StopInfo":{"type":"object","properties":{"airport_code":{"type":"string","description":"IATA code of the airport where the leg stops.","example":"DFW"},"duration_minutes":{"type":"number","description":"Layover length in minutes (arrival of the inbound segment to departure of the next). Omitted when the upstream times cannot be resolved.","example":95}}},"CachedSegment":{"type":"object","properties":{"airline":{"type":"string","description":"IATA code of the MARKETING carrier — whose flight number you booked.","example":"AA"},"airline_name":{"type":"string","example":"American Airlines"},"flight_number":{"type":"string","description":"Provider flight number, forwarded unchanged. Depending on the upstream provider it may be a bare number such as `460` or a composed designator such as `AS460`; the carrier code is also carried separately when known.","example":"460"},"departure_airport":{"type":"string","example":"JFK"},"departure_city":{"type":"string","example":"New York"},"departure_time":{"type":"string","description":"Local time at the airport, RFC 3339 with that airport’s UTC offset (e.g. `2026-09-23T11:55:00-04:00`). Parse it as an instant; do not assume UTC and do not compare two of these as strings. On the rare airport whose timezone the platform does not know, a cached time keeps its local wall-clock digits but is written as UTC (`…Z`), so it reads as an instant that is off by that airport’s true offset; the offset is never omitted here, unlike on a live leg. The `*_local` fields of a booked or exchanged itinerary are NOT this: those carry wall-clock time with no offset, by design.","example":"2026-09-23T11:55:00-04:00"},"arrival_airport":{"type":"string","example":"DFW"},"arrival_city":{"type":"string","example":"Dallas"},"arrival_time":{"type":"string","description":"Local time at the airport, RFC 3339 with that airport’s UTC offset (e.g. `2026-09-23T11:55:00-04:00`). Parse it as an instant; do not assume UTC and do not compare two of these as strings. On the rare airport whose timezone the platform does not know, a cached time keeps its local wall-clock digits but is written as UTC (`…Z`), so it reads as an instant that is off by that airport’s true offset; the offset is never omitted here, unlike on a live leg. The `*_local` fields of a booked or exchanged itinerary are NOT this: those carry wall-clock time with no offset, by design.","example":"2026-09-23T15:10:00-05:00"},"duration_minutes":{"type":"number","example":255},"layover_minutes":{"type":"number","description":"Wait between this segment’s arrival and the next departure. Absent on the last segment of a leg, and whenever the upstream times could not be resolved.","example":95},"operating_carrier":{"type":"string","description":"IATA code of the carrier that actually OPERATES the segment, when the cached fare recorded one. A value differing from `airline` is a codeshare the passenger must be told about. Equality is not guaranteed to be stated: the field is absent when the cached record carries no operating carrier, which says nothing about who operates the flight.","example":"MQ"}}},"CachedFlightLeg":{"type":"object","properties":{"origin":{"type":"string","example":"JFK"},"destination":{"type":"string","example":"LAX"},"departure_datetime":{"type":"string","description":"Local time at the airport, RFC 3339 with that airport’s UTC offset (e.g. `2026-09-23T11:55:00-04:00`). Parse it as an instant; do not assume UTC and do not compare two of these as strings. On the rare airport whose timezone the platform does not know, a cached time keeps its local wall-clock digits but is written as UTC (`…Z`), so it reads as an instant that is off by that airport’s true offset; the offset is never omitted here, unlike on a live leg. The `*_local` fields of a booked or exchanged itinerary are NOT this: those carry wall-clock time with no offset, by design.","example":"2026-07-15T08:30:00-04:00"},"arrival_datetime":{"type":"string","description":"Local time at the airport, RFC 3339 with that airport’s UTC offset (e.g. `2026-09-23T11:55:00-04:00`). Parse it as an instant; do not assume UTC and do not compare two of these as strings. On the rare airport whose timezone the platform does not know, a cached time keeps its local wall-clock digits but is written as UTC (`…Z`), so it reads as an instant that is off by that airport’s true offset; the offset is never omitted here, unlike on a live leg. The `*_local` fields of a booked or exchanged itinerary are NOT this: those carry wall-clock time with no offset, by design.","example":"2026-07-15T11:55:00-07:00"},"duration_minutes":{"type":"number","example":385},"airline":{"type":"string","example":"AA"},"airline_name":{"type":"string","example":"American Airlines"},"stops":{"type":"number","example":0},"stop_infos":{"type":"array","items":{"$ref":"#/components/schemas/StopInfo"},"description":"Layover detail, one entry per stop, in travel order. Absent when the leg is non-stop (`stops: 0`) or when upstream detail is incomplete for any stop; `stops` still reports the connection count in that case.","example":[{"airport_code":"DFW","duration_minutes":95}]},"segments":{"type":"array","items":{"$ref":"#/components/schemas/CachedSegment"},"description":"The flown segments of this leg, in travel order — one per flight. A non-stop leg has one. Read `operating_carrier` here to disclose a codeshare the cached fare recorded. Absent when the cached row has no segment breakdown, in which case `stops` and `stop_infos` are all that is known about the leg."}}},"TimeRange":{"type":"object","properties":{"earliest":{"type":"string","pattern":"^([01]\\d|2[0-3]):[0-5]\\d$","example":"08:00"},"latest":{"type":"string","pattern":"^([01]\\d|2[0-3]):[0-5]\\d$","example":"12:00"}},"description":"Inclusive local time-of-day window, 24-hour \"HH:MM\". Either bound may be omitted: only `earliest` means \"at or after\", only `latest` means \"at or before\"."},"CheckedBaggage":{"type":"object","properties":{"pieces":{"type":"integer","description":"Number of bags included at this allowance.","example":1},"weight":{"type":"number","description":"Weight allowance per bag, in `weight_unit`.","example":23},"weight_unit":{"type":"string","description":"`KG` or `LB`. Absent when the provider states no recognised unit.","example":"KG"},"dimensions":{"type":"string","description":"Size limit as the carrier states it. Free text, not a parseable measurement.","example":"158 cm linear"},"description":{"type":"string","description":"Text the platform writes from the allowance, not the carrier’s own prose: for checked bags `1 bag, 23KG each`, `2 checked bags included` or `No checked bags included`; for cabin bags `1 carry-on bag` or `2 cabin bags`, with ` and 1 personal item` when one is included. Present on fares with NO free allowance too (`No checked bags included`), so its presence is not evidence a bag is included.","example":"1 bag, 23KG each"}},"description":"A baggage allowance. Until v0.2.0 this was documented as a string while the platform sent this object — clients generated against the old spec typed it as text."},"Segment":{"type":"object","properties":{"airline":{"type":"string","description":"IATA code of the MARKETING carrier — whose flight number you booked.","example":"AA"},"airline_name":{"type":"string","example":"American Airlines"},"flight_number":{"type":"string","description":"Marketing flight number as the provider sends it on a live offer: a bare number such as `460`, without the carrier code, which `airline` carries.","example":"460"},"aircraft":{"type":"string","description":"IATA aircraft type code for the equipment scheduled on this segment. Absent when the provider reports no equipment; some providers never do, so their itineraries carry no `aircraft` at all (and are dropped by the `aircraft_types` filter).","example":"32Q"},"departure_airport":{"type":"string","example":"JFK"},"departure_city":{"type":"string","description":"IATA city code of the departure airport (`NYC` for JFK), from the platform’s airport catalog: a code, not a name. Absent when the airport is not in the catalog.","example":"NYC"},"departure_time":{"type":"string","description":"Local time at the airport, RFC 3339 with that airport’s UTC offset (e.g. `2026-09-23T11:55:00-04:00`). Parse it as an instant; do not assume UTC and do not compare two of these as strings. The offset is a property of this leg/segment shape wherever it appears — a flight search, and an itinerary echoed back on a quote or trip read — not of the search response alone. On the rare airport whose timezone the platform does not know, the offset is omitted and the value is local wall-clock time. The `*_local` fields of a booked or exchanged itinerary are NOT this: those carry wall-clock time with no offset, by design.","example":"2026-09-23T11:55:00-04:00"},"arrival_airport":{"type":"string","example":"DFW"},"arrival_city":{"type":"string","description":"IATA city code of the arrival airport (`CHI` for ORD), from the platform’s airport catalog: a code, not a name. Absent when the airport is not in the catalog.","example":"DFW"},"arrival_time":{"type":"string","description":"Local time at the airport, RFC 3339 with that airport’s UTC offset (e.g. `2026-09-23T11:55:00-04:00`). Parse it as an instant; do not assume UTC and do not compare two of these as strings. The offset is a property of this leg/segment shape wherever it appears — a flight search, and an itinerary echoed back on a quote or trip read — not of the search response alone. On the rare airport whose timezone the platform does not know, the offset is omitted and the value is local wall-clock time. The `*_local` fields of a booked or exchanged itinerary are NOT this: those carry wall-clock time with no offset, by design.","example":"2026-09-23T15:10:00-05:00"},"duration_minutes":{"type":"number","example":255},"layover_minutes":{"type":"number","description":"Wait between this segment’s arrival and the next departure. Absent on the last segment of a leg, and whenever the upstream times could not be resolved.","example":95},"operating_carrier":{"type":"string","description":"IATA code of the carrier that actually OPERATES the segment. A value differing from `airline` is a codeshare the passenger must be told about. Whether equality is stated depends on the provider: some always send it, including when it equals `airline`; others send it only when they disclose an operating carrier, so an absent value says nothing about who operates the flight.","example":"MQ"}}},"FlightLeg":{"type":"object","properties":{"origin":{"type":"string","example":"JFK"},"destination":{"type":"string","example":"LAX"},"departure_datetime":{"type":"string","description":"Local time at the airport, RFC 3339 with that airport’s UTC offset (e.g. `2026-09-23T11:55:00-04:00`). Parse it as an instant; do not assume UTC and do not compare two of these as strings. The offset is a property of this leg/segment shape wherever it appears — a flight search, and an itinerary echoed back on a quote or trip read — not of the search response alone. On the rare airport whose timezone the platform does not know, the offset is omitted and the value is local wall-clock time. The `*_local` fields of a booked or exchanged itinerary are NOT this: those carry wall-clock time with no offset, by design.","example":"2026-07-15T08:30:00-04:00"},"arrival_datetime":{"type":"string","description":"Local time at the airport, RFC 3339 with that airport’s UTC offset (e.g. `2026-09-23T11:55:00-04:00`). Parse it as an instant; do not assume UTC and do not compare two of these as strings. The offset is a property of this leg/segment shape wherever it appears — a flight search, and an itinerary echoed back on a quote or trip read — not of the search response alone. On the rare airport whose timezone the platform does not know, the offset is omitted and the value is local wall-clock time. The `*_local` fields of a booked or exchanged itinerary are NOT this: those carry wall-clock time with no offset, by design.","example":"2026-07-15T11:55:00-07:00"},"duration_minutes":{"type":"number","example":385},"airline":{"type":"string","example":"AA"},"airline_name":{"type":"string","example":"American Airlines"},"stops":{"type":"number","example":0},"stop_infos":{"type":"array","items":{"$ref":"#/components/schemas/StopInfo"},"description":"Layover detail, one entry per stop, in travel order. Absent when the leg is non-stop (`stops: 0`) or when upstream detail is incomplete for any stop; `stops` still reports the connection count in that case.","example":[{"airport_code":"DFW","duration_minutes":95}]},"segments":{"type":"array","items":{"$ref":"#/components/schemas/Segment"},"description":"The flown segments of this leg, in travel order — one per aircraft. A non-stop leg has one. Read `operating_carrier` here to disclose codeshares. Absent when the provider returned no segment breakdown, in which case `stops` and `stop_infos` are all that is known about the leg."}}},"Amount":{"type":"object","properties":{"value":{"type":"integer","description":"Integer amount in MINOR units, always paired with decimal_places — divide by 10 ** decimal_places to display. Example: value 15977 with decimal_places 2 is 159.77 USD. Rendering this field directly shows prices 100x too high for 2-decimal currencies.","example":41250},"currency":{"type":"string","description":"ISO 4217 currency code.","example":"USD"},"decimal_places":{"type":"integer","description":"Scale of the integer value/amount: display = integer / 10 ** decimal_places. Always sent alongside minor-unit amounts. Usually the ISO 4217 digits of the currency (2 for USD/EUR/GBP, 0 for JPY/KRW, 3 for BHD/JOD/KWD) but currency-converted prices can carry a different scale — ALWAYS use the decimal_places sent with the amount, never a hardcoded 2. Only if the field is genuinely absent on a value-shaped object, fall back to the ISO digits for the currency.","example":2},"display":{"type":"string","description":"The same figure as a string ready to show: the ISO 4217 code, a space, then the amount, e.g. \"USD 159.77\". With decimal_places it is written at exactly that scale (value 1561500, JPY, decimal_places 2 is \"JPY 15615.00\"); without, at the ISO digits of the currency. Show this; compute with the number. Absent when the figure is not money (a cancellation step expressed as a percent or a number of nights) or could not be read unambiguously.","example":"USD 412.50"}},"required":["value","currency"]},"Itinerary":{"type":"object","properties":{"offer_token":{"type":"string","description":"This cached itinerary’s offer token: pass it unchanged as `offer_token` to `POST /v1/flight_search` to price-check it live. It cannot be added to a trip.","example":"es-VVN8QUZ8MjAyNzA2MTU6QUY6Nzo6WXwyMDI3MDYyMjpBRjo2OjpZ"},"id":{"type":"string","description":"Deprecated: use `offer_token` instead. Same value.","example":"es-VVN8QUZ8MjAyNzA2MTU6QUY6Nzo6WXwyMDI3MDYyMjpBRjo2OjpZ","deprecated":true},"origin":{"type":"object","properties":{"code":{"type":"string"},"name":{"type":"string"}},"required":["code"],"example":{"code":"JFK","name":"New York John F. Kennedy"}},"destination":{"type":"object","properties":{"code":{"type":"string"},"name":{"type":"string"}},"required":["code"],"example":{"code":"CDG","name":"Paris Charles de Gaulle"}},"total":{"allOf":[{"$ref":"#/components/schemas/Amount"},{"description":"One adult's fare (`priced_for: \"one_adult\"`): the cache holds one-adult fares only. To price the whole party, pass `offer_token` to `POST /v1/flight_search` with the party."}]},"cabin_class":{"type":"string","example":"economy"},"outbound":{"allOf":[{"$ref":"#/components/schemas/CachedFlightLeg"},{"description":"The outbound journey this price is for — the departure date and time are in `departure_datetime`. Absent on cached rows stored before the platform kept the flown itinerary."}]},"inbound":{"allOf":[{"$ref":"#/components/schemas/CachedFlightLeg"},{"description":"The return journey. Present on a round trip only, and absent on an older cached row the same way `outbound` is."}]},"priced_at":{"type":"string","description":"When this price was collected, RFC 3339 UTC. These results come from the cache and can be days old — re-price with `flight_search` before quoting or booking.","example":"2026-09-19T03:57:25Z"}},"required":["offer_token","id","origin","destination"]},"MonitoredMoney":{"type":"object","properties":{"amount":{"type":"number","description":"Alternative to `value` on some endpoints (the two never appear together); its scale depends on decimal_places. When this object carries decimal_places, amount is an INTEGER in minor units — divide by 10 ** decimal_places (e.g. select_ancillaries total_with_ancillaries). When there is no decimal_places field, amount is a decimal in MAJOR units, safe to display as-is (e.g. trip and checkout totals)."},"value":{"type":"integer","description":"Integer amount in MINOR units, always paired with decimal_places — divide by 10 ** decimal_places to display. Example: value 15977 with decimal_places 2 is 159.77 USD. Rendering this field directly shows prices 100x too high for 2-decimal currencies.","example":41250},"currency":{"type":"string","description":"ISO 4217 currency code.","example":"USD"},"decimal_places":{"type":"integer","description":"Scale of the integer value/amount: display = integer / 10 ** decimal_places. Always sent alongside minor-unit amounts. Usually the ISO 4217 digits of the currency (2 for USD/EUR/GBP, 0 for JPY/KRW, 3 for BHD/JOD/KWD) but currency-converted prices can carry a different scale — ALWAYS use the decimal_places sent with the amount, never a hardcoded 2. Only if the field is genuinely absent on a value-shaped object, fall back to the ISO digits for the currency.","example":2},"display":{"type":"string","description":"The same figure as a string ready to show: the ISO 4217 code, a space, then the amount, e.g. \"USD 159.77\". With decimal_places it is written at exactly that scale (value 1561500, JPY, decimal_places 2 is \"JPY 15615.00\"); without, at the ISO digits of the currency. Show this; compute with the number. Absent when the figure is not money (a cancellation step expressed as a percent or a number of nights) or could not be read unambiguously.","example":"USD 412.50"}}},"WebhookMoney":{"type":"object","properties":{"amount":{"type":"number","description":"Major currency units.","example":188.65},"currency":{"type":"string","example":"USD"}},"required":["amount","currency"],"description":"Money as a webhook payload carries it: `amount` in MAJOR currency units (188.65, not 18865) beside its ISO currency. This is the trip’s money shape, not the integer minor-unit form the post-booking totals use — do not divide it."},"WebhookEvent":{"type":"string","enum":["booking.processing","booking.completed","booking.failed","booking.partial","servicing.completed","servicing.exchange_confirmed","servicing.ticket_issued","servicing.failed"],"description":"A booking lifecycle event. `booking.processing` — Payment is authorized and the booking is being confirmed with the suppliers. No supplier reference exists yet. `booking.completed` — Every item of the booking is confirmed and the payment is captured. `booking.failed` — No item of the booking could be confirmed. The payment hold is released and nothing is charged. `booking.partial` — Payment is captured but only some items were confirmed; the trip needs attention. A distinct event, so a handler written before it existed can never mistake it for a success. `servicing.completed` — A post-booking operation on one booked item (a cancellation) is complete and its money is settled. Distinct from booking.completed: it reports a booking being undone, not made. `servicing.exchange_confirmed` — The supplier confirmed an exchange of one booked item. For a flight, the new e-ticket number may follow in servicing.ticket_issued. `servicing.ticket_issued` — The new e-ticket number of an exchanged flight is available. `servicing.failed` — An exchange or a refund could not be completed. The original booking is unchanged unless the payload says otherwise."},"SupplierReference":{"type":"object","properties":{"issuer":{"type":"string","description":"Who issued the reference: the airline’s IATA code, the hotel’s name, or the rental company’s name.","example":"AF"},"reference":{"type":"string","description":"The reference itself.","example":"XM9L2K"}},"required":["issuer","reference"]},"PartialBookingItem":{"type":"object","properties":{"item_id":{"type":"string","example":"itm_4b91c2"},"kind":{"type":"string","description":"\"flight\", \"hotel\", \"car\" or \"ground\".","example":"flight"},"status":{"type":"string","enum":["completed","failed"],"description":"Whether THIS item was booked. At least one of each is what makes it partial.","example":"completed"},"supplier_references":{"type":"array","items":{"$ref":"#/components/schemas/SupplierReference"},"description":"The confirmations the supplier itself issued for this item, on a completed item. Absent on a failed item. The Jinko reference is the event-level `booking_ref`."},"booking_reference":{"type":"string","description":"Deprecated: use `supplier_references` instead. A confirmation for this item; when the airline’s own locator is not known it can hold an id only Jinko’s intermediary answers to.","example":"XM9L2K","deprecated":true},"captured_amount":{"allOf":[{"$ref":"#/components/schemas/WebhookMoney"},{"description":"Deprecated: use `captured_amount_money` instead.","deprecated":true}]},"captured_amount_money":{"$ref":"#/components/schemas/MoneyValue"}},"required":["item_id","kind","status"],"description":"One item of a partially fulfilled trip. `status` is this item’s own outcome, and at least one `completed` beside one `failed` is what makes the booking partial. `captured_amount_money` is what was charged for THIS item (`captured_amount`, deprecated, is the same figure in major units); a failed item was not charged and carries neither it nor `supplier_references`."},"PartialBookingData":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/PartialBookingItem"},"description":"Every item on the trip with its own outcome, booked and failed alike."},"captured_total":{"allOf":[{"$ref":"#/components/schemas/WebhookMoney"},{"description":"Deprecated: use `captured_total_money` instead.","deprecated":true}]},"captured_total_money":{"$ref":"#/components/schemas/MoneyValue"}},"required":["items"],"description":"The `data` object carried by `booking.partial`. `items` lists every item on the trip with its own outcome, booked and failed alike. `captured_total_money` is what was actually charged — the completed items only, NOT the trip total, so reconciling against the original quote will not balance. `captured_total` (deprecated) is the same figure in major units."},"EmailManageBookingURL":{"type":"string","format":"uri","description":"Jinko web app booking link. Personal data: its query includes the traveller’s last name."},"EmailFlightSegment":{"type":"object","properties":{"departure_city_name":{"type":"string","description":"Departure city, shown in the layover row."},"departure_airport_name":{"type":"string","description":"Departure airport name."},"departure_iata":{"type":"string","description":"Departure airport IATA code."},"departure_time":{"type":"string","description":"Departure local time, display string."},"arrival_airport_name":{"type":"string","description":"Arrival airport name."},"arrival_iata":{"type":"string","description":"Arrival airport IATA code."},"arrival_time":{"type":"string","description":"Arrival local time, display string."},"duration_formatted":{"type":"string","description":"Segment duration, such as 2h 15m."},"flight_number":{"type":"string","description":"Marketing flight number."},"aircraft":{"type":"string","description":"Aircraft type."},"airline_name":{"type":"string","description":"Marketing airline name."},"airline_logo_url":{"type":"string","format":"uri","description":"Marketing airline logo URL."},"layover_before":{"type":"string","description":"Layover before this segment; omitted on the first."}},"description":"One flown segment as booking confirmation, cancellation and exchange emails show it."},"EmailFlight":{"type":"object","properties":{"label":{"type":"string","description":"Leg heading: Outbound, Return, or Leg N of M."},"departure_date":{"type":"string","description":"Leg departure date, display string."},"cabin_class":{"type":"string","description":"Cabin class label, such as Economy."},"segments":{"type":"array","items":{"$ref":"#/components/schemas/EmailFlightSegment"},"description":"Segments in flown order."}},"description":"One leg as booking confirmation, cancellation and exchange emails show it."},"EmailFlightHeadline":{"type":"object","properties":{"destination_name":{"type":"string","description":"Destination city of the first leg, as in the headline."}},"description":"The first leg as cancellation and exchange headlines name it."},"EmailPassenger":{"type":"object","properties":{"first_name":{"type":"string","description":"First name. Personal data."},"last_name":{"type":"string","description":"Last name. Personal data."},"type":{"type":"string","description":"Adult, Child or Infant."},"date_of_birth":{"type":"string","description":"Date of birth as stored. Personal data."},"ticket_number":{"type":"string","description":"E-ticket number, set where the email lists it per traveller (flight exchange). Personal data."}},"description":"A traveller on booking confirmation, flight cancellation and exchange emails. Personal data."},"EmailRefund":{"type":"object","properties":{"amount_formatted":{"type":"string","description":"Refund settled for this item; omitted when none is settled."},"original_amount_formatted":{"type":"string","description":"What was originally paid."},"card_brand":{"type":"string","description":"Brand of the card refunded to."},"last_four":{"type":"string","description":"Last four of that card. Sensitive payment data."},"method_label":{"type":"string","description":"Wallet refunded to when there is no card identity."},"eta_label":{"type":"string","description":"When the refund should land, such as within 5–10 business days."},"issued_at":{"type":"string","description":"Refund issue date. Not sent today."},"fee_formatted":{"type":"string","description":"Cancellation fee or penalty, display string."},"fee_label":{"type":"string","description":"Fee label."}},"description":"Refund block of a cancellation email."},"FlightCancellationEmailContent":{"type":"object","properties":{"pending_seat_refunds":{"type":"array","items":{"type":"string"},"description":"Seat-refund notes. Not sent today in webhook events."},"pnr":{"type":"string","description":"Jinko booking reference, JNK-XXXXXX."},"reason":{"type":"string","description":"Customer’s free-text reason. Not sent today."},"legs":{"type":"array","items":{"$ref":"#/components/schemas/EmailFlight"},"description":"Cancelled itinerary; omitted after an exchange superseded it."},"outbound_flight":{"$ref":"#/components/schemas/EmailFlightHeadline"},"passengers":{"type":"array","items":{"$ref":"#/components/schemas/EmailPassenger"},"description":"Passengers. Personal data."},"refund":{"$ref":"#/components/schemas/EmailRefund"},"rebook_url":{"type":"string","format":"uri","description":"Rebook link. Not sent today."}},"description":"What the default flight cancellation email shows, including the first cancelled leg in its headline and the refund block. Values are display strings as the email shows them."},"BookingCalendarFile":{"type":"object","properties":{"method":{"type":"string","description":"The iTIP method (RFC 5546) this file carries: `REQUEST` for the events the booking still holds, `CANCEL` for the ones it no longer does. One file carries exactly one method — a booking that both retires and confirms flights, which is what an exchange does, comes back as two files rather than one mixed one.","example":"REQUEST"},"filename":{"type":"string","description":"The name the confirmation or cancellation email attached this same file under.","example":"jinko-JNK-A0AUR2.ics"},"content_type":{"type":"string","description":"The media type, including the `method` parameter. Attach or serve the file under this exact value: the method parameter is what makes a mail client act on the file instead of storing it as a plain attachment.","example":"text/calendar; charset=utf-8; method=REQUEST"},"content":{"type":"string","description":"The iCalendar file itself. Real CRLF line endings, as iCalendar requires — the example escapes them only so it can be read here. Byte-identical to what the email carried for the current state of this booking.","example":"BEGIN:VCALENDAR\\r\\nVERSION:2.0\\r\\n…\\r\\nEND:VCALENDAR\\r\\n"}}},"BookingCalendar":{"type":"object","properties":{"files":{"type":"array","items":{"$ref":"#/components/schemas/BookingCalendarFile"},"description":"The iCalendar files this booking is carried in, cancellation first and then invitation — the order the emails attach them in. Never empty: a booking with nothing to put in a calendar omits `calendar` altogether."}},"description":"The calendar entries for this booking, as iCalendar files. Absent — not null, and never an empty list — when the booking holds no flight and no hotel, and whenever the files could not be built."},"FlightCancellationCustomerEmail":{"type":"object","properties":{"to":{"type":"string","format":"email","description":"Recipient contact address; omitted when there is none. Personal data."},"subject":{"type":"string","description":"Subject line built from the same email data."},"template":{"type":"string","enum":["flight-cancellation"]},"content":{"$ref":"#/components/schemas/FlightCancellationEmailContent"},"calendar":{"$ref":"#/components/schemas/BookingCalendar"}},"required":["subject","template","content"],"description":"Flight cancellation or void. Calendar contains the attached .ics files, omitted when none are attached or there is no recipient."},"EmailHotelAddress":{"type":"object","properties":{"formatted":{"type":"string","description":"Address on one line."},"phone":{"type":"string","description":"Property phone number."},"map_url":{"type":"string","format":"uri","description":"Map link for the property."}},"description":"Hotel address block."},"EmailHotelSummary":{"type":"object","properties":{"name":{"type":"string","description":"Hotel name."},"image_url":{"type":"string","format":"uri","description":"Main photo URL."},"address":{"$ref":"#/components/schemas/EmailHotelAddress"}},"description":"The property and address as the hotel cancellation email shows them."},"EmailHotelStay":{"type":"object","properties":{"check_in_date":{"type":"string","description":"Check-in date, display string; raw YYYY-MM-DD on hotel cancellation."},"check_in_time":{"type":"string","description":"Check-in time. Not sent today."},"check_out_date":{"type":"string","description":"Check-out date, displayed in the same format as check_in_date."},"check_out_time":{"type":"string","description":"Check-out time. Not sent today."},"duration_label":{"type":"string","description":"Length of stay, such as 3 nights."}},"description":"Check-in and check-out."},"EmailHotelRoom":{"type":"object","properties":{"name":{"type":"string","description":"Room name."},"count_label":{"type":"string","description":"Room count, such as 1 room."},"occupancy_label":{"type":"string","description":"Occupancy, such as 2 guests."}},"description":"The booked room."},"EmailGuestName":{"type":"object","properties":{"first_name":{"type":"string","description":"First name. Personal data."},"last_name":{"type":"string","description":"Last name. Personal data."}},"description":"A guest on the hotel cancellation email. Personal data."},"HotelCancellationEmailContent":{"type":"object","properties":{"pnr":{"type":"string","description":"Jinko booking reference, JNK-XXXXXX."},"cancelled_at":{"type":"string","description":"Completion date, display string."},"reason":{"type":"string","description":"Customer’s free-text reason. Not sent today."},"hotel":{"$ref":"#/components/schemas/EmailHotelSummary"},"stay":{"$ref":"#/components/schemas/EmailHotelStay"},"room":{"$ref":"#/components/schemas/EmailHotelRoom"},"guests":{"type":"array","items":{"$ref":"#/components/schemas/EmailGuestName"},"description":"Guests. Personal data."},"refund":{"$ref":"#/components/schemas/EmailRefund"},"rebook_url":{"type":"string","format":"uri","description":"Rebook link. Not sent today."},"applied_tier_label":{"type":"string","description":"Label of the cancellation tier that applied. Not sent today."},"applied_tier_sublabel":{"type":"string","description":"Sublabel of the cancellation tier that applied. Not sent today."}},"description":"What the default hotel cancellation email shows, including property, room, refund and applied cancellation tier labels. Stay dates are the raw stored strings. Values are display strings as the email shows them."},"HotelCancellationCustomerEmail":{"type":"object","properties":{"to":{"type":"string","format":"email","description":"Recipient contact address; omitted when there is none. Personal data."},"subject":{"type":"string","description":"Subject line built from the same email data."},"template":{"type":"string","enum":["hotel-cancellation"]},"content":{"$ref":"#/components/schemas/HotelCancellationEmailContent"},"calendar":{"$ref":"#/components/schemas/BookingCalendar"}},"required":["subject","template","content"],"description":"Hotel cancellation. Calendar contains the attached .ics files, omitted when none are attached or there is no recipient."},"EmailCarStop":{"type":"object","properties":{"branch_name":{"type":"string","description":"Rental branch."},"address":{"type":"string","description":"Branch address with city."},"phone":{"type":"string","description":"Branch phone."},"date_time_label":{"type":"string","description":"Branch-local date and time with the zone in the string."}},"description":"One end of a rental."},"EmailCarExtra":{"type":"object","properties":{"label":{"type":"string","description":"Extra with quantity, such as Infant seat × 2."},"detail":{"type":"string","description":"Money line, such as €30.00 · paid now; omitted on cancellation."}},"description":"One rate extra."},"EmailCar":{"type":"object","properties":{"vehicle_name":{"type":"string","description":"Vehicle, with or similar when only the class is guaranteed."},"vehicle_class":{"type":"string","description":"Class line, such as Economy · Automatic · Petrol."},"supplier_name":{"type":"string","description":"Rental company the driver meets."},"supplier_logo_url":{"type":"string","format":"uri","description":"Rental company logo URL."},"pick_up":{"$ref":"#/components/schemas/EmailCarStop"},"drop_off":{"$ref":"#/components/schemas/EmailCarStop"},"pay_now_formatted":{"type":"string","description":"Part collected at checkout; authorization-time email only."},"due_at_desk_formatted":{"type":"string","description":"Amount due at the desk in its currency; never part of a charged total."},"extras":{"type":"array","items":{"$ref":"#/components/schemas/EmailCarExtra"},"description":"Rate extras in the order chosen."}},"description":"A self-drive rental, including pick-up and drop-off branches and times."},"EmailGuest":{"type":"object","properties":{"first_name":{"type":"string","description":"First name or an unnamed-occupant line such as 1 adult, 1 child (age 5). Personal data."},"last_name":{"type":"string","description":"Last name; a minor’s includes (age N) on hotel confirmations. Personal data."},"room_label":{"type":"string","description":"Room the guest occupies on a multi-room stay."}},"description":"A hotel guest or car driver on hotel booking, car cancellation and car exchange emails. Personal data."},"CarCancellationEmailContent":{"type":"object","properties":{"pnr":{"type":"string","description":"Jinko booking reference, JNK-XXXXXX."},"confirmation_code":{"type":"string","description":"Rental supplier’s reservation number."},"cancelled_at":{"type":"string","description":"Completion date, display string."},"reason":{"type":"string","description":"Customer’s free-text reason. Not sent today in webhook events."},"car":{"$ref":"#/components/schemas/EmailCar"},"drivers":{"type":"array","items":{"$ref":"#/components/schemas/EmailGuest"},"description":"Drivers. Personal data."},"refund":{"$ref":"#/components/schemas/EmailRefund"},"refund_pending_review":{"type":"boolean","description":"Cancelled, with the refund amount awaiting human review."},"fee_unknown":{"type":"boolean","description":"No fee schedule could be established."},"non_refundable":{"type":"boolean","description":"The fee consumed the whole charge; nothing is returned."}},"required":["refund_pending_review","fee_unknown","non_refundable"],"description":"What the default car cancellation email shows for the cancelled rental. Values are display strings as the email shows them; booleans are always sent. Pending refund review omits refund and sets non_refundable to false."},"CarCancellationCustomerEmail":{"type":"object","properties":{"to":{"type":"string","format":"email","description":"Recipient contact address; omitted when there is none. Personal data."},"subject":{"type":"string","description":"Subject line built from the same email data."},"template":{"type":"string","enum":["car-cancellation"]},"content":{"$ref":"#/components/schemas/CarCancellationEmailContent"}},"required":["subject","template","content"],"description":"Car rental cancellation. This email attaches no calendar."},"ServicingCompletedData":{"type":"object","properties":{"operation":{"type":"string","description":"The operation that finished (\"svc_…\"), the same handle the commit answered and the status routes take. Absent when the cancellation ran on an older path that has no such handle.","example":"svc_8cd41f"},"operation_kind":{"type":"string","description":"What the operation did, e.g. `cancel`.","example":"cancel"},"booked_item_id":{"type":"string","description":"The booked item the operation acted on — `booked_item_id` on get_booking.","example":"itm_aaaaaaaaaaaaafhuxsdxpbzrbfz5hm3a"},"item":{"type":"integer","description":"Deprecated: use `booked_item_id` instead. The booked item as an integer.","example":9182,"deprecated":true},"state":{"type":"string","description":"Always `succeeded`: the event is sent only when the operation succeeded.","example":"succeeded"},"refund":{"allOf":[{"$ref":"#/components/schemas/MoneyValue"},{"nullable":true}]},"penalty":{"allOf":[{"$ref":"#/components/schemas/MoneyValue"},{"nullable":true}]},"booking_ref":{"type":"string","example":"JNK-ABC123"},"servicing_event":{"type":"string","description":"The event name again, `servicing.completed`.","example":"servicing.completed"},"manage_booking_url":{"$ref":"#/components/schemas/EmailManageBookingURL"},"customer_emails":{"type":"array","items":{"oneOf":[{"$ref":"#/components/schemas/FlightCancellationCustomerEmail"},{"$ref":"#/components/schemas/HotelCancellationCustomerEmail"},{"$ref":"#/components/schemas/CarCancellationCustomerEmail"}],"discriminator":{"propertyName":"template","mapping":{"flight-cancellation":"#/components/schemas/FlightCancellationCustomerEmail","hotel-cancellation":"#/components/schemas/HotelCancellationCustomerEmail","car-cancellation":"#/components/schemas/CarCancellationCustomerEmail"}}},"minItems":1,"description":"One entry per email Jinko sends or would send at this moment, in sending order, even if email delivery is disabled. Template selects the content shape."}},"required":["operation_kind","item","state","booking_ref","servicing_event"],"description":"The `data` object carried by `servicing.completed`. `refund` is what the payment provider settled back to the customer for this item; `penalty` is the supplier fee the customer agreed to. Either is `null` when the operation carries no such figure — which is not zero. The cancellation email is included when available. Its manage-booking link is not sent today."},"BookingFailedEmailContent":{"type":"object","properties":{"card_brand":{"type":"string","description":"Brand of the card whose hold was released."},"last_four":{"type":"string","description":"Last four of that card. Sensitive payment data."},"method_label":{"type":"string","description":"Wallet name when there is no card identity."},"amount_formatted":{"type":"string","description":"Amount held and not charged, display string."},"provider_noun":{"type":"string","description":"Who refused: airline, hotel, transport operator, rental company, or carrier."},"product_noun":{"type":"string","description":"What failed: flight, stay, journey, car, or booking."},"reason_code":{"type":"string","description":"Deprecated: use `failure_reason` instead. The failure code selecting the email copy, in the platform’s stored words (`offer_not_available` where `failure_reason` says `offer_unavailable`). Omitted when unclassified.","deprecated":true},"failure_reason":{"type":"string","description":"The failure code selecting the email copy, in the words of the `booking.failed` data: specific copy exists for price_changed and offer_unavailable. Omitted when unclassified. May differ from data.failure_reason, which describes the whole trip."},"product_name":{"type":"string","description":"Hotel name when exactly one hotel item failed."}},"description":"What the booking failure email shows, including the resolved provider and product nouns. Values are display strings as the email shows them."},"BookingFailedCustomerEmail":{"type":"object","properties":{"to":{"type":"string","format":"email","description":"Recipient contact address; omitted when there is none. Personal data."},"subject":{"type":"string","description":"Subject line built from the same email data."},"template":{"type":"string","enum":["booking-failed"]},"content":{"$ref":"#/components/schemas/BookingFailedEmailContent"}},"required":["subject","template","content"],"description":"Booking could not be completed; hold released. This email attaches no calendar."},"BookingFailedData":{"type":"object","properties":{"failure_reason":{"type":"string","enum":["price_changed","offer_unavailable","booking_not_created","booking_cancelled_at_provider"],"description":"Curated failure code; omitted when unclassified."},"failure_message":{"type":"string","description":"Customer sentence for price_changed and offer_unavailable only."},"customer_emails":{"type":"array","items":{"oneOf":[{"$ref":"#/components/schemas/BookingFailedCustomerEmail"}],"discriminator":{"propertyName":"template","mapping":{"booking-failed":"#/components/schemas/BookingFailedCustomerEmail"}}},"minItems":1,"description":"One entry per email Jinko sends or would send at this moment, in sending order, even if email delivery is disabled. Template selects the content shape."}},"description":"Booking failure details and the booking failure email. Its content.reason_code may differ from failure_reason. The email can be present even when no curated failure code exists."},"EmailFareConditions":{"type":"object","properties":{"refund_policy":{"type":"string","description":"Refund policy sentence."},"change_policy":{"type":"string","description":"Change policy sentence."},"carry_on_baggage":{"type":"string","description":"Carry-on allowance."},"checked_baggage":{"type":"string","description":"Checked baggage allowance."},"exchange_policy":{"type":"string","description":"Exchangeable or Non-exchangeable for ground transport."},"seat_reservation":{"type":"string","description":"Ground seat reservation policy. Not sent today."},"bike_policy":{"type":"string","description":"Ground bike policy. Not sent today."},"pet_policy":{"type":"string","description":"Ground pet policy. Not sent today."}},"description":"Fare conditions on a booking confirmation item: flight fields on a flight, ground fields on a train."},"EmailBookedSeat":{"type":"object","properties":{"seat_number":{"type":"string","description":"Seat, such as 14C."},"passenger_name":{"type":"string","description":"Passenger the seat is for. Personal data."},"segment_label":{"type":"string","description":"Segment, such as CDG → JFK."},"status":{"type":"string","description":"Outcome code: CONFIRMED, PENDING or FAILED."},"price_formatted":{"type":"string","description":"Seat price, display string; omitted for an included seat."},"status_label":{"type":"string","description":"Outcome label shown to the traveller."}},"description":"One seat selection and its airline outcome."},"EmailBookedBag":{"type":"object","properties":{"label":{"type":"string","description":"Bag label with weight."},"quantity":{"type":"integer","minimum":1,"description":"Number of bags; always sent."},"status_label":{"type":"string","description":"Outcome label shown to the traveller."},"price_formatted":{"type":"string","description":"Bag price, display string."}},"required":["quantity"],"description":"One purchased bag and its airline outcome."},"EmailHotel":{"type":"object","properties":{"name":{"type":"string","description":"Hotel name."},"image_url":{"type":"string","format":"uri","description":"Main photo URL."},"stars_label":{"type":"string","description":"Rating as glyphs, such as ★★★★."},"lodging_type":{"type":"string","description":"Accommodation type, such as Aparthotel."},"address":{"$ref":"#/components/schemas/EmailHotelAddress"}},"description":"The property and address as booking confirmation and hotel confirmation emails show them."},"EmailCancellationTier":{"type":"object","properties":{"label":{"type":"string","description":"Tier label, such as Free cancellation."},"deadline_label":{"type":"string","description":"Tier deadline, display string."},"sublabel":{"type":"string","description":"Explanatory sentence under the label."}},"description":"One row of the hotel cancellation timeline."},"EmailHotelPolicy":{"type":"object","properties":{"title":{"type":"string","description":"Row title."},"body":{"type":"string","description":"Row text."}},"description":"One titled policy row: board, rate information, taxes paid at the hotel, total paid, payment or room cancellation."},"EmailTrainSegment":{"type":"object","properties":{"operator_name":{"type":"string","description":"Carrier or operator."},"train_type":{"type":"string","description":"Transport mode."},"train_number":{"type":"string","description":"Service number. Not sent today."},"origin_station_name":{"type":"string","description":"Departure station."},"origin_city_name":{"type":"string","description":"Departure city. Not sent today."},"destination_station_name":{"type":"string","description":"Arrival station."},"destination_city_name":{"type":"string","description":"Arrival city. Not sent today."},"departure_time_label":{"type":"string","description":"Departure time, display string."},"arrival_time_label":{"type":"string","description":"Arrival time, display string."},"duration_formatted":{"type":"string","description":"Duration, display string; authorization-time email only."}},"description":"One ground transport segment."},"EmailTrain":{"type":"object","properties":{"departure_date_label":{"type":"string","description":"Journey date, display string."},"travel_class":{"type":"string","description":"Fare or class label."},"segments":{"type":"array","items":{"$ref":"#/components/schemas/EmailTrainSegment"},"description":"Segments of the journey."}},"description":"A ground transport journey: rail, coach or ferry."},"EmailBookingItem":{"type":"object","properties":{"kind":{"type":"string","enum":["flight","hotel","train","car"],"description":"Product kind; train is ground transport."},"legs":{"type":"array","items":{"$ref":"#/components/schemas/EmailFlight"},"description":"Flight legs in flown order."},"fare_conditions":{"$ref":"#/components/schemas/EmailFareConditions"},"booked_seats":{"type":"array","items":{"$ref":"#/components/schemas/EmailBookedSeat"},"description":"Seat outcomes for a flight."},"booked_bags":{"type":"array","items":{"$ref":"#/components/schemas/EmailBookedBag"},"description":"Bag outcomes for a flight."},"hotel":{"$ref":"#/components/schemas/EmailHotel"},"stay":{"$ref":"#/components/schemas/EmailHotelStay"},"room":{"$ref":"#/components/schemas/EmailHotelRoom"},"cancellation_tiers":{"type":"array","items":{"$ref":"#/components/schemas/EmailCancellationTier"},"description":"Hotel cancellation timeline."},"cancellation_text":{"type":"string","description":"Hotel cancellation summary, shown when there are no tiers."},"policies":{"type":"array","items":{"$ref":"#/components/schemas/EmailHotelPolicy"},"description":"Hotel policy rows."},"train":{"$ref":"#/components/schemas/EmailTrain"},"car":{"$ref":"#/components/schemas/EmailCar"}},"required":["kind"],"description":"One product covered by a booking confirmation email. Only fields of its kind are present: flight legs and fare conditions, hotel property/stay/room, ground journey or car rental."},"EmailChargeLine":{"type":"object","properties":{"label":{"type":"string","description":"Line label, such as Fare difference."},"amount_formatted":{"type":"string","description":"Line amount, display string."}},"description":"One charges line."},"EmailChargesBreakdown":{"type":"object","properties":{"lines":{"type":"array","items":{"$ref":"#/components/schemas/EmailChargeLine"},"description":"Lines in display order."},"total_formatted":{"type":"string","description":"Total, display string."},"total_label":{"type":"string","description":"Total label, such as Total charged or Total refunded."}},"description":"Charges section of the email."},"EmailPayment":{"type":"object","properties":{"card_brand":{"type":"string","description":"Card brand, such as Visa."},"last_four":{"type":"string","description":"Card last four. Sensitive payment data."},"method_label":{"type":"string","description":"Wallet name when there is no card identity, such as Stripe Link."},"status":{"type":"string","enum":["authorized","charged"],"description":"Which label the email shows: `authorized` (\"Authorized on\" or \"Authorized · charge pending\") or `charged` (\"Charged on\"). `charged_at` is the date shown after that label."},"charged_at":{"type":"string","description":"Date shown after the payment label (\"Charged on\" or \"Authorized on\"), display string."},"amount_formatted":{"type":"string","description":"Amount, display string such as €188.65."}},"description":"Payment line of booking confirmation, flight ticketing and exchange emails."},"BookingConfirmationEmailContent":{"type":"object","properties":{"trip_reference":{"type":"string","description":"Jinko booking reference, JNK-XXXXXX."},"items":{"type":"array","items":{"$ref":"#/components/schemas/EmailBookingItem"},"description":"One entry per product this email covers."},"travelers":{"type":"array","items":{"$ref":"#/components/schemas/EmailPassenger"},"description":"Travellers. Personal data."},"charges":{"$ref":"#/components/schemas/EmailChargesBreakdown"},"payment":{"$ref":"#/components/schemas/EmailPayment"},"payment_notice":{"type":"string","description":"Payment notice. Not sent today."}},"description":"What the default booking confirmation email shows at authorization, or after capture for ground and car items. Values are display strings as the email shows them. Payment is the authorized amount before capture and the captured amount afterwards. Charges are not sent today."},"BookingConfirmationCustomerEmail":{"type":"object","properties":{"to":{"type":"string","format":"email","description":"Recipient contact address; omitted when there is none. Personal data."},"subject":{"type":"string","description":"Subject line built from the same email data."},"template":{"type":"string","enum":["booking-confirmation"]},"content":{"$ref":"#/components/schemas/BookingConfirmationEmailContent"}},"required":["subject","template","content"],"description":"Booking confirmation at authorization, or for ground/car after capture. This email attaches no calendar."},"BookingProcessingData":{"type":"object","properties":{"manage_booking_url":{"$ref":"#/components/schemas/EmailManageBookingURL"},"customer_emails":{"type":"array","items":{"oneOf":[{"$ref":"#/components/schemas/BookingConfirmationCustomerEmail"}],"discriminator":{"propertyName":"template","mapping":{"booking-confirmation":"#/components/schemas/BookingConfirmationCustomerEmail"}}},"minItems":1,"description":"One entry per email Jinko sends or would send at this moment, in sending order, even if email delivery is disabled. Template selects the content shape."}},"required":["customer_emails"],"description":"Payment authorized; the booking is being confirmed with suppliers. Carries the booking confirmation email and its manage-booking link."},"CompletedBookingItem":{"type":"object","properties":{"item_id":{"type":"string","description":"The trip item id.","example":"prod-flight-1"},"kind":{"type":"string","description":"\"flight\", \"hotel\", \"car\" or \"ground\".","example":"flight"},"booked_item_id":{"type":"string","pattern":"^itm_[a-z2-7]+$(?![\\s\\S])","description":"The booked item’s handle: `booked_item_id` on get_booking, and what servicing takes. Omitted when the platform cannot name the item.","example":"itm_aaaabbbbccccdddd"},"supplier_references":{"type":"array","items":{"$ref":"#/components/schemas/SupplierReference"},"description":"The confirmations the supplier itself issued for this item; `[]` when none is known. The Jinko reference is the event-level `booking_ref`."}},"required":["item_id","kind","supplier_references"],"description":"One booked item of a completed booking, with what a partner needs to tell the traveller without calling get_booking: its handle and the supplier’s own confirmations."},"EmailAirlineLocator":{"type":"object","properties":{"airline_name":{"type":"string","description":"Carrier name, IATA code, or Airline when unknown."},"locator":{"type":"string","description":"That carrier’s record locator."}},"description":"One carrier’s record locator on a codeshare or interline booking."},"EmailTicketingFlightSegment":{"type":"object","properties":{"departure_city_name":{"type":"string","description":"Departure city, shown in the layover row."},"departure_airport_name":{"type":"string","description":"Departure airport name."},"departure_iata":{"type":"string","description":"Departure airport IATA code."},"departure_time":{"type":"string","description":"Departure local time, display string."},"arrival_airport_name":{"type":"string","description":"Arrival airport name."},"arrival_iata":{"type":"string","description":"Arrival airport IATA code."},"arrival_time":{"type":"string","description":"Arrival local time, display string."},"duration_formatted":{"type":"string","description":"Segment duration, such as 2h 15m."},"flight_number":{"type":"string","description":"Marketing flight number."},"aircraft":{"type":"string","description":"Aircraft type."},"airline_name":{"type":"string","description":"Marketing airline name."},"airline_logo_url":{"type":"string","format":"uri","description":"Marketing airline logo URL."},"layover_before":{"type":"string","description":"Layover before this segment; omitted on the first."},"operating_airline_name":{"type":"string","description":"Operating carrier of a codeshare segment; omitted when the marketing carrier flies it."}},"description":"One flown segment on the ticketing confirmation, including the operating carrier."},"EmailTicketingFlight":{"type":"object","properties":{"label":{"type":"string","description":"Leg heading: Outbound, Return, or Leg N of M."},"departure_date":{"type":"string","description":"Leg departure date, display string."},"cabin_class":{"type":"string","description":"Cabin class label, such as Economy."},"segments":{"type":"array","items":{"$ref":"#/components/schemas/EmailTicketingFlightSegment"},"description":"Segments in flown order."}},"description":"One leg as the flight ticketing confirmation shows it."},"EmailFlightFareConditions":{"type":"object","properties":{"refund_policy":{"type":"string","description":"Refund policy sentence."},"change_policy":{"type":"string","description":"Change policy sentence."},"carry_on_baggage":{"type":"string","description":"Carry-on allowance."},"checked_baggage":{"type":"string","description":"Checked baggage allowance."}},"description":"Fare conditions shown by flight ticketing and exchange emails."},"EmailTicketedPassenger":{"type":"object","properties":{"first_name":{"type":"string","description":"First name. Personal data."},"last_name":{"type":"string","description":"Last name. Personal data."},"type":{"type":"string","description":"Adult, Child or Infant."},"date_of_birth":{"type":"string","description":"Date of birth as stored. Personal data."},"ticket_number":{"type":"string","description":"This passenger’s e-ticket number. Personal data."}},"description":"A passenger on the flight ticketing confirmation. Personal data."},"FlightTicketingConfirmationEmailContent":{"type":"object","properties":{"booked_seats":{"type":"array","items":{"$ref":"#/components/schemas/EmailBookedSeat"},"description":"Seat outcomes over all flight items."},"booked_bags":{"type":"array","items":{"$ref":"#/components/schemas/EmailBookedBag"},"description":"Bag outcomes over all flight items."},"pnr":{"type":"string","description":"Jinko booking reference, not the airline PNR."},"destination_city_name":{"type":"string","description":"Destination of the first leg, as in the headline."},"airline_pnr":{"type":"string","description":"Airline record locator; shown when airline_locators is absent."},"airline_locators":{"type":"array","items":{"$ref":"#/components/schemas/EmailAirlineLocator"},"description":"Per-carrier locators on a codeshare or interline booking."},"legs":{"type":"array","items":{"$ref":"#/components/schemas/EmailTicketingFlight"},"description":"Legs in flown order."},"fare_conditions":{"$ref":"#/components/schemas/EmailFlightFareConditions"},"passengers":{"type":"array","items":{"$ref":"#/components/schemas/EmailTicketedPassenger"},"description":"Passengers with their ticket numbers. Personal data."},"payment":{"$ref":"#/components/schemas/EmailPayment"},"charges":{"$ref":"#/components/schemas/EmailChargesBreakdown"}},"description":"What the default flight ticketing confirmation shows: the first flight item, with captured payment and fare conditions. Values are display strings as the email shows them. Charges are not sent today."},"FlightTicketingConfirmationCustomerEmail":{"type":"object","properties":{"to":{"type":"string","format":"email","description":"Recipient contact address; omitted when there is none. Personal data."},"subject":{"type":"string","description":"Subject line built from the same email data."},"template":{"type":"string","enum":["flight-ticketing-confirmation"]},"content":{"$ref":"#/components/schemas/FlightTicketingConfirmationEmailContent"},"calendar":{"$ref":"#/components/schemas/BookingCalendar"}},"required":["subject","template","content"],"description":"Flight confirmation with e-tickets issued. Calendar contains the attached .ics files, omitted when none are attached or there is no recipient."},"HotelBookingConfirmationEmailContent":{"type":"object","properties":{"pnr":{"type":"string","description":"Jinko booking reference, JNK-XXXXXX."},"supplier_booking_id":{"type":"string","description":"Provider confirmation number."},"hotel":{"$ref":"#/components/schemas/EmailHotel"},"stay":{"$ref":"#/components/schemas/EmailHotelStay"},"room":{"$ref":"#/components/schemas/EmailHotelRoom"},"guests":{"type":"array","items":{"$ref":"#/components/schemas/EmailGuest"},"description":"Guests. Personal data."},"cancellation_tiers":{"type":"array","items":{"$ref":"#/components/schemas/EmailCancellationTier"},"description":"Cancellation timeline."},"cancellation_text":{"type":"string","description":"Cancellation summary, shown when there are no tiers."},"policies":{"type":"array","items":{"$ref":"#/components/schemas/EmailHotelPolicy"},"description":"Policy rows, including Total paid and Payment."}},"description":"What the default hotel booking confirmation shows: the first hotel item, its property, stay and room. Values are display strings as the email shows them."},"HotelBookingConfirmationCustomerEmail":{"type":"object","properties":{"to":{"type":"string","format":"email","description":"Recipient contact address; omitted when there is none. Personal data."},"subject":{"type":"string","description":"Subject line built from the same email data."},"template":{"type":"string","enum":["hotel-booking-confirmation"]},"content":{"$ref":"#/components/schemas/HotelBookingConfirmationEmailContent"},"calendar":{"$ref":"#/components/schemas/BookingCalendar"}},"required":["subject","template","content"],"description":"Hotel stay confirmation. Calendar contains the attached .ics files, omitted when none are attached or there is no recipient."},"BookingCompletedData":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/CompletedBookingItem"},"description":"Every booked item, each with its `booked_item_id` and `supplier_references`. Absent on events sent before contract 0.75.0."},"manage_booking_url":{"$ref":"#/components/schemas/EmailManageBookingURL"},"customer_emails":{"type":"array","items":{"oneOf":[{"$ref":"#/components/schemas/FlightTicketingConfirmationCustomerEmail"},{"$ref":"#/components/schemas/HotelBookingConfirmationCustomerEmail"},{"$ref":"#/components/schemas/BookingConfirmationCustomerEmail"}],"discriminator":{"propertyName":"template","mapping":{"flight-ticketing-confirmation":"#/components/schemas/FlightTicketingConfirmationCustomerEmail","hotel-booking-confirmation":"#/components/schemas/HotelBookingConfirmationCustomerEmail","booking-confirmation":"#/components/schemas/BookingConfirmationCustomerEmail"}}},"minItems":1,"description":"One entry per email Jinko sends or would send at this moment, in sending order, even if email delivery is disabled. Template selects the content shape."}},"description":"The booked items with their supplier references, and the post-capture emails in sending order: flight, hotel, then one booking confirmation for ground and one for car, plus their manage-booking link."},"FlightExchangeEmailContent":{"type":"object","properties":{"jinko_ref":{"type":"string","description":"Original booking’s Jinko reference, JNK-XXXXXX."},"pnr":{"type":"string","description":"Fallback for jinko_ref, a deprecated duplicate."},"previous_pnr":{"type":"string","description":"Fallback for previous_airline_pnr, a duplicate."},"airline_pnr":{"type":"string","description":"Airline locator after the exchange."},"previous_airline_pnr":{"type":"string","description":"Airline locator before the exchange; shown only when it differs from airline_pnr."},"ticket_number":{"type":"string","description":"E-ticket number; shown only when revalidated. Personal data."},"charges":{"$ref":"#/components/schemas/EmailChargesBreakdown"},"payment":{"$ref":"#/components/schemas/EmailPayment"},"legs":{"type":"array","items":{"$ref":"#/components/schemas/EmailFlight"},"description":"New itinerary legs."},"outbound_flight":{"$ref":"#/components/schemas/EmailFlightHeadline"},"fare_conditions":{"$ref":"#/components/schemas/EmailFlightFareConditions"},"passengers":{"type":"array","items":{"$ref":"#/components/schemas/EmailPassenger"},"description":"Passengers. Personal data."},"document_revalidated":{"type":"boolean","description":"No new document: the held e-ticket was revalidated and keeps its number."},"document_unobserved":{"type":"boolean","description":"Reissued, with the new e-ticket number not known yet; servicing.ticket_issued may follow."}},"required":["document_revalidated","document_unobserved"],"description":"What the default flight exchange email shows: new itinerary, first-leg headline and new fare conditions. Values are display strings as the email shows them; booleans are always sent. Charges are the quoted fare difference, fee and total, before settlement. Payment is not sent today."},"FlightExchangeCustomerEmail":{"type":"object","properties":{"to":{"type":"string","format":"email","description":"Recipient contact address; omitted when there is none. Personal data."},"subject":{"type":"string","description":"Subject line built from the same email data."},"template":{"type":"string","enum":["flight-exchange"]},"content":{"$ref":"#/components/schemas/FlightExchangeEmailContent"},"calendar":{"$ref":"#/components/schemas/BookingCalendar"}},"required":["subject","template","content"],"description":"Flight exchange confirmation. Calendar contains the attached .ics files, omitted when none are attached or there is no recipient."},"EmailChangeEntry":{"type":"object","properties":{"label":{"type":"string","description":"What changed: Vehicle, Pick-up, Drop-off."},"before":{"type":"string","description":"Value before, display string."},"after":{"type":"string","description":"Value after, display string."}},"description":"One before/after row of a car exchange."},"EmailCardPayment":{"type":"object","properties":{"card_brand":{"type":"string","description":"Card brand."},"last_four":{"type":"string","description":"Card last four. Sensitive payment data."}},"description":"Card line of a car exchange email."},"CarExchangeEmailContent":{"type":"object","properties":{"pnr":{"type":"string","description":"Original Jinko booking reference, unchanged."},"confirmation_code":{"type":"string","description":"Rental supplier’s reservation number, unchanged."},"exchanged_at":{"type":"string","description":"Exchange date, display string."},"car":{"$ref":"#/components/schemas/EmailCar"},"changes":{"type":"array","items":{"$ref":"#/components/schemas/EmailChangeEntry"},"description":"What changed; may be absent."},"drivers":{"type":"array","items":{"$ref":"#/components/schemas/EmailGuest"},"description":"Drivers. Personal data."},"settlement_charged":{"type":"boolean","description":"The difference was collected."},"settlement_refunded":{"type":"boolean","description":"The difference was refunded."},"refund_pending_review":{"type":"boolean","description":"Changed, with the refund of the difference awaiting human review; no amounts."},"delta_formatted":{"type":"string","description":"Absolute price difference; the flag gives the direction."},"new_total_formatted":{"type":"string","description":"Rental total after the exchange."},"payment":{"$ref":"#/components/schemas/EmailCardPayment"}},"required":["settlement_charged","settlement_refunded","refund_pending_review"],"description":"What the default car exchange email shows for the rental as it now stands. Values are display strings as the email shows them; booleans are always sent. When all three settlement flags are false there is no difference to settle. Pending review omits amounts and payment; no difference omits delta_formatted. Payment is not sent today."},"CarExchangeCustomerEmail":{"type":"object","properties":{"to":{"type":"string","format":"email","description":"Recipient contact address; omitted when there is none. Personal data."},"subject":{"type":"string","description":"Subject line built from the same email data."},"template":{"type":"string","enum":["car-exchange"]},"content":{"$ref":"#/components/schemas/CarExchangeEmailContent"}},"required":["subject","template","content"],"description":"Car rental change confirmation. This email attaches no calendar."},"ServicingExchangeConfirmedData":{"type":"object","properties":{"operation":{"type":"string","description":"Servicing operation handle (svc_…), sent only when the operation has one. Other exchange identifiers are never sent in this field."},"booked_item_id":{"type":"string","description":"The booked item serviced — `booked_item_id` on get_booking; omitted when unknown."},"item":{"type":"integer","description":"Deprecated: use `booked_item_id` instead. The booked item as an integer.","deprecated":true},"booking_ref":{"type":"string","description":"Original booking’s Jinko reference, JNK-XXXXXX."},"operation_kind":{"type":"string","enum":["exchange"],"description":"The servicing operation: exchange."},"manage_booking_url":{"$ref":"#/components/schemas/EmailManageBookingURL"},"customer_emails":{"type":"array","items":{"oneOf":[{"$ref":"#/components/schemas/FlightExchangeCustomerEmail"},{"$ref":"#/components/schemas/CarExchangeCustomerEmail"}],"discriminator":{"propertyName":"template","mapping":{"flight-exchange":"#/components/schemas/FlightExchangeCustomerEmail","car-exchange":"#/components/schemas/CarExchangeCustomerEmail"}}},"minItems":1,"description":"One entry per email Jinko sends or would send at this moment, in sending order, even if email delivery is disabled. Template selects the content shape."}},"required":["operation_kind","customer_emails"],"description":"The supplier confirmed an exchange of one booked item. Carries the flight exchange or car exchange email. The manage-booking link is currently sent only for car exchanges."},"FlightExchangeTicketNumberEmailContent":{"type":"object","properties":{"pnr":{"type":"string","description":"Original booking’s Jinko reference, JNK-XXXXXX."},"airline_pnr":{"type":"string","description":"Airline locator after the exchange."},"customer_name":{"type":"string","description":"Greeting name: the contact’s first name. Personal data."},"ticket_numbers":{"type":"array","items":{"type":"string"},"description":"Reissued e-ticket numbers. Personal data."}},"description":"What the ticket-number follow-up email shows after a flight exchange. Values are display strings as the email shows them."},"FlightExchangeTicketNumberCustomerEmail":{"type":"object","properties":{"to":{"type":"string","format":"email","description":"Recipient contact address; omitted when there is none. Personal data."},"subject":{"type":"string","description":"Subject line built from the same email data."},"template":{"type":"string","enum":["flight-exchange-ticket-number"]},"content":{"$ref":"#/components/schemas/FlightExchangeTicketNumberEmailContent"}},"required":["subject","template","content"],"description":"New e-ticket number of an exchanged flight. This email attaches no calendar."},"ServicingTicketIssuedData":{"type":"object","properties":{"operation":{"type":"string","description":"Servicing operation handle (svc_…), sent only when the operation has one. Other exchange identifiers are never sent in this field."},"booked_item_id":{"type":"string","description":"The booked item serviced — `booked_item_id` on get_booking; omitted when unknown."},"item":{"type":"integer","description":"Deprecated: use `booked_item_id` instead. The booked item as an integer.","deprecated":true},"booking_ref":{"type":"string","description":"Original booking’s Jinko reference, JNK-XXXXXX."},"operation_kind":{"type":"string","enum":["exchange"],"description":"The servicing operation: exchange."},"customer_emails":{"type":"array","items":{"oneOf":[{"$ref":"#/components/schemas/FlightExchangeTicketNumberCustomerEmail"}],"discriminator":{"propertyName":"template","mapping":{"flight-exchange-ticket-number":"#/components/schemas/FlightExchangeTicketNumberCustomerEmail"}}},"minItems":1,"description":"One entry per email Jinko sends or would send at this moment, in sending order, even if email delivery is disabled. Template selects the content shape."}},"required":["operation_kind","customer_emails"],"description":"The reissued e-ticket number is known, following a flight exchange with document_unobserved true. Carries the ticket-number follow-up email."},"EmailLegacyFlight":{"type":"object","properties":{"origin_name":{"type":"string","description":"Origin city name."},"destination_name":{"type":"string","description":"Destination city name."},"departure_date":{"type":"string","description":"Departure date, display string."},"departure_time":{"type":"string","description":"Departure time, 24-hour display string."},"arrival_time":{"type":"string","description":"Arrival time, 24-hour display string."},"duration_formatted":{"type":"string","description":"Duration, or 0h 00m when unknown."},"airline_name":{"type":"string","description":"Airline name."},"flight_number":{"type":"string","description":"First segment’s flight number."},"cabin_class":{"type":"string","description":"Cabin class label."}},"description":"Single-journey card of an exchange or refund failure email."},"EmailNamedPassenger":{"type":"object","properties":{"first_name":{"type":"string","description":"First name. Personal data."},"last_name":{"type":"string","description":"Last name. Personal data."},"type":{"type":"string","description":"Adult, Child or Infant."}},"description":"A traveller on an exchange or refund failure email. Personal data."},"ExchangeFailedEmailContent":{"type":"object","properties":{"original_pnr":{"type":"string","description":"Booking reference the email says is unchanged."},"original_booking_ref":{"type":"string","description":"Booking reference to quote to support."},"original_outbound":{"$ref":"#/components/schemas/EmailLegacyFlight"},"original_inbound":{"$ref":"#/components/schemas/EmailLegacyFlight"},"passengers":{"type":"array","items":{"$ref":"#/components/schemas/EmailNamedPassenger"},"description":"Passengers or drivers. Personal data."},"exchange_noun":{"type":"string","description":"Operation name: Flight Exchange or Car Rental Change."},"change_target_phrase":{"type":"string","description":"What the traveller wanted to change: these flights or this rental."},"traveler_label":{"type":"string","description":"People list heading: Passengers or Drivers."},"item_summary":{"type":"string","description":"One-line recap of the unchanged booking for car exchanges."}},"description":"What the exchange failure email shows, including current outbound/return flights when applicable and resolved flight or rental nouns. Values are display strings as the email shows them."},"ExchangeFailedCustomerEmail":{"type":"object","properties":{"to":{"type":"string","format":"email","description":"Recipient contact address; omitted when there is none. Personal data."},"subject":{"type":"string","description":"Subject line built from the same email data."},"template":{"type":"string","enum":["exchange-failed"]},"content":{"$ref":"#/components/schemas/ExchangeFailedEmailContent"}},"required":["subject","template","content"],"description":"Flight exchange or car rental change could not be completed. This email attaches no calendar."},"RefundFailedEmailContent":{"type":"object","properties":{"product_label":{"type":"string","description":"Product noun: flight, hotel or booking; defaults to booking."},"booking_reference":{"type":"string","description":"Jinko booking reference, JNK-XXXXXX."},"provider_reference":{"type":"string","description":"Airline PNR or hotel confirmation code."},"outbound":{"$ref":"#/components/schemas/EmailLegacyFlight"},"inbound":{"$ref":"#/components/schemas/EmailLegacyFlight"},"hotel_name":{"type":"string","description":"Hotel name for hotel refunds."},"hotel_address":{"type":"string","description":"Hotel address for hotel refunds."},"stay_label":{"type":"string","description":"Stay dates as stored, such as 2026-06-01 – 2026-06-04."},"room_label":{"type":"string","description":"Room name for hotel refunds."},"refund_amount_formatted":{"type":"string","description":"Expected refund; the email says support will confirm it."},"reason":{"type":"string","description":"Explanation. Not sent today."},"travelers":{"type":"array","items":{"$ref":"#/components/schemas/EmailNamedPassenger"},"description":"Travellers. Personal data."}},"description":"What the refund failure email shows, including outbound/return flights for flight refunds or the hotel stay for hotel refunds. Values are display strings as the email shows them."},"RefundFailedCustomerEmail":{"type":"object","properties":{"to":{"type":"string","format":"email","description":"Recipient contact address; omitted when there is none. Personal data."},"subject":{"type":"string","description":"Subject line built from the same email data."},"template":{"type":"string","enum":["refund-failed"]},"content":{"$ref":"#/components/schemas/RefundFailedEmailContent"}},"required":["subject","template","content"],"description":"Automated refund or void could not be completed. This email attaches no calendar."},"ServicingFailedData":{"type":"object","properties":{"operation":{"type":"string","description":"Servicing operation handle (svc_…), sent only when the operation has one. Other exchange identifiers are never sent in this field."},"booked_item_id":{"type":"string","description":"The booked item serviced — `booked_item_id` on get_booking; omitted when unknown."},"item":{"type":"integer","description":"Deprecated: use `booked_item_id` instead. The booked item as an integer.","deprecated":true},"booking_ref":{"type":"string","description":"Original booking’s Jinko reference, JNK-XXXXXX."},"operation_kind":{"type":"string","enum":["exchange","refund","void"],"description":"The operation that failed. The refund failure email covers refunds and voids."},"customer_emails":{"type":"array","items":{"oneOf":[{"$ref":"#/components/schemas/ExchangeFailedCustomerEmail"},{"$ref":"#/components/schemas/RefundFailedCustomerEmail"}],"discriminator":{"propertyName":"template","mapping":{"exchange-failed":"#/components/schemas/ExchangeFailedCustomerEmail","refund-failed":"#/components/schemas/RefundFailedCustomerEmail"}}},"minItems":1,"description":"One entry per email Jinko sends or would send at this moment, in sending order, even if email delivery is disabled. Template selects the content shape."}},"required":["operation_kind","customer_emails"],"description":"An exchange, refund or void could not be completed. Carries its failure email. No operation handle is sent today."},"WebhookEventPayload":{"type":"object","properties":{"event":{"$ref":"#/components/schemas/WebhookEvent"},"booking_ref":{"type":"string","description":"Empty string when the event concerns no single booking.","example":"JNK-A0AUR2"},"status":{"type":"string","description":"Deprecated: branch on `event`, which already names the outcome. Resulting booking state: `processing` for booking.processing, `confirmed` for booking.completed, `failed` for booking.failed, `partial` for booking.partial, `servicing.completed` for servicing.completed, `servicing.exchange_confirmed` for servicing.exchange_confirmed, `servicing.ticket_issued` for servicing.ticket_issued, `servicing.failed` for servicing.failed.","example":"confirmed","deprecated":true},"occurred_at":{"type":"string","description":"When the event happened, RFC 3339 UTC — NOT when it was delivered. A retry or a replay carries the original instant.","example":"2026-09-02T08:55:34Z"},"event_id":{"type":"string","description":"Unique per occurrence of an event: a servicing event, which can happen more than once on one booking, has a new one each time (each cancellation has its own). Deliveries are deduplicated on it, and a retry or a replay repeats it — key your own idempotency off this. Treat it as an opaque string: its format is not part of the contract.","example":"evt_2097152_booking.partial"},"livemode":{"type":"boolean","description":"False when the event came from a sandbox booking.","example":true},"data":{"anyOf":[{"$ref":"#/components/schemas/PartialBookingData"},{"$ref":"#/components/schemas/ServicingCompletedData"},{"$ref":"#/components/schemas/BookingFailedData"},{"$ref":"#/components/schemas/BookingProcessingData"},{"$ref":"#/components/schemas/BookingCompletedData"},{"$ref":"#/components/schemas/ServicingExchangeConfirmedData"},{"$ref":"#/components/schemas/ServicingTicketIssuedData"},{"$ref":"#/components/schemas/ServicingFailedData"}]}},"required":["event","booking_ref","status","occurred_at","event_id","livemode"],"description":"The signed body POSTed to a registered webhook URL, also available in the delivery log until its payload is purged. Verify the signature against the secret returned once at registration before trusting a delivery. `data` is event-specific: `PartialBookingData` on `booking.partial`, `ServicingCompletedData` on `servicing.completed`, `BookingFailedData` on `booking.failed`, `BookingProcessingData` on `booking.processing`, `BookingCompletedData` on `booking.completed`, `ServicingExchangeConfirmedData` on `servicing.exchange_confirmed`, `ServicingTicketIssuedData` on `servicing.ticket_issued`, `ServicingFailedData` on `servicing.failed`. `manage_booking_url` and `customer_emails[]` hold traveller personal data and mirror what Jinko’s email shows. Email content uses display strings; empty strings, nulls, empty lists and empty objects are omitted from new fields. Integers and booleans are always sent. Existing data fields retain their original behaviour, including nulls."},"HealthResponse":{"type":"object","properties":{"status":{"type":"string","enum":["ok"]},"service":{"type":"string","enum":["jinko-api"]},"version":{"type":"string","example":"0.1.0"}},"required":["status","service","version"]},"FlightPriceAdviceAmount":{"type":"object","properties":{"value":{"type":"integer","minimum":0,"exclusiveMinimum":true,"maximum":9007199254740991,"description":"Positive integer in currency minor units.","example":32000},"currency":{"type":"string","pattern":"^[A-Z]{3}$","example":"USD"},"decimal_places":{"type":"integer","minimum":0,"example":2}},"required":["value","currency","decimal_places"],"additionalProperties":false,"description":"One adult total fare for the supplied trip, including tax and excluding optional paid extras. Value 32000, USD, decimal_places 2 means USD 320.00."},"FlightPriceAdviceContext":{"type":"object","properties":{"origin":{"type":"string","pattern":"^[A-Z]{3}$","example":"NYC"},"destination":{"type":"string","pattern":"^[A-Z]{3}$","example":"LAX"},"departure_date":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","example":"2026-10-20"},"trip_type":{"type":"string","enum":["oneway","roundtrip"]},"return_date":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"The requested return date. Present on round-trip responses, absent on one-way responses.","example":"2026-10-27"},"stops":{"type":"number","description":"Actual number of flight connections, or null when the request omitted stops.","nullable":true,"anyOf":[{"type":"number","enum":[0,1,2]},{"not":{"type":"number"}}]},"cabin_class":{"type":"string","enum":["economy","premium_economy","business","first"]},"current_price":{"$ref":"#/components/schemas/FlightPriceAdviceAmount"},"price_basis":{"type":"string","enum":["per_adult_total_including_tax"]},"evaluated_at":{"type":"string","format":"date-time"},"apex_days":{"type":"integer","minimum":0,"exclusiveMinimum":true}},"required":["origin","destination","departure_date","trip_type","stops","cabin_class","current_price","price_basis","evaluated_at","apex_days"],"additionalProperties":false},"FlightPriceAdviceExpectedFareRecommendationBasis":{"type":"object","properties":{"policy":{"type":"string","enum":["expected_fare"]},"price_label":{"type":"string","enum":["low","typical","high"]},"selected_horizon_days":{"anyOf":[{"type":"number","enum":[1]},{"type":"number","enum":[3]}]},"wait_direction":{"type":"string","enum":["increase","decrease","unchanged"]}},"required":["policy","price_label","selected_horizon_days","wait_direction"],"additionalProperties":false},"FlightPriceAdviceModalDirectionRecommendationBasis":{"type":"object","properties":{"policy":{"type":"string","enum":["modal_direction"]},"price_label":{"type":"string","enum":["low","typical","high"]},"selected_horizon_days":{"type":"number","nullable":true,"not":{"type":"number"}},"wait_direction":{"type":"string","enum":["increase","decrease"]}},"required":["policy","price_label","selected_horizon_days","wait_direction"],"additionalProperties":false},"FlightPriceAdvicePriceAssessmentRecommendationBasis":{"type":"object","properties":{"policy":{"type":"string","enum":["price_assessment"]},"price_label":{"type":"string","enum":["low"]},"selected_horizon_days":{"type":"number","nullable":true,"not":{"type":"number"}},"wait_direction":{"type":"number","nullable":true,"not":{"type":"number"}}},"required":["policy","price_label","selected_horizon_days","wait_direction"],"additionalProperties":false},"FlightPriceAdviceResponse":{"type":"object","properties":{"schema_version":{"type":"string","enum":["3.2"]},"status":{"type":"string","enum":["available","partial","unavailable","not_ready"]},"reason":{"type":"string","nullable":true,"anyOf":[{"type":"string","enum":["statistics_not_ready","future_statistics","stale_statistics","unsupported_currency","unsupported_trip_type","stops_unknown","unsupported_stops","route_not_covered","insufficient_data","horizon_not_supported","unsafe_projected_amount","request_cancelled","partial_statistics","statistics_unavailable"]},{"not":{"type":"string"}}]},"summary":{"type":"string","minLength":1},"context":{"$ref":"#/components/schemas/FlightPriceAdviceContext"},"price_assessment":{"oneOf":[{"type":"object","properties":{"status":{"type":"string","enum":["available"]},"reason_code":{"type":"number","nullable":true,"not":{"type":"number"}},"label":{"type":"string","enum":["low","typical","high"]},"typical_price":{"$ref":"#/components/schemas/FlightPriceAdviceAmount"},"typical_range":{"type":"object","properties":{"low":{"$ref":"#/components/schemas/FlightPriceAdviceAmount"},"high":{"$ref":"#/components/schemas/FlightPriceAdviceAmount"}},"required":["low","high"],"additionalProperties":false},"percentile":{"type":"number","minimum":0,"maximum":100},"difference_pct":{"type":"number"},"comparison_basis":{"type":"string","enum":["historical_comparable_low_fares"]},"evidence":{"type":"object","properties":{"artifact_id":{"type":"string","pattern":"^price-advice-v[1-9][0-9]*-[a-f0-9]{64}$"},"fit_window":{"type":"object","properties":{"start":{"type":"string","format":"date-time"},"end_exclusive":{"type":"string","format":"date-time"}},"required":["start","end_exclusive"],"additionalProperties":false},"source_price_unit":{"type":"string","enum":["amd_totalpricenuc_millicents"]},"currency_mapping":{"type":"string","enum":["gateway_platform_usd_millicents_to_usd_minor_units_divide_by_10"]},"apex_band":{"type":"string","enum":["0-2","3-6","7-13","14-20","21-29","30-59","60-89","90-179",">=180"]},"support":{"type":"object","properties":{"daily_units":{"type":"integer","minimum":0,"exclusiveMinimum":true},"observation_days":{"type":"integer","minimum":0,"exclusiveMinimum":true},"departure_dates":{"type":"integer","minimum":0,"exclusiveMinimum":true},"flight_identities":{"type":"integer","minimum":0,"exclusiveMinimum":true}},"required":["daily_units","observation_days","departure_dates","flight_identities"],"additionalProperties":false},"stay_band":{"type":"string","minLength":1,"description":"The band of stay lengths (days from departure_date to return_date) whose comparable round-trip fares were used. Present on round-trip responses, absent on one-way responses."}},"required":["artifact_id","fit_window","source_price_unit","currency_mapping","apex_band","support"],"additionalProperties":false}},"required":["status","reason_code","label","typical_price","typical_range","percentile","difference_pct","comparison_basis","evidence"],"additionalProperties":false},{"type":"object","properties":{"status":{"type":"string","enum":["unavailable"]},"reason_code":{"type":"string","enum":["statistics_not_ready","future_statistics","stale_statistics","unsupported_currency","unsupported_trip_type","stops_unknown","unsupported_stops","route_not_covered","insufficient_data","horizon_not_supported","unsafe_projected_amount","request_cancelled","partial_statistics","statistics_unavailable"]},"label":{"type":"number","nullable":true,"not":{"type":"number"}},"typical_price":{"type":"number","nullable":true,"not":{"type":"number"}},"typical_range":{"type":"number","nullable":true,"not":{"type":"number"}},"percentile":{"type":"number","nullable":true,"not":{"type":"number"}},"difference_pct":{"type":"number","nullable":true,"not":{"type":"number"}},"comparison_basis":{"type":"string","enum":["historical_comparable_low_fares"]},"evidence":{"type":"number","nullable":true,"not":{"type":"number"}}},"required":["status","reason_code","label","typical_price","typical_range","percentile","difference_pct","comparison_basis","evidence"],"additionalProperties":false}]},"wait_assessment":{"anyOf":[{"type":"object","properties":{"status":{"type":"string","enum":["available"]},"reason_code":{"type":"number","nullable":true,"not":{"type":"number"}},"horizons":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"horizon_days":{"type":"number","enum":[1]},"status":{"type":"string","enum":["available"]},"reason_code":{"type":"number","nullable":true,"not":{"type":"number"}},"direction":{"type":"string","enum":["increase","decrease","unchanged","mixed"]},"forecasted_price":{"$ref":"#/components/schemas/FlightPriceAdviceAmount"},"probabilities":{"type":"object","properties":{"increase":{"type":"number","minimum":0,"maximum":1},"decrease":{"type":"number","minimum":0,"maximum":1},"unchanged":{"type":"number","minimum":0,"maximum":1}},"required":["increase","decrease","unchanged"],"additionalProperties":false}},"required":["horizon_days","status","reason_code","direction","forecasted_price","probabilities"],"additionalProperties":false},{"type":"object","properties":{"horizon_days":{"type":"number","enum":[3]},"status":{"type":"string","enum":["available"]},"reason_code":{"type":"number","nullable":true,"not":{"type":"number"}},"direction":{"type":"string","enum":["increase","decrease","unchanged","mixed"]},"forecasted_price":{"$ref":"#/components/schemas/FlightPriceAdviceAmount"},"probabilities":{"type":"object","properties":{"increase":{"type":"number","minimum":0,"maximum":1},"decrease":{"type":"number","minimum":0,"maximum":1},"unchanged":{"type":"number","minimum":0,"maximum":1}},"required":["increase","decrease","unchanged"],"additionalProperties":false}},"required":["horizon_days","status","reason_code","direction","forecasted_price","probabilities"],"additionalProperties":false}]},"minItems":2,"maxItems":2}},"required":["status","reason_code","horizons"],"additionalProperties":false},{"type":"object","properties":{"status":{"type":"string","enum":["partial"]},"reason_code":{"type":"string","enum":["partial_statistics"]},"horizons":{"anyOf":[{"type":"array","items":{"anyOf":[{"type":"object","properties":{"horizon_days":{"type":"number","enum":[1]},"status":{"type":"string","enum":["available"]},"reason_code":{"type":"number","nullable":true,"not":{"type":"number"}},"direction":{"type":"string","enum":["increase","decrease","unchanged","mixed"]},"forecasted_price":{"$ref":"#/components/schemas/FlightPriceAdviceAmount"},"probabilities":{"type":"object","properties":{"increase":{"type":"number","minimum":0,"maximum":1},"decrease":{"type":"number","minimum":0,"maximum":1},"unchanged":{"type":"number","minimum":0,"maximum":1}},"required":["increase","decrease","unchanged"],"additionalProperties":false}},"required":["horizon_days","status","reason_code","direction","forecasted_price","probabilities"],"additionalProperties":false},{"type":"object","properties":{"horizon_days":{"type":"number","enum":[3]},"status":{"type":"string","enum":["unavailable"]},"reason_code":{"type":"string","enum":["statistics_not_ready","future_statistics","stale_statistics","unsupported_currency","unsupported_trip_type","stops_unknown","unsupported_stops","route_not_covered","insufficient_data","horizon_not_supported","unsafe_projected_amount","request_cancelled","partial_statistics","statistics_unavailable"]},"direction":{"type":"number","nullable":true,"not":{"type":"number"}},"forecasted_price":{"type":"number","nullable":true,"not":{"type":"number"}},"probabilities":{"type":"object","properties":{"increase":{"type":"number","nullable":true,"not":{"type":"number"}},"decrease":{"type":"number","nullable":true,"not":{"type":"number"}},"unchanged":{"type":"number","nullable":true,"not":{"type":"number"}}},"required":["increase","decrease","unchanged"],"additionalProperties":false}},"required":["horizon_days","status","reason_code","direction","forecasted_price","probabilities"],"additionalProperties":false}]},"minItems":2,"maxItems":2},{"type":"array","items":{"anyOf":[{"type":"object","properties":{"horizon_days":{"type":"number","enum":[1]},"status":{"type":"string","enum":["unavailable"]},"reason_code":{"type":"string","enum":["statistics_not_ready","future_statistics","stale_statistics","unsupported_currency","unsupported_trip_type","stops_unknown","unsupported_stops","route_not_covered","insufficient_data","horizon_not_supported","unsafe_projected_amount","request_cancelled","partial_statistics","statistics_unavailable"]},"direction":{"type":"number","nullable":true,"not":{"type":"number"}},"forecasted_price":{"type":"number","nullable":true,"not":{"type":"number"}},"probabilities":{"type":"object","properties":{"increase":{"type":"number","nullable":true,"not":{"type":"number"}},"decrease":{"type":"number","nullable":true,"not":{"type":"number"}},"unchanged":{"type":"number","nullable":true,"not":{"type":"number"}}},"required":["increase","decrease","unchanged"],"additionalProperties":false}},"required":["horizon_days","status","reason_code","direction","forecasted_price","probabilities"],"additionalProperties":false},{"type":"object","properties":{"horizon_days":{"type":"number","enum":[3]},"status":{"type":"string","enum":["available"]},"reason_code":{"type":"number","nullable":true,"not":{"type":"number"}},"direction":{"type":"string","enum":["increase","decrease","unchanged","mixed"]},"forecasted_price":{"$ref":"#/components/schemas/FlightPriceAdviceAmount"},"probabilities":{"type":"object","properties":{"increase":{"type":"number","minimum":0,"maximum":1},"decrease":{"type":"number","minimum":0,"maximum":1},"unchanged":{"type":"number","minimum":0,"maximum":1}},"required":["increase","decrease","unchanged"],"additionalProperties":false}},"required":["horizon_days","status","reason_code","direction","forecasted_price","probabilities"],"additionalProperties":false}]},"minItems":2,"maxItems":2}]}},"required":["status","reason_code","horizons"],"additionalProperties":false},{"type":"object","properties":{"status":{"type":"string","enum":["unavailable"]},"reason_code":{"type":"string","enum":["statistics_not_ready","future_statistics","stale_statistics","unsupported_currency","unsupported_trip_type","stops_unknown","unsupported_stops","route_not_covered","insufficient_data","horizon_not_supported","unsafe_projected_amount","request_cancelled","partial_statistics","statistics_unavailable"]},"horizons":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"horizon_days":{"type":"number","enum":[1]},"status":{"type":"string","enum":["unavailable"]},"reason_code":{"type":"string","enum":["statistics_not_ready","future_statistics","stale_statistics","unsupported_currency","unsupported_trip_type","stops_unknown","unsupported_stops","route_not_covered","insufficient_data","horizon_not_supported","unsafe_projected_amount","request_cancelled","partial_statistics","statistics_unavailable"]},"direction":{"type":"number","nullable":true,"not":{"type":"number"}},"forecasted_price":{"type":"number","nullable":true,"not":{"type":"number"}},"probabilities":{"type":"object","properties":{"increase":{"type":"number","nullable":true,"not":{"type":"number"}},"decrease":{"type":"number","nullable":true,"not":{"type":"number"}},"unchanged":{"type":"number","nullable":true,"not":{"type":"number"}}},"required":["increase","decrease","unchanged"],"additionalProperties":false}},"required":["horizon_days","status","reason_code","direction","forecasted_price","probabilities"],"additionalProperties":false},{"type":"object","properties":{"horizon_days":{"type":"number","enum":[3]},"status":{"type":"string","enum":["unavailable"]},"reason_code":{"type":"string","enum":["statistics_not_ready","future_statistics","stale_statistics","unsupported_currency","unsupported_trip_type","stops_unknown","unsupported_stops","route_not_covered","insufficient_data","horizon_not_supported","unsafe_projected_amount","request_cancelled","partial_statistics","statistics_unavailable"]},"direction":{"type":"number","nullable":true,"not":{"type":"number"}},"forecasted_price":{"type":"number","nullable":true,"not":{"type":"number"}},"probabilities":{"type":"object","properties":{"increase":{"type":"number","nullable":true,"not":{"type":"number"}},"decrease":{"type":"number","nullable":true,"not":{"type":"number"}},"unchanged":{"type":"number","nullable":true,"not":{"type":"number"}}},"required":["increase","decrease","unchanged"],"additionalProperties":false}},"required":["horizon_days","status","reason_code","direction","forecasted_price","probabilities"],"additionalProperties":false}]},"minItems":2,"maxItems":2}},"required":["status","reason_code","horizons"],"additionalProperties":false}]},"recommendation":{"anyOf":[{"type":"object","properties":{"action":{"type":"string","enum":["buy_now","wait"]},"basis":{"oneOf":[{"$ref":"#/components/schemas/FlightPriceAdviceExpectedFareRecommendationBasis"},{"$ref":"#/components/schemas/FlightPriceAdviceModalDirectionRecommendationBasis"},{"$ref":"#/components/schemas/FlightPriceAdvicePriceAssessmentRecommendationBasis"}],"discriminator":{"propertyName":"policy","mapping":{"expected_fare":"#/components/schemas/FlightPriceAdviceExpectedFareRecommendationBasis","modal_direction":"#/components/schemas/FlightPriceAdviceModalDirectionRecommendationBasis","price_assessment":"#/components/schemas/FlightPriceAdvicePriceAssessmentRecommendationBasis"}}},"summary":{"type":"string","minLength":1}},"required":["action","basis","summary"],"additionalProperties":false},{"type":"object","properties":{"action":{"type":"string","enum":["no_clear_signal"]},"basis":{"type":"object","nullable":true,"oneOf":[{"$ref":"#/components/schemas/FlightPriceAdviceExpectedFareRecommendationBasis"},{"$ref":"#/components/schemas/FlightPriceAdviceModalDirectionRecommendationBasis"},{"$ref":"#/components/schemas/FlightPriceAdvicePriceAssessmentRecommendationBasis"},{"type":"number","nullable":true,"not":{"type":"number"}}]},"summary":{"type":"string","minLength":1}},"required":["action","basis","summary"],"additionalProperties":false}]},"live_quotes_used":{"type":"boolean","enum":[false]}},"required":["schema_version","status","reason","summary","context","price_assessment","wait_assessment","recommendation","live_quotes_used"],"additionalProperties":false,"description":"Price advice. Results may be available, partial, unavailable, or not ready; fixed 1-day and 3-day horizons are always present."},"ErrorResponse":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["INVALID_SEGMENTS_TO_EXCHANGE","PARTIAL_EXCHANGE_UNSUPPORTED","LEG_ALREADY_DEPARTED","DEPARTURE_DATE_IN_PAST","LEG_DATES_OUT_OF_ORDER","INVALID_EXCHANGE_REQUEST","NOT_EXCHANGEABLE","CREDENTIAL_FORMAT_INVALID","PAYMENT_TYPE_NOT_ENABLED","PAYMENT_CREDENTIAL_INVALID","TRIP_OWNED_BY_OTHER_PAYMENT","IDEMPOTENCY_KEY_REUSED","ATTEMPT_IN_PROGRESS","ATTEMPT_TERMINAL","TEMPORARILY_UNAVAILABLE","AUTH_REQUIRED","PAYMENT_REQUIRED","RATE_LIMITED","BAD_REQUEST","FORBIDDEN","NOT_FOUND","CONFLICT","GONE","QUOTE_PENDING","QUOTE_EXPIRED","TRIP_EXPIRED","TRIP_STATE_CONFLICT","OFFER_EXPIRED","OFFER_UNAVAILABLE","MISSING_CUSTOMER_DETAILS","INVALID_PHONE_NUMBER","CURRENCY_UNSUPPORTED","HOTEL_NAME_LOW_CONFIDENCE","DESTINATION_LOW_CONFIDENCE","UPSTREAM_REJECTED","UPSTREAM_UNAVAILABLE","UPSTREAM_TIMEOUT","UPSTREAM_ERROR","INTERNAL"],"description":"What went wrong, as a stable machine-readable code. This is a closed set — branch on it rather than on `message`, which is prose and may change. New codes arrive in a minor version, so treat an unknown one as its HTTP status. A code can also stop being emitted: it leaves this set in a minor version, named in the changelog, and a branch you wrote for it goes unreached rather than wrong.","example":"BAD_REQUEST"},"message":{"type":"string","description":"What went wrong, in prose. These codes always carry the same message, and it never includes text from a supplier: `RATE_LIMITED` — \"Too many requests. Retry after the Retry-After delay.\"; `OFFER_EXPIRED` — \"This offer has expired. Search again.\"; `OFFER_UNAVAILABLE` — \"This offer is no longer available. Search again.\"; `UPSTREAM_REJECTED` — \"The request was declined for this offer. Try another offer.\"; `UPSTREAM_UNAVAILABLE` — \"The service is temporarily unavailable. Retry after the Retry-After delay.\"; `UPSTREAM_TIMEOUT` — \"The request timed out before a result was available. Retry the request.\"; `UPSTREAM_ERROR` — \"The request could not be completed.\"."},"doc_url":{"type":"string"},"field":{"type":"string","description":"On a 400 `BAD_REQUEST` that names one refused input: the path of that field in your request, e.g. `selections[1].quantity` for the second selection of a `select_ancillaries` call. Absent when the refusal names no single field.","example":"selections[1].quantity"}},"required":["code","message"]}},"required":["error"]},"FlightPriceAdviceRequest":{"type":"object","properties":{"origin":{"type":"string","pattern":"^[A-Z]{3}$","example":"NYC"},"destination":{"type":"string","pattern":"^[A-Z]{3}$","example":"LAX"},"departure_date":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","example":"2026-10-20"},"trip_type":{"type":"string","enum":["oneway","roundtrip"]},"return_date":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Return date in YYYY-MM-DD format, a real calendar date on or after departure_date. Required when trip_type is roundtrip; must be omitted when trip_type is oneway.","example":"2026-10-27"},"stops":{"type":"number","description":"Actual number of flight connections: 0 for nonstop, 1, or 2. Omit when unknown. For a round trip, 0 means nonstop on both the outbound and the inbound flight; 1 and 2 are not covered for round trips.","enum":[0,1,2]},"cabin_class":{"type":"string","enum":["economy","premium_economy","business","first"]},"current_price":{"$ref":"#/components/schemas/FlightPriceAdviceAmount"}},"required":["origin","destination","departure_date","trip_type","cabin_class","current_price"],"additionalProperties":false,"description":"A flight and its current price. Trip type, the optional actual connection count, and cabin are validated as values; a valid request does not imply that statistics cover it. A round trip names its return_date."},"HotelBed":{"type":"object","properties":{"bed_type":{"type":"string"},"count":{"type":"integer","minimum":1}},"required":["bed_type","count"]},"CancellationStep":{"type":"object","properties":{"cancel_time":{"type":"string","description":"Deadline after which this tier applies. RFC 3339 with the supplier’s declared UTC offset. Steps whose deadline has no declared timezone are omitted from the ladder — the platform never invents an offset — so a schedule can be shorter than the supplier’s own. Tiers are chronological, earliest first, so the first entry is the deadline that matters.","example":"2026-08-18T10:00:00+02:00"},"amount":{"type":"number","description":"Deprecated: use `amount_money` instead. Read `amount_money`, `percent` or `nights`, whichever this step carries. What the guest is charged on or after `cancel_time`: a sum in MAJOR units of `currency`, a percentage or a number of nights, as `type` says.","example":606.13,"deprecated":true},"currency":{"type":"string","description":"Deprecated: use `amount_money` instead. The currency of `amount`.","example":"EUR","deprecated":true},"type":{"type":"string","description":"How the charge is expressed — a sum (\"amount\", \"flat_fee\"; read `amount_money`), a percentage (\"percent\", \"percentage\"; read `percent`) or a number of nights (\"nights\"; read `nights`).","example":"amount"},"display":{"type":"string","description":"Deprecated: use `amount_money` instead. The same figure as a string ready to show: the ISO 4217 code, a space, then the amount, e.g. \"USD 159.77\". With decimal_places it is written at exactly that scale (value 1561500, JPY, decimal_places 2 is \"JPY 15615.00\"); without, at the ISO digits of the currency. Show this; compute with the number. Absent when the figure is not money (a cancellation step expressed as a percent or a number of nights) or could not be read unambiguously.","example":"EUR 606.13","deprecated":true},"amount_money":{"allOf":[{"$ref":"#/components/schemas/MoneyValue"},{"description":"What the guest is charged on or after `cancel_time`, when this step is a sum. Absent on a percentage or nights step, and when the supplier named no currency."}]},"percent":{"type":"number","description":"The charge as a percentage of the stay price, when this step is a percentage, on the 0–100 scale the supplier states it in: 50 is half the stay price, 100 the whole of it. Never a 0–1 fraction. Absent otherwise.","example":50},"nights":{"type":"number","description":"The charge as a number of nights of the stay, when this step is expressed in nights. Absent otherwise.","example":1}}},"TaxLine":{"type":"object","properties":{"amount":{"type":"number","description":"Deprecated: use `amount_money` instead. The tax in MAJOR units of `currency`.","example":164.49,"deprecated":true},"currency":{"type":"string","description":"Deprecated: use `amount_money` instead. The currency of `amount`.","example":"EUR","deprecated":true},"amount_money":{"allOf":[{"$ref":"#/components/schemas/MoneyValue"},{"description":"The tax. When the supplier named no currency for the line, it is the currency the rate is priced in."}]},"description":{"type":"string","description":"The provider’s own wording for the tax. Free text, not a code.","example":"19, VAT National taxes on accommodation"},"included":{"type":"boolean","description":"True when the amount is ALREADY inside `total_amount_money`; false when the guest pays it at the property on top. Getting this backwards misquotes the trip.","example":true},"display":{"type":"string","description":"Deprecated: use `amount_money` instead. The same figure as a string ready to show: the ISO 4217 code, a space, then the amount, e.g. \"USD 159.77\". With decimal_places it is written at exactly that scale (value 1561500, JPY, decimal_places 2 is \"JPY 15615.00\"); without, at the ISO digits of the currency. Show this; compute with the number. Absent when the figure is not money (a cancellation step expressed as a percent or a number of nights) or could not be read unambiguously.","example":"EUR 164.49","deprecated":true}}},"HotelPaymentType":{"type":"string","enum":["pay_now","pay_at_property","deposit"],"description":"How the rate can be settled. `pay_now` — paid when the booking is made. `pay_at_property` — paid to the hotel at the property. `deposit` — a deposit is taken."},"HotelRatePromotion":{"type":"object","properties":{"code":{"type":"string"},"name":{"type":"string"},"remark":{"type":"string"}}},"Occupancy":{"type":"object","properties":{"adults":{"type":"integer","minimum":1},"children_ages":{"type":"array","items":{"type":"integer","minimum":0,"maximum":17}}},"required":["adults"],"description":"The party one room is requested or priced for: the number of adults and the age of each child."},"HotelBundleRoomPolicy":{"type":"object","properties":{"type":{"type":"string"},"description":{"type":"string"},"fee_amount":{"type":"number","description":"Deprecated: use `fee_amount_money` instead. Customer policy fee in MAJOR units of `fee_currency`.","deprecated":true},"fee_currency":{"type":"string","description":"Deprecated: use `fee_amount_money` instead. The currency of `fee_amount`.","deprecated":true},"fee_amount_money":{"allOf":[{"$ref":"#/components/schemas/MoneyValue"},{"description":"Customer cancellation or policy fee, with exact minor units and currency precision."}]}}},"HotelBundleRoom":{"type":"object","properties":{"occupancy_number":{"type":"integer","minimum":1,"description":"Stable one-based room slot within this bundle.","example":1},"room_id":{"type":"string"},"mapped_room_id":{"type":"integer"},"room_name":{"type":"string","example":"Superior Double"},"rate_id":{"type":"string"},"occupancy":{"$ref":"#/components/schemas/Occupancy"},"board_type":{"type":"string","example":"BB"},"board_name":{"type":"string","example":"Breakfast Included"},"max_occupancy":{"type":"integer"},"taxes_and_fees":{"type":"array","items":{"$ref":"#/components/schemas/TaxLine"}},"policies":{"type":"array","items":{"$ref":"#/components/schemas/HotelBundleRoomPolicy"}},"is_refundable":{"type":"boolean"},"cancellation_policy":{"type":"array","items":{"$ref":"#/components/schemas/CancellationStep"}},"free_cancellation_until":{"type":"string"},"payment_types":{"type":"array","items":{"$ref":"#/components/schemas/HotelPaymentType"},"description":"How the rate can be settled. Each value appears at most once.","example":["pay_now"]},"remarks":{"type":"string"}},"required":["occupancy_number","occupancy"],"description":"One room of a hotel item — the single room a one-room item books, or one slot of a room-bundle rate — with the party it was priced for (`occupancy`) and its display and policy details. The containing item owns the customer-facing total; supplier net, suggested retail, commission and room-local supplier price figures are intentionally not part of this public shape."},"HotelRate":{"type":"object","properties":{"offer_id":{"type":"string","description":"Legacy rooms[].rates[] compatibility token. New integrations use the hotel-level `offers[].trip_item_token`; add that token to the trip exactly once.","example":"htl_2d67a7441c60e5de"},"board_type":{"type":"string","example":"RO"},"board_name":{"type":"string","example":"Room Only"},"total_amount":{"type":"number","description":"Deprecated: use `total_amount_money` instead. Price for the whole stay in `currency`, in MAJOR units at the currency’s own precision.","example":1818.4,"deprecated":true},"total_amount_display":{"type":"string","description":"Deprecated: use `total_amount_money` instead. The rate total as a string ready to show: the ISO 4217 code, a space, then `total_amount` at the currency’s ISO digits, e.g. \"EUR 606.13\".","example":"EUR 606.13","deprecated":true},"currency":{"type":"string","description":"Deprecated: use `total_amount_money` instead. The currency of `total_amount`.","example":"EUR","deprecated":true},"total_amount_money":{"allOf":[{"$ref":"#/components/schemas/MoneyValue"},{"description":"Price for the whole stay."}]},"is_refundable":{"type":"boolean","example":false},"free_cancellation_until":{"type":"string","description":"Last instant the stay can be cancelled at no charge. RFC 3339 with the supplier’s declared UTC offset. Absent when the supplier declared no timezone — the platform never invents an offset, so a deadline it cannot place on the clock is withheld rather than published ambiguously — and absent when the rate is non-refundable or the provider named no deadline.","example":"2026-08-18T10:00:00+02:00"},"cancellation_schedule":{"type":"array","items":{"$ref":"#/components/schemas/CancellationStep"},"description":"The full penalty ladder, chronological. Absent when the provider published none — which is not a statement that cancelling is free."},"taxes_breakdown":{"type":"array","items":{"$ref":"#/components/schemas/TaxLine"},"description":"Taxes and fees on this rate. ALWAYS present, so an empty array means \"no tax is known\" and can be shown as such; check `included` per line before adding anything to `total_amount_money`."},"payment_types":{"type":"array","items":{"$ref":"#/components/schemas/HotelPaymentType"},"description":"How the rate can be settled. Each value appears at most once.","example":["pay_now"]},"promotions":{"type":"array","items":{"$ref":"#/components/schemas/HotelRatePromotion"}},"booking_scope":{"type":"string","description":"LEGACY-ONLY compatibility metadata. Current canonical `offers[]` never returns `booking_scope`; use `room_rates.length` to determine how many rooms an offer covers.","example":"room_bundle"},"num_rooms":{"type":"integer","minimum":1,"description":"LEGACY-ONLY compatibility metadata. Current canonical `offers[]` represents room count through `room_rates.length` and never returns `num_rooms`.","example":2},"rooms":{"type":"array","items":{"$ref":"#/components/schemas/HotelBundleRoom"},"description":"LEGACY-ONLY compatibility room details. Current canonical `offers[]` returns `room_rates[]` and never returns this `rooms` field."}},"description":"Deprecated compatibility shape under `rooms[].rates[]`. Current canonical `offers[]` does not return `booking_scope`, `num_rooms`, or `rooms`; use `room_rates[]` instead."},"HotelRoom":{"type":"object","properties":{"room_id":{"type":"string","example":"rm_1"},"room_name":{"type":"string","example":"Superior Double"},"description":{"type":"string","description":"Free-text room description, when the provider supplied one."},"requested_occupancy":{"type":"integer","description":"The occupancy these rates were priced for — an echo of what you searched, not a property of the room. Pricing a different party size means searching again.","example":2},"room_capacity":{"type":"integer","description":"How many the room sleeps, from the provider’s content catalogue. Absent when the catalogue carries no figure — absent means unknown, never zero. Same field as on hotel_details and on a hotel trip item.","example":2},"max_occupancy":{"type":"integer","description":"Deprecated: an alias of `requested_occupancy` (same value), never the room’s capacity. Read `requested_occupancy` for the priced party size and `room_capacity` for how many the room sleeps.","deprecated":true,"example":2},"images":{"type":"array","items":{"type":"string"}},"amenities":{"type":"array","items":{"type":"string"}},"beds":{"type":"array","items":{"$ref":"#/components/schemas/HotelBed"}},"rates":{"type":"array","items":{"$ref":"#/components/schemas/HotelRate"},"description":"DEPRECATED compatibility projection. Read the hotel-level `offers[]` list for new integrations and add an offer’s `trip_item_token` to the trip. Multi-room searches return this as an empty array.","deprecated":true}}},"HotelOfferPromotion":{"type":"object","properties":{"name":{"type":"string"},"remark":{"type":"string"}},"description":"A provider promotion applied to the offer, as it is to be shown to the guest: its name and the remark that goes with it."},"HotelRoomRate":{"type":"object","properties":{"room_id":{"type":"string","description":"References one entry in this hotel’s `rooms[]` metadata."},"occupancy_number":{"type":"integer","minimum":1,"description":"Stable one-based occupancy slot from the search request."},"occupancy":{"$ref":"#/components/schemas/Occupancy"},"room_name":{"type":"string"},"board_type":{"type":"string"},"board_name":{"type":"string"},"max_occupancy":{"type":"integer"},"taxes_and_fees":{"type":"array","items":{"$ref":"#/components/schemas/TaxLine"}},"policies":{"type":"array","items":{"$ref":"#/components/schemas/HotelBundleRoomPolicy"}},"is_refundable":{"type":"boolean"},"cancellation_policy":{"type":"array","items":{"$ref":"#/components/schemas/CancellationStep"}},"free_cancellation_until":{"type":"string"},"payment_types":{"type":"array","items":{"$ref":"#/components/schemas/HotelPaymentType"},"description":"How the rate can be settled. Each value appears at most once.","example":["pay_now"]},"remarks":{"type":"string"}},"required":["room_id","occupancy_number","occupancy","taxes_and_fees","is_refundable"],"description":"Display and policy details for one requested room occupancy. It carries no customer price allocation; the containing offer owns the authoritative whole-stay total."},"HotelOffer":{"type":"object","properties":{"offer_id":{"type":"string","description":"Deprecated: use `trip_item_token` instead. Same value.","example":"htl_2d67a7441c60e5de","deprecated":true},"board_type":{"type":"string","example":"RO"},"board_name":{"type":"string","example":"Room Only"},"total_amount":{"type":"number","description":"Deprecated: use `total_amount_money` instead. Price for the whole stay in `currency`, in MAJOR units at the currency’s own precision.","example":1818.4,"deprecated":true},"total_amount_display":{"type":"string","description":"Deprecated: use `total_amount_money` instead. The rate total as a string ready to show: the ISO 4217 code, a space, then `total_amount` at the currency’s ISO digits, e.g. \"EUR 606.13\".","example":"EUR 606.13","deprecated":true},"currency":{"type":"string","description":"Deprecated: use `total_amount_money` instead. The currency of `total_amount`.","example":"EUR","deprecated":true},"total_amount_money":{"allOf":[{"$ref":"#/components/schemas/MoneyValue"},{"description":"Price for the whole stay."}]},"is_refundable":{"type":"boolean"},"free_cancellation_until":{"type":"string","description":"Last instant the stay can be cancelled at no charge. RFC 3339 with the supplier’s declared UTC offset. Absent when the supplier declared no timezone — the platform never invents an offset, so a deadline it cannot place on the clock is withheld rather than published ambiguously — and absent when the rate is non-refundable or the provider named no deadline.","example":"2026-08-18T10:00:00+02:00"},"cancellation_schedule":{"type":"array","items":{"$ref":"#/components/schemas/CancellationStep"},"description":"The full penalty ladder, chronological. Absent when the provider published none — which is not a statement that cancelling is free."},"taxes_breakdown":{"type":"array","items":{"$ref":"#/components/schemas/TaxLine"}},"payment_types":{"type":"array","items":{"$ref":"#/components/schemas/HotelPaymentType"},"description":"How the rate can be settled. Each value appears at most once.","example":["pay_now"]},"promotions":{"type":"array","items":{"$ref":"#/components/schemas/HotelOfferPromotion"}},"trip_item_token":{"type":"string","description":"The token `trip(add_item)` takes as `trip_item_token`. Add it once even when `room_rates` contains several occupancies.","example":"htl_2d67a7441c60e5de"},"room_rates":{"type":"array","items":{"$ref":"#/components/schemas/HotelRoomRate"},"description":"One entry per requested occupancy. One occupancy means one room; multiple occupancies mean a multi-room offer."}},"required":["offer_id","total_amount","currency","is_refundable","taxes_breakdown","trip_item_token","room_rates"]},"Hotel":{"type":"object","properties":{"hotel_id":{"type":"string","description":"Opaque hotel identifier. Pass it to hotel_details, hotel_reviews and `hotel_ids` exactly as received; do not parse or build it. The value a hotel is known by can change between contract versions, so read it from a fresh hotel_search rather than keeping it.","example":"lp1d2c3"},"hotel_ref":{"type":"string","description":"Deprecated: use `hotel_id` (search) or `id` (details, reviews). The hotel's identifier behind a source prefix, as earlier contract versions returned it; hotel_details and hotel_reviews still accept it. Pass it back exactly as received; do not parse or build it.","example":"h1:lp1d2c3","deprecated":true},"name":{"type":"string","example":"Hôtel Le Marais"},"address":{"type":"string","example":"12 Rue de Rivoli"},"city":{"type":"string","example":"Paris"},"country":{"type":"string","description":"ISO 3166-1 alpha-2 country code, LOWER case as the platform sends it.","example":"fr"},"star_rating":{"type":"number","example":4},"category":{"type":"string","description":"The provider's own category wording, which `star_rating` cannot express. Free text.","example":"4 KEYS"},"lodging_type":{"type":"string","example":"Aparthotel"},"rating":{"type":"number","description":"Guest score out of 10.","example":8.6},"review_count":{"type":"number","description":"Number of guest reviews behind `rating`.","example":1243},"main_photo":{"type":"string","description":"Full-size hero image URL.","example":"https://images.example.com/hotels/main.jpg"},"thumbnail":{"type":"string","description":"Small preview image URL.","example":"https://images.example.com/hotels/thumb.jpg"},"latitude":{"type":"number","example":48.8566},"longitude":{"type":"number","example":2.3522},"distance_km":{"type":"number","minimum":0,"description":"Straight-line distance from the search center, in kilometers."},"recommendation_score":{"type":"number","minimum":0,"maximum":1,"description":"How well the hotel fits the request, from 0 to 1; results are ordered by it. Not a guest rating."},"rooms":{"type":"array","items":{"$ref":"#/components/schemas/HotelRoom"}},"offers":{"type":"array","items":{"$ref":"#/components/schemas/HotelOffer"},"description":"Bookable whole-stay offers. Choose one and add its `trip_item_token` to the trip exactly once."}},"description":"A hotel and its available rooms. Until v0.2.0 this schema declared `images`, `thumbnail_url` and `country_code`, none of which hotel search has ever sent; the real names are `main_photo`, `thumbnail` and `country`."},"HotelAiSearchAnswer":{"type":"object","properties":{"context_id":{"type":"string","nullable":true,"description":"The conversation; send it with the next message to continue.","example":"ctx_example"},"task_id":{"type":"string","example":"atk_example"},"status":{"type":"string","enum":["working","input_required","completed","failed","canceled"],"description":"`working`: the search is still running, read the task again. `input_required`: the message asks a question; answer it in the same conversation. `completed`: the hotels are in the answer. `failed`: see `error`. `canceled`: a later message or a cancel replaced it."},"message":{"type":"string","description":"The reply to show the traveller.","example":"Found 37 hotels in Paris for Oct 24–25 (1 night, 2 adults, 1 room)."},"question":{"type":"string","description":"What the reply asks for, when `status` is `input_required`.","example":"dates"},"criteria":{"type":"object","additionalProperties":{"nullable":true},"description":"The search the request was understood as, in the `hotel_search` request fields, plus `board_types` and `refundable_only` for the rate filters applied to the results."},"notes":{"type":"array","items":{"type":"object","additionalProperties":{"nullable":true}},"description":"Assumptions and adjustments made while understanding the request."},"hotels":{"type":"array","items":{"$ref":"#/components/schemas/Hotel"}},"has_more":{"type":"boolean","description":"More hotels are available; ask for more in the same conversation."},"reference":{"type":"boolean","description":"The hotels are a 1-night reference search shown while the check-out date is asked."},"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["code","message"],"description":"Why the task failed, when `status` is `failed`."},"superseded_by":{"type":"string","description":"The task that replaced this one, when `status` is `canceled`."}},"required":["context_id","task_id","status"]},"HotelAiSearchRequest":{"type":"object","properties":{"text":{"type":"string","minLength":1,"maxLength":2000,"description":"The traveller's request in one sentence, or the answer to the question asked in the previous turn.","example":"A hotel in Paris next weekend for 2 adults with breakfast included"},"context_id":{"type":"string","minLength":1,"maxLength":100,"description":"The conversation to continue, from an earlier answer. Omit to start a new conversation.","example":"ctx_example"},"task_id":{"type":"string","minLength":1,"maxLength":100,"description":"The task of that conversation the message answers. Optional: without it, a message continues the task waiting for an answer, or starts a new one.","example":"atk_example"},"currency":{"type":"string","pattern":"^[A-Za-z]{3}$","description":"Currency of the prices (ISO 4217).","example":"EUR"},"guest_nationality":{"type":"string","pattern":"^[A-Za-z]{2}$","description":"Nationality of the guests (ISO 3166-1 alpha-2).","example":"FR"},"timezone":{"type":"string","description":"The traveller's IANA time zone. Relative dates such as \"next weekend\" or \"tonight\" are computed in it.","example":"Europe/Paris"}},"required":["text","timezone"]},"DestinationSuggestion":{"type":"object","properties":{"city_name":{"type":"string","example":"Paris"},"iata_code":{"type":"string","example":"PAR"},"image_url":{"type":"string","example":"https://images.gojinko.com/destinations/par.jpg"},"location":{"type":"object","properties":{"latitude":{"type":"number"},"longitude":{"type":"number"}},"required":["latitude","longitude"],"example":{"latitude":48.8566,"longitude":2.3522}},"lowest_fare":{"allOf":[{"$ref":"#/components/schemas/Itinerary"},{"description":"One adult's fare (`priced_for: \"one_adult\"`): the cache holds one-adult fares only."}]},"flights":{"type":"array","items":{"$ref":"#/components/schemas/Itinerary"},"description":"One adult's fare (`priced_for: \"one_adult\"`): the cache holds one-adult fares only."}},"required":["flights"]},"FindDestinationResponse":{"type":"object","properties":{"priced_for":{"type":"string","enum":["one_adult"],"description":"Whose price the cached fares in this answer are: always `one_adult`. Every cached total is one adult's fare; this request takes no passenger counts. Price the whole party with `POST /v1/flight_search`.","example":"one_adult"},"destinations":{"type":"array","items":{"$ref":"#/components/schemas/DestinationSuggestion"}},"total":{"type":"number","example":12},"next_page_token":{"type":"string","example":"eyJvZmZzZXQiOjIwfQ=="}},"required":["priced_for","destinations"]},"DateRange":{"type":"object","properties":{"start":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"end":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"}},"required":["start","end"],"description":"Inclusive range of dates. `start` must be on or before `end`; a one-day range has `start` equal to `end`.","example":{"start":"2026-06-01","end":"2026-06-30"}},"IntentInput":{"type":"object","properties":{"user_intent":{"type":"string","nullable":true,"description":"The user's natural-language intent: the Alpic PII-stripped paraphrase when available, else a best-effort fallback to the client-provided NL query.","example":"find a cheap flight to Tokyo"}}},"FindDestinationRequest":{"type":"object","properties":{"origins":{"type":"array","items":{"type":"string"},"minItems":1,"description":"IATA city or airport codes. See `origin_type` for how an untyped code is read.","example":["NYC"]},"destinations":{"type":"array","items":{"type":"string"},"description":"IATA city or airport codes. Omit for global discovery (`find_destination`) or to scan every destination from the origins. See `destination_type` for how an untyped code is read.","example":["PAR"]},"origin_type":{"type":"string","enum":["city","airport"],"description":"How to read the code: `city` searches every airport of the city, `airport` only that one airport. Omit it and the platform applies one rule: a code that names a multi-airport city is searched as the city; every other code is searched as the airport. Untyped NYC, LON or PAR is the city; untyped JFK, LHR or CDG is the airport; and untyped LAX, MIA or LAS — codes a city shares with its main airport — is the CITY, so results can land at any airport of that city. If the user named the airport, send `airport`; if they named the city, omit the field.","example":"city"},"destination_type":{"type":"string","enum":["city","airport"],"description":"How to read the code: `city` searches every airport of the city, `airport` only that one airport. Omit it and the platform applies one rule: a code that names a multi-airport city is searched as the city; every other code is searched as the airport. Untyped NYC, LON or PAR is the city; untyped JFK, LHR or CDG is the airport; and untyped LAX, MIA or LAS — codes a city shares with its main airport — is the CITY, so results can land at any airport of that city. If the user named the airport, send `airport`; if they named the city, omit the field.","example":"city"},"departure_dates":{"type":"array","items":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"example":["2026-07-15"]},"departure_date_ranges":{"type":"array","items":{"$ref":"#/components/schemas/DateRange"}},"return_dates":{"type":"array","items":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"example":["2026-07-22"]},"return_date_ranges":{"type":"array","items":{"$ref":"#/components/schemas/DateRange"}},"stay_days":{"type":"integer","minimum":0,"exclusiveMinimum":true,"example":7},"stay_days_range":{"type":"object","properties":{"min":{"type":"integer"},"max":{"type":"integer"}},"required":["min","max"]},"trip_type":{"type":"string","enum":["oneway","roundtrip"],"example":"roundtrip"},"cabin_class":{"type":"string","enum":["economy","premium_economy","business","first"],"example":"economy"},"direct_only":{"type":"boolean","description":"Only direct flights. Cached fares record whether a fare is direct but not how many stops it makes, so this is the only stop filter here; POST /v1/flight_search takes `max_stops`.","example":false},"accuracy":{"type":"string","enum":["standard","high"],"description":"How strict the freshness check on cached fares is. `standard` (default) returns fares estimated to be at least 80% likely to still be available at the cached price; `high` raises that to 95%, so fewer fares come back.","example":"standard"},"max_total":{"type":"number","minimum":0,"exclusiveMinimum":true,"description":"Caps the cached fare's total. Cached fares are priced for one adult.","example":800},"currency":{"type":"string","example":"USD"},"locale":{"type":"string","example":"en-US"},"limit":{"type":"integer","minimum":1,"maximum":100,"default":20,"description":"The maximum number of destinations returned, 1 to 100, default 20. It counts destinations, not itineraries: each destination carries up to `flights_per_destination` itineraries."},"offset":{"type":"integer","minimum":0,"default":0},"sort_by":{"type":"string","enum":["lowest","recommendation"],"default":"lowest"},"intent":{"$ref":"#/components/schemas/IntentInput"},"flights_per_destination":{"type":"integer","minimum":1,"maximum":10,"default":1}},"required":["origins","trip_type"],"example":{"origins":["NYC"],"trip_type":"roundtrip","departure_date_ranges":[{"start":"2026-09-01","end":"2026-09-30"}],"stay_days":7,"cabin_class":"economy","max_total":800,"currency":"USD","flights_per_destination":1}},"FlightCalendarResponse":{"type":"object","properties":{"priced_for":{"type":"string","enum":["one_adult"],"description":"Whose price the cached fares in this answer are: always `one_adult`. Every cached total is one adult's fare; this request takes no passenger counts. Price the whole party with `POST /v1/flight_search`.","example":"one_adult"},"itineraries":{"type":"array","items":{"$ref":"#/components/schemas/Itinerary"}},"next_page_token":{"type":"string","example":"eyJvZmZzZXQiOjIwfQ=="}},"required":["priced_for","itineraries"]},"FlightDiscoveryRequest":{"type":"object","properties":{"origins":{"type":"array","items":{"type":"string"},"minItems":1,"description":"IATA city or airport codes. See `origin_type` for how an untyped code is read.","example":["NYC"]},"destinations":{"type":"array","items":{"type":"string"},"description":"IATA city or airport codes. Omit for global discovery (`find_destination`) or to scan every destination from the origins. See `destination_type` for how an untyped code is read.","example":["PAR"]},"origin_type":{"type":"string","enum":["city","airport"],"description":"How to read the code: `city` searches every airport of the city, `airport` only that one airport. Omit it and the platform applies one rule: a code that names a multi-airport city is searched as the city; every other code is searched as the airport. Untyped NYC, LON or PAR is the city; untyped JFK, LHR or CDG is the airport; and untyped LAX, MIA or LAS — codes a city shares with its main airport — is the CITY, so results can land at any airport of that city. If the user named the airport, send `airport`; if they named the city, omit the field.","example":"city"},"destination_type":{"type":"string","enum":["city","airport"],"description":"How to read the code: `city` searches every airport of the city, `airport` only that one airport. Omit it and the platform applies one rule: a code that names a multi-airport city is searched as the city; every other code is searched as the airport. Untyped NYC, LON or PAR is the city; untyped JFK, LHR or CDG is the airport; and untyped LAX, MIA or LAS — codes a city shares with its main airport — is the CITY, so results can land at any airport of that city. If the user named the airport, send `airport`; if they named the city, omit the field.","example":"city"},"departure_dates":{"type":"array","items":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"example":["2026-07-15"]},"departure_date_ranges":{"type":"array","items":{"$ref":"#/components/schemas/DateRange"}},"return_dates":{"type":"array","items":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"example":["2026-07-22"]},"return_date_ranges":{"type":"array","items":{"$ref":"#/components/schemas/DateRange"}},"stay_days":{"type":"integer","minimum":0,"exclusiveMinimum":true,"example":7},"stay_days_range":{"type":"object","properties":{"min":{"type":"integer"},"max":{"type":"integer"}},"required":["min","max"]},"trip_type":{"type":"string","enum":["oneway","roundtrip"],"example":"roundtrip"},"cabin_class":{"type":"string","enum":["economy","premium_economy","business","first"],"example":"economy"},"direct_only":{"type":"boolean","description":"Only direct flights. Cached fares record whether a fare is direct but not how many stops it makes, so this is the only stop filter here; POST /v1/flight_search takes `max_stops`.","example":false},"accuracy":{"type":"string","enum":["standard","high"],"description":"How strict the freshness check on cached fares is. `standard` (default) returns fares estimated to be at least 80% likely to still be available at the cached price; `high` raises that to 95%, so fewer fares come back.","example":"standard"},"max_total":{"type":"number","minimum":0,"exclusiveMinimum":true,"description":"Caps the cached fare's total. Cached fares are priced for one adult.","example":800},"currency":{"type":"string","example":"USD"},"locale":{"type":"string","example":"en-US"},"limit":{"type":"integer","minimum":1,"maximum":100,"default":20,"description":"What `limit` counts depends on the route; 1 to 100, default 20. On `flight_calendar`, the maximum number of itineraries returned across the entire matching date range after `sort_by` is applied. This is a global result cap, not one itinerary per date pair: with the default `sort_by: lowest`, `limit: N` returns the cheapest N itineraries overall, so some matching date pairs may be absent. On `find_dates`, the maximum number of date pairs, each answered with its cheapest itinerary and spread across the requested departure window. On `lowest_fare`, not applied: that route returns at most ten itineraries, cheapest first."},"offset":{"type":"integer","minimum":0,"default":0},"sort_by":{"type":"string","enum":["lowest","recommendation"],"default":"lowest"},"intent":{"$ref":"#/components/schemas/IntentInput"}},"required":["origins","trip_type"],"example":{"origins":["NYC"],"destinations":["PAR"],"trip_type":"roundtrip","departure_date_ranges":[{"start":"2026-09-01","end":"2026-09-30"}],"stay_days":7,"cabin_class":"economy","currency":"USD"}},"Money":{"type":"object","properties":{"amount":{"type":"number","description":"Deprecated: use `value` instead (the same integer) where this object carries decimal_places. Where it does not, the figure is in MAJOR units and the field holding this object is itself deprecated: read its `*_money` sibling. The two-scale rule applies only to deprecated fields. Alternative to `value` on some endpoints (the two never appear together); its scale depends on decimal_places. When this object carries decimal_places, amount is an INTEGER in minor units — divide by 10 ** decimal_places (e.g. select_ancillaries total_with_ancillaries). When there is no decimal_places field, amount is a decimal in MAJOR units, safe to display as-is (e.g. trip and checkout totals).","deprecated":true},"value":{"type":"integer","description":"Integer amount in MINOR units, always paired with decimal_places — divide by 10 ** decimal_places to display. Example: value 15977 with decimal_places 2 is 159.77 USD. Rendering this field directly shows prices 100x too high for 2-decimal currencies.","example":41250},"currency":{"type":"string","description":"ISO 4217 currency code.","example":"USD"},"decimal_places":{"type":"integer","description":"Scale of the integer value/amount: display = integer / 10 ** decimal_places. Always sent alongside minor-unit amounts. Usually the ISO 4217 digits of the currency (2 for USD/EUR/GBP, 0 for JPY/KRW, 3 for BHD/JOD/KWD) but currency-converted prices can carry a different scale — ALWAYS use the decimal_places sent with the amount, never a hardcoded 2. Only if the field is genuinely absent on a value-shaped object, fall back to the ISO digits for the currency.","example":2},"display":{"type":"string","description":"The same figure as a string ready to show: the ISO 4217 code, a space, then the amount, e.g. \"USD 159.77\". With decimal_places it is written at exactly that scale (value 1561500, JPY, decimal_places 2 is \"JPY 15615.00\"); without, at the ISO digits of the currency. Show this; compute with the number. Absent when the figure is not money (a cancellation step expressed as a percent or a number of nights) or could not be read unambiguously.","example":"USD 412.50"}},"description":"A money figure in one of two scales. New integrations read the `*_money` field beside it (a `MoneyValue`) where there is one, and `value` on objects that carry it; the rule below applies only to the deprecated forms. When `decimal_places` is present, `value` (or the deprecated `amount`) is an INTEGER in minor units: divide by 10 ** decimal_places. When it is absent, the deprecated `amount` is already in MAJOR units. Branch on whether decimal_places is present in the response you received, never on the currency code or the field name. `display` is the same figure as a string to show."},"MoneyWithoutAmount":{"type":"object","properties":{"value":{"type":"integer","description":"Integer amount in MINOR units, always paired with decimal_places — divide by 10 ** decimal_places to display. Example: value 15977 with decimal_places 2 is 159.77 USD. Rendering this field directly shows prices 100x too high for 2-decimal currencies.","example":41250},"currency":{"type":"string","description":"ISO 4217 currency code.","example":"USD"},"decimal_places":{"type":"integer","description":"Scale of the integer value/amount: display = integer / 10 ** decimal_places. Always sent alongside minor-unit amounts. Usually the ISO 4217 digits of the currency (2 for USD/EUR/GBP, 0 for JPY/KRW, 3 for BHD/JOD/KWD) but currency-converted prices can carry a different scale — ALWAYS use the decimal_places sent with the amount, never a hardcoded 2. Only if the field is genuinely absent on a value-shaped object, fall back to the ISO digits for the currency.","example":2},"display":{"type":"string","description":"The same figure as a string ready to show: the ISO 4217 code, a space, then the amount, e.g. \"USD 159.77\". With decimal_places it is written at exactly that scale (value 1561500, JPY, decimal_places 2 is \"JPY 15615.00\"); without, at the ISO digits of the currency. Show this; compute with the number. Absent when the figure is not money (a cancellation step expressed as a percent or a number of nights) or could not be read unambiguously.","example":"USD 412.50"}},"description":"A money figure: `value` is an INTEGER in minor units (divide by 10 ** `decimal_places` to get the amount), `currency` is its ISO 4217 code and `display` is the same figure as a string ready to show. The `Money` shape without its deprecated `amount` member, for a figure that never carried one."},"Fare":{"type":"object","properties":{"trip_item_token":{"type":"string","description":"The token that adds this fare to a trip: pass it to `POST /v1/trip` as `add_item.trip_item_token`. Opaque: the offer’s `offer_id`, a colon, then the provider’s reference for the fare. Usable until `expires_at`; after that, search again and use the fresh token.","example":"offer__3a9f1b27:0-1-0"},"searched_at":{"type":"string","format":"date-time","description":"When the supplier search for this fare began, in RFC 3339 format. Repeated searches can return the same trip_item_token; use this timestamp to assess freshness. Absent when the search time is unknown.","example":"2026-09-23T15:43:40Z"},"expires_at":{"type":"string","format":"date-time","description":"Latest known time to use this fare, in RFC 3339 format: the earlier of the supplier deadline, when supplied, and Jinko’s 75-minute token lifetime measured from searched_at. Without a supplier deadline this bounds token retention only. Availability and price can change sooner. Complete checkout promptly; after this time, search again and use the latest result. Absent means unknown, not unlimited validity. This is not a quote or payment deadline.","example":"2026-09-23T16:58:40Z"},"cabin_class":{"type":"string","description":"The cabin this fare sells: `economy`, `premium_economy`, `business` or `first`. For a fare over several flight segments it is the cabin of the main segment — the longest flight — since a short connecting segment can sit in another cabin.","example":"economy"},"brand_name":{"type":"string","description":"The fare brand as the airline names it, read from the first segment that carries a fare component; when the provider names no brand, the cabin name it sent instead. Absent when neither is known.","example":"Main Cabin"},"total_price":{"$ref":"#/components/schemas/Money"},"price_per_person":{"$ref":"#/components/schemas/MoneyWithoutAmount"},"included_baggage":{"allOf":[{"$ref":"#/components/schemas/CheckedBaggage"},{"description":"Checked-bag allowance of this fare, read from the first segment that states one (it is not reported per leg). Absent when no segment states a checked-bag allowance; a stated zero is sent as `pieces: 0`."}]},"carry_on_baggage":{"allOf":[{"$ref":"#/components/schemas/CheckedBaggage"},{"description":"Cabin-bag allowance of this fare, read from the first segment that states a carry-on bag or a personal item (it is not reported per leg). Absent when no segment states one."}]},"is_changeable":{"type":"boolean","description":"Whether the fare allows a voluntary change, folded from every change rule of the itinerary (the most restrictive wins). Not the same reading as the `changeable_only` filter: the filter checks the before-departure rules only, while this flag is `true` on a fare whose only decided rule allows a change after departure (`change_policy` then ends in `after departure`). `false` is an answer: the fare rules did not confirm a change is allowed.","example":true},"is_refundable":{"type":"boolean","description":"Whether the fare can be cancelled, with or without a fee, folded from every cancellation rule of the itinerary (the most restrictive wins). Not the same reading as the `refundable_only` filter: the filter checks the before-departure rules only, while this flag is `true` on a fare whose only decided rule allows cancellation after departure (`refund_policy` then ends in `after departure`). `false` is an answer: the fare rules did not confirm a refund is allowed.","example":false},"refund_policy":{"type":"string","description":"The refund terms in words, before booking: `Free cancellation`, `Refundable` (fee not stated), `Refundable with fee` or `Non-refundable`; when the only decided rule applies after departure, the same four with ` after departure` appended, e.g. `Refundable with fee after departure`. A cancellation rule with an unspecified penalty reads `Non-refundable`. Absent when the fare carries no cancellation rule. Read it beside `is_refundable`, which says only whether a refund is possible at all.","example":"Refundable with fee"},"change_policy":{"type":"string","description":"The change terms in words, before booking: `Free changes`, `Changes allowed` (fee not stated), `Changes allowed with fee` or `No changes allowed`; when the only decided rule applies after departure, the same four with ` after departure` appended, e.g. `Changes allowed with fee after departure`. Absent when the fare carries no change rule, or only one with an unspecified penalty. Read it beside `is_changeable`, which says only whether a change is possible at all.","example":"Changes allowed with fee"},"checked_bag_included":{"type":"boolean","description":"Whether the price already includes at least one free checked bag, read from `included_baggage`: `pieces` is 1 or more. `included_baggage` comes from the first segment that states an allowance, so this is not the same reading as the `checked_bag_included` filter, which needs a free bag on every segment. `weight` is the per-bag limit that applies when a bag IS included, so it carries no allowance on its own; prose in `included_baggage` does not count either.","example":true}}},"FlightOffer":{"type":"object","properties":{"offer_id":{"type":"string","description":"Identifier of this offer: `offer__` followed by 8 hex characters. Send it as `offer_token` to re-price the offer; it is also the prefix of each fare’s `trip_item_token`.","example":"offer__3a9f1b27"},"origin":{"type":"object","properties":{"code":{"type":"string","description":"City code of the requested origin: the code sent as `origin` when it was typed `city`, otherwise the city of that airport (`NYC` for JFK). Not the airport flown; read `outbound.origin` for that. An empty string when the platform cannot map the requested code to a city."},"name":{"type":"string"}},"example":{"code":"NYC","name":"New York"}},"destination":{"type":"object","properties":{"code":{"type":"string","description":"City code of the requested destination, resolved as `origin.code` is. Not the airport flown; read `outbound.destination` for that. An empty string when the platform cannot map the requested code to a city."},"name":{"type":"string"}},"example":{"code":"LAX","name":"Los Angeles"}},"is_round_trip":{"type":"boolean","example":false},"total_duration_minutes":{"type":"number","example":385},"outbound":{"$ref":"#/components/schemas/FlightLeg"},"inbound":{"$ref":"#/components/schemas/FlightLeg"},"additional_legs":{"type":"array","items":{"$ref":"#/components/schemas/FlightLeg"},"description":"Third and later legs of a multi-city itinerary, in travel order. Empty for one-way and round-trip offers.","example":[]},"fares":{"type":"array","items":{"$ref":"#/components/schemas/Fare"}}},"required":["offer_id","origin","destination","additional_legs","fares"]},"FlightSearchResponse":{"type":"object","properties":{"offers":{"type":"array","items":{"$ref":"#/components/schemas/FlightOffer"}},"exact_match_found":{"type":"boolean","description":"Price-check mode only (request carried `offer_token`): true when the live offer has the same flights as the cached one. Absent in search mode.","example":true},"result":{"type":"string","enum":["no_matches"],"description":"Search mode only. `no_matches` means the search completed and no offer matched: relay `instruction`, which asks which filter to relax only when the request sent a filter that narrows the results. `offers` is empty. Absent when offers were found. A search that could not be completed at all answers an error instead (`UPSTREAM_REJECTED`, `UPSTREAM_ERROR`, `UPSTREAM_UNAVAILABLE` or `UPSTREAM_TIMEOUT`), never an empty list."},"instruction":{"type":"string","description":"Present with `result: no_matches`: what to tell the user or do next. It attributes the empty result to the filters only when the request sent a filter that narrows the results; otherwise it reads \"Search completed successfully, but no flights were found for this route and dates. Ask the user whether to try other dates or airports before searching again.\"","example":"Search completed successfully, but no offers satisfied every filter sent: the filter combination is too restrictive for this route and dates. Ask the user which filter to relax before searching again."}},"required":["offers"]},"FlightSearchRequest":{"type":"object","properties":{"origin":{"type":"string","description":"3-letter IATA city or airport code. See `origin_type` for how an untyped code is read. `origin` and `destination` must differ. A 3-letter code the platform’s location catalog does not recognise is not refused: it is searched as an airport, and if no provider sells it the search answers `result: no_matches`.","example":"JFK"},"destination":{"type":"string","description":"3-letter IATA city or airport code. See `destination_type` for how an untyped code is read: LAX alone is the Los Angeles CITY; send `destination_type: \"airport\"` for the airport only. `origin` and `destination` must differ. A 3-letter code the platform’s location catalog does not recognise is not refused: it is searched as an airport, and if no provider sells it the search answers `result: no_matches`.","example":"LAX"},"origin_type":{"type":"string","enum":["city","airport"],"description":"How to read the code: `city` searches every airport of the city, `airport` only that one airport. Omit it and the platform applies one rule: a code that names a multi-airport city is searched as the city; every other code is searched as the airport. Untyped NYC, LON or PAR is the city; untyped JFK, LHR or CDG is the airport; and untyped LAX, MIA or LAS — codes a city shares with its main airport — is the CITY, so results can land at any airport of that city. If the user named the airport, send `airport`; if they named the city, omit the field. The provider decides which airports a city holds, so a city search can return airports the catalog does not list under that code: searched as a city, LAX returns Ontario (ONT) fares, often as the cheapest offers. Each offer’s legs carry the airports flown; the offer-level `origin` / `destination` carry the city.","example":"airport"},"destination_type":{"type":"string","enum":["city","airport"],"description":"How to read the code: `city` searches every airport of the city, `airport` only that one airport. Omit it and the platform applies one rule: a code that names a multi-airport city is searched as the city; every other code is searched as the airport. Untyped NYC, LON or PAR is the city; untyped JFK, LHR or CDG is the airport; and untyped LAX, MIA or LAS — codes a city shares with its main airport — is the CITY, so results can land at any airport of that city. If the user named the airport, send `airport`; if they named the city, omit the field. The provider decides which airports a city holds, so a city search can return airports the catalog does not list under that code: searched as a city, LAX returns Ontario (ONT) fares, often as the cheapest offers. Each offer’s legs carry the airports flown; the offer-level `origin` / `destination` carry the city.","example":"airport"},"departure_date":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Departure date, `YYYY-MM-DD`: the local date at `origin`. Required in search mode, with `origin` and `destination`; not allowed beside `offer_token`. A date that does not exist on the calendar (`2026-02-30`) is refused with 422 naming the field.","example":"2026-09-01"},"return_date":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Return date, `YYYY-MM-DD`, on or after `departure_date` (a same-day return is valid). Sending it makes the search a round trip; see `trip_type`."},"trip_type":{"type":"string","enum":["oneway","roundtrip"],"description":"`oneway` or `roundtrip`. Omit it and `return_date` decides: present is a round trip, absent a one-way trip. `roundtrip` requires `return_date`, and `oneway` refuses it (422 naming `return_date`)."},"cabin_class":{"type":"string","nullable":true,"enum":["economy","premium_economy","business","first"],"description":"Omit this field or send `null` to let the provider default the search to economy without applying a cabin filter. Send an explicit enum value to require each fare’s representative cabin to equal it. The representative cabin is the cabin on the longest flight segment; shorter connecting segments may use another cabin. An omitted or `null` cabin applies no cabin filter.","example":"economy"},"direct_only":{"type":"boolean","description":"DEPRECATED — send `max_stops: 0` instead. Still accepted: `direct_only` is folded into `max_stops` (`true` becomes `max_stops: 0`) before the search runs, so only `max_stops` reaches the platform. Setting it beside a non-zero `max_stops` is rejected.","deprecated":true,"example":false},"max_stops":{"type":"integer","minimum":0,"maximum":2,"description":"Maximum stops per leg: `0` non-stop (the way to ask for direct flights), `1` one connection, `2` two. Applies to every leg of the trip.","example":1},"multi_fare":{"type":"boolean","description":"Branded fare ladder: several fares per itinerary (upsell options). Defaults to `true` when omitted; send `false` for a single fare per itinerary and a smaller response. An offer carries at most 10 fares, the cheapest fare of each cabin kept first; a provider that sells no fare ladder returns a single fare either way.","example":true},"max_total":{"type":"number","minimum":0,"exclusiveMinimum":true,"description":"Drop fares whose total price (all passengers) exceeds this cap, in `currency`, major units. A fare priced in another currency is usually converted to `currency` with the platform’s FX rate before the comparison, but not always: a provider whose first priced offer is already in `currency` drops its offers in other currencies as unverifiable instead of converting them. A fare whose currency cannot be converted is not returned.","example":800},"include_carriers":{"type":"array","items":{"type":"string","pattern":"^[A-Za-z0-9]{2}$"},"description":"Keep only itineraries marketed by these IATA carriers. Matching means every segment is marketed by a listed carrier (codeshares count by marketing carrier). When nothing matches, the search returns no offers with `result: no_matches`; the platform never widens to other carriers.","example":["AF","DL"]},"exclude_carriers":{"type":"array","items":{"type":"string","pattern":"^[A-Za-z0-9]{2}$"},"description":"Drop itineraries marketed by these IATA carriers. Must not overlap `include_carriers`.","example":["NK"]},"departure_time_range":{"allOf":[{"$ref":"#/components/schemas/TimeRange"},{"description":"Filter the OUTBOUND leg by local departure time-of-day."}]},"arrival_time_range":{"allOf":[{"$ref":"#/components/schemas/TimeRange"},{"description":"Filter the OUTBOUND leg by local arrival time-of-day."}]},"return_departure_time_range":{"allOf":[{"$ref":"#/components/schemas/TimeRange"},{"description":"Filter the RETURN leg by local departure time-of-day (round-trip only)."}]},"return_arrival_time_range":{"allOf":[{"$ref":"#/components/schemas/TimeRange"},{"description":"Filter the RETURN leg by local arrival time-of-day (round-trip only)."}]},"connection_time_min_minutes":{"type":"integer","minimum":0,"description":"Shortest acceptable layover, in minutes, applied to every connection of every leg. Must not exceed `connection_time_max_minutes`. Conservative, like `refundable_only`: an itinerary with a connection whose times are unknown cannot be checked and is dropped.","example":60},"connection_time_max_minutes":{"type":"integer","minimum":0,"description":"Longest acceptable layover, in minutes, applied to every connection of every leg. Conservative in the same way as `connection_time_min_minutes`: a connection whose times are unknown drops the itinerary.","example":240},"max_total_duration_minutes":{"type":"integer","minimum":0,"exclusiveMinimum":true,"description":"Cap each leg’s door-to-door elapsed travel time, in minutes.","example":900},"refundable_only":{"type":"boolean","description":"Keep only fares that can be cancelled before departure (with or without a fee). Conservative: a fare whose rules the platform cannot verify is DROPPED, so carriers that publish no rule data disappear under this filter. The filter reads the before-departure cancellation rules only; `is_refundable` on a returned fare is a wider reading of the same rules (it is `true` on a fare whose only decided rule allows cancellation after departure), so the flag is not this filter.","example":true},"changeable_only":{"type":"boolean","description":"Keep only fares that allow a voluntary change. Conservative in the same way as `refundable_only`: unverifiable fares are dropped. The filter reads the before-departure change rules only; `is_changeable` on a returned fare is the wider reading `refundable_only` describes.","example":true},"checked_bag_included":{"type":"boolean","description":"Keep only fares whose price already includes a checked bag on EVERY segment. Conservative, like `refundable_only`: a segment that states no allowance makes the fare unverifiable and it is dropped. The response flag of the same name is read from the first segment that states an allowance, not from every segment, so without this filter a fare flagged `checked_bag_included: true` can still lack a free bag on a later segment.","example":true},"single_carrier_only":{"type":"boolean","description":"Keep only itineraries marketed end-to-end by one carrier.","example":true},"via_airports":{"type":"array","items":{"type":"string","pattern":"^[A-Za-z]{3}$"},"description":"Restrict connections to these airports: an itinerary qualifies only when EVERY connection is one of them. Non-stop itineraries have no connection to check and are kept. Some providers are asked for at most the first 9 listed airports per leg; the whole list is still applied to what they return.","example":["AMS"]},"exclude_via_airports":{"type":"array","items":{"type":"string","pattern":"^[A-Za-z]{3}$"},"description":"Ban connections at these airports. Must not overlap `via_airports`.","example":["LHR"]},"aircraft_types":{"type":"array","items":{"type":"string","pattern":"^[A-Za-z0-9]{3}$"},"description":"Keep only itineraries whose every segment flies one of these IATA equipment codes. Conservative, like `refundable_only`: a segment whose equipment is unknown makes the itinerary unverifiable and it is dropped, so a provider that reports no equipment (see `aircraft` on the segment) disappears under this filter entirely.","example":["320","77W"]},"origin_alternate_airports":{"type":"array","items":{"type":"string","pattern":"^[A-Za-z]{3}$"},"description":"ADDITIONAL departure airports searched alongside `origin`. Widening only — the anchor in `origin` is always searched, so this can add results but never remove any. THE ANCHOR DECIDES RANKING AND TRUNCATION, which makes the two airports NOT interchangeable: measured on one route, anchor `JFK` with alternate `EWR` returned 15 JFK / 35 EWR, while anchor `EWR` with alternate `JFK` returned 2 JFK / 48 EWR — 13 itineraries differed between the two. Put the airport that matters most in `origin`. Works with an airport or a city anchor: `origin` NYC with `[\"BOS\"]` searches New York's airports and Boston; an alternate inside the city is redundant and harmless. On a round trip the platform mirrors the list onto the return leg, so it also covers where the return lands. Some providers search only the anchor.","example":["EWR","LGA"]},"destination_alternate_airports":{"type":"array","items":{"type":"string","pattern":"^[A-Za-z]{3}$"},"description":"ADDITIONAL arrival airports searched alongside `destination`. Widening only — the anchor in `destination` is always searched, so this can add results but never remove any. THE ANCHOR DECIDES RANKING AND TRUNCATION, which makes the two airports NOT interchangeable the same way `origin_alternate_airports` describes: put the airport that matters most in `destination`. Works with an airport or a city anchor, as `origin_alternate_airports` describes. On a round trip the platform mirrors the list onto the return leg, so it also covers where the return departs from. Some providers search only the anchor.","example":["ORY"]},"nearby_airports":{"type":"boolean","description":"Also search the alternate airports around each leg’s origin and destination. Widening: more results, not fewer.","example":true},"same_connection_airport_only":{"type":"boolean","description":"Keep only itineraries whose connections leave from the same airport they arrived at (no cross-town transfer).","example":true},"same_origin_airport_only":{"type":"boolean","description":"Keep only round trips that return to the airport the trip departed from. Round trip only: on a one-way search it is accepted and has no effect.","example":true},"same_turnaround_airport_only":{"type":"boolean","description":"Keep only round trips whose return departs from the airport the outbound arrived at. Round trip only: on a one-way search it is accepted and has no effect.","example":true},"limit":{"type":"integer","minimum":1,"maximum":300,"description":"TOTAL number of flights to return (1–300), not a per-page size: `limit: 5` returns at most five flights. Search mode only. Omit to let the platform choose: the cheapest flight plus up to 100 alternatives, so up to 101 flights. Applied after the cross-provider merge and sort, so it only trims the returned set — the providers still bound the real count.","example":20},"adults":{"type":"integer","minimum":1,"default":1,"description":"Adult travelers (12+). At most 9 travellers who need a seat: `adults + children` ≤ 9. Lap infants are not counted."},"children":{"type":"integer","minimum":0,"description":"Child travelers (2–11). At most 9 travellers who need a seat: `adults + children` ≤ 9. Lap infants are not counted."},"infants":{"type":"integer","minimum":0,"description":"Infant travelers (under 2), traveling on an adult’s lap. Seated infants (own seat) are not supported."},"currency":{"type":"string","description":"ISO 4217 currency code, case-insensitive (`eur` is `EUR`): prices come back with the upper-case code and a `display`. Defaults to USD. A currency the platform cannot price in is refused with 400."},"locale":{"type":"string"},"offer_token":{"type":"string","description":"Price-check mode: re-price one known offer instead of searching a route. Route and filter fields are not allowed beside it (422); the passenger counts and `currency` are. Two kinds of token behave differently. A cached-itinerary `offer_token` from `flight_calendar`, `find_dates`, `lowest_fare`, `find_destination` or a price monitor is re-shopped LIVE on its route and dates with no cabin, stop or carrier constraint: `offers[0]` is the cheapest live offer with the same flights and cabin when one exists, `exact_match_found` says whether it did, and up to 100 alternatives follow. An `offer_id` from an earlier `flight_search` is answered from the cache of that search without re-pricing (`exact_match_found: true`), unless the passenger counts or `currency` differ, in which case it is re-shopped; once every fare of the cached offer has passed its `expires_at` it answers `410 OFFER_EXPIRED`: search again.","example":"offer__3a9f1b27"},"intent":{"$ref":"#/components/schemas/IntentInput"}},"example":{"origin":"JFK","destination":"CDG","departure_date":"2026-09-01","return_date":"2026-09-08","trip_type":"roundtrip","cabin_class":"economy","adults":1,"max_stops":1,"checked_bag_included":true,"currency":"USD"}},"MonitoredCarrier":{"type":"object","properties":{"code":{"type":"string","example":"DL"},"name":{"type":"string","example":"Delta Air Lines"},"flight_number":{"type":"string","description":"Provider flight number, forwarded unchanged. Depending on the upstream provider it may be a bare number such as `460` or a composed designator such as `AS460`; the carrier code is also carried separately when known.","example":"263"}}},"MonitoredFlight":{"type":"object","properties":{"offer_token":{"type":"string","example":"oft_7c2e9a1b4d"},"total_price":{"allOf":[{"$ref":"#/components/schemas/MonitoredMoney"},{"description":"One adult's fare (`priced_for: \"one_adult\"`): the cache holds one-adult fares only. To price the whole party, pass `offer_token` to `POST /v1/flight_search` with the party."}]},"outbound_carrier":{"$ref":"#/components/schemas/MonitoredCarrier"},"inbound_carrier":{"$ref":"#/components/schemas/MonitoredCarrier"},"outbound_stops":{"type":"number","description":"Number of stops on the outbound journey. Always sent, `0` for a non-stop.","example":0},"inbound_stops":{"type":"number","description":"Number of stops on the return journey. Omitted on a one-way monitor and, unlike `outbound_stops`, also when the return is non-stop. When a return leg is present (`inbound_departure_local` is sent), read an absent `inbound_stops` as `0`.","example":1},"outbound_duration_minutes":{"type":"number","example":470},"inbound_duration_minutes":{"type":"number","example":520},"outbound_departure_local":{"type":"string","description":"Local time at the airport, RFC 3339 WITH that airport’s UTC offset (e.g. `2026-07-15T10:15:00-04:00`), despite the `_local` suffix: the same kind of instant as the datetimes on a cached itinerary leg, not the zone-less `*_local` wall-clock fields of a booked or exchanged itinerary. Parse it as an instant; do not assume UTC and do not compare two of these as strings. On the rare airport whose timezone the platform does not know, the value keeps its local wall-clock digits but is written as UTC (`…Z`).","example":"2026-07-15T10:15:00-04:00"},"outbound_arrival_local":{"type":"string","description":"Local time at the airport, RFC 3339 WITH that airport’s UTC offset (e.g. `2026-07-15T10:15:00-04:00`), despite the `_local` suffix: the same kind of instant as the datetimes on a cached itinerary leg, not the zone-less `*_local` wall-clock fields of a booked or exchanged itinerary. Parse it as an instant; do not assume UTC and do not compare two of these as strings. On the rare airport whose timezone the platform does not know, the value keeps its local wall-clock digits but is written as UTC (`…Z`).","example":"2026-07-15T13:05:00-07:00"},"inbound_departure_local":{"type":"string","description":"Local time at the airport, RFC 3339 WITH that airport’s UTC offset (e.g. `2026-07-15T10:15:00-04:00`), despite the `_local` suffix: the same kind of instant as the datetimes on a cached itinerary leg, not the zone-less `*_local` wall-clock fields of a booked or exchanged itinerary. Parse it as an instant; do not assume UTC and do not compare two of these as strings. On the rare airport whose timezone the platform does not know, the value keeps its local wall-clock digits but is written as UTC (`…Z`).","example":"2026-07-22T17:40:00-07:00"},"inbound_arrival_local":{"type":"string","description":"Local time at the airport, RFC 3339 WITH that airport’s UTC offset (e.g. `2026-07-15T10:15:00-04:00`), despite the `_local` suffix: the same kind of instant as the datetimes on a cached itinerary leg, not the zone-less `*_local` wall-clock fields of a booked or exchanged itinerary. Parse it as an instant; do not assume UTC and do not compare two of these as strings. On the rare airport whose timezone the platform does not know, the value keeps its local wall-clock digits but is written as UTC (`…Z`).","example":"2026-07-23T07:25:00-04:00"},"cabin_class":{"type":"string","example":"economy"},"fare_brand":{"type":"string","example":"Main"}}},"PriceMonitoringResponse":{"type":"object","properties":{"priced_for":{"type":"string","enum":["one_adult"],"description":"Whose price the cached fares in this answer are: always `one_adult`. Every cached total is one adult's fare; this request takes no passenger counts. Price the whole party with `POST /v1/flight_search`.","example":"one_adult"},"status":{"type":"string","enum":["ok","stale"],"description":"`ok`: the cache held a matching itinerary and the cheapest is in `flight`. `stale`: no matching cached itinerary, and `flight` is absent; retry later, or price live with `POST /v1/flight_search`.","example":"ok"},"monitored_at":{"type":"string","example":"2026-06-01T12:34:56Z"},"flight":{"$ref":"#/components/schemas/MonitoredFlight"},"request_echo":{"nullable":true}},"required":["priced_for","status"]},"PriceMonitoringRequest":{"type":"object","properties":{"origin":{"type":"string","example":"PAR"},"destination":{"type":"string","example":"NYC"},"departure_date":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"return_date":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"origin_type":{"type":"string","enum":["city","airport"],"description":"How to read the code: `city` searches every airport of the city, `airport` only that one airport. Omit it and the platform applies one rule: a code that names a multi-airport city is searched as the city; every other code is searched as the airport. Untyped NYC, LON or PAR is the city; untyped JFK, LHR or CDG is the airport; and untyped LAX, MIA or LAS — codes a city shares with its main airport — is the CITY, so results can land at any airport of that city. If the user named the airport, send `airport`; if they named the city, omit the field."},"destination_type":{"type":"string","enum":["city","airport"],"description":"How to read the code: `city` searches every airport of the city, `airport` only that one airport. Omit it and the platform applies one rule: a code that names a multi-airport city is searched as the city; every other code is searched as the airport. Untyped NYC, LON or PAR is the city; untyped JFK, LHR or CDG is the airport; and untyped LAX, MIA or LAS — codes a city shares with its main airport — is the CITY, so results can land at any airport of that city. If the user named the airport, send `airport`; if they named the city, omit the field."},"cabin_class":{"type":"string","enum":["economy","premium_economy","business","first"]},"direct_only":{"type":"boolean","description":"Only direct flights. Cached fares record whether a fare is direct but not how many stops it makes, so this is the only stop filter here; POST /v1/flight_search takes `max_stops`."},"max_total":{"type":"number","minimum":0,"exclusiveMinimum":true,"description":"Caps the cached fare’s total. Cached fares are priced for one adult, so this is the cap on one adult’s fare."},"include_carriers":{"type":"array","items":{"type":"string"}},"exclude_carriers":{"type":"array","items":{"type":"string"}},"accuracy":{"type":"string","enum":["standard","high"],"description":"How strict the freshness check on cached fares is. `standard` (default) returns fares estimated to be at least 80% likely to still be available at the cached price; `high` raises that to 95%, so fewer fares come back."},"currency":{"type":"string"},"locale":{"type":"string"},"intent":{"$ref":"#/components/schemas/IntentInput"}},"required":["origin","destination","departure_date"]},"ScheduleCarrier":{"type":"object","properties":{"code":{"type":"string","example":"AF"},"name":{"type":"string","example":"Air France"}}},"ScheduleEndpoint":{"type":"object","properties":{"airport":{"type":"string","example":"BOS"},"city_code":{"type":"string","example":"BOS"},"city_name":{"type":"string","example":"Boston"},"time_local":{"type":"string","description":"Local date and time at the airport, `YYYY-MM-DDTHH:MM`, with NO UTC offset. It is not an instant: it cannot be compared across timezones, and parsing it as UTC is wrong by the airport’s offset.","example":"2026-10-11T18:25"}},"required":["airport","time_local"]},"ScheduleSegment":{"type":"object","properties":{"marketing_carrier":{"type":"string","example":"AF"},"marketing_carrier_info":{"$ref":"#/components/schemas/ScheduleCarrier"},"operating_carrier":{"type":"string","example":"DL"},"operating_carrier_info":{"$ref":"#/components/schemas/ScheduleCarrier"},"flight_number":{"type":"string","example":"333"},"cabin_class":{"type":"string","enum":["economy","premium_economy","business","first"],"description":"This segment’s cabin in this trip."},"depart":{"$ref":"#/components/schemas/ScheduleEndpoint"},"arrive":{"$ref":"#/components/schemas/ScheduleEndpoint"}},"required":["marketing_carrier","flight_number","depart","arrive"]},"ScheduleSlice":{"type":"object","properties":{"origin":{"type":"string","example":"BOS"},"destination":{"type":"string","example":"NCE"},"stops":{"type":"integer","description":"Segment count minus one.","example":1},"segments":{"type":"array","items":{"$ref":"#/components/schemas/ScheduleSegment"}}},"required":["origin","destination","stops","segments"]},"ScheduleTrip":{"type":"object","properties":{"trip_type":{"type":"string","enum":["oneway","roundtrip"],"example":"roundtrip"},"cabin_class":{"type":"string","enum":["economy","premium_economy","business","first"],"description":"The highest cabin over all segments of the trip."},"validating_carrier":{"type":"string","example":"AF"},"last_seen":{"type":"string","description":"When a fare search last observed this trip (UTC). It is NOT a statement that the trip can be booked today.","example":"2026-09-30T04:12:09Z"},"slices":{"type":"array","items":{"$ref":"#/components/schemas/ScheduleSlice"},"description":"One slice for a one-way trip; outbound then inbound for a round trip."}},"required":["trip_type","validating_carrier","last_seen","slices"]},"FlightScheduleResponse":{"type":"object","properties":{"trips":{"type":"array","items":{"$ref":"#/components/schemas/ScheduleTrip"},"description":"The page of observed trips. Empty when nothing matches."},"total":{"type":"integer","description":"Trips matching the whole request across all pages. A lower bound when `truncated` is true.","example":116},"truncated":{"type":"boolean","description":"True when an airport pair held more trips than one catalog read covers. Narrow by `cabin_class`, `max_stops` or a single airport to see the rest."},"pagination":{"type":"object","properties":{"next_page_token":{"type":"string","description":"Send back as `page_token` for the next page; empty on the last page. A page may hold fewer trips than `limit` — only an empty token ends the result."}},"required":["next_page_token"]}},"required":["trips","total","truncated","pagination"]},"FlightScheduleRequest":{"type":"object","properties":{"origin":{"type":"string","pattern":"^[A-Za-z]{3}$","example":"BOS"},"destination":{"type":"string","pattern":"^[A-Za-z]{3}$","example":"NCE"},"origin_type":{"type":"string","enum":["city","airport"],"description":"How to read the code: `city` searches every airport of the city, `airport` only that one airport. Omit it and the platform applies one rule: a code that names a multi-airport city is searched as the city; every other code is searched as the airport. Untyped NYC, LON or PAR is the city; untyped JFK, LHR or CDG is the airport; and untyped LAX, MIA or LAS — codes a city shares with its main airport — is the CITY, so results can land at any airport of that city. If the user named the airport, send `airport`; if they named the city, omit the field."},"destination_type":{"type":"string","enum":["city","airport"],"description":"How to read the code: `city` searches every airport of the city, `airport` only that one airport. Omit it and the platform applies one rule: a code that names a multi-airport city is searched as the city; every other code is searched as the airport. Untyped NYC, LON or PAR is the city; untyped JFK, LHR or CDG is the airport; and untyped LAX, MIA or LAS — codes a city shares with its main airport — is the CITY, so results can land at any airport of that city. If the user named the airport, send `airport`; if they named the city, omit the field."},"trip_type":{"type":"string","enum":["oneway","roundtrip"],"description":"`oneway` returns trips observed as one-way offers and takes no `return_date`. `roundtrip` returns observed outbound + inbound pairings and requires `return_date`.","example":"roundtrip"},"departure_date":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Local departure day of the outbound journey.","example":"2026-10-11"},"return_date":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Local departure day of the inbound journey. Required when `trip_type` is `roundtrip`; not accepted when it is `oneway`.","example":"2026-10-18"},"cabin_class":{"type":"string","enum":["economy","premium_economy","business","first"],"description":"Filters on the trip’s cabin (the highest over its segments). Omit for all cabins — unlike flight search there is no economy default."},"max_stops":{"type":"integer","minimum":0,"maximum":3,"description":"Most stops allowed in EACH direction. Omit for no bound.","example":1},"limit":{"type":"integer","minimum":1,"maximum":200,"description":"Page size. Defaults to 50.","example":50},"page_token":{"type":"string","minLength":1,"description":"`pagination.next_page_token` of the previous page. Send it with the same request fields."},"intent":{"$ref":"#/components/schemas/IntentInput"}},"required":["origin","destination","trip_type","departure_date"],"example":{"origin":"BOS","destination":"NCE","trip_type":"roundtrip","departure_date":"2026-10-11","return_date":"2026-10-18","max_stops":1}},"TripResponseItem":{"type":"object","properties":{"item_id":{"type":"string","example":"itm_4b91c2"},"kind":{"type":"string","description":"What the item is: \"flight\", \"hotel\", \"car\" or \"ground\". Same field name as on get_trip and checkout.","example":"flight"},"type":{"type":"string","description":"Deprecated: use `kind` (same value).","deprecated":true,"example":"flight"},"description":{"type":"string","description":"A summary of the selected item."},"offer_id":{"type":"string","description":"Deprecated: use `trip_item_token` instead. The offer handle this item was created from, exactly as it was submitted to `trip(add_item)`. On a hotel item it is the `htl_…` rate token; on a flight item it is the itinerary id. Two caveats when reconciling: on a hotel whose occupancy the provider re-shopped, this stays the token you SUBMITTED even though the priced rate moved; and a flight added twice on the same itinerary with different fares is de-duplicated by itinerary, so the echo can name the first fare added.","example":"htl_2d67a7441c60e5de","deprecated":true},"trip_item_token":{"type":"string","description":"The handle `trip(add_item)` took, echoed verbatim. On a hotel or car item it is the offer’s `trip_item_token` from the search. On a flight item it is the itinerary id and the fare joined by a colon, absent when the fare half was never resolved (a caller-supplied snapshot skips the resolver); read that absence as \"the platform cannot name the fare\", never as a different fare.","example":"of_9c1d2e:FARE_ABC"},"hotel_id":{"type":"string","description":"The property this hotel item books — the same id `hotel_search` publishes, so an item joins straight back to the search result it came from. Hotel items only.","example":"lp1f2c3"},"special_requests":{"type":"array","items":{"type":"string"},"description":"The special requests submitted with `trip(add_item)` for this hotel item, as stored — trimmed, blank entries dropped — and forwarded to the hotel supplier at booking. Requests only — the hotel may not honour them, and a confirmed booking does not imply it will. Hotel items only; absent when none were given (JIN-2499)."},"booking_scope":{"type":"string","description":"`room_bundle` means the submitted hotel offer token covers every room in `rooms`. Absent on every other hotel item, a single room included: read `rooms` for the rooms an item covers, and `booking_scope` only to learn whether one token books them all.","example":"room_bundle"},"num_rooms":{"type":"integer","minimum":1,"description":"Number of rooms this hotel item covers: 1 for a single room, the bundle size for a room bundle. Hotel items only; absent when the platform cannot state the party the item was priced for.","example":1},"rooms":{"type":"array","items":{"$ref":"#/components/schemas/HotelBundleRoom"},"description":"One entry per room this hotel item covers, single room or bundle (JIN-2565). Each entry states in `occupancy` the party that room was priced for — adults and children’s ages — so a checkout page can show the guest who the rate is for before payment, and a wrong child age, which changes the price and the room, is caught before the money moves. On a single room the entry’s room, board, refundability and tax lines are the item’s own, the same figures `hotel_terms` states; on a room bundle each entry carries that room’s details from the supplier. The item price remains the authoritative customer total for every room together. Absent when the platform holds no party for the item."},"price":{"type":"number","description":"Deprecated: use `price_money` instead. Item total as a number in major currency units, with currency in the separate `currency` field. Absent until pricing is available; absence does not mean free.","example":545.55,"deprecated":true},"currency":{"type":"string","description":"Deprecated: use `price_money` instead. ISO currency code for the item price. Absent until pricing is available.","example":"EUR","deprecated":true},"price_display":{"type":"string","description":"Deprecated: use `price_money` instead. The item price as a string ready to show, when X-Money-Display: iso is requested.","example":"EUR 545.55","deprecated":true},"price_money":{"allOf":[{"$ref":"#/components/schemas/MoneyValue"},{"description":"Item total. Absent until pricing is available; absence does not mean free."}]}}},"TripIdentityDocument":{"type":"object","properties":{"status":{"type":"string","description":"`absent`: none of the document's fields were sent. `saved`: stored, and sent to the airline with the flight booking (this does not mean the airline accepted it). `invalid`: received, but it cannot be sent as is, so the booking goes ahead without it; `invalid_fields` names what to correct. More values may be added: treat one you do not know as not usable.","example":"saved"},"invalid_fields":{"type":"array","items":{"type":"string"},"description":"The missing or failing fields, by their `upsert_travelers` names. Sent only when `status` is `invalid`.","example":["passport_expiry"]}},"required":["status"]},"TripTraveler":{"type":"object","properties":{"traveler_ref":{"type":"string","description":"The reference to send as `traveler_ref` when selecting a per-traveler ancillary: `pax_<N>`, the traveler's 1-based position in `travelers`.","example":"pax_1"},"pax_ref_id":{"type":"string","description":"Deprecated: use `traveler_ref` instead. Same value.","example":"pax_1","deprecated":true},"first_name":{"type":"string","example":"John"},"last_name":{"type":"string","example":"Doe"},"date_of_birth":{"type":"string","example":"1990-01-15"},"gender":{"type":"string","example":"MALE"},"traveler_type":{"type":"string","enum":["adult","child","infant"],"example":"adult"},"passenger_type":{"type":"string","description":"Deprecated: use `traveler_type` instead. The same type in uppercase.","example":"ADULT","deprecated":true},"identity_documents":{"type":"object","properties":{"passport":{"$ref":"#/components/schemas/TripIdentityDocument"},"known_traveler_number":{"$ref":"#/components/schemas/TripIdentityDocument"},"redress_number":{"$ref":"#/components/schemas/TripIdentityDocument"}},"required":["passport","known_traveler_number","redress_number"],"description":"Whether each travel document sent on `upsert_travelers` was saved and will be sent with a flight booking. The passport, Known Traveler and redress numbers themselves are not returned. `upsert_travelers` replaces the whole traveler list, so a document left out of a later upsert reads `absent`."}},"description":"A traveler stored on the trip. A `frequent_flyer` sent on write is echoed back alongside these when present; travel documents are reported in `identity_documents`, without their values."},"HotelRoomPrimaryGuest":{"type":"object","properties":{"occupancy_number":{"type":"integer","minimum":1,"description":"Stable one-based room slot within the selected room bundle.","example":1},"first_name":{"type":"string"},"last_name":{"type":"string"},"email":{"type":"string","format":"email"}},"required":["occupancy_number","first_name","last_name","email"],"additionalProperties":false},"HotelRoomGuests":{"type":"object","additionalProperties":{"type":"array","items":{"$ref":"#/components/schemas/HotelRoomPrimaryGuest"},"minItems":1},"description":"Optional primary adult room-contact overrides keyed by the returned hotel item_id/product_id. Each product array is non-empty and occupancy_number values are unique positive room slots; omitted slots use persisted adult travelers in order with the trip contact email. The trip must already exist because the keys refer to its items."},"TripResponse":{"type":"object","properties":{"trip_id":{"type":"string","example":"trip_1048576"},"status":{"type":"string","enum":["draft","quoting","quoted","fulfillment_prepared","fulfilling","fulfilled","partially_fulfilled","failed","cancelled","expired"],"description":"Where the trip is in its lifecycle, as one value for the whole trip. The booking’s progress after payment is `booking_state` (GET /v1/trip/{trip_id}); the four booking values below (`fulfilling`, `fulfilled`, `partially_fulfilled`, `failed`) are deprecated in favour of it and still sent. `draft`: being built or priced, nothing paid. `fulfilling`: payment was submitted and booking started. Then exactly one outcome: `fulfilled` (`booking_state` `completed`), `partially_fulfilled` (`partial`) or `failed` (`failed`). `cancelled`: replaced by a new trip when it was re-shopped. `expired` is the 24-hour lapse: it is evaluated LAZILY, on the read that first observes it, so this call is where a dead trip becomes visible. Once a trip is expired, adding items, setting travelers and checkout are refused with `TRIP_EXPIRED`; any other state that refuses a write answers `TRIP_STATE_CONFLICT` and names the state here. Start a new trip — a lapsed one cannot be revived. `quoting`, `quoted` and `fulfillment_prepared` are reserved and not currently set; quote progress is in `quote.status`.","example":"draft"},"actions_performed":{"type":"array","items":{"type":"string"},"example":["created","add_item"]},"items":{"type":"array","items":{"$ref":"#/components/schemas/TripResponseItem"}},"travelers":{"type":"array","items":{"$ref":"#/components/schemas/TripTraveler"}},"contact":{"type":"object","properties":{"email":{"type":"string"},"phone":{"type":"string"},"title":{"type":"string"}}},"hotel_room_guests":{"$ref":"#/components/schemas/HotelRoomGuests"},"totals":{"type":"object","properties":{"currency":{"type":"string","description":"Deprecated: use `total_money` instead. ISO currency code of `total`.","deprecated":true},"total":{"type":"number","description":"Deprecated: use `total_money` instead. The trip total in major currency units. Absent on a mixed-currency trip.","deprecated":true},"total_display":{"type":"string","description":"Deprecated: use `total_money` instead. The trip total as a string ready to show, e.g. \"USD 318.40\". Absent with `total`.","example":"USD 318.40","deprecated":true},"total_money":{"allOf":[{"$ref":"#/components/schemas/MoneyValue"},{"description":"The trip total. Absent when the trip holds items priced in more than one currency, and while nothing is priced."}]}},"example":{"currency":"USD","total":318.4}}},"required":["trip_id"]},"TripStateResponse":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["TRIP_EXPIRED","TRIP_STATE_CONFLICT"]},"message":{"type":"string"},"doc_url":{"type":"string"}},"required":["code","message"]},"status":{"type":"string","enum":["draft","quoting","quoted","fulfillment_prepared","fulfilling","fulfilled","partially_fulfilled","failed","cancelled","expired"],"description":"The trip state that refused the write. `expired` on TRIP_EXPIRED; on TRIP_STATE_CONFLICT it is whichever state the trip is actually in. Nothing was changed, so re-reading the trip returns this same state.","example":"expired"}},"required":["error"]},"FrequentFlyer":{"type":"object","properties":{"airline":{"type":"string","description":"IATA code of the loyalty program issuer (e.g. `LH` for Lufthansa Miles & More), not necessarily the operating carrier — alliances credit miles on partner flights.","example":"LH"},"number":{"type":"string","description":"Membership / account number for that loyalty program.","example":"992100100"}},"required":["airline","number"],"description":"Optional per-passenger frequent-flyer membership so airline miles are credited on the booking. Flights only; when present, both airline and number are required."},"Traveler":{"type":"object","properties":{"first_name":{"type":"string","description":"For flights, the airline must be able to print the name on the ticket: Latin letters (accents allowed), spaces, hyphens and apostrophes only. The last name holds at most 29 characters (the airline cannot ticket a longer surname), and first, middle and last name together at most 63. Two travellers in one trip cannot share the same full name. A name that breaks these rules is refused at upsert_travelers or checkout with 400 BAD_REQUEST and `error.field` naming the traveller."},"last_name":{"type":"string","description":"For flights, the airline must be able to print the name on the ticket: Latin letters (accents allowed), spaces, hyphens and apostrophes only. The last name holds at most 29 characters (the airline cannot ticket a longer surname), and first, middle and last name together at most 63. Two travellers in one trip cannot share the same full name. A name that breaks these rules is refused at upsert_travelers or checkout with 400 BAD_REQUEST and `error.field` naming the traveller."},"date_of_birth":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Date of birth (YYYY-MM-DD, nothing before or after it). Required for flights, and for a `child` or `infant` staying at a hotel; optional for an adult on a hotel-only trip. For flights it cannot be in the future and must agree with `traveler_type` on the trip's flight dates; otherwise upsert_travelers or checkout answers 400 BAD_REQUEST with `error.field` naming the traveller and field."},"gender":{"type":"string","enum":["MALE","FEMALE"],"description":"Required for flights; optional for hotel-only trips."},"traveler_type":{"type":"string","enum":["adult","child","infant"],"description":"For flights, the traveller's age on the date the booking ages travellers on decides the type — 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 an `infant` must still be under 2 on the trip's last flight. A type the `date_of_birth` contradicts is refused at upsert_travelers (once the trip holds the flight) or at checkout with 400 BAD_REQUEST and `error.field` naming the traveller. A flight searched for another party is re-priced for these travellers at checkout, on the same flights and fare family; when that fare is not sold for them, checkout answers 409 `OFFER_UNAVAILABLE`. Each hotel in the trip is booked with these travellers (a `room_bundle` hotel whose rooms `hotel_room_guests` names is booked for those room guests instead), so they cannot name more adults or more children than its rooms were priced for (the hotel_search `occupancies`: `adults` and `children_ages`), and each child's age at check-in must be one of the priced `children_ages`. The hotel counts a traveller by age at check-in, 18 or older being an adult, and a traveller with no `date_of_birth` as an adult. upsert_travelers or checkout answers 400 BAD_REQUEST naming the first traveller over a count or with an age the room was not priced for; search the hotel again for the party."},"nationality":{"type":"string"},"passport_number":{"type":"string"},"passport_expiry":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Passport expiry (YYYY-MM-DD, nothing before or after it). For flights it cannot be before the trip's last flight; otherwise upsert_travelers or checkout answers 400 BAD_REQUEST with `error.field` naming the traveller."},"passport_country":{"type":"string"},"frequent_flyer":{"$ref":"#/components/schemas/FrequentFlyer"},"known_traveler_number":{"type":"string","description":"US trusted-traveler Known Traveler Number (TSA PreCheck / Global Entry). Flights only; sent to the airline as Secure Flight data so PreCheck eligibility prints on the boarding pass.","example":"998765432"},"known_traveler_issuing_country":{"type":"string","description":"ISO 3166-1 alpha-2 country that issued the Known Traveler Number. Defaults to `US` downstream; ignored without known_traveler_number.","example":"US"},"redress_number":{"type":"string","description":"DHS redress control number (TRIP program). Flights only; sent to the airline as Secure Flight data.","example":"1234567"},"redress_issuing_country":{"type":"string","description":"ISO 3166-1 alpha-2 country that issued the redress number. Defaults to `US` downstream; ignored without redress_number.","example":"US"}},"required":["first_name","last_name","traveler_type"]},"Contact":{"type":"object","properties":{"email":{"type":"string","format":"email","description":"Contact email for booking confirmations. A trip with a flight needs it before payment: without a contact email, checkout answers 422 `MISSING_CUSTOMER_DETAILS` with `email` in `missing_fields`."},"phone":{"type":"string","description":"Contact phone in international format: a leading `+` and country code, e.g. `+12025550147`. Spaces, hyphens, dots and parentheses are accepted and removed. A number without the leading `+` is rejected with `INVALID_PHONE_NUMBER`. Required to complete a booking.","example":"+12025550147"},"title":{"type":"string","description":"Honorific of the booking contact (e.g. \"mr\", \"ms\", \"mx\"). Free text, resolved downstream per provider. Required to book a car rental; optional otherwise.","example":"mr"}},"required":["email","phone"],"description":"Booking contact (email + phone, optionally title). Optional while building the trip, but required before booking."},"TripRequest":{"type":"object","properties":{"trip_id":{"type":"string"},"add_item":{"type":"object","properties":{"trip_item_token":{"type":"string"},"special_requests":{"type":"array","items":{"type":"string","maxLength":500},"maxItems":10,"description":"HOTEL ITEMS ONLY. Free-text special requests for the property, one per entry — late arrival, quiet room, high floor, bedding preference, non-smoking room, baby cot, accessibility needs. They apply to the whole stay and are forwarded to the hotel supplier with the booking as REQUESTS ONLY: neither Jinko nor the supplier guarantees them, and a confirmed booking does not mean the hotel will honour them. At most 10 entries of 500 characters each and 1500 characters in total, counted after trimming; blank entries are dropped. Ignored on flight and ground items. Echoed back on the item as `special_requests`, trimmed and without the blank entries (JIN-2499).","example":["Expected arrival around 11:30 PM.","A quiet room on a high floor, if available."]}},"required":["trip_item_token"]},"remove_item":{"type":"object","properties":{"item_id":{"type":"string"}},"required":["item_id"]},"upsert_travelers":{"type":"object","properties":{"travelers":{"type":"array","items":{"$ref":"#/components/schemas/Traveler"},"description":"Travelers on the trip (replaces all). Required to complete a booking; the first traveler also supplies the booking contact name."},"contact":{"$ref":"#/components/schemas/Contact"}},"required":["travelers"]},"hotel_room_guests":{"$ref":"#/components/schemas/HotelRoomGuests"},"intent":{"$ref":"#/components/schemas/IntentInput"}},"example":{"trip_id":"trip_1048576","add_item":{"trip_item_token":"htl_2d67a7441c60e5de","special_requests":["Expected arrival around 11:30 PM.","A quiet room on a high floor, if available."]}}},"PaymentAttemptOutcome":{"type":"object","properties":{"payment_attempt_id":{"type":"string"},"trip_id":{"type":"string","description":"The trip this attempt pays, written with a `trip_` prefix (`trip_1048576`); the trip answers carry the same id without it (`1048576`). Strip the prefix to compare the two.","example":"trip_1048576"},"quote_id":{"type":"string","description":"The quote this payment is taken against.","example":"2097152"},"quoted_cart_id":{"type":"integer","description":"Deprecated: use `quote_id` instead. The same quote as an integer.","deprecated":true},"fulfillment_cart_id":{"type":"integer","description":"Deprecated: use `booking_ref` instead. An internal number for the booking this payment funds.","deprecated":true},"payment_status":{"type":"string","enum":["not_started","pending","requires_authentication","authorized","captured","declined","cancelling","cancelled","expired","failed"]},"booking_status":{"type":"string","description":"The detailed status of the booking this payment started, `not_scheduled` until booking begins. `booking_state` on GET /v1/trip/{trip_id} is its summary."},"failure_reason":{"type":"string","nullable":true,"description":"Why the booking failed, when it failed after payment and the cause is known: `offer_unavailable`, `price_changed`, `booking_not_created` (the supplier holds no booking; nothing was booked and no money was taken), `booking_cancelled_at_provider`, `ticketing_refused`. Null otherwise. For `offer_unavailable`, the supplier no longer sells the priced offer. Nothing was booked and the card authorisation was released. Search again and book a new offer. The `booking.failed` webhook carries the same reason with a sentence to show the traveller (`failure_message`).","example":"booking_not_created"},"items":{"type":"array","items":{"type":"object","properties":{"booked_item_id":{"type":"string","description":"The booked item, as get_booking and the servicing routes name it. Absent only when the item cannot be named yet.","example":"itm_aaaaaaaaaaaaafhuxsdxpbzrbfz5hm3a"},"item_id":{"type":"integer","description":"Deprecated: use `booked_item_id` instead. The booked item as an integer.","deprecated":true},"outcome":{"type":"string"},"failure_reason":{"type":"string","nullable":true,"description":"Why the booking failed, when it failed after payment and the cause is known: `offer_unavailable`, `price_changed`, `booking_not_created` (the supplier holds no booking; nothing was booked and no money was taken), `booking_cancelled_at_provider`, `ticketing_refused`. Null otherwise. For `offer_unavailable`, the supplier no longer sells the priced offer. Nothing was booked and the card authorisation was released. Search again and book a new offer. The `booking.failed` webhook carries the same reason with a sentence to show the traveller (`failure_message`).","example":"booking_not_created"}},"required":["item_id","outcome"]}},"booking_ref":{"type":"string","description":"The booking reference. Absent until the booking has one; never null."},"payment_error":{"type":"object","nullable":true,"properties":{"code":{"type":"string","enum":["CREDENTIAL_FORMAT_INVALID","PAYMENT_TYPE_NOT_ENABLED","PAYMENT_CREDENTIAL_INVALID","TRIP_OWNED_BY_OTHER_PAYMENT","IDEMPOTENCY_KEY_REUSED","ATTEMPT_IN_PROGRESS","CARD_DECLINED","CARD_ALIAS_EXPIRED","PAYMENT_CREDENTIAL_REJECTED","AUTHENTICATION_REQUIRED","AUTHENTICATION_FAILED","AUTHENTICATION_ABANDONED","QUOTE_EXPIRED","ATTEMPT_TERMINAL","PAYMENT_OUTCOME_UNKNOWN","FULFILLMENT_DISPATCH_DELAYED","RECONCILIATION_REQUIRED","TEMPORARILY_UNAVAILABLE"]},"message":{"type":"string"}},"required":["code","message"],"description":"Why the payment has not gone through, or null. On the `checkout_url` payment page, a card the issuer declines (`card_declined`) or whose authentication fails (`authentication_failed`) is reported here while `payment_status` stays `pending` and `recovery.action` is `poll`: the payer can use another card on the same page until the quote expires. It returns to null when a card is authorized. `CARD_ALIAS_EXPIRED`: The card alias expired before the booking was dispatched. Follow `recovery.action`; when it is `retry_new_key`: Submit the card again on the pay hostname with a new Idempotency-Key."},"recovery":{"type":"object","properties":{"action":{"type":"string","enum":["poll","authenticate","retry_same_key","retry_new_key","recheckout","contact_support","stop"],"description":"The next step. `authenticate`: when `authentication` is present, open `authentication.url` before `authentication.expires_at`. For a Shared Payment Token (`spt`), first read the token's `next_action.use_stripe_sdk.value` with your own Stripe secret key and append it unchanged: open `authentication.url + \"#hv=\" + <value>`, or call `stripe.handleNextAction({ hashedValue })` with Stripe.js on your own page. Then poll `GET /v1/trip/{trip_id}/payment_attempts/{payment_attempt_id}` until `payment_status` is none of `requires_authentication`, `cancelling` or `pending`."},"replacement_credential_required":{"type":"boolean"}},"required":["action","replacement_credential_required"]},"recovery_status":{"type":"string","enum":["automatic","manual_review"]},"quote_expires_at":{"type":"string","format":"date-time"},"retry_after_seconds":{"type":"integer","description":"Seconds to wait before reading the payment attempt again. Present only while `recovery.action` is `poll`; absent otherwise, never null."},"authentication":{"type":"object","properties":{"url":{"type":"string"},"expires_at":{"type":"string","format":"date-time"}},"required":["url","expires_at"],"description":"Where the customer authenticates when `recovery.action` is `authenticate`: open `url` before `expires_at`. For a Shared Payment Token (`spt`), `url` is a Jinko-hosted page that needs the token's hashed value: read the issued token's `next_action.use_stripe_sdk.value` with your own Stripe secret key and open `url + \"#hv=\" + <value>`, passing the value unchanged. The value travels in the URL fragment, which keeps it out of server logs."},"money":{"type":"object","properties":{"authorized":{"$ref":"#/components/schemas/Money"},"captured":{"$ref":"#/components/schemas/Money"},"released":{"$ref":"#/components/schemas/Money"},"refunded":{"$ref":"#/components/schemas/Money"},"externally_reimbursed":{"$ref":"#/components/schemas/Money"},"resolution_status":{"type":"string","enum":["pending","resolved","manual_review","external_reimbursement_pending","externally_reimbursed"]},"refund_reference":{"type":"string"}}}},"required":["payment_attempt_id","trip_id","quote_id","quoted_cart_id","payment_status","booking_status","payment_error","recovery","recovery_status"]},"AncillarySeat":{"type":"object","properties":{"seat_number":{"type":"string","example":"14A"},"available":{"type":"boolean"},"chargeable":{"type":"boolean","description":"True when the seat has a price."},"price":{"allOf":[{"$ref":"#/components/schemas/Money"},{"description":"Deprecated: use `price_money` instead. The seat price in major units in the trip currency.","deprecated":true}]},"price_money":{"allOf":[{"$ref":"#/components/schemas/MoneyValue"},{"description":"The exact seat price in the trip currency. Absent for a free seat."}]},"characteristics":{"type":"array","items":{"type":"string"},"example":["WINDOW"]}},"description":"A numbered seat. Read `price_money` for its exact price in the trip currency. Both price fields are absent for a free seat."},"AncillarySeatMap":{"type":"object","properties":{"segment_ref_id":{"type":"string","example":"seg_1"},"flight_number":{"type":"string","example":"W62202"},"seats":{"type":"array","items":{"$ref":"#/components/schemas/AncillarySeat"}}},"description":"The numbered seats for the segment covered by a seat offer. Select a seat with `available` true; its `price_money`, when present, is exact money in the trip currency."},"AncillaryOffer":{"type":"object","properties":{"offer_id":{"type":"string","description":"Opaque identifier of this ancillary offer. Pass it back exactly as received in `selections[].offer_id`; do not parse or build it.","example":"anc_3f9a1c27e4b8d051"},"type":{"type":"string","description":"Ancillary family — \"bag\", \"seat\", \"meal\", \"assistance\", … An open set. Offers with type \"seat\" carry `seat_map` and no `price_per_unit`: the price is per seat, in `seat_map.seats[].price_money`.","example":"bag"},"label":{"type":"string","description":"The provider label verbatim, kept as the source of truth. Some labels arrive with HTML entities and \"&\" component-joiners — show `display_label` instead.","example":"bag&1 x large 55 x 40 x 20 cm&Includes Speedy Boarding"},"display_label":{"type":"string","description":"Human-readable rendering of `label`: HTML entities decoded, \"&\" joiners normalised to \", \". This is the one to display.","example":"bag, 1 x large 55 x 40 x 20 cm, Includes Speedy Boarding"},"description":{"type":"string"},"price_per_unit":{"allOf":[{"$ref":"#/components/schemas/Money"},{"description":"Deprecated: use `price_per_unit_money` instead. The unit price in MAJOR currency units.","deprecated":true}]},"price_per_unit_money":{"allOf":[{"$ref":"#/components/schemas/MoneyValue"},{"description":"The price of one unit of this ancillary. Absent when the offer carries no price, and on seat offers, which price each seat in the seat map."}]},"scope":{"type":"string","description":"Seat offers only: one seat per passenger per segment.","example":"PER_PAX_PER_SEGMENT"},"segment_ref_ids":{"type":"array","items":{"type":"string"},"description":"The segment(s) a seat offer covers, one per offer. Pass the one entry back as the selection's `segment_ref_ids`.","example":["seg_1"]},"seat_map":{"$ref":"#/components/schemas/AncillarySeatMap"},"seat_map_status":{"type":"string","enum":["pending","ready"],"description":"Seat offers only: pending means the seat map is still loading and seat_map.seats is empty; read again. Ready means seats are listed."},"per_pax":{"type":"boolean","description":"True when the price is charged per passenger rather than once per booking.","example":true},"max_quantity":{"type":"integer","example":2},"paid_at":{"type":"string","enum":["now","local"],"description":"Car rate extras only. `now`: charged by Jinko at checkout and inside the item price once selected. `local`: collected by the rental desk on pick-up — selectable, but never part of the charge. Absent for flight ancillaries, which are always charged by Jinko.","example":"now"}},"required":["offer_id"]},"SelectedAncillary":{"type":"object","properties":{"offer_id":{"type":"string","description":"Opaque identifier of this ancillary offer. Pass it back exactly as received in `selections[].offer_id`; do not parse or build it.","example":"anc_3f9a1c27e4b8d051"},"traveler_ref":{"type":"string","description":"The traveler the selection applies to; absent on a booking-level ancillary.","example":"pax_1"},"pax_ref_id":{"type":"string","description":"Deprecated: use `traveler_ref` instead. Same value.","example":"pax_1","deprecated":true},"quantity":{"type":"integer","example":1},"type":{"type":"string","description":"Ancillary family on a booked outcome, e.g. \"seat\" or \"bag\" (lowercase, open set). May be absent before booking.","example":"seat"},"seat_number":{"type":"string","example":"14A"},"segment_ref_ids":{"type":"array","items":{"type":"string"},"example":["seg_1"]},"price":{"allOf":[{"$ref":"#/components/schemas/Money"},{"description":"Deprecated: use `price_money` instead. The unit price of the chosen seat, set by the server: major units in the trip currency. Absent for a free seat or a selection without its own unit price.","deprecated":true}]},"price_money":{"allOf":[{"$ref":"#/components/schemas/MoneyValue"},{"description":"The exact unit price of the chosen seat, set by the server. Absent for a free seat or a selection without its own unit price, e.g. a bag."}]},"status":{"type":"string","enum":["pending","confirmed","failed"],"description":"Absent before booking. For seats, `pending` means the airline has not confirmed the seat yet. `confirmed` means booked with the airline. `failed` means not booked."},"not_charged":{"type":"boolean","description":"true when this seat is not charged to the traveler."},"failure_reason":{"type":"string","description":"Why an ancillary failed, or why a pending airline outcome needs reconciliation."}},"required":["offer_id"],"description":"An ancillary selected on a trip item, as the trip read and the select_ancillaries response both return it. Provider data is never included."},"PriceChange":{"type":"object","properties":{"original":{"allOf":[{"$ref":"#/components/schemas/Money"},{"description":"Deprecated: use `original_money` instead. The selling price shown at search time, before the quote, in MAJOR currency units.","deprecated":true}]},"current":{"allOf":[{"$ref":"#/components/schemas/Money"},{"description":"Deprecated: use `current_money` instead. The selling price the trip is quoted at — the same figure as `price` — in MAJOR currency units.","deprecated":true}]},"delta":{"allOf":[{"$ref":"#/components/schemas/Money"},{"description":"Deprecated: use `delta_money` instead. `current` minus `original`, SIGNED, in MAJOR currency units: positive when the price rose, negative when it fell.","deprecated":true}]},"original_money":{"allOf":[{"$ref":"#/components/schemas/MoneyValue"},{"description":"The selling price shown at search time, before the quote."}]},"current_money":{"allOf":[{"$ref":"#/components/schemas/MoneyValue"},{"description":"The selling price the trip is quoted at — the same figure as `price_money`."}]},"delta_money":{"allOf":[{"$ref":"#/components/schemas/MoneyValue"},{"description":"`current_money` minus `original_money`, SIGNED: `value` is positive when the price rose, negative when it fell."}]}},"required":["original","current","delta"],"description":"The move between the price shown at search time and the price the trip is quoted at, stated as three figures so the caller can report it without subtracting. Read `original_money`, `current_money` and `delta_money`; the deprecated `original`, `current` and `delta` carry the same figures in major currency units. All are in one currency, matching the item’s price. Present ONLY when the two differ — an item whose price held carries no `price_change` at all, and `price_changed` is true whenever it is present."},"HotelTerms":{"type":"object","properties":{"terms_as_of":{"type":"string","enum":["search","quote"],"description":"How fresh these terms are. `search` — captured in the search snapshot the item was created from and held since. `quote` — re-read from the provider when the trip was quoted. Today the platform always answers `search`.","example":"search"},"taxes_breakdown":{"type":"array","items":{"$ref":"#/components/schemas/TaxLine"},"description":"Taxes and fees on the held rate. An empty array means no tax is known, not that there is none; check `included` per line before adding anything to the item price."},"cancellation_schedule":{"type":"array","items":{"$ref":"#/components/schemas/CancellationStep"},"description":"The penalty ladder for this rate, chronological. Absent when the provider published none — which is not a statement that cancelling is free."},"free_cancellation_until":{"type":"string","description":"Last instant the stay can be cancelled at no charge. RFC 3339 with the supplier’s declared UTC offset. Absent when the supplier declared no timezone — the platform never invents an offset, so a deadline it cannot place on the clock is withheld rather than published ambiguously — and absent when the rate is non-refundable or the provider named no deadline.","example":"2026-08-18T10:00:00+02:00"},"is_refundable":{"type":"boolean","example":false},"board_type":{"type":"string","example":"RO"},"board_name":{"type":"string","example":"Room Only"},"payment_types":{"type":"array","items":{"$ref":"#/components/schemas/HotelPaymentType"},"description":"How the rate can be settled. Each value appears at most once.","example":["pay_now"]},"room_name":{"type":"string","example":"Superior Double"},"room_capacity":{"type":"integer","example":2}},"description":"The rate terms behind a hotel item — cancellation ladder, taxes, board and room — as the platform holds them. They were captured at SEARCH time and are not re-confirmed at quote: read `terms_as_of` before treating any of them as a guarantee of what the provider will charge at booking. Present on hotel items only."},"TripItem":{"type":"object","properties":{"item_id":{"type":"string","example":"itm_4b91c2"},"kind":{"type":"string","description":"What the item is: \"flight\", \"hotel\", \"car\" or \"ground\".","example":"flight"},"offer_id":{"type":"string","description":"Deprecated: use `trip_item_token` instead. The offer handle this item was created from, exactly as it was submitted to `trip(add_item)`. On a hotel item it is the `htl_…` rate token; on a flight item it is the itinerary id. Two caveats when reconciling: on a hotel whose occupancy the provider re-shopped, this stays the token you SUBMITTED even though the priced rate moved; and a flight added twice on the same itinerary with different fares is de-duplicated by itinerary, so the echo can name the first fare added.","example":"htl_2d67a7441c60e5de","deprecated":true},"trip_item_token":{"type":"string","description":"The handle `trip(add_item)` took, echoed verbatim. On a hotel or car item it is the offer’s `trip_item_token` from the search. On a flight item it is the itinerary id and the fare joined by a colon, absent when the fare half was never resolved (a caller-supplied snapshot skips the resolver); read that absence as \"the platform cannot name the fare\", never as a different fare.","example":"of_9c1d2e:FARE_ABC"},"hotel_id":{"type":"string","description":"The property this hotel item books — the same id `hotel_search` publishes, so an item joins straight back to the search result it came from. Hotel items only.","example":"lp1f2c3"},"special_requests":{"type":"array","items":{"type":"string"},"description":"The special requests submitted with `trip(add_item)` for this hotel item, as stored — trimmed, blank entries dropped — and forwarded to the hotel supplier at booking. Requests only — the hotel may not honour them, and a confirmed booking does not imply it will. Hotel items only; absent when none were given (JIN-2499)."},"booking_scope":{"type":"string","description":"`room_bundle` means the submitted hotel offer token covers every room in `rooms`. Absent on every other hotel item, a single room included: read `rooms` for the rooms an item covers, and `booking_scope` only to learn whether one token books them all.","example":"room_bundle"},"num_rooms":{"type":"integer","minimum":1,"description":"Number of rooms this hotel item covers: 1 for a single room, the bundle size for a room bundle. Hotel items only; absent when the platform cannot state the party the item was priced for.","example":1},"rooms":{"type":"array","items":{"$ref":"#/components/schemas/HotelBundleRoom"},"description":"One entry per room this hotel item covers, single room or bundle (JIN-2565). Each entry states in `occupancy` the party that room was priced for — adults and children’s ages — so a checkout page can show the guest who the rate is for before payment, and a wrong child age, which changes the price and the room, is caught before the money moves. On a single room the entry’s room, board, refundability and tax lines are the item’s own, the same figures `hotel_terms` states; on a room bundle each entry carries that room’s details from the supplier. The item price remains the authoritative customer total for every room together. Absent when the platform holds no party for the item."},"price":{"allOf":[{"$ref":"#/components/schemas/Money"},{"description":"Deprecated: use `price_money` instead. Item total in major currency units. Absent until the trip has been quoted.","deprecated":true}]},"price_money":{"allOf":[{"$ref":"#/components/schemas/MoneyValue"},{"description":"Item total. Absent until the trip has been quoted — an unpriced item carries no `price_money` key at all, so absence means \"not priced yet\", not \"free\"."}]},"pax_count":{"type":"number","example":1},"available_ancillaries":{"type":"array","items":{"$ref":"#/components/schemas/AncillaryOffer"},"description":"Ancillaries that can still be selected on this item. Empty once nothing is on offer; populated only after the item has been quoted."},"selected_ancillaries":{"type":"array","items":{"$ref":"#/components/schemas/SelectedAncillary"},"description":"Ancillaries already selected on this item."},"price_changed":{"type":"boolean","description":"True when the provider re-priced this item between the search snapshot and the live quote. Checkout PROCEEDS at the new `price` — show the change before taking payment. Read `price_change` for the two figures and the signed difference. Absent when the price held.","example":true},"original_price":{"allOf":[{"$ref":"#/components/schemas/Money"},{"description":"Deprecated: use `original_price_money` instead. The snapshot price recorded at search time, in major currency units.","deprecated":true}]},"original_price_money":{"allOf":[{"$ref":"#/components/schemas/MoneyValue"},{"description":"The snapshot price recorded at search time. Present only when `price_changed` is true; compare it against `price_money` to show the caller what moved."}]},"price_change":{"$ref":"#/components/schemas/PriceChange"},"required_attributes":{"type":"object","properties":{"source":{"type":"string","description":"Origin of the requirement. Known values: \"provider\" (carrier-stated, authoritative) and \"derived\" (inferred from the itinerary; advisory only, not a compliance statement). The value is passed through without additional validation, so new sources may appear.","example":"provider"},"per_passenger":{"type":"array","items":{"type":"string"},"description":"Attribute names that must be collected for each passenger. Names are an open set in the provider’s own vocabulary.","example":["PassportNumber","PassportExpiryDate","DateOfBirth"]},"booking":{"type":"array","items":{"type":"string"},"description":"Attribute names that must be collected once per booking.","example":["BillingAddress","PostCode"]},"per_passenger_conditional":{"type":"array","items":{"type":"string"},"description":"Attribute names that may be required for each passenger depending on carrier, route, or passenger details. Collect them when in doubt.","example":["Nationality"]},"booking_conditional":{"type":"array","items":{"type":"string"},"description":"Attribute names that may be required once per booking depending on carrier, route, or passenger details. Collect them when in doubt."}},"description":"Extra attributes the carrier requires collected before booking this item — e.g. passport fields on carriers that mandate travel documents. Contents are passed through without additional validation. When this object is absent, no requirement is known; absence is NOT a statement that no document is needed."},"hotel_terms":{"$ref":"#/components/schemas/HotelTerms"}}},"BookingRef":{"type":"object","properties":{"item_id":{"type":"string","example":"itm_4b91c2"},"kind":{"type":"string","example":"flight"},"supplier_references":{"type":"array","items":{"$ref":"#/components/schemas/SupplierReference"},"description":"The confirmations the supplier itself issued for this item: one per airline record locator, the hotel’s confirmation number, the rental company’s confirmation. Empty when none is known yet. The traveler quotes these to the airline, hotel or rental desk; `POST /v1/get_booking` takes the trip-level `booking_ref`."},"booking_reference":{"type":"string","description":"Deprecated: use `supplier_references` instead. A confirmation reference for this item; when the airline’s own locator is not known it can hold an id only Jinko’s intermediary answers to.","example":"8Q69NYQTT","deprecated":true},"pnr":{"type":"string","description":"Deprecated: use `supplier_references` instead. The same value as `booking_reference`.","example":"XM9L2K","deprecated":true},"offer_id":{"type":"string","description":"Deprecated: use `trip_item_token` instead. The offer handle this item was created from, exactly as it was submitted to `trip(add_item)`. On a hotel item it is the `htl_…` rate token; on a flight item it is the itinerary id. Two caveats when reconciling: on a hotel whose occupancy the provider re-shopped, this stays the token you SUBMITTED even though the priced rate moved; and a flight added twice on the same itinerary with different fares is de-duplicated by itinerary, so the echo can name the first fare added.","example":"htl_2d67a7441c60e5de","deprecated":true},"trip_item_token":{"type":"string","description":"The handle `trip(add_item)` took, echoed verbatim. On a hotel or car item it is the offer’s `trip_item_token` from the search. On a flight item it is the itinerary id and the fare joined by a colon, absent when the fare half was never resolved (a caller-supplied snapshot skips the resolver); read that absence as \"the platform cannot name the fare\", never as a different fare.","example":"of_9c1d2e:FARE_ABC"},"hotel_id":{"type":"string","description":"The property this hotel item books — the same id `hotel_search` publishes, so an item joins straight back to the search result it came from. Hotel items only.","example":"lp1f2c3"},"special_requests":{"type":"array","items":{"type":"string"},"description":"The special requests submitted with `trip(add_item)` for this hotel item, as stored — trimmed, blank entries dropped — and forwarded to the hotel supplier at booking. Requests only — the hotel may not honour them, and a confirmed booking does not imply it will. Hotel items only; absent when none were given (JIN-2499)."}}},"GetTripResponse":{"type":"object","properties":{"seat_map_url":{"type":"string","description":"Link to a page where the traveller picks seats on a visual seat map. Present only when a flight item has a seat-map session. Expires; read the trip again for a fresh link."},"payment_attempt":{"$ref":"#/components/schemas/PaymentAttemptOutcome"},"trip_id":{"type":"string","example":"trip_1048576"},"status":{"type":"string","enum":["draft","quoting","quoted","fulfillment_prepared","fulfilling","fulfilled","partially_fulfilled","failed","cancelled","expired"],"description":"Where the trip is in its lifecycle, as one value for the whole trip. The booking’s progress after payment is `booking_state` (GET /v1/trip/{trip_id}); the four booking values below (`fulfilling`, `fulfilled`, `partially_fulfilled`, `failed`) are deprecated in favour of it and still sent. `draft`: being built or priced, nothing paid. `fulfilling`: payment was submitted and booking started. Then exactly one outcome: `fulfilled` (`booking_state` `completed`), `partially_fulfilled` (`partial`) or `failed` (`failed`). `cancelled`: replaced by a new trip when it was re-shopped. `expired` is the 24-hour lapse: it is evaluated LAZILY, on the read that first observes it, so this call is where a dead trip becomes visible. Once a trip is expired, adding items, setting travelers and checkout are refused with `TRIP_EXPIRED`; any other state that refuses a write answers `TRIP_STATE_CONFLICT` and names the state here. Start a new trip — a lapsed one cannot be revived. `quoting`, `quoted` and `fulfillment_prepared` are reserved and not currently set; quote progress is in `quote.status`.","example":"draft"},"travelers":{"type":"array","items":{"$ref":"#/components/schemas/TripTraveler"}},"contact":{"type":"object","properties":{"email":{"type":"string"},"phone":{"type":"string"},"title":{"type":"string"}}},"hotel_room_guests":{"$ref":"#/components/schemas/HotelRoomGuests"},"items":{"type":"array","items":{"$ref":"#/components/schemas/TripItem"}},"total_amount":{"allOf":[{"$ref":"#/components/schemas/Money"},{"description":"Deprecated: use `total_amount_money` instead. The trip total in major currency units.","deprecated":true}]},"total_amount_money":{"allOf":[{"$ref":"#/components/schemas/MoneyValue"},{"description":"The trip total. Absent until the trip has been quoted."}]},"quote":{"type":"object","properties":{"quote_id":{"type":"string","description":"The quote’s id: pass it as `quote_id` to the typed agent payment, or to the MCP `submit_agent_payment`, to pay exactly this quote.","example":"2097152"},"quoted_cart_id":{"type":"number","description":"Deprecated: use `quote_id` instead. The same quote as an integer.","deprecated":true},"status":{"type":"string"},"expires_at":{"type":"string","description":"The payment deadline of a successfully priced quote. Absent on a failed quote; a failed pricing attempt does not establish a payable price."},"failure_reason":{"type":"string","enum":["offer_expired","offer_unavailable","provider_error"],"description":"Known reason pricing failed. Present only on a failed quote with a classified failure. For offer_expired or offer_unavailable, search again and replace the unavailable item before checkout. provider_error is an upstream technical pricing failure: retry checkout once; if it still fails, choose another flight or contact support. offer_expired describes a search offer that could not be priced, not an established quote expiring."},"failure_message":{"type":"string","description":"Safe explanation of a failed pricing attempt, when available. Use failure_reason for programmatic handling; the wording can change. Where the explanation would carry the supplier’s own words, it reads “The booking could not be completed. Contact Jinko support.”"},"invalid_reason":{"type":"string","description":"Present when the latest priced quote can no longer be paid. Known value: `travelers_changed`, the trip's travelers changed after it was priced. Treat any other value the same way: the quote is not payable. Absent otherwise, including when the quote simply passed its `expires_at`. Payment against this quote is refused with **410 `QUOTE_EXPIRED`** (`reason: travelers_changed`); call `POST /v1/checkout` again to re-quote."}},"description":"Latest pricing attempt. POST /v1/trip does not start pricing; POST /v1/checkout starts it and waits for success or failure. Reading the trip does not trigger pricing. A failed quote has no payable price.","example":{"quote_id":"2097152","status":"quoted","expires_at":"2026-06-01T13:04:56Z"}},"fulfillment":{"type":"object","properties":{"fulfillment_cart_id":{"type":"number","description":"Deprecated: use `booking_ref` instead. An internal number for the booking.","deprecated":true},"status":{"type":"string","description":"Progress of the booking that started when payment was submitted, in detail; `booking_state` is its summary. `awaiting_payment`: waiting for the payment to be confirmed. `processing`: booking with the suppliers. Outcomes: `completed` (every item booked; trip `fulfilled`), `partial` (some items booked; trip `partially_fulfilled`), `failed` (nothing booked; trip `failed`), `cancelled`. `needs_review`: held for a person to resolve; not final. Other known values: pending, preparing, prepared, confirming, exchange_partial_failure, expired_quote. Additional values may appear."},"phase":{"type":"string"},"failure_reason":{"type":"string","description":"Classified reason for a failed fulfillment, when available.","example":"price_changed"},"scheduled_at":{"type":"string","format":"date-time","description":"Scheduled fulfillment time (RFC 3339), when available.","example":"2026-06-01T12:35:00Z"}}},"booking_state":{"type":"string","enum":["awaiting_payment","processing","completed","partial","failed","cancelled","needs_review"],"description":"The booking’s progress once payment has started; absent before. `awaiting_payment`: waiting for the payment to be confirmed. `processing`: booking with the suppliers. Then one outcome: `completed` (every item booked), `partial` (some items booked), `failed` (nothing booked) or `cancelled`. `needs_review`: held for a person at Jinko to resolve; not final. `fulfillment.status` keeps the detailed status behind it.","example":"processing"},"booking_ref":{"type":"string","description":"The Jinko booking reference for this trip — the value `POST /v1/get_booking`, cancel, refund and exchange take as `booking_ref`, and the `booking_ref` every webhook event carries. Present as soon as booking has been scheduled for the trip, which can be before payment completes (a hosted checkout still in progress, or an agent submit that fell back to `checkout_url`, leaves the booking `awaiting_payment`). Presence does not mean paid: read `booking_state`. The value never changes afterwards. One per trip.","example":"JNK-XEVGW5"},"bookings":{"type":"array","items":{"$ref":"#/components/schemas/BookingRef"},"description":"One entry per item with a booking reference, including items whose booking was attempted and failed. The supplier confirmation can be empty on failed items; the Jinko reference is the trip-level `booking_ref`."},"created_at":{"type":"string","example":"2026-06-01T12:30:00Z"},"updated_at":{"type":"string","example":"2026-06-01T12:34:56Z"}},"required":["trip_id"]},"TripAncillariesResponse":{"type":"object","properties":{"trip_id":{"type":"string"},"status":{"type":"string","example":"ready"},"quote_id":{"type":"string","example":"2097152"},"quoted_cart_id":{"type":"number","description":"Deprecated: use `quote_id` instead. The same quote as an integer.","deprecated":true},"expires_at":{"type":"string"},"retry_after_seconds":{"type":"integer"},"items":{"type":"array","items":{"$ref":"#/components/schemas/TripItem"}}},"required":["trip_id","status"]},"AncillaryResponse":{"type":"object","properties":{"trip_id":{"type":"string","example":"trip_1048576"},"item_id":{"type":"string","description":"The trip item the selection applied to — `items[].item_id` on the trip.","example":"itm_4b91c2"},"quoted_item_id":{"type":"number","description":"Deprecated: use `item_id` instead. An internal number for the priced item.","example":8810231,"deprecated":true},"selected_ancillaries":{"type":"array","items":{"$ref":"#/components/schemas/SelectedAncillary"}},"total_with_ancillaries":{"allOf":[{"$ref":"#/components/schemas/Money"},{"description":"The item total including the selected ancillaries, in minor units: `amount` is an integer to divide by 10 ** `decimal_places` (e.g. `{amount: 23959, currency: \"EUR\", decimal_places: 2}` is EUR 239.59). `display` is the same figure to show."}]}}},"AncillaryRequest":{"type":"object","properties":{"trip_id":{"type":"string"},"item_id":{"type":"string"},"selections":{"type":"array","items":{"type":"object","properties":{"offer_id":{"type":"string","description":"The `offer_id` of an offer in the item’s `available_ancillaries`, exactly as received."},"category":{"type":"string","description":"The server resolves the category from the offer."},"traveler_ref":{"type":"string","description":"Use the traveler's `traveler_ref` from GET /v1/trip."},"segment_ref_ids":{"type":"array","items":{"type":"string"},"description":"For a seat, pass the offer's one `segment_ref_id`."},"seat_number":{"type":"string","description":"The seat to assign, required when the offer is a seat offer. Pick one from the offer's `seat_map.seats` with `available` true. One seat per passenger per segment, and each seat to at most one passenger.","example":"14A"},"journey_ref_id":{"type":"string"},"quantity":{"type":"number"}},"required":["offer_id"]}},"intent":{"$ref":"#/components/schemas/IntentInput"}},"required":["trip_id","item_id","selections"],"example":{"trip_id":"trip_1048576","item_id":"itm_4b91c2","selections":[{"offer_id":"anc_3f9a1c27e4b8d051","traveler_ref":"pax_1","quantity":1},{"offer_id":"anc_9b04e7d2c6a1f835","traveler_ref":"pax_1","segment_ref_ids":["seg_1"],"seat_number":"14A"}]}},"AgentSPTParams":{"type":"object","properties":{"max_amount":{"type":"integer","description":"Deprecated: use `max_amount_money` instead. Locked trip total in the smallest currency unit — scope the Shared Payment Token to at most this.","example":259449,"deprecated":true},"currency":{"type":"string","description":"Deprecated: use `max_amount_money` instead. The trip currency: `USD` or `EUR`, depending on the partner's Stripe account binding. `agent_spt_params` is present only when the partner accepts SPT for this cart currency.","example":"USD","deprecated":true},"max_amount_money":{"allOf":[{"$ref":"#/components/schemas/MoneyValue"},{"description":"Locked trip total — scope the Shared Payment Token to at most `value` (minor units) in `currency`, the trip currency (`USD` or `EUR`, depending on the partner's Stripe account binding)."}]},"stripe_profile":{"type":"string","description":"Stripe Agentic Commerce network_business_profile id the SPT is granted against. Empty until the preview account is provisioned."},"expires_at":{"type":"string","description":"When the quote these params are scoped to expires (RFC 3339) — the same instant as `expires_at` on the checkout response. Submitting a Shared Payment Token after it answers **410 `QUOTE_EXPIRED`** and redeems nothing; call `POST /v1/checkout` again and mint a fresh token against the new params. The quote can also be refused earlier if the trip's travelers change after pricing (`reason: travelers_changed`).","example":"2026-06-10T13:04:56Z"}},"required":["max_amount","currency"],"description":"Parameters to scope a Shared Payment Token to, so an agent can pay programmatically instead of opening `checkout_url`: mint the token against these, then call POST /v1/agent_payment/submit with `trip_id` and the token. The currency is the trip currency: `USD` or `EUR`, depending on the partner's Stripe account binding. **Absent when the partner does not accept SPT for this trip currency**; use the returned `accepted_payment_types` to select an available payment method, or pay through `checkout_url`. The human checkout path ignores this object."},"CheckoutResponse":{"type":"object","properties":{"quote_id":{"type":"string","description":"The quote this checkout priced. Pass it as `quote_id` to the typed agent payment, or to the MCP `submit_agent_payment`, to pay exactly this quote.","example":"2097152"},"quoted_cart_id":{"type":"integer","description":"Deprecated: use `quote_id` instead. The same quote as an integer.","deprecated":true},"accepted_payment_types":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["spt"]},"requires":{"type":"object","properties":{"headers":{"type":"array","items":{"type":"string"}},"fields":{"type":"array","items":{"type":"string"}}},"required":["headers","fields"]},"authentication":{"type":"string","enum":["customer_handoff","unavailable"]}},"required":["type","requires","authentication"]}},"session_id":{"type":"string","description":"The hosted payment page session behind `checkout_url`. Nothing on this API takes it; send the traveler to `checkout_url`.","example":"cs_1048576"},"checkout_url":{"type":"string","example":"https://app.gojinko.com/checkout?t=<signed token>"},"status":{"type":"string","description":"Checkout state — \"ready\", \"pending\" or \"failed\".","example":"ready"},"expires_at":{"type":"string","description":"When the quote behind this checkout stops being payable (RFC 3339). At most one hour from the call, and earlier when an offer in the trip carries a shorter deadline of its own (some airline fares last twenty minutes from their search) — pay before it passes. It is NOT a price hold: the supplier guarantees neither the price nor availability for the window, so an offer can lapse inside it. Nor is it the lifetime of `checkout_url`, which stays openable longer. After this instant the platform REFUSES to take payment rather than charging a stale price: `POST /v1/agent_payment/submit` answers **410 `QUOTE_EXPIRED`** and no payment object is created. To recover, call `POST /v1/checkout` again on the same trip — it re-quotes at the current price — and pay against the new `expires_at`. The hosted page cannot recover on its own: past this instant it shows the price as expired and the customer has to be sent a fresh checkout. The quote can also stop being payable before this instant if the trip's travelers change after pricing: payment is then refused with **410 `QUOTE_EXPIRED`** and `reason: travelers_changed`.","example":"2026-06-01T12:39:56Z"},"payment_type":{"type":"string","enum":["intent","checkout","agent"],"description":"Which payment rail this trip is on. None of the values means payment has been collected: checkout only prices the trip, and you must still act to get it paid. `intent` — nothing has been charged or authorized yet; this is what a trip created through this API returns. Send the traveler to `checkout_url` to pay on the Jinko checkout page, or pay programmatically with POST /v1/agent_payment/submit using a type listed in `accepted_payment_types`. `checkout` — a payment page is already open for this trip; send the traveler to `checkout_url` to complete it. `agent` — a programmatic payment was already submitted for this quote through POST /v1/agent_payment/submit; check its outcome before submitting again. To choose how to pay, read `accepted_payment_types` and `agent_spt_params`, not this field.","example":"intent"},"total_amount":{"allOf":[{"$ref":"#/components/schemas/Money"},{"description":"Deprecated: use `total_amount_money` instead. The trip total in major currency units.","deprecated":true}]},"total_amount_money":{"allOf":[{"$ref":"#/components/schemas/MoneyValue"},{"description":"The trip total the checkout is priced at."}]},"items":{"type":"array","items":{"$ref":"#/components/schemas/TripItem"}},"agent_spt_params":{"$ref":"#/components/schemas/AgentSPTParams"}},"required":["quote_id","quoted_cart_id","accepted_payment_types"]},"QuoteExpiredResponse":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["QUOTE_EXPIRED"]},"message":{"type":"string"},"doc_url":{"type":"string"}},"required":["code","message"]},"expires_at":{"type":"string","description":"The instant the refused quote expired (RFC 3339). For a deadline lapse this is the past deadline; when `reason` is `travelers_changed` it is the instant of the refusal.","example":"2026-09-04T12:39:56Z"},"quote_id":{"type":"string","description":"The quote that was refused — the same `quote_id` the trip and checkout responses carry, so a caller holding several can tell which one this refusal is about.","example":"2097152"},"quoted_cart_id":{"type":"number","description":"Deprecated: use `quote_id` instead. The same quote as an integer.","example":2097152,"deprecated":true},"reason":{"type":"string","enum":["travelers_changed"],"description":"Present when the quote was invalidated because the trip's travelers changed after it was priced (`travelers_changed`). Absent for a plain deadline lapse. Either way the recovery is the same: call `POST /v1/checkout` again to re-quote, then pay against the new quote.","example":"travelers_changed"}},"required":["error"]},"MissingCustomerDetailsResponse":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["MISSING_CUSTOMER_DETAILS"]},"message":{"type":"string"},"doc_url":{"type":"string"}},"required":["code","message"]},"missing_fields":{"type":"array","items":{"type":"string"},"description":"Field names the booking still needs, in the trip traveler vocabulary (e.g. `passport_number`).","example":["nationality","passport_country","passport_expiry","passport_number"]},"missing_by_domain":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}},"description":"The same fields grouped by product domain (e.g. `flight`).","example":{"flight":["passport_number","passport_expiry","passport_country","nationality"]}}},"required":["error"]},"CheckoutRequest":{"type":"object","properties":{"trip_id":{"type":"string","example":"trip_1048576"},"intent":{"$ref":"#/components/schemas/IntentInput"}},"required":["trip_id"]},"AgentPaymentSubmitResponse":{"type":"object","properties":{"quote_id":{"type":"string","description":"The quote this payment was taken against. This submission names no quote, so it pays the trip’s latest payable quote; compare this with the `quote_id` the traveler approved.","example":"2097152"},"fulfillment_cart_id":{"type":"integer","description":"Deprecated: use `booking_ref` instead. An internal number for the booking.","deprecated":true},"status":{"type":"string","example":"processing"},"payment_verified":{"type":"boolean"},"message":{"type":"string"},"booking_ref":{"type":"string","description":"The Jinko booking reference for the trip just paid — hand it to `POST /v1/get_booking` (`booking_ref`) to retrieve or service the booking. Present when the token was authorized (and when the trip was already past payment); absent when the payment could not be authorized and `checkout_url` is returned instead. When `payment_status` is `requires_authentication` it can be present before payment completes: read `payment_status`, not this field, to tell whether the payment went through.","example":"JNK-XEVGW5"},"checkout_url":{"type":"string","description":"Hosted-checkout fallback, present when the payment could not be authorized (for example a card decline). Not returned when `payment_status` is `requires_authentication`."},"recap_token":{"type":"string"},"payment_status":{"type":"string","description":"Status of the payment attempt behind this submission: `requires_authentication`, `pending`, `authorized`, `captured`, `declined`, `failed`, `cancelling`, `cancelled` or `expired`. `requires_authentication`: the card issuer asked for 3-D Secure; `payment_verified` is false and no `checkout_url` is returned. Send the traveler to `authentication_url`, with the Shared Payment Token's hashed value appended as that field describes, to complete it before `quote_expires_at`, then poll `GET /v1/trip/{trip_id}` until `payment_attempt.payment_status` is none of `requires_authentication`, `cancelling` or `pending` (`cancelling` is a transient cleanup state and `pending` is still in progress — keep polling). `authorized` / `captured`: the payment went through. `failed` / `declined` / `cancelled`: it did not — submit again with a new token before `quote_expires_at`. `expired`: it did not, and the quote expired with it — call `POST /v1/checkout` again to price a new quote, then mint and submit a new token.","example":"requires_authentication"},"quote_expires_at":{"type":"string","format":"date-time","description":"Deadline for completing this payment, including 3-D Secure authentication (RFC 3339). After it the quote has expired: call `POST /v1/checkout` again for a new quote, then submit a new token.","example":"2026-06-01T12:39:56Z"},"authentication_url":{"type":"string","format":"uri","description":"Jinko-hosted page where the traveler completes 3-D Secure, present when `payment_status` is `requires_authentication` and valid until `quote_expires_at`. Read the Shared Payment Token you issued from the Stripe API (`GET https://api.stripe.com/v1/shared_payment/issued_tokens/{id}` with your own Stripe secret key, `{id}` being the token you submitted) and append `#hv=` and its `next_action.use_stripe_sdk.value`, unchanged: `authentication_url + \"#hv=\" + next_action.use_stripe_sdk.value`. The value travels in the URL fragment, which keeps it out of server logs. The traveler completes the bank challenge on that page before `quote_expires_at`. To run the challenge on your own page instead, load Stripe.js with your own publishable key and call `stripe.handleNextAction({ hashedValue })` with that value. Then poll `GET /v1/trip/{trip_id}` until `payment_attempt.payment_status` is none of `requires_authentication`, `cancelling` or `pending`."}}},"PaymentRefusalResponse":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"payment_error":{"type":"object","nullable":true,"properties":{"code":{"type":"string","enum":["CREDENTIAL_FORMAT_INVALID","PAYMENT_TYPE_NOT_ENABLED","PAYMENT_CREDENTIAL_INVALID","TRIP_OWNED_BY_OTHER_PAYMENT","IDEMPOTENCY_KEY_REUSED","ATTEMPT_IN_PROGRESS","CARD_DECLINED","CARD_ALIAS_EXPIRED","PAYMENT_CREDENTIAL_REJECTED","AUTHENTICATION_REQUIRED","AUTHENTICATION_FAILED","AUTHENTICATION_ABANDONED","QUOTE_EXPIRED","ATTEMPT_TERMINAL","PAYMENT_OUTCOME_UNKNOWN","FULFILLMENT_DISPATCH_DELAYED","RECONCILIATION_REQUIRED","TEMPORARILY_UNAVAILABLE"]},"message":{"type":"string"}},"required":["code","message"]},"recovery":{"type":"object","properties":{"action":{"type":"string","enum":["poll","authenticate","retry_same_key","retry_new_key","recheckout","contact_support","stop"],"description":"The next step. `authenticate`: when `authentication` is present, open `authentication.url` before `authentication.expires_at`. For a Shared Payment Token (`spt`), first read the token's `next_action.use_stripe_sdk.value` with your own Stripe secret key and append it unchanged: open `authentication.url + \"#hv=\" + <value>`, or call `stripe.handleNextAction({ hashedValue })` with Stripe.js on your own page. Then poll `GET /v1/trip/{trip_id}/payment_attempts/{payment_attempt_id}` until `payment_status` is none of `requires_authentication`, `cancelling` or `pending`."},"replacement_credential_required":{"type":"boolean"}},"required":["action","replacement_credential_required"]},"expires_at":{"type":"string","description":"The instant the refused quote expired (RFC 3339). For a deadline lapse this is the past deadline; when `reason` is `travelers_changed` it is the instant of the refusal.","example":"2026-09-04T12:39:56Z"},"quote_id":{"type":"string","description":"The quote that was refused — the same `quote_id` the trip and checkout responses carry, so a caller holding several can tell which one this refusal is about.","example":"2097152"},"quoted_cart_id":{"type":"number","description":"Deprecated: use `quote_id` instead. The same quote as an integer.","example":2097152,"deprecated":true},"reason":{"type":"string","enum":["travelers_changed"],"description":"Present when the quote was invalidated because the trip's travelers changed after it was priced (`travelers_changed`). Absent for a plain deadline lapse. Either way the recovery is the same: call `POST /v1/checkout` again to re-quote, then pay against the new quote.","example":"travelers_changed"}}}]},"TypedAgentPaymentSubmitRequest":{"type":"object","properties":{"trip_id":{"type":"string"},"quote_id":{"type":"string","pattern":"^[1-9][0-9]{0,17}$(?![\\s\\S])","description":"The quote the traveler approved: the `quote_id` checkout returned. The payment is taken against this quote only; a quote that is no longer payable is refused with 410 `QUOTE_EXPIRED` and nothing is charged.","example":"2097152"},"payment":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","enum":["spt"]},"shared_payment_token":{"type":"string","minLength":1}},"required":["type","shared_payment_token"],"additionalProperties":false}]}},"required":["trip_id","quote_id","payment"],"additionalProperties":false},"AgentPaymentSubmitRequest":{"type":"object","properties":{"trip_id":{"type":"string","example":"trip_1048576"},"shared_payment_token":{"type":"string","example":"spt_1Nq8L2eZvKYlo2C0"}},"required":["trip_id","shared_payment_token"]},"HotelSearchContext":{"type":"object","properties":{"checkin":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"checkout":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"occupancies":{"type":"array","items":{"$ref":"#/components/schemas/Occupancy"},"minItems":1},"currency":{"type":"string"},"guest_nationality":{"type":"string","minLength":2,"maxLength":2},"trip_id":{"type":"string"}},"required":["checkin","checkout","occupancies","currency"],"description":"Normalized first-request stay and occupancy frozen by the search session and repeated on every page. trip_id only associates the search with a trip for display."},"DestinationResolution":{"type":"object","properties":{"shape":{"type":"string","enum":["city","coordinates","query","hotel_name","hotel_ids"],"description":"Which destination selector the request used. It says which of the other fields are present: the city fields on `city`, `hotel_id`/`hotel_name`/`hotel_city` on `hotel_name`, `hotel_ids` on `hotel_ids`.","example":"city"},"match":{"type":"string","enum":["exact","fuzzy","provider_matched","fallback_string"],"description":"`exact` — the destination was found verbatim: a city catalog group, a place located under the name asked for (`resolved_place`), coordinates, hotel ids, or a hotel name scoring 1.0. `fuzzy` — a close-but-different label was auto-picked: on the city shape, a single candidate scoring ≥ 0.85 with no runner-up ≥ 0.6, or a place located under another name (`resolved_place`); on hotel_name, a lookup scoring < 1.0 — the response also carries a warning naming the substitution. `provider_matched` — a `query` selector was forwarded as-is and the supplier chose the place. `fallback_string` — `city_name` could not be resolved at all, so the raw string was forwarded to the supplier unchanged; `status` says why.","example":"exact"},"status":{"type":"string","enum":["resolved","unknown_city","implausible_spread","unsupported_shape","resolver_unavailable","not_attempted"],"description":"City shape ONLY. Absent for coordinates, query, hotel_name and hotel_ids.","example":"resolved"},"requested_city":{"type":"string","example":"Sain Malo"},"requested_country":{"type":"string","example":"FR"},"resolved_city":{"type":"string","example":"Saint Malo"},"resolved_country":{"type":"string","example":"FR"},"latitude":{"type":"number","example":48.649},"longitude":{"type":"number","example":-2.0257},"radius_km":{"type":"number","description":"The radius actually searched, with `latitude`/`longitude` as its centre; `radius_source` says where it came from. City and query shapes: present when the destination resolved to a centre (a catalog city group or a located place). Coordinates shape: absent when the supplier default was used and is unknown to the platform."},"radius_source":{"type":"string","enum":["caller","city","default","place"],"description":"Where `radius_km` came from. `caller` — the request’s own `radius_km`. `city` — the resolved city’s extent (its catalog spread, or the located city’s size when `resolution_source` is `geocoder`), floored at the platform minimum. `default` — the platform default, for coordinates sent without a radius. `place` — the size of the located place for its `place_type` (a district, landmark, station, address, airport, island or region). A `city` or `default` radius may be widened when the first search finds too little; `radius_km` is then the radius finally searched. Absent on the hotel_name and hotel_ids shapes.","example":"city"},"catalog_hotel_count":{"type":"integer","description":"How many properties the matched city catalog group holds. Absent when the search centre did not come from a catalog group (`resolution_source` `geocoder`).","example":41},"place_type":{"type":"string","enum":["city","district","poi","station","address","airport","island","region"],"description":"city and query shapes, when the platform located the destination itself: the kind of place searched. `poi` is a landmark or venue. More values may be added. Absent when the destination was not located this way.","example":"island"},"resolution_source":{"type":"string","enum":["catalog","geocoder"],"description":"With `place_type`: where the search centre came from. `catalog` — the platform’s city catalog group, used only when it agrees with the located place (`resolved_city` and `catalog_hotel_count` are then present). `geocoder` — the located place itself.","example":"geocoder"},"resolved_place":{"type":"string","description":"With `place_type`: the name of the place searched, the one to quote back to the user.","example":"Fuerteventura, Las Palmas, Spain"},"search_bounds":{"type":"object","properties":{"south":{"type":"number"},"west":{"type":"number"},"north":{"type":"number"},"east":{"type":"number"}},"required":["south","west","north","east"],"description":"Island and region searches only: the box, in degrees, that every returned hotel lies inside, so a search around an island never returns hotels from the next one.","example":{"south":28.0439,"west":-14.51,"north":28.757,"east":-13.8224}},"alternatives":{"type":"array","items":{"type":"object","properties":{"resolved_place":{"type":"string"},"place_type":{"type":"string","enum":["city","district","poi","station","address","airport","island","region"]},"resolved_country":{"type":"string"},"latitude":{"type":"number"},"longitude":{"type":"number"},"radius_km":{"type":"number","minimum":0,"exclusiveMinimum":true,"maximum":50}},"required":["resolved_place","place_type","latitude","longitude","radius_km"]},"maxItems":3,"description":"With `place_type`: up to three other places the destination may name — the same name in another country or state, the island or region around the town searched when an island or region was asked for, or another place the text names. To search one of them instead, send its `latitude`, `longitude` and `radius_km` (at most 50, the limit the request accepts). Absent when the destination names one place.","example":[{"resolved_place":"Lexington, MA, USA","place_type":"city","resolved_country":"US","latitude":42.4473497,"longitude":-71.2271531,"radius_km":6.1}]},"score":{"type":"number","minimum":0,"maximum":1,"description":"Only present when `match` is `fuzzy` (the city shape’s auto-pick similarity) or on the hotel_name shape (the name lookup’s own score).","example":0.9},"query":{"type":"string"},"hotel_id":{"type":"string","example":"lp1d2c3"},"hotel_name":{"type":"string"},"hotel_city":{"type":"string"},"nearby_alternatives_included":{"type":"boolean","description":"hotel_name shape only: true when nearby hotels were also priced."},"hotel_ids":{"type":"array","items":{"type":"string"},"description":"hotel_ids shape only: the ids that were rate-shopped."},"hotel_ids_without_rates":{"type":"array","items":{"type":"string"},"description":"hotel_name shape: present when the matched hotel priced nothing. hotel_ids shape: the requested ids that priced nothing."}},"description":"How the request’s destination resolved into these hotels. Present on every successful search. Rendering rule for an agent or tool: say something ONLY when there is something to say — `match` is `fuzzy` or `fallback_string`, `resolved_city` differs from `requested_city`, or the platform located a place that is not a city (`place_type` other than `city`), or `alternatives` is present. `exact` and `provider_matched` otherwise need no header line of their own. When `resolved_place` is present, name it as the place searched, with `place_type` when it is not a city, instead of `resolved_city`. When `alternatives` is present, name them too and let the user pick before booking."},"EmptyReason":{"type":"string","enum":["no_availability_for_dates","destination_not_found","catalog_exhausted","all_dropped_by_budget","no_catalog_match","all_dropped_by_star_filter","all_dropped_by_rating_filter"],"description":"Why `hotels` is empty. Absent whenever `hotels` is non-empty.","example":"no_availability_for_dates"},"HotelSearchResponse":{"type":"object","properties":{"hotels":{"type":"array","items":{"$ref":"#/components/schemas/Hotel"}},"nearby_alternatives":{"type":"array","items":{"$ref":"#/components/schemas/Hotel"},"description":"Other hotels near the one a `hotel_name` search matched, priced for the same dates and occupancy, so they can be compared without a second search. Same shape as `hotels`, and not counted in `total`. Present only on a `hotel_name` search that found usable neighbours."},"total":{"type":"number","example":87},"search_handle":{"type":"string","description":"Deprecated: use `next_handle` instead. The identifier of the search session on its own, which no endpoint accepts. `next_handle` carries the session and the next page, `has_more` says whether there is one and `expires_at` when the session ends.","deprecated":true,"example":"hs_example"},"next_handle":{"type":"string","nullable":true,"description":"Opaque handle for the next deterministic page. Send it by itself to hotel_search. Null means the fixed search scope is exhausted.","example":"hp_example_2"},"has_more":{"type":"boolean","description":"Whether this fixed search session has another page."},"expires_at":{"type":"string","format":"date-time","description":"RFC 3339 expiry for the search session and its page handles.","example":"2027-01-15T10:30:00Z"},"search_context":{"$ref":"#/components/schemas/HotelSearchContext"},"warnings":{"type":"array","items":{"type":"string"},"description":"Non-fatal advisories, e.g. budget-filter outcomes or ignored filters."},"destination_resolution":{"$ref":"#/components/schemas/DestinationResolution"},"empty_reason":{"$ref":"#/components/schemas/EmptyReason"}}},"HotelNameCandidate":{"type":"object","properties":{"hotel_id":{"type":"string","example":"lp1a2b3c"},"name":{"type":"string","example":"Hotel Le Marais"},"city":{"type":"string","example":"Paris"},"score":{"type":"number","minimum":0,"maximum":1,"example":0.82}},"required":["hotel_id","name","score"]},"LowConfidenceSuggestedRetry":{"type":"object","properties":{"destination":{"type":"object","additionalProperties":{"nullable":true},"description":"The exact fields to retry the search with — merge into the failed request unchanged for an exact match.","example":{"city_name":"Saint Malo","country_code":"FR"}},"rationale":{"type":"string","example":"the closest catalog city to the requested name; retry with it, or ask the user when the candidates differ"}},"required":["destination"],"description":"The one-shot fix, when the top candidate is confident enough to name one."},"HotelNameLowConfidenceResponse":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["HOTEL_NAME_LOW_CONFIDENCE"]},"message":{"type":"string"},"doc_url":{"type":"string"},"top_candidates":{"type":"array","items":{"$ref":"#/components/schemas/HotelNameCandidate"},"description":"Closest matches, ordered by score descending, at most 5."},"suggested_retry":{"$ref":"#/components/schemas/LowConfidenceSuggestedRetry"}},"required":["code","message","top_candidates"]}},"required":["error"]},"DestinationCityCandidate":{"type":"object","properties":{"city":{"type":"string","example":"Saint Malo"},"country_code":{"type":"string","example":"FR"},"hotel_count":{"type":"integer","example":326},"score":{"type":"number","minimum":0,"maximum":1,"example":0.75}},"required":["city","country_code","hotel_count","score"]},"DestinationLowConfidenceResponse":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["DESTINATION_LOW_CONFIDENCE"]},"message":{"type":"string"},"doc_url":{"type":"string"},"top_candidates":{"type":"array","items":{"$ref":"#/components/schemas/DestinationCityCandidate"},"description":"Closest catalog cities, ordered by score descending, at most 5."},"suggested_retry":{"$ref":"#/components/schemas/LowConfidenceSuggestedRetry"}},"required":["code","message","top_candidates"]}},"required":["error"]},"HotelSearchContinuationRequest":{"type":"object","properties":{"next_handle":{"type":"string","minLength":1,"description":"Opaque continuation handle returned by a prior hotel_search. Send this field by itself; the search session keeps destination, dates, occupancy, currency and filters.","example":"hp_example_2"}},"required":["next_handle"],"additionalProperties":false},"HotelSearchRequest":{"anyOf":[{"$ref":"#/components/schemas/HotelSearchContinuationRequest"},{"type":"object","properties":{"next_handle":{"not":{}},"query":{"type":"string","example":"Paris"},"city_name":{"type":"string"},"country_code":{"type":"string","minLength":2,"maxLength":2},"latitude":{"type":"number"},"longitude":{"type":"number"},"radius_km":{"type":"number","minimum":0,"exclusiveMinimum":true,"maximum":50},"hotel_ids":{"type":"array","items":{"type":"string"}},"hotel_name":{"type":"string","minLength":1,"description":"Search one specific hotel by its name. The name is matched against the hotel catalog; the closest match is priced when it scores at least 0.7, and the response then has `destination_resolution.shape` `hotel_name` plus `nearby_alternatives` within 3 km. Add `city_name` and `country_code` to narrow the match. When no candidate scores 0.7, the call returns 422 `HOTEL_NAME_LOW_CONFIDENCE` with `top_candidates` and a `suggested_retry`. With `hotel_name`, `query`, `city_name` and coordinates do not set the search area. Property filters (min_rating, min_star_rating, max_star_rating, star_rating, min_reviews, hotel_type_ids, chain_ids, facility_ids) are not accepted with hotel_name or hotel_ids and are rejected with 400. `hotel_ids`, when also sent, takes precedence. Name lookup covers part of the hotel inventory only.","example":"Park Plaza Beijing Wangfujing"},"checkin":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Arrival date, `YYYY-MM-DD`. Today (UTC) or later."},"checkout":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Departure date, `YYYY-MM-DD`. After `checkin`: a stay has at least one night."},"occupancies":{"type":"array","items":{"$ref":"#/components/schemas/Occupancy"},"description":"Each array element requests one room. One occupancy returns a one-room offer and keeps the deprecated `rooms[].rates[]` compatibility projection; multiple occupancies return multi-room `offers[]`. Add the chosen offer’s trip_item_token to a trip once."},"adults":{"type":"integer","minimum":1,"description":"Deprecated: use `occupancies` instead, one element per room with its own `adults`. Shorthand for the adults of the whole party: without `rooms` one room is priced for all of them, with `rooms` they are spread evenly over that many rooms. Ignored when `occupancies` is sent.","deprecated":true},"children":{"type":"array","items":{"type":"integer","minimum":0,"maximum":17},"description":"Deprecated: use `occupancies[].children_ages` instead, which places each child in a room. Shorthand for the age of each child of the whole party, spread over the rooms of `rooms`. Ignored when `occupancies` is sent.","deprecated":true},"rooms":{"type":"integer","minimum":1,"description":"Deprecated: use `occupancies` instead, one element per room. Shorthand for how many rooms the `adults` and `children` of the party are spread over, evenly. Ignored when `occupancies` is sent.","deprecated":true},"currency":{"type":"string"},"guest_nationality":{"type":"string","minLength":2,"maxLength":2,"description":"The lead guest’s nationality, ISO 3166-1 alpha-2 (`FR`). Sent to the hotel supplier, which can offer different rates and prices by nationality, and kept on the offers this search returns. When omitted, `US` is used. The value applied is echoed in `search_context.guest_nationality`.","example":"FR"},"min_rating":{"type":"number","description":"Minimum guest score (0-10) for destination searches. When omitted, or 0, no floor applies, and hotels without a guest score are returned too. Not accepted together with hotel_name or hotel_ids."},"min_star_rating":{"type":"integer","minimum":1,"maximum":5},"star_rating":{"type":"integer","minimum":1,"maximum":5,"description":"Deprecated: use `min_star_rating` and `max_star_rating` instead; send the same value as both for an exact match. Still applied: it fills whichever of the two bounds the request leaves unset.","deprecated":true},"max_star_rating":{"type":"integer","minimum":1,"maximum":5},"min_reviews":{"type":"integer","description":"Minimum number of guest reviews."},"max_results":{"type":"integer","description":"Requested page size (default 50); next_handle returns the next page of the same search. A hotel_name or hotel_ids search returns every named hotel that has rates."},"offset":{"type":"integer","minimum":0,"description":"Deprecated: use `next_handle` instead. Skips this many hotels at the hotel source before the first page, default 0. A search that answers with `next_handle` refuses a non-zero offset: page by sending that handle alone.","deprecated":true},"max_budget_per_night":{"type":"number","minimum":0,"exclusiveMinimum":true,"description":"Keep only hotels whose cheapest per-night price (per room, in the request currency) is within this budget; qualifying hotels keep all their rates.","example":150},"hotel_type_ids":{"type":"array","items":{"type":"string"},"description":"Filter by property type id. Each entry is a numeric property type id, as a string; property type names are refused."},"chain_ids":{"type":"array","items":{"type":"string"}},"facility_ids":{"type":"array","items":{"type":"string"},"description":"Filter by facility. Each entry is a Jinko facility dictionary id (numeric, as a string), listed in the docs under Hotel facility dictionary. Facility names are refused on this endpoint."},"intent":{"$ref":"#/components/schemas/IntentInput"}},"required":["checkin","checkout"]}],"example":{"city_name":"Paris","country_code":"fr","checkin":"2027-03-10","checkout":"2027-03-13","occupancies":[{"adults":2}],"currency":"USD"}},"HotelPolicy":{"type":"object","properties":{"name":{"type":"string","example":"Pets"},"description":{"type":"string","example":"Pets are not allowed."},"policy_type":{"type":"string","example":"pets"},"child_allowed":{"type":"boolean","description":"Whether this policy says the property accepts children. Absent when it does not say.","example":true},"pets_allowed":{"type":"boolean","description":"Whether this policy says the property accepts pets. Absent when it does not say.","example":false}}},"HotelDetailsRoom":{"type":"object","properties":{"id":{"type":"string","example":"rm_1"},"name":{"type":"string","example":"Superior Double"},"description":{"type":"string"},"amenities":{"type":"array","items":{"type":"string"},"example":["Air conditioning","Safe"]},"amenity_items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer","description":"Stable numeric id."},"label":{"type":"string","description":"English label."}},"required":["id","label"]},"description":"Room amenities with a stable amenity id. Room amenity ids are a separate list from the facility dictionary.","example":[{"id":312,"label":"Shower"},{"id":339,"label":"TV"}]},"bed_types":{"type":"array","items":{"type":"string"},"example":["1 double bed"]},"room_capacity":{"type":"integer","description":"How many the room sleeps, from the property’s own room metadata. Same field as on hotel_search and on a hotel trip item.","example":2},"max_occupancy":{"type":"integer","description":"Deprecated: use `room_capacity` (same value).","deprecated":true,"example":2},"max_adults":{"type":"integer","description":"Maximum number of adults the room accepts.","example":2},"max_children":{"type":"integer","description":"Maximum number of children the room accepts; 0 means the room does not take children.","example":1},"size_sqm":{"type":"number","example":18.5},"views":{"type":"array","items":{"type":"string"},"example":["Courtyard"]},"images":{"type":"array","items":{"type":"string"}}}},"HotelDetailsResponse":{"type":"object","properties":{"hotel":{"type":"object","properties":{"id":{"type":"string","description":"Opaque hotel identifier, the value hotel_search returns as `hotel_id`. Pass it back exactly as received; do not parse or build it.","example":"lp1d2c3"},"hotel_ref":{"type":"string","description":"Deprecated: use `hotel_id` (search) or `id` (details, reviews). The hotel's identifier behind a source prefix, as earlier contract versions returned it; hotel_details and hotel_reviews still accept it. Pass it back exactly as received; do not parse or build it.","example":"h1:lp1d2c3","deprecated":true},"name":{"type":"string","example":"Hôtel Le Marais"},"description":{"type":"string","example":"A boutique 4-star hotel in the heart of the Marais district."},"star_rating":{"type":"number","example":4},"rating":{"type":"number","description":"Guest score out of 10.","example":8.6},"review_count":{"type":"integer","description":"Number of guest reviews behind `rating`.","example":1243},"child_allowed":{"type":"boolean","description":"Whether the property accepts children. Absent when the property does not say; `false` is a stated no.","example":true},"pets_allowed":{"type":"boolean","description":"Whether the property accepts pets. Absent when the property does not say; `false` is a stated no.","example":false},"address":{"type":"string","example":"12 Rue de Rivoli"},"city":{"type":"string","example":"Paris"},"country_code":{"type":"string","example":"FR"},"coordinates":{"type":"object","properties":{"latitude":{"type":"number"},"longitude":{"type":"number"}},"example":{"latitude":48.8566,"longitude":2.3522}},"images":{"type":"array","items":{"type":"string"},"example":["https://images.gojinko.com/hotels/htl_8f21a9c0/1.jpg"]},"facilities":{"type":"array","items":{"type":"string"},"example":["Free WiFi","Air conditioning","24-hour front desk"]},"facility_items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer","description":"Stable numeric id."},"label":{"type":"string","description":"English label."}},"required":["id","label"]},"description":"Facilities as dictionary entries: `id` is the Jinko facility dictionary id (the value `facility_ids` accepts on hotel_search), `label` the English label. Facilities without a dictionary id appear in `facilities` only.","example":[{"id":107,"label":"Free WiFi"},{"id":2,"label":"Air conditioning"}]},"policies":{"type":"array","items":{"$ref":"#/components/schemas/HotelPolicy"}},"check_in_time":{"type":"string","example":"15:00"},"check_out_time":{"type":"string","example":"11:00"}}},"rooms":{"type":"array","items":{"$ref":"#/components/schemas/HotelDetailsRoom"},"description":"Room-level metadata for this property. These are DESCRIPTIVE only and carry no price — `room_id` correlates them to the priced rooms in a hotel_search response."}}},"HotelReviewsResponse":{"type":"object","properties":{"hotel":{"type":"object","properties":{"id":{"type":"string","description":"Opaque hotel identifier, the value hotel_search returns as `hotel_id` and hotel_details as `id`. Pass it back exactly as received; do not parse or build it.","example":"lp39fe9"},"hotel_ref":{"type":"string","description":"Deprecated: use `hotel_id` (search) or `id` (details, reviews). The hotel's identifier behind a source prefix, as earlier contract versions returned it; hotel_details and hotel_reviews still accept it. Pass it back exactly as received; do not parse or build it.","example":"h1:lp1d2c3","deprecated":true},"name":{"type":"string","description":"Hotel name.","example":"Park Plaza Beijing Wangfujing"},"rating":{"type":"number","description":"Guest score out of 10.","example":8.6},"review_count":{"type":"integer","description":"Number of guest reviews behind `rating`.","example":1243}},"required":["id"],"description":"The hotel the reviews are about."},"summary":{"type":"object","nullable":true,"properties":{"pros":{"type":"array","items":{"type":"string"},"description":"What guests praise, as short phrases.","example":["Central location","Helpful staff"]},"cons":{"type":"array","items":{"type":"string"},"description":"What guests criticise, as short phrases.","example":["Small rooms"]},"categories":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"Category name.","example":"Cleanliness"},"rating":{"type":"number","description":"Category score out of 10.","example":8.9},"description":{"type":"string","description":"What guests say about this category.","example":"Guests praise the housekeeping."}}},"description":"Score per category, such as cleanliness, location or staff.","example":[{"name":"Cleanliness","rating":8.9,"description":"Guests praise the housekeeping."}]},"updated_at":{"type":"string","description":"When the summary was last updated (RFC 3339). Absent when unknown.","example":"2026-09-30T00:00:00Z"}},"required":["pros","cons","categories"],"description":"Summary of what guests say. Null when the hotel has no review summary."},"reviews":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Review id.","example":"12345"},"headline":{"type":"string","description":"Review title.","example":"Great stay"},"score":{"type":"number","description":"This reviewer's score out of 10.","example":9},"pros":{"type":"string","description":"What this reviewer liked, as they wrote it.","example":"Location, breakfast"},"cons":{"type":"string","description":"What this reviewer disliked, as they wrote it.","example":"Street noise at night"},"date":{"type":"string","description":"Review date, `YYYY-MM-DD`. Absent when unknown.","example":"2026-09-12"},"language":{"type":"string","description":"Language code of the review.","example":"en"},"reviewer_name":{"type":"string","description":"The reviewer's display name.","example":"Ana"}}},"description":"One page of individual reviews, newest first. Empty when the hotel has no reviews or the page starts past the last one.","example":[{"id":"12345","headline":"Great stay","score":9,"pros":"Location, breakfast","cons":"Street noise at night","date":"2026-09-12","language":"en","reviewer_name":"Ana"}]},"page":{"type":"object","properties":{"limit":{"type":"integer","description":"Reviews requested per page.","example":10},"offset":{"type":"integer","description":"Reviews skipped before this page.","example":0},"total_count":{"type":"integer","nullable":true,"description":"Total reviews available for this hotel, when known.","example":1243}},"required":["limit","offset","total_count"],"description":"Where this page sits among the hotel's reviews."}},"required":["hotel","summary","reviews","page"]},"BestPriceGuaranteeOffer":{"type":"object","properties":{"offer_id":{"type":"string","description":"Deprecated: use `trip_item_token` instead. Same value.","example":"htl_neg_ogydqmrsgaydamjqgezdgnbvgy","deprecated":true},"trip_item_token":{"type":"string","description":"The new offer. Pass it as `add_item.trip_item_token` to `POST /v1/trip`, then check out as for any hotel offer. A trip that holds it can hold nothing else: start a new trip, or send `remove_item` for the offer it replaces together with `add_item` in one call. Adding it beside another item is refused with 409.","example":"htl_neg_ogydqmrsgaydamjqgezdgnbvgy"},"price_money":{"allOf":[{"$ref":"#/components/schemas/MoneyValue"},{"description":"What the new offer costs, payable at booking: the price matched to the reference. The price at checkout can move with the supplier."}]},"original":{"type":"object","properties":{"kind":{"type":"string","description":"How the Jinko product was named in the request. `offer`: a hotel search offer, in `trip_item_token`. Other kinds may be added; read the fields that kind names.","example":"offer"},"trip_item_token":{"type":"string","description":"The offer that was submitted. Present when `kind` is `offer`.","example":"htl_8f3c1d5e9a7b2c40"},"offer_id":{"type":"string","description":"Deprecated: use `trip_item_token` instead. Same value.","example":"htl_8f3c1d5e9a7b2c40","deprecated":true},"total_money":{"allOf":[{"$ref":"#/components/schemas/MoneyValue"},{"description":"The standard price of the same rate at the time of the review. It can be lower than the price the earlier search showed for the submitted offer: a search for one hotel can price a rate lower than an area search did."}]}},"required":["kind","total_money"],"description":"The offer the new one replaces, and its standard price."},"reference":{"type":"object","properties":{"total_money":{"allOf":[{"$ref":"#/components/schemas/MoneyValue"},{"description":"The reference price as submitted."}]},"verified":{"type":"boolean","description":"`true` when Jinko read the reference page and it showed the same stay at or under the submitted total.","example":true}},"required":["total_money","verified"],"description":"The reference the offer was reviewed against."},"saving_money":{"allOf":[{"$ref":"#/components/schemas/MoneyValue"},{"description":"`original.total_money` minus `price_money`: the saving against the current standard price. Zero when that price was already at or below the reference."}]},"expires_at":{"type":"string","description":"Until when the new offer can be added to a trip (RFC 3339, UTC). At most 30 minutes after the review.","example":"2026-10-12T10:30:00Z"}},"required":["offer_id","trip_item_token","price_money","original","reference","saving_money","expires_at"]},"BestPriceGuaranteeDeclineReason":{"type":"string","description":"Why there is no offer. The reasons known today are listed here; the set grows, so treat a reason that is not listed as \"no offer\". `feature_disabled`: Best Price Guarantee is not enabled for this key. `product_not_found`: the offer is not known, or older than 75 minutes — search again. `product_not_supported`: not a single-room hotel offer from a search. `rate_unavailable`: the same room, board and cancellation terms could not be found again — search again and resubmit. `reference_source_not_allowed`, `reference_invalid`, `currency_mismatch`: the reference cannot be used as submitted. `price_cannot_be_matched`: this price cannot be offered for this rate. `reference_not_lower`: the current price of this rate is already at or below the reference. After either of these two, search the hotel alone (`hotel_ids`) to get its current price: it can be lower than the price the earlier search showed. `objective_not_supported`: the requested objective is not available. `temporarily_unavailable`, `timeout`: the review did not complete — submitting again may succeed. Reasons from the check of the reference page, each with a specific `decline.detail`: `hotel_mismatch`: the page is for another hotel; `stay_mismatch`: the link or the page is for other dates or another number of guests, so it cannot be compared; `conditions_mismatch`: the page sells the stay only with another room, board or cancellation terms; `reference_mismatch`: the page shows a higher total for the stay than the reference submitted.","example":"price_cannot_be_matched"},"BestPriceGuaranteeResponse":{"type":"object","properties":{"id":{"type":"string","description":"Identifies the request; read its result with `GET /v1/best_price_guarantee/{id}`. Absent when the submission was declined before being recorded (`feature_disabled`, an unknown product).","example":"ngo_mfrggzdfmztwq2lknnwg23tpob"},"status":{"type":"string","description":"`pending`: the review is still running — read the request again; this is not a decline. It can take up to a few minutes when the reference page is checked. `offered`: `offer` holds the new offer. `declined`: no offer, see `decline.reason`. `expired`: an offer was made and not used before `expires_at`. Treat any other value as `pending`.","example":"offered"},"poll_after_seconds":{"type":"integer","description":"Present while `status` is `pending`: a suggested pause before reading again.","example":2},"offer":{"$ref":"#/components/schemas/BestPriceGuaranteeOffer"},"decline":{"type":"object","properties":{"reason":{"$ref":"#/components/schemas/BestPriceGuaranteeDeclineReason"},"detail":{"type":"string","description":"What the check of the reference page found, when the decline comes from it. Written for the traveller: pass it on as it is.","example":"the page shows a stay from 2026-11-20 to 2026-11-22; the Jinko offer is from 2026-11-17 to 2026-11-19"}},"required":["reason"],"description":"Present when `status` is `declined`."}},"required":["status"],"example":{"id":"ngo_mfrggzdfmztwq2lknnwg23tpob","status":"offered","offer":{"trip_item_token":"htl_neg_ogydqmrsgaydamjqgezdgnbvgy","offer_id":"htl_neg_ogydqmrsgaydamjqgezdgnbvgy","price_money":{"value":41250,"currency":"USD","decimal_places":2,"display":"USD 412.50"},"original":{"kind":"offer","trip_item_token":"htl_8f3c1d5e9a7b2c40","offer_id":"htl_8f3c1d5e9a7b2c40","total_money":{"value":43780,"currency":"USD","decimal_places":2,"display":"USD 437.80"}},"reference":{"total_money":{"value":41250,"currency":"USD","decimal_places":2,"display":"USD 412.50"},"verified":true},"saving_money":{"value":2530,"currency":"USD","decimal_places":2,"display":"USD 25.30"},"expires_at":"2026-10-12T10:30:00Z"}}},"BestPriceGuaranteeReference":{"type":"object","properties":{"url":{"type":"string","minLength":1,"description":"The https link to the page where the traveller saw the lower price. It must be on a site accepted as a reference; another site is answered `declined` with reason `reference_source_not_allowed`.","example":"https://www.booking.com/hotel/fr/example.html?checkin=2026-11-10"},"screenshot_url":{"type":"string","minLength":1,"description":"An https address a screenshot of that page can be downloaded from. Required on most accounts: a submission without it is then answered `declined` with reason `reference_invalid`.","example":"https://files.example.com/screenshots/8f3.png"},"total":{"type":"string","minLength":1,"description":"The total payable AT BOOKING on that page, as a plain decimal string: no currency symbol, no thousands separator, at most the decimal places of `currency`. Taxes payable at the property are not part of it.","example":"412.50"},"currency":{"type":"string","minLength":3,"maxLength":3,"description":"The ISO 4217 currency of `total`. It must be the currency of the offer being compared (`total_amount_money.currency` of the offer in `hotel_search`). A price in another currency is answered `declined` with reason `currency_mismatch`; nothing is converted.","example":"USD"}},"required":["url","total","currency"]},"BestPriceGuaranteeRequest":{"type":"object","properties":{"product":{"type":"string","enum":["hotel"],"description":"The kind of product the request is about. Only `hotel` exists today.","example":"hotel"},"objective":{"type":"string","enum":["price_parity"],"description":"What is asked for. `price_parity` (the default, and the only value today) asks for the reference price, nothing lower.","example":"price_parity"},"trip_item_token":{"type":"string","minLength":1,"description":"The hotel offer the traveller compared: `hotels[].offers[].trip_item_token` from `POST /v1/hotel_search`. It names the hotel, the dates, the party and the rate. An offer is kept for 75 minutes after the search that returned it.","example":"htl_8f3c1d5e9a7b2c40"},"reference":{"$ref":"#/components/schemas/BestPriceGuaranteeReference"},"wait_seconds":{"type":"integer","minimum":0,"maximum":20,"description":"How long this call waits for the result before answering, 0–20 seconds (default 0: answer at once). It only decides when the call returns; the review continues either way, and a call that waited without a result answers `pending`.","example":15}},"required":["product","trip_item_token","reference"]},"CarImage":{"type":"object","properties":{"url":{"type":"string","example":"https://images.example.com/cars/6f2a.jpg"}},"description":"One supplier-published photo of the vehicle class. Only the URL is carried: the provider labels a size rather than publishing pixel dimensions."},"CarVehicle":{"type":"object","properties":{"acriss_code":{"type":"string","example":"CDMR"},"name":{"type":"string","example":"Honda Accord or similar"},"category":{"type":"string","example":"compact"},"type":{"type":"string","example":"sedan"},"transmission":{"type":"string","example":"manual"},"fuel_type":{"type":"string"},"seats":{"type":"string","description":"A string — suppliers publish ranges like \"5-7\".","example":"5"},"doors":{"type":"string","description":"A string, like seats."},"big_suitcases":{"type":"integer","example":2},"small_suitcases":{"type":"integer","example":1},"air_conditioned":{"type":"boolean"},"built_in_gps":{"type":"boolean"},"model_guaranteed":{"type":"boolean","description":"False is the ordinary \"or similar\" rental promise: the supplier guarantees the class, not this exact model. Do not present the model as confirmed unless this is true."},"supplier_code":{"type":"string","description":"The supplier's own code for this vehicle class, not for the rental company — cars from one company carry many different values here. Opaque; do not parse or match companies on it.","example":"D"},"images":{"type":"array","items":{"$ref":"#/components/schemas/CarImage"}}}},"CarAmount":{"type":"object","properties":{"value":{"type":"integer","description":"Integer amount in MINOR units, always paired with decimal_places — divide by 10 ** decimal_places to display. Example: value 15977 with decimal_places 2 is 159.77 USD. Rendering this field directly shows prices 100x too high for 2-decimal currencies.","example":41250},"currency":{"type":"string","description":"ISO 4217 currency code.","example":"EUR"},"decimal_places":{"type":"integer","description":"Scale of the integer value/amount: display = integer / 10 ** decimal_places. Always sent alongside minor-unit amounts. Usually the ISO 4217 digits of the currency (2 for USD/EUR/GBP, 0 for JPY/KRW, 3 for BHD/JOD/KWD) but currency-converted prices can carry a different scale — ALWAYS use the decimal_places sent with the amount, never a hardcoded 2. Only if the field is genuinely absent on a value-shaped object, fall back to the ISO digits for the currency.","example":2},"display":{"type":"string","description":"The same figure as a string ready to show: the ISO 4217 code, a space, then the amount, e.g. \"USD 159.77\". With decimal_places it is written at exactly that scale (value 1561500, JPY, decimal_places 2 is \"JPY 15615.00\"); without, at the ISO digits of the currency. Show this; compute with the number. Absent when the figure is not money (a cancellation step expressed as a percent or a number of nights) or could not be read unambiguously.","example":"EUR 412.50"}}},"CarCoverage":{"type":"object","properties":{"name":{"type":"string","example":"Collision Damage Waiver"},"excess":{"$ref":"#/components/schemas/CarAmount"},"included_in_vehicle_price":{"type":"boolean"},"mandatory":{"type":"boolean"},"paid_at":{"type":"string","example":"now"},"price":{"$ref":"#/components/schemas/CarAmount"}},"description":"One insurance line on a package. `excess` is the driver’s maximum liability under it — absent when the supplier stated none, which is not zero excess. `price` is the line’s own charge when it is not already inside the vehicle price."},"CarFee":{"type":"object","properties":{"name":{"type":"string","example":"One-way fee"},"included_in_vehicle_price":{"type":"boolean"},"mandatory":{"type":"boolean"},"paid_at":{"type":"string","example":"local"},"price":{"$ref":"#/components/schemas/CarAmount"}},"description":"One fee attached to the rental, with when it is paid (`paid_at`) and whether it is already inside the vehicle price."},"CarPackage":{"type":"object","properties":{"name":{"type":"string","example":"Fully Inclusive"},"code":{"type":"string","description":"The provider's own code for this rate package. Opaque.","example":"FI"},"supplier_name":{"type":"string","description":"The rental company the driver actually collects the car from — Avis, Hertz, Europcar, Sixt. It is that company, not Jinko or any intermediary, whose desk, deposit rules and fuel policy apply, so it is the supplier identity to show a customer. Read off the pick-up branch, so a one-way rental names the collecting company, never the returning one.","example":"Avis"},"supplier_code":{"type":"string","description":"The pick-up branch's own code, scoped to a station rather than to a company. Opaque, and NOT comparable with vehicle.supplier_code — match companies on supplier_name.","example":"DEMO_BCN"},"fuel_policy":{"type":"string","example":"full_to_full"},"mileage_unlimited":{"type":"boolean"},"mileage_allowance":{"type":"string"},"inclusions":{"type":"array","items":{"nullable":true}},"coverages":{"type":"array","items":{"$ref":"#/components/schemas/CarCoverage"}},"fees":{"type":"array","items":{"$ref":"#/components/schemas/CarFee"}}}},"CarBranch":{"type":"object","properties":{"name":{"type":"string","example":"Lyon Saint-Exupery Airport"},"address":{"type":"string"},"city":{"type":"string","example":"Lyon"},"country":{"type":"string","example":"FR"},"phone":{"type":"string"},"date_time":{"type":"string","description":"The pick-up or drop-off time at this branch, branch-local.","example":"2026-09-12T10:00:00"},"time_zone":{"type":"string","example":"Europe/Paris"},"latitude":{"type":"number"},"longitude":{"type":"number"},"opening_hours":{"type":"string","description":"The branch's published hours, as supplied. Free text, not a parseable schedule."},"out_of_hours":{"type":"boolean","description":"This pick-up or drop-off falls outside the published hours, which usually carries a surcharge. Only asserted when that day's hours were published, so false means \"within hours, or not stated\" and never \"checked and fine\"."},"requires_flight_number":{"type":"string","description":"The branch policy: \"never\", \"always\" or \"out_of_hours_pickup\". Jinko carries no flight number today, so \"always\" means this branch cannot be booked — prefer an offer from another branch.","example":"never"},"terminals":{"type":"array","items":{"type":"string"},"description":"The airport terminals this branch serves, when it is at one."},"is_meet_and_greet":{"type":"boolean","description":"The supplier meets the driver instead of staffing a desk, so there is nowhere to walk up to. Tell the customer to expect a meeting point."}}},"CarOfferPrice":{"type":"object","properties":{"pay_now":{"allOf":[{"$ref":"#/components/schemas/CarAmount"},{"description":"The only amount Jinko charges at checkout."}]},"due_at_desk":{"allOf":[{"$ref":"#/components/schemas/CarAmount"},{"description":"Collected by the rental desk locally, in local currency. Display-only."}]},"estimated_total":{"allOf":[{"$ref":"#/components/schemas/CarAmount"},{"description":"pay_now + due_at_desk, where both are known."}]},"deposit":{"allOf":[{"$ref":"#/components/schemas/CarAmount"},{"description":"Security hold taken at the desk, not a charge."}]}}},"CarDriverAgeRange":{"type":"object","properties":{"min":{"type":"integer","example":25},"max":{"type":"integer","example":75}},"description":"The age band this price was quoted for, as the supplier states it. A driver outside the band is refused at the counter."},"CarCancellationFee":{"type":"object","properties":{"type":{"type":"string","example":"cancellation"},"fee":{"$ref":"#/components/schemas/CarAmount"},"non_refundable":{"type":"boolean"},"applicable_from":{"type":"string"},"applicable_to":{"type":"string"},"applicable_now":{"type":"boolean"},"seconds_before_pick_up":{"type":"number"}}},"CarOffer":{"type":"object","properties":{"trip_item_token":{"type":"string","description":"The trip item token. Pass verbatim to POST /v1/trip as `add_item.trip_item_token` to add this rental to a trip.","example":"car_9f3b2a17c4e8d501"},"offer_id":{"type":"string","description":"Deprecated: use `trip_item_token` instead. Same value.","example":"car_9f3b2a17c4e8d501","deprecated":true},"vehicle":{"$ref":"#/components/schemas/CarVehicle"},"package":{"$ref":"#/components/schemas/CarPackage"},"pick_up":{"$ref":"#/components/schemas/CarBranch"},"drop_off":{"$ref":"#/components/schemas/CarBranch"},"price":{"$ref":"#/components/schemas/CarOfferPrice"},"driver_age_range":{"$ref":"#/components/schemas/CarDriverAgeRange"},"on_request":{"type":"boolean","description":"The supplier confirms availability after booking rather than instantly."},"cancellation_fees":{"type":"array","items":{"$ref":"#/components/schemas/CarCancellationFee"},"description":"Fee tiers by time before pick-up. An EMPTY list means the schedule is unknown — never render it as free cancellation."},"expires_at":{"type":"string","description":"When the trip_item_token stops being addable to a trip. Re-search after this.","example":"2026-09-12T10:30:00Z"}},"required":["trip_item_token","offer_id"]},"CarResolvedPlace":{"type":"object","properties":{"name":{"type":"string","example":"Lyon City Centre"},"kind":{"type":"string","description":"One of \"airport\", \"railway\", \"port\" or \"bus\" — absent for a city-centre rental office.","example":"airport"},"city":{"type":"string","example":"Lyon"},"country":{"type":"string","example":"FR"}},"description":"How a free-text `place` was interpreted. Echoed so the caller can confirm the resolution."},"CarSearchResponse":{"type":"object","properties":{"offers":{"type":"array","items":{"$ref":"#/components/schemas/CarOffer"}},"currency":{"type":"string","example":"EUR"},"resolved_place":{"$ref":"#/components/schemas/CarResolvedPlace"},"candidates":{"type":"array","items":{"$ref":"#/components/schemas/CarResolvedPlace"},"description":"Returned instead of offers when a free-text `place` is ambiguous. Retry with `place` set to a candidate's `name`; when two candidates share a name, qualify it with the city — `\"City Centre, Lyon\"`."},"warnings":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}},"CarGeo":{"type":"object","properties":{"latitude":{"type":"number","minimum":-90,"maximum":90,"example":45.7256},"longitude":{"type":"number","minimum":-180,"maximum":180,"example":5.0811},"range":{"type":"integer","minimum":50,"description":"Search radius around the coordinates, in metres (minimum 50).","example":5000}},"required":["latitude","longitude","range"],"description":"Coordinates + radius. Searches the closest rental branches. Geo search is round-trip only: `drop_off` must be omitted or name the same place."},"CarPickUp":{"type":"object","properties":{"date_time":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}$","description":"Pick-up date-time, branch-local, no timezone.","example":"2026-09-12T10:00:00"},"airport_code":{"type":"string","pattern":"^[A-Za-z]{3}$","description":"IATA airport code.","example":"LYS"},"place":{"type":"string","minLength":1,"description":"Free-text location — a city, district or landmark. Resolved server-side; when the text is ambiguous the response carries `candidates` instead of offers — retry with a candidate's `name`.","example":"Lyon city centre"},"geo":{"$ref":"#/components/schemas/CarGeo"}},"required":["date_time"]},"CarDropOff":{"type":"object","properties":{"date_time":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}$","description":"Drop-off date-time, branch-local. May instead be given as the top-level `drop_off_date_time`."},"airport_code":{"type":"string","pattern":"^[A-Za-z]{3}$","description":"IATA airport code.","example":"LYS"},"place":{"type":"string","minLength":1,"description":"Free-text location — a city, district or landmark. Resolved server-side; when the text is ambiguous the response carries `candidates` instead of offers — retry with a candidate's `name`.","example":"Lyon city centre"},"geo":{"$ref":"#/components/schemas/CarGeo"}},"description":"Where the car is returned. Omit for a round trip (return to the pick-up branch) and set `drop_off_date_time` instead."},"CarSearchRequest":{"type":"object","properties":{"pick_up":{"$ref":"#/components/schemas/CarPickUp"},"drop_off":{"$ref":"#/components/schemas/CarDropOff"},"drop_off_date_time":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}$","description":"Drop-off date-time for a round trip, branch-local. Required when `drop_off` is omitted.","example":"2026-09-15T10:00:00"},"driver_age":{"type":"integer","minimum":18,"maximum":99,"description":"Driver's age at pick-up (18-99). Pricing and availability are age-dependent.","example":30},"residence_country":{"type":"string","pattern":"^[A-Za-z]{2}$","description":"Driver's country of residence, ISO 3166-1 alpha-2. Rates and inclusions vary by residence.","example":"FR"},"currency":{"type":"string","minLength":3,"maxLength":3,"description":"ISO 4217 display currency. Defaults to USD.","example":"EUR"},"lang":{"type":"string","description":"BCP-47 language tag for vehicle and branch text.","example":"en-gb"},"intent":{"$ref":"#/components/schemas/IntentInput"}},"required":["pick_up","driver_age","residence_country"],"description":"`pick_up` names exactly one of `airport_code`, `place` or `geo`, and a drop-off time must come from `drop_off.date_time` or `drop_off_date_time`. These rules are enforced at runtime but cannot be expressed in JSON Schema, so they do not appear in `required`.","example":{"pick_up":{"airport_code":"LYS","date_time":"2026-09-12T10:00:00"},"drop_off_date_time":"2026-09-15T10:00:00","driver_age":30,"residence_country":"FR","currency":"EUR"}},"CarCancelPreviewResponse":{"type":"object","properties":{"quote_id":{"type":"string","description":"This preview’s quote. Pass it as `quote_id` to car_cancel_commit — it binds the commit to the fee quoted here. Treat as an opaque string; the format may evolve.","example":"ccl_2f7c9a4e8b1d4c6f9e3a5b7d1c2e4f6a"},"cancellation_id":{"type":"string","description":"Deprecated: use `quote_id` instead. Same value.","example":"ccl_2f7c9a4e8b1d4c6f9e3a5b7d1c2e4f6a","deprecated":true},"cancellable":{"type":"boolean","example":true},"fee_known":{"type":"boolean","description":"false means the fee could not be established — UNKNOWN, not free. Render as \"we will confirm the fee\".","example":true},"fee_type":{"type":"string","example":"cancellation"},"fee":{"$ref":"#/components/schemas/CarAmount"},"paid":{"allOf":[{"$ref":"#/components/schemas/CarAmount"},{"description":"What the customer originally paid."}]},"refund_amount":{"allOf":[{"$ref":"#/components/schemas/CarAmount"},{"description":"paid minus fee. Absent whenever the fee is unknown — absent is not zero."}]},"currency":{"type":"string","example":"EUR"},"non_refundable":{"type":"boolean"},"refund_pending_review":{"type":"boolean","description":"true means the refund figure is right but will be issued by hand rather than automatically (the rental was changed after it was paid for). The cancellation itself still goes through online; not a failure."},"refund_review_reason":{"type":"string","description":"Why a person is involved, whenever `manual_required` or `refund_pending_review` is true, in words the customer can be shown. Where the reason would be the supplier’s own words — as on a `cancellable: false` preview of a rental that cannot be cancelled at all — it reads “This request needs manual handling by Jinko support.”","example":"the cancellation fee for this rental could not be established; a person will complete the cancellation"},"manual_required":{"type":"boolean","description":"true means this cancellation cannot be completed online — the fee could not be established, the rental was changed after it was paid for, or the fee is in another currency or above the rental. `cancellable` is false, no refund figure is given and `refund_review_reason` says why. It is not a dead end: car_cancel_commit takes this `cancellation_id` with `manual_ok: true`, which records the cancellation for a Jinko agent to complete and answers `state: pending` with `refund_pending_review: true`. Without the flag such a commit is refused 409 `manual_required`, so a customer is never put in an agent queue unasked. Absent (or false) on every quote that can be committed online.","example":false},"expires_at":{"type":"string","description":"When this preview stops being committable (RFC 3339). A commit after it is refused 409 `QUOTE_EXPIRED`; preview again. Absent where the quote carries no deadline.","example":"2026-09-21T10:15:00Z"}}},"CarServicingConflictCode":{"type":"string","enum":["quote_drift","QUOTE_EXPIRED","active_operation_exists","not_cancellable","funds_unavailable","manual_required"],"description":"Which refusal this is. `manual_required` — the preview said `manual_required: true` (fee unknown, rental changed after payment, fee in another currency or above the rental) and the commit did not carry `manual_ok: true`; resend the same commit with it, once the customer agrees, to hand the cancellation to a Jinko agent. `quote_drift` — the refund moved since the preview (the fee tier changed as pick-up drew nearer); `retry_quote_id` is a fresh preview reference and `current.refund` the figure now — show it to the customer and commit against `retry_quote_id`. `QUOTE_EXPIRED` — the preview stopped binding (`expires_at`); preview again. `active_operation_exists` — an exchange or another cancellation is in flight for this rental; wait for it to finish (car_cancel_status) rather than starting a second. `not_cancellable` — the rental can no longer be cancelled as it stands (already cancelled since the preview, or over); `error.message` carries the reason, and car_cancel_status reports the cancellation that stands. `funds_unavailable` — the original payment cannot cover the refund yet; `shortfall` is how much is missing and `blocking` names the refunds that have to settle first. In every case NOTHING was cancelled and no money moved."},"CarServicingConflictResponse":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"CONFLICT"},"message":{"type":"string"},"doc_url":{"type":"string"}},"required":["code","message"]},"code":{"$ref":"#/components/schemas/CarServicingConflictCode"},"retry_quote_id":{"type":"string","description":"A fresh `quote_id`, on `quote_drift`. Show the customer the figure in `current.refund`, then commit against this `quote_id` as you would one from car_cancel_preview.","example":"ccl_01J8AB4N7GLY3Q0DXK2M5R9VWT"},"requote":{"type":"string","description":"Deprecated: use `retry_quote_id` instead. Same value.","example":"ccl_01J8AB4N7GLY3Q0DXK2M5R9VWT","deprecated":true},"current":{"type":"object","properties":{"refund":{"$ref":"#/components/schemas/CarAmount"}},"description":"The refund as it stands now, on `quote_drift`: what the customer gets if they accept the fresh preview."},"active_operation":{"type":"string","description":"The cancellation already running on this rental, on `active_operation_exists`. Follow it with car_cancel_status.","example":"ccl_01J7ZR5Q2KME8V4TBN3P8XD6WQ"},"shortfall":{"allOf":[{"$ref":"#/components/schemas/CarAmount"},{"description":"How much of the refund could not be reserved, on `funds_unavailable` — money still moving on the original payment, not a fact about this rental."}]},"blocking":{"type":"array","items":{"type":"string"},"description":"What is holding that money, on `funds_unavailable`: the refunds already in flight against the same payment, which settle before this one can be reserved. Opaque handles — identity only.","example":["re_3UBIxK2eZvKYlo2C"]}},"required":["error","code"]},"BookingItemServicing":{"type":"object","properties":{"can_refund":{"type":"boolean"},"can_void":{"type":"boolean"},"can_exchange":{"type":"boolean"},"refund_support_level":{"type":"string","enum":["AUTO","MANUAL_REQUIRED","UNSUPPORTED"]},"exchange_support_level":{"type":"string","enum":["AUTO","MANUAL_REQUIRED","UNSUPPORTED"]},"refund_unavailable_reason":{"type":"string"},"exchange_unavailable_reason":{"type":"string"},"refund_reason_code":{"type":"string"},"exchange_reason_code":{"type":"string"},"reason_code":{"type":"string"},"support_level":{"type":"string","enum":["AUTO","MANUAL_REQUIRED","UNSUPPORTED"]}},"required":["can_refund","can_void","can_exchange"],"description":"Eligibility of this booked item and its current product. Read the live refund or exchange check before committing; eligibility can change with time."},"BookingItem":{"type":"object","properties":{"booked_item_id":{"type":"string","description":"The item to name with `booked_item_id` on the next request.","example":"itm_aaaaaaaaaaaaafhuxsdxpbzrbfz5hm3a"},"item_id":{"type":"integer","minimum":0,"exclusiveMinimum":true,"maximum":9007199254740991,"description":"Deprecated: use `booked_item_id` instead. The booked item as an integer.","example":42,"deprecated":true},"domain":{"type":"string"},"summary":{"type":"string"},"status":{"type":"string","description":"This item's state in Jinko's own booking and servicing flow. In progress: `pending`, `awaiting_payment`, `preparing`, `prepared`, `processing`, `confirming`. Final: `completed` (booked; for a flight, ticketed), `failed`, `cancelled` (nothing is booked any more: the checkout was never paid, or a cancellation succeeded), `expired_quote` (a quoted price, at checkout or for an exchange, expired before it was confirmed). Needing attention: `exchange_partial_failure`, `needs_review`. Treat the list as open: new values may be added. It changes only through Jinko's steps (payment, booking, ticketing, or a cancellation or exchange you request), never because an airline changed or cancelled a flight.","example":"completed"},"booking_ref":{"type":"string","description":"The Jinko booking reference of the booking this item belongs to, also after an exchange.","example":"JNK-A0AUR2"},"booking_reference":{"type":"string","description":"Deprecated: use `booking_ref` instead. The Jinko reference of the cart this item lives on: after an exchange, the exchange’s own reference.","deprecated":true},"servicing":{"$ref":"#/components/schemas/BookingItemServicing"}},"required":["item_id","domain"]},"BookingSelectionConflict":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["CONFLICT"]},"message":{"type":"string"}},"required":["code","message"]},"code":{"type":"string","enum":["item_selection_required"]},"items":{"type":"array","items":{"$ref":"#/components/schemas/BookingItem"}}},"required":["error","code","items"],"description":"Several items match the requested domain. Choose a booked_item_id from items (the same safe projection as get_booking) and repeat the request with booking_ref and booked_item_id."},"CarCancelPreviewRequest":{"type":"object","properties":{"booking_ref":{"type":"string","minLength":1,"description":"Jinko booking reference. Call get_booking first, then select its booked_item_id for servicing.","example":"JNK-A0AUR2"},"booked_item_id":{"type":"string","pattern":"^itm_[a-z2-7]+$(?![\\s\\S])","description":"The booked item, from get_booking items[].booked_item_id. Preserved across exchanges. Omit only when the booking has exactly one item of the requested domain; otherwise a 409 returns items to choose from.","example":"itm_aaaaaaaaaaaaafhuxsdxpbzrbfz5hm3a"},"last_name":{"type":"string","minLength":1,"description":"Required for guest lookup. A credential that owns the booking may omit it; ownership is checked by the platform.","example":"Doe"},"intent":{"$ref":"#/components/schemas/IntentInput"}},"required":["booking_ref"],"example":{"booking_ref":"JNK-8PT9VS","last_name":"Doe"}},"CarCancelCommitResponse":{"type":"object","properties":{"quote_id":{"type":"string","description":"The quote this cancellation was committed against.","example":"ccl_2f7c9a4e8b1d4c6f9e3a5b7d1c2e4f6a"},"cancellation_id":{"type":"string","description":"Deprecated: use `quote_id` instead. Same value.","example":"ccl_2f7c9a4e8b1d4c6f9e3a5b7d1c2e4f6a","deprecated":true},"state":{"type":"string","description":"`confirmed` — cancelled and the refund issued or on its way. `pending` — recorded but not finished: the platform is still completing it, or (with `refund_pending_review: true`) a Jinko agent has it; read car_cancel_status, do not commit again. `rejected` — the rental company declined; the booking still stands and `rejected_reason` says why. `failed` — the attempt did not complete; read car_cancel_status before trying again.","example":"confirmed"},"fee_known":{"type":"boolean"},"fee":{"$ref":"#/components/schemas/CarAmount"},"paid":{"$ref":"#/components/schemas/CarAmount"},"refund_amount":{"$ref":"#/components/schemas/CarAmount"},"refund_pending_review":{"type":"boolean","description":"true means the booking IS cancelled (or, with `state: pending`, recorded for a Jinko agent) but the refund needs a person before it is issued — do not quote `refund_amount` as final."},"stripe_refund_id":{"type":"string","description":"Deprecated. An internal payment reference.","deprecated":true},"rejected_reason":{"type":"string"},"completed_at":{"type":"string","example":"2026-08-20T15:18:00Z"}}},"CarCancelCommitRequest":{"type":"object","properties":{"booking_ref":{"type":"string","minLength":1,"description":"Jinko booking reference. Call get_booking first, then select its booked_item_id for servicing.","example":"JNK-A0AUR2"},"booked_item_id":{"type":"string","pattern":"^itm_[a-z2-7]+$(?![\\s\\S])","description":"The booked item, from get_booking items[].booked_item_id. Preserved across exchanges. Omit only when the booking has exactly one item of the requested domain; otherwise a 409 returns items to choose from.","example":"itm_aaaaaaaaaaaaafhuxsdxpbzrbfz5hm3a"},"last_name":{"type":"string","minLength":1,"description":"Required for guest lookup. A credential that owns the booking may omit it; ownership is checked by the platform.","example":"Doe"},"intent":{"$ref":"#/components/schemas/IntentInput"},"quote_id":{"type":"string","minLength":1,"description":"The `quote_id` from a preceding car_cancel_preview. Binds the commit to the fee the customer saw.","example":"ccl_2f7c9a4e8b1d4c6f9e3a5b7d1c2e4f6a"},"reason":{"type":"string","description":"Optional free-text cancellation reason.","example":"Change of plans"},"manual_ok":{"type":"boolean","description":"Send `true` to commit a preview that answered `manual_required: true`. The cancellation is then recorded for a Jinko agent — NOTHING is sent to the rental company by this call — and the answer is `state: pending` with `refund_pending_review: true`; the agent completes it and settles the refund by hand, and car_cancel_status reports the outcome. Without the flag such a commit is refused 409 `manual_required`, so a customer is never put in an agent queue unasked: send it only once the customer has agreed to that. On a preview that can be committed online the flag changes nothing.","example":false}},"required":["booking_ref","quote_id"],"example":{"booking_ref":"JNK-8PT9VS","last_name":"Doe","quote_id":"ccl_2f7c9a4e8b1d4c6f9e3a5b7d1c2e4f6a"}},"CarCancelStatusResponse":{"type":"object","properties":{"quote_id":{"type":"string","description":"The quote this cancellation was committed against.","example":"ccl_2f7c9a4e8b1d4c6f9e3a5b7d1c2e4f6a"},"cancellation_id":{"type":"string","description":"Deprecated: use `quote_id` instead. Same value.","example":"ccl_2f7c9a4e8b1d4c6f9e3a5b7d1c2e4f6a","deprecated":true},"state":{"type":"string","description":"The same words as car_cancel_commit: `preview` (quoted, never committed), `pending` (recorded, not finished — the platform or a Jinko agent still has it), `confirmed`, `rejected`, `failed`.","example":"confirmed"},"fee_known":{"type":"boolean"},"fee":{"$ref":"#/components/schemas/CarAmount"},"paid":{"$ref":"#/components/schemas/CarAmount"},"refund_amount":{"$ref":"#/components/schemas/CarAmount"},"refund_pending_review":{"type":"boolean"},"stripe_refund_id":{"type":"string","description":"Deprecated. An internal payment reference.","deprecated":true},"rejected_reason":{"type":"string"}}},"CarCancelStatusRequest":{"type":"object","properties":{"booking_ref":{"type":"string","minLength":1,"description":"Jinko booking reference. Call get_booking first, then select its booked_item_id for servicing.","example":"JNK-A0AUR2"},"booked_item_id":{"type":"string","pattern":"^itm_[a-z2-7]+$(?![\\s\\S])","description":"The booked item, from get_booking items[].booked_item_id. Preserved across exchanges. Omit only when the booking has exactly one item of the requested domain; otherwise a 409 returns items to choose from.","example":"itm_aaaaaaaaaaaaafhuxsdxpbzrbfz5hm3a"},"last_name":{"type":"string","minLength":1,"description":"Required for guest lookup. A credential that owns the booking may omit it; ownership is checked by the platform.","example":"Doe"},"intent":{"$ref":"#/components/schemas/IntentInput"}},"required":["booking_ref"],"example":{"booking_ref":"JNK-8PT9VS","last_name":"Doe"}},"DestinationSnapshotRow":{"type":"object","properties":{"rank":{"type":"number","example":1},"destination":{"type":"string","example":"City:PAR"},"share":{"type":"number","example":0.182}}},"DestinationResponse":{"type":"object","properties":{"rows":{"type":"array","items":{"$ref":"#/components/schemas/DestinationSnapshotRow"}}},"required":["rows"]},"MarketSnapshotRow":{"type":"object","properties":{"rank":{"type":"number","example":1},"market":{"type":"string","example":"Country:US"},"share":{"type":"number","example":0.214}}},"MarketResponse":{"type":"object","properties":{"rows":{"type":"array","items":{"$ref":"#/components/schemas/MarketSnapshotRow"}}},"required":["rows"]},"AudienceSnapshotRow":{"type":"object","properties":{"rank":{"type":"number","example":1},"audience":{"type":"string","example":"Couple"},"share":{"type":"number","example":0.37}}},"AudienceResponse":{"type":"object","properties":{"rows":{"type":"array","items":{"$ref":"#/components/schemas/AudienceSnapshotRow"}}},"required":["rows"]},"TrendSeries":{"type":"object","properties":{"label":{"type":"string","description":"Dimension value for ranked endpoints.","example":"FR"},"origin":{"type":"string","example":"Country:US"},"destination":{"type":"string","example":"Country:FR"},"series":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string","example":"2026-06-20"},"value":{"type":"number","example":1234}},"required":["date","value"]}},"summary":{"type":"object","properties":{"total":{"type":"number","example":34567},"avg_per_day":{"type":"number","example":1191},"growth_pct":{"type":"number","nullable":true,"example":12.3},"peak_date":{"type":"string","example":"2026-06-14"},"peak_value":{"type":"number","example":1890}}}},"required":["series"]},"TrendResponse":{"type":"object","properties":{"granularity":{"type":"string","example":"week"},"from":{"type":"string","example":"2026-06-19"},"to":{"type":"string","example":"2026-06-25"},"series":{"type":"array","items":{"$ref":"#/components/schemas/TrendSeries"}}},"required":["granularity","from","to","series"]},"ItemExchangeView":{"type":"object","properties":{"state":{"type":"string","description":"`attention_required` when the request is with Jinko support; `in_progress` when support is completing it; `succeeded` when completed; `failed` when support closed it.","example":"attention_required"},"operation":{"type":"string","description":"The operation is `exchange`.","example":"exchange"},"reason":{"type":"string"},"park_deadline":{"type":"string"},"next_run_at":{"type":"string"},"paid":{"$ref":"#/components/schemas/Money"},"refund_due":{"type":"object","properties":{"amount":{"type":"number","description":"Deprecated: use `amount_money` instead. Alternative to `value` on some endpoints (the two never appear together); its scale depends on decimal_places. When this object carries decimal_places, amount is an INTEGER in minor units — divide by 10 ** decimal_places (e.g. select_ancillaries total_with_ancillaries). When there is no decimal_places field, amount is a decimal in MAJOR units, safe to display as-is (e.g. trip and checkout totals).","deprecated":true},"value":{"type":"integer","description":"Deprecated: use `amount_money` instead. Integer amount in MINOR units, always paired with decimal_places — divide by 10 ** decimal_places to display. Example: value 15977 with decimal_places 2 is 159.77 USD. Rendering this field directly shows prices 100x too high for 2-decimal currencies.","example":41250,"deprecated":true},"currency":{"type":"string","description":"Deprecated: use `amount_money` instead. ISO 4217 currency code.","example":"USD","deprecated":true},"decimal_places":{"type":"integer","description":"Deprecated: use `amount_money` instead. Scale of the integer value/amount: display = integer / 10 ** decimal_places. Always sent alongside minor-unit amounts. Usually the ISO 4217 digits of the currency (2 for USD/EUR/GBP, 0 for JPY/KRW, 3 for BHD/JOD/KWD) but currency-converted prices can carry a different scale — ALWAYS use the decimal_places sent with the amount, never a hardcoded 2. Only if the field is genuinely absent on a value-shaped object, fall back to the ISO digits for the currency.","example":2,"deprecated":true},"display":{"type":"string","description":"Deprecated: use `amount_money` instead. The same figure as a string ready to show: the ISO 4217 code, a space, then the amount, e.g. \"USD 159.77\". With decimal_places it is written at exactly that scale (value 1561500, JPY, decimal_places 2 is \"JPY 15615.00\"); without, at the ISO digits of the currency. Show this; compute with the number. Absent when the figure is not money (a cancellation step expressed as a percent or a number of nights) or could not be read unambiguously.","example":"USD 412.50","deprecated":true},"amount_money":{"allOf":[{"$ref":"#/components/schemas/MoneyValue"},{"description":"What the operation says the customer is owed."}]},"penalty":{"$ref":"#/components/schemas/Money"},"source":{"type":"string","description":"Where the figure came from: `quoted` (what the platform expects) or `provider_confirmed` (what the supplier agreed to).","example":"quoted"}}},"refunded":{"type":"object","properties":{"amount":{"type":"number","description":"Deprecated: use `amount_money` instead. Alternative to `value` on some endpoints (the two never appear together); its scale depends on decimal_places. When this object carries decimal_places, amount is an INTEGER in minor units — divide by 10 ** decimal_places (e.g. select_ancillaries total_with_ancillaries). When there is no decimal_places field, amount is a decimal in MAJOR units, safe to display as-is (e.g. trip and checkout totals).","deprecated":true},"value":{"type":"integer","description":"Deprecated: use `amount_money` instead. Integer amount in MINOR units, always paired with decimal_places — divide by 10 ** decimal_places to display. Example: value 15977 with decimal_places 2 is 159.77 USD. Rendering this field directly shows prices 100x too high for 2-decimal currencies.","example":41250,"deprecated":true},"currency":{"type":"string","description":"Deprecated: use `amount_money` instead. ISO 4217 currency code.","example":"USD","deprecated":true},"decimal_places":{"type":"integer","description":"Deprecated: use `amount_money` instead. Scale of the integer value/amount: display = integer / 10 ** decimal_places. Always sent alongside minor-unit amounts. Usually the ISO 4217 digits of the currency (2 for USD/EUR/GBP, 0 for JPY/KRW, 3 for BHD/JOD/KWD) but currency-converted prices can carry a different scale — ALWAYS use the decimal_places sent with the amount, never a hardcoded 2. Only if the field is genuinely absent on a value-shaped object, fall back to the ISO digits for the currency.","example":2,"deprecated":true},"display":{"type":"string","description":"Deprecated: use `amount_money` instead. The same figure as a string ready to show: the ISO 4217 code, a space, then the amount, e.g. \"USD 159.77\". With decimal_places it is written at exactly that scale (value 1561500, JPY, decimal_places 2 is \"JPY 15615.00\"); without, at the ISO digits of the currency. Show this; compute with the number. Absent when the figure is not money (a cancellation step expressed as a percent or a number of nights) or could not be read unambiguously.","example":"USD 412.50","deprecated":true},"amount_money":{"allOf":[{"$ref":"#/components/schemas/MoneyValue"},{"description":"What was refunded, as read back from the payment provider."}]},"at":{"type":"string","example":"2026-09-03T12:30:00Z"},"stripe_reference":{"type":"string","description":"Deprecated. An internal payment reference.","example":"re_3UBIxK2eZvKYlo2C","deprecated":true}}}},"description":"An exchange Jinko support handles by hand. Present only when the item has such a request; absent otherwise. `operation` is `exchange`. State `attention_required` means the request is with Jinko support and `refund_due` is the quoted refund; `in_progress` means support is completing it; `succeeded` means completed and `refunded` says what was refunded and when; `failed` means support closed the request. Hidden while the item has a cancellation (`cancellation.state` other than `none`)."},"SeatRefund":{"type":"object","properties":{"offer_id":{"type":"string"},"seat_number":{"type":"string","example":"12B"},"traveler_ref":{"type":"string","example":"pax_1"},"pax_ref_id":{"type":"string","description":"Deprecated: use `traveler_ref` instead. Same value.","example":"pax_1","deprecated":true},"status":{"type":"string","enum":["pending_airline_refund","refunded","not_refunded"]},"amount":{"allOf":[{"$ref":"#/components/schemas/MoneyValue"},{"description":"The seat price while pending, then the actual customer refund amount (zero when not refunded). Not part of the separate quoted fare refund."}]},"carrier":{"type":"string","description":"Airline display name.","example":"United"},"message":{"type":"string","description":"Customer-facing sentence explaining the seat refund.","example":"Your paid seat will be refunded once United refunds Jinko."},"airline_refund_registered":{"type":"boolean","description":"Whether the airline refund has been registered with the payment provider."}},"required":["offer_id","seat_number","status","amount","carrier","message","airline_refund_registered"],"description":"A carrier-settled paid seat is refunded once the airline refunds Jinko. It is not part of the quoted fare refund; track its separate status here."},"BookedAncillary":{"type":"object","properties":{"type":{"type":"string","enum":["seat","bag","meal","cabin_bag_upgrade","sports_equipment","priority_boarding","assistance","insurance","lounge","frequent_flyer","other"],"description":"What was booked: a seat, a bag, a meal or another service. Treat the list as open: new kinds may be added.","example":"seat"},"seat_number":{"type":"string","example":"12B"},"traveler_ref":{"type":"string","example":"pax_1"},"pax_ref_id":{"type":"string","description":"Deprecated: use `traveler_ref` instead. Same value.","example":"pax_1","deprecated":true},"segment_ref_ids":{"type":"array","items":{"type":"string"},"example":["seg_1"]},"status":{"type":"string","enum":["confirmed","pending","failed"],"description":"For seats, `pending` means the airline has not confirmed the seat yet. `confirmed` means booked with the airline. `failed` means not booked."},"not_charged":{"type":"boolean","description":"true when this seat is not charged to the traveler."},"failure_reason":{"type":"string","nullable":true,"description":"Public partner-facing explanation of a failed or unresolved outcome; never internal diagnostics."},"price":{"$ref":"#/components/schemas/Money"},"refund":{"$ref":"#/components/schemas/SeatRefund"}},"required":["type","status"],"description":"An ancillary selected for this booked item: a seat, a bag, a meal or another service. `seat_number` is set on a seat. Price is omitted for an included or free ancillary; otherwise it uses the item confirmation’s total_paid money shape, in the trip currency. Booking payment totals are unchanged by this list."},"BookingFlightSegment":{"type":"object","properties":{"airline":{"type":"string","description":"Marketing carrier code."},"airline_name":{"type":"string","description":"Marketing carrier name."},"airline_logo":{"type":"string","description":"Marketing carrier logo URL."},"flight_number":{"type":"string","description":"Flight number without the carrier code.","example":"57"},"operating_carrier":{"type":"string","description":"Operating carrier code."},"operating_carrier_info":{"type":"object","properties":{"code":{"type":"string"},"name":{"type":"string"},"logo":{"type":"string"}}},"aircraft":{"type":"string","example":"777"},"departure_airport":{"type":"string"},"departure_city":{"type":"string"},"departure_city_name":{"type":"string"},"arrival_airport":{"type":"string"},"arrival_city":{"type":"string"},"arrival_city_name":{"type":"string"},"departure_time":{"type":"string","description":"The scheduled time as booked, in the airport's local time with its UTC offset (RFC 3339), e.g. `2026-11-27T18:05:00-05:00`. Without an offset when the airport's time zone is unknown.","example":"2026-11-27T18:05:00-05:00"},"arrival_time":{"type":"string","description":"The scheduled time as booked, in the airport's local time with its UTC offset (RFC 3339), e.g. `2026-11-27T18:05:00-05:00`. Without an offset when the airport's time zone is unknown.","example":"2026-11-28T07:25:00+01:00"},"departure_time_local":{"type":"string","description":"The same wall-clock time without an offset (`YYYY-MM-DDTHH:MM:SS`).","example":"2026-11-27T18:05:00"},"arrival_time_local":{"type":"string","description":"The same wall-clock time without an offset (`YYYY-MM-DDTHH:MM:SS`).","example":"2026-11-28T07:25:00"},"duration_minutes":{"type":"integer"},"layover_minutes":{"type":"integer","description":"Minutes on the ground after this segment, before the next one departs; absent on the last segment of a journey."}},"description":"A flight segment as booked and ticketed. No terminal and no per-segment status are given."},"BookingFlightJourney":{"type":"object","properties":{"origin":{"type":"string"},"origin_name":{"type":"string"},"origin_airport_name":{"type":"string"},"destination":{"type":"string"},"destination_name":{"type":"string"},"destination_airport_name":{"type":"string"},"departure_datetime":{"type":"string","description":"First departure of the journey. The scheduled time as booked, in the airport's local time with its UTC offset (RFC 3339), e.g. `2026-11-27T18:05:00-05:00`. Without an offset when the airport's time zone is unknown.","example":"2026-11-27T18:05:00-05:00"},"arrival_datetime":{"type":"string","description":"Final arrival of the journey. The scheduled time as booked, in the airport's local time with its UTC offset (RFC 3339), e.g. `2026-11-27T18:05:00-05:00`. Without an offset when the airport's time zone is unknown.","example":"2026-11-28T07:25:00+01:00"},"duration_minutes":{"type":"integer"},"stops":{"type":"integer"},"stop_infos":{"type":"array","items":{"type":"object","properties":{"airport_code":{"type":"string"},"duration_minutes":{"type":"integer"}}}},"airline":{"type":"string"},"airline_name":{"type":"string"},"airline_logo":{"type":"string"},"segments":{"type":"array","items":{"$ref":"#/components/schemas/BookingFlightSegment"}}},"description":"One journey of the booked and ticketed itinerary, with its flight segments in flown order."},"BookingFlightDetails":{"type":"object","properties":{"pnr":{"type":"string","description":"Deprecated: use `supplier_references` instead. The validating carrier’s record locator; when it is not known, an id only Jinko’s intermediary answers to.","deprecated":true},"ticket_numbers":{"type":"array","items":{"type":"string"},"description":"The ticket numbers issued for this item. Absent when none is recorded.","example":["0010000000001","0010000000002"]},"airline_locators":{"type":"array","items":{"type":"object","properties":{"carrier_code":{"type":"string"},"carrier_name":{"type":"string"},"locator":{"type":"string"}}},"description":"Deprecated: use `supplier_references` instead. Each airline's own record locator, one per carrier; the item's `supplier_references` lists the same locators.","deprecated":true},"trip_type":{"type":"string","description":"The itinerary shape: `one_way`, `round_trip`, `open_jaw` or `multi_city`. Absent on a booking confirmed before it was recorded.","example":"round_trip"},"origin_city_code":{"type":"string"},"destination_city_code":{"type":"string"},"cabin_class":{"type":"string"},"validating_carrier":{"type":"string"},"validating_carrier_info":{"type":"object","properties":{"code":{"type":"string"},"name":{"type":"string"},"logo":{"type":"string"}}},"legs":{"type":"array","items":{"$ref":"#/components/schemas/BookingFlightJourney"},"description":"Every journey of the itinerary in flown order, as booked and ticketed. It is not refreshed from the airline afterwards: a schedule change or cancellation made by the airline is not reflected here. Absent on a booking confirmed before legs were recorded. After an exchange that re-issued part of the itinerary it holds the re-issued journeys only, so read `trip_type` rather than counting legs."},"outbound_flight":{"$ref":"#/components/schemas/BookingFlightJourney"},"inbound_flight":{"$ref":"#/components/schemas/BookingFlightJourney"},"refund_policy":{"type":"string"},"change_policy":{"type":"string"},"carry_on_baggage":{"type":"string"},"checked_baggage":{"type":"string"}},"description":"Flight confirmation details as booked and ticketed. `outbound_flight` and `inbound_flight` are `legs[0]` and `legs[1]` under older names; `inbound_flight` is the second journey, not necessarily a return."},"BookingHotelDetails":{"type":"object","properties":{"hotel_id":{"type":"string","example":"lp1d2c3"},"hotel_name":{"type":"string","example":"NH Paris Opéra Faubourg"},"address":{"type":"string"},"star_rating":{"type":"integer","example":4},"main_photo_url":{"type":"string"},"check_in":{"type":"string","example":"2026-11-15"},"check_out":{"type":"string","example":"2026-11-17"},"num_nights":{"type":"integer","example":2},"room_name":{"type":"string","example":"Standard Double"},"room_description":{"type":"string"},"max_occupancy":{"type":"integer","example":2},"board_type":{"type":"string","description":"The board code of the meal plan the guest bought, as the rate sold it — the same value the trip item’s `hotel_terms.board_type` stated at checkout (`RO` room only, `BB` bed and breakfast, `HB` half board, …; some suppliers use `BI` for breakfast included). On a multi-room booking, present only when every room is on the same board; read `rooms[].board_type` otherwise. Absent on bookings confirmed before the meal plan was recorded.","example":"BI"},"board_name":{"type":"string","description":"The meal plan the guest bought, in words, as the rate sold it — the same value the trip item’s `hotel_terms.board_name` stated at checkout. On a multi-room booking the rooms’ distinct boards joined with \"; \". Absent on bookings confirmed before the meal plan was recorded.","example":"Breakfast Included"},"is_refundable":{"type":"boolean","example":true},"cancellation_summary":{"type":"string"},"free_cancellation_until":{"type":"string"},"hotel_confirmation_number":{"type":"string","description":"Deprecated: use `supplier_references` instead. The hotel’s own confirmation number, once the property issued one.","deprecated":true},"rooms":{"type":"array","items":{"type":"object","properties":{"room_number":{"type":"integer","example":1},"room_name":{"type":"string","example":"Twin Room"},"board_type":{"type":"string","example":"BB"},"board_name":{"type":"string","example":"BED AND BREAKFAST"},"is_refundable":{"type":"boolean"},"cancellation_summary":{"type":"string"},"free_cancellation_until":{"type":"string"}}},"description":"Each room of a multi-room booking with its own meal plan and cancellation terms, in room order. Absent on a single-room booking."}},"description":"The stay a booked hotel item confirms: the property, the dates, the room, the meal plan the guest bought (`board_type`, `board_name`) and the cancellation terms read at checkout. The platform sends more and all of it reaches you."},"Booking":{"type":"object","properties":{"booking_ref":{"type":"string","description":"The Jinko booking reference of this booking. Looked up with the reference of one of its exchanges, it is still the booking’s own reference. Hand it to cancel, refund and exchange as `booking_ref`.","example":"JNK-XEVGW5"},"booking_reference":{"type":"string","description":"Deprecated: use `booking_ref` instead. The reference the booking was looked up with, which can be an exchange’s own reference.","example":"JNK-XEVGW5","deprecated":true},"booking_state":{"type":"string","enum":["awaiting_payment","processing","completed","partial","failed","cancelled","needs_review"],"description":"The booking’s progress once payment has started; absent before. `awaiting_payment`: waiting for the payment to be confirmed. `processing`: booking with the suppliers. Then one outcome: `completed` (every item booked), `partial` (some items booked), `failed` (nothing booked) or `cancelled`. `needs_review`: held for a person at Jinko to resolve; not final. `fulfillment.status` keeps the detailed status behind it.","example":"completed"},"status":{"type":"string","description":"The booking's state in Jinko's own booking and servicing flow. In progress: `pending`, `awaiting_payment`, `preparing`, `prepared`, `processing`, `confirming`. Final: `completed` (booked; for a flight, ticketed), `partial` (some items booked, others failed), `failed`, `cancelled` (nothing is booked any more: the checkout was never paid, or every item was cancelled), `expired_quote` (a quoted price, at checkout or for an exchange, expired before it was confirmed). Needing attention: `exchange_partial_failure`, `needs_review`. Treat the list as open: new values may be added. It changes only through Jinko's steps (payment, booking, ticketing, or a cancellation or exchange you request), never because an airline changed or cancelled a flight.","example":"completed"},"failure_reason":{"type":"string","nullable":true,"description":"Why the booking failed, when it failed after payment and the cause is known: `offer_unavailable`, `price_changed`, `booking_not_created` (the supplier holds no booking; nothing was booked and no money was taken), `booking_cancelled_at_provider`, `ticketing_refused`. Null otherwise. For `offer_unavailable`, the supplier no longer sells the priced offer. Nothing was booked and the card authorisation was released. Search again and book a new offer. The `booking.failed` webhook carries the same reason with a sentence to show the traveller (`failure_message`).","example":"booking_not_created"},"type":{"type":"string","description":"What was booked — \"flight\" or \"hotel\".","example":"flight"},"last_name":{"type":"string","example":"Doe"},"total_paid":{"allOf":[{"$ref":"#/components/schemas/MoneyValue"},{"description":"What was captured for this booking, gross: refunds are NOT subtracted (the same figure as `paid` in `servicing_summary`; `net_paid` there is the figure after refunds). Absent when the captured amount is unknown, and when the booking was charged in more than one currency — read `servicing_summary`, one row per currency, then."}]},"refund_status":{"type":"object","properties":{"state":{"type":"string","description":"`in_progress`, `succeeded` or `failed`.","example":"succeeded"},"initiated_at":{"type":"string"},"completed_at":{"type":"string"},"refunded_amount":{"$ref":"#/components/schemas/Money"}}},"servicing_summary":{"type":"array","items":{"type":"object","properties":{"currency":{"type":"string","example":"USD"},"paid":{"$ref":"#/components/schemas/Money"},"refunded":{"$ref":"#/components/schemas/Money"},"net_paid":{"$ref":"#/components/schemas/Money"},"attention_required":{"type":"boolean"}},"description":"One currency’s servicing totals. `paid` includes the original charge as well as money collected for exchanges (settled ADD_COLLECT collections). `attention_required` is true on the booking currency’s row while a manual exchange case is open."}},"items":{"type":"array","items":{"allOf":[{"$ref":"#/components/schemas/BookingItem"},{"type":"object","properties":{"booking_ref":{"type":"string","description":"The Jinko booking reference of the booking this item belongs to: the same value as the booking’s `booking_ref`, also after an exchange.","example":"JNK-XEVGW5"},"servicing":{"allOf":[{"$ref":"#/components/schemas/BookingItemServicing"},{"type":"object","properties":{"cancellation":{"type":"object","properties":{"state":{"type":"string","description":"`none` when nothing has cancelled the item; otherwise `in_progress`, `attention_required`, `succeeded` or `failed`.","example":"succeeded"},"operation":{"type":"string","example":"svc_01J7ZR5Q2KME8V4T"},"reason":{"type":"string"},"park_deadline":{"type":"string"},"next_run_at":{"type":"string"},"paid":{"$ref":"#/components/schemas/Money"},"refund_due":{"type":"object","properties":{"amount":{"type":"number","description":"Deprecated: use `amount_money` instead. Alternative to `value` on some endpoints (the two never appear together); its scale depends on decimal_places. When this object carries decimal_places, amount is an INTEGER in minor units — divide by 10 ** decimal_places (e.g. select_ancillaries total_with_ancillaries). When there is no decimal_places field, amount is a decimal in MAJOR units, safe to display as-is (e.g. trip and checkout totals).","deprecated":true},"value":{"type":"integer","description":"Deprecated: use `amount_money` instead. Integer amount in MINOR units, always paired with decimal_places — divide by 10 ** decimal_places to display. Example: value 15977 with decimal_places 2 is 159.77 USD. Rendering this field directly shows prices 100x too high for 2-decimal currencies.","example":41250,"deprecated":true},"currency":{"type":"string","description":"Deprecated: use `amount_money` instead. ISO 4217 currency code.","example":"USD","deprecated":true},"decimal_places":{"type":"integer","description":"Deprecated: use `amount_money` instead. Scale of the integer value/amount: display = integer / 10 ** decimal_places. Always sent alongside minor-unit amounts. Usually the ISO 4217 digits of the currency (2 for USD/EUR/GBP, 0 for JPY/KRW, 3 for BHD/JOD/KWD) but currency-converted prices can carry a different scale — ALWAYS use the decimal_places sent with the amount, never a hardcoded 2. Only if the field is genuinely absent on a value-shaped object, fall back to the ISO digits for the currency.","example":2,"deprecated":true},"display":{"type":"string","description":"Deprecated: use `amount_money` instead. The same figure as a string ready to show: the ISO 4217 code, a space, then the amount, e.g. \"USD 159.77\". With decimal_places it is written at exactly that scale (value 1561500, JPY, decimal_places 2 is \"JPY 15615.00\"); without, at the ISO digits of the currency. Show this; compute with the number. Absent when the figure is not money (a cancellation step expressed as a percent or a number of nights) or could not be read unambiguously.","example":"USD 412.50","deprecated":true},"amount_money":{"allOf":[{"$ref":"#/components/schemas/MoneyValue"},{"description":"What the operation says the customer is owed."}]},"penalty":{"$ref":"#/components/schemas/Money"},"source":{"type":"string","description":"Where the figure came from: `quoted` (what the platform expects) or `provider_confirmed` (what the supplier agreed to).","example":"quoted"}}},"refunded":{"type":"object","properties":{"amount":{"type":"number","description":"Deprecated: use `amount_money` instead. Alternative to `value` on some endpoints (the two never appear together); its scale depends on decimal_places. When this object carries decimal_places, amount is an INTEGER in minor units — divide by 10 ** decimal_places (e.g. select_ancillaries total_with_ancillaries). When there is no decimal_places field, amount is a decimal in MAJOR units, safe to display as-is (e.g. trip and checkout totals).","deprecated":true},"value":{"type":"integer","description":"Deprecated: use `amount_money` instead. Integer amount in MINOR units, always paired with decimal_places — divide by 10 ** decimal_places to display. Example: value 15977 with decimal_places 2 is 159.77 USD. Rendering this field directly shows prices 100x too high for 2-decimal currencies.","example":41250,"deprecated":true},"currency":{"type":"string","description":"Deprecated: use `amount_money` instead. ISO 4217 currency code.","example":"USD","deprecated":true},"decimal_places":{"type":"integer","description":"Deprecated: use `amount_money` instead. Scale of the integer value/amount: display = integer / 10 ** decimal_places. Always sent alongside minor-unit amounts. Usually the ISO 4217 digits of the currency (2 for USD/EUR/GBP, 0 for JPY/KRW, 3 for BHD/JOD/KWD) but currency-converted prices can carry a different scale — ALWAYS use the decimal_places sent with the amount, never a hardcoded 2. Only if the field is genuinely absent on a value-shaped object, fall back to the ISO digits for the currency.","example":2,"deprecated":true},"display":{"type":"string","description":"Deprecated: use `amount_money` instead. The same figure as a string ready to show: the ISO 4217 code, a space, then the amount, e.g. \"USD 159.77\". With decimal_places it is written at exactly that scale (value 1561500, JPY, decimal_places 2 is \"JPY 15615.00\"); without, at the ISO digits of the currency. Show this; compute with the number. Absent when the figure is not money (a cancellation step expressed as a percent or a number of nights) or could not be read unambiguously.","example":"USD 412.50","deprecated":true},"amount_money":{"allOf":[{"$ref":"#/components/schemas/MoneyValue"},{"description":"What was refunded, as read back from the payment provider."}]},"at":{"type":"string","example":"2026-09-03T12:30:00Z"},"stripe_reference":{"type":"string","description":"Deprecated. An internal payment reference.","example":"re_3UBIxK2eZvKYlo2C","deprecated":true}}}}},"exchange":{"$ref":"#/components/schemas/ItemExchangeView"}}}],"description":"Eligibility of this booked item and its current product. Read the live refund or exchange check before committing; eligibility can change with time."},"supplier_references":{"type":"array","items":{"$ref":"#/components/schemas/SupplierReference"},"description":"The confirmations the supplier itself issued for this item: one per airline record locator, the hotel’s confirmation number, the rental company’s confirmation. Empty when none is known yet."},"failure_reason":{"type":"string","nullable":true,"description":"Why the booking failed, when it failed after payment and the cause is known: `offer_unavailable`, `price_changed`, `booking_not_created` (the supplier holds no booking; nothing was booked and no money was taken), `booking_cancelled_at_provider`, `ticketing_refused`. Null otherwise. For `offer_unavailable`, the supplier no longer sells the priced offer. Nothing was booked and the card authorisation was released. Search again and book a new offer. The `booking.failed` webhook carries the same reason with a sentence to show the traveller (`failure_message`).","example":"booking_not_created"},"booked_ancillaries":{"type":"array","items":{"$ref":"#/components/schemas/BookedAncillary"}},"confirmation":{"type":"object","properties":{"confirmation_number":{"type":"string","description":"Deprecated: use `supplier_references` instead. A confirmation number for this item; when the airline’s own locator is not known it can hold an id only Jinko’s intermediary answers to.","deprecated":true},"product_summary":{"type":"string"},"total_paid":{"$ref":"#/components/schemas/Money"},"details":{"type":"object","properties":{"flight":{"$ref":"#/components/schemas/BookingFlightDetails"},"car":{"type":"object","properties":{"due_at_desk":{"$ref":"#/components/schemas/Money"},"deposit":{"$ref":"#/components/schemas/Money"},"estimated_total":{"$ref":"#/components/schemas/Money"},"ancillaries":{"type":"array","items":{"type":"object","properties":{"offer_id":{"type":"string"},"label":{"type":"string"},"quantity":{"type":"integer"},"paid_at":{"type":"string"},"price":{"$ref":"#/components/schemas/Money"}}}},"cancellation_fees":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"},"fee":{"$ref":"#/components/schemas/Money"},"non_refundable":{"type":"boolean"},"applicable_from":{"type":"string"},"applicable_to":{"type":"string"}}}}}},"hotel":{"$ref":"#/components/schemas/BookingHotelDetails"}}}}},"cancellation":{"type":"object","properties":{"state":{"type":"string","example":"confirmed"},"refund_method":{"type":"string"},"monetary_refund_minor":{"type":"integer","description":"Deprecated: use `monetary_refund_money` instead. The money refund in minor units of `currency`, with no `decimal_places`.","deprecated":true},"voucher_refund_minor":{"type":"integer","description":"Deprecated: use `voucher_refund_money` instead. The voucher refund in minor units of `currency`, with no `decimal_places`.","deprecated":true},"fee_minor":{"type":"integer","description":"Deprecated: use `fee_money` instead. The cancellation fee in minor units of `currency`, with no `decimal_places`.","deprecated":true},"currency":{"type":"string","description":"Deprecated: use `monetary_refund_money` instead. The currency of the three `*_minor` figures; each `*_money` field carries its own.","deprecated":true},"monetary_refund_money":{"allOf":[{"$ref":"#/components/schemas/MoneyValue"},{"description":"The refund paid back as money."}]},"voucher_refund_money":{"allOf":[{"$ref":"#/components/schemas/MoneyValue"},{"description":"The refund given as a supplier voucher."}]},"fee_money":{"allOf":[{"$ref":"#/components/schemas/MoneyValue"},{"description":"The cancellation fee."}]},"fee_known":{"type":"boolean"}}},"exchange":{"type":"object","properties":{"exchange_ref":{"type":"string"},"state":{"type":"string"},"delta_direction":{"type":"string","description":"`add_collect`, `refund` or `even`: which way the difference moves.","example":"add_collect"},"delta_amount_minor":{"type":"integer","description":"Deprecated: use `delta_amount_money` instead. The absolute difference in minor units of `currency`, with no `decimal_places`.","deprecated":true},"new_total_minor":{"type":"integer","description":"Deprecated: use `new_total_money` instead. The replacement rental total in minor units of `currency`, with no `decimal_places`.","deprecated":true},"currency":{"type":"string","description":"Deprecated: use `delta_amount_money` instead. The currency of the two `*_minor` figures; each `*_money` field carries its own.","deprecated":true},"delta_amount_money":{"allOf":[{"$ref":"#/components/schemas/MoneyValue"},{"description":"The ABSOLUTE difference between the old and the new rental; `delta_direction` says which way it moves."}]},"new_total_money":{"allOf":[{"$ref":"#/components/schemas/MoneyValue"},{"description":"The replacement rental’s total."}]}}}}}]}},"can_refund":{"type":"boolean","description":"OR aggregate over items[].servicing.can_refund; select an item before acting.","deprecated":true},"can_void":{"type":"boolean","description":"OR aggregate over items[].servicing.can_void; select an item before acting.","deprecated":true},"can_exchange":{"type":"boolean","description":"OR aggregate over items[].servicing.can_exchange; select an item before acting.","deprecated":true},"refund_support_level":{"type":"string","enum":["AUTO","MANUAL_REQUIRED","UNSUPPORTED"],"description":"Use items[].servicing.refund_support_level.","deprecated":true},"exchange_support_level":{"type":"string","enum":["AUTO","MANUAL_REQUIRED","UNSUPPORTED"],"description":"Use items[].servicing.exchange_support_level.","deprecated":true},"created_at":{"type":"string","example":"2026-05-20T09:14:00Z"},"calendar":{"$ref":"#/components/schemas/BookingCalendar"}},"description":"The booked trip. Minimally described — the platform sends more (items, per-item confirmations, provider state) and all of it reaches you. The two `*_support_level` fields are a SNAPSHOT: a fare’s void window closes with time, so before acting on a refund or exchange read the level from a live POST /v1/refund_check or POST /v1/exchange_shop rather than from here. The flight itinerary (`items[].confirmation.details.flight.legs`) is the one booked and ticketed. It is not refreshed from the airline, and no webhook reports an airline's schedule change or cancellation."},"GetBookingResponse":{"type":"object","properties":{"booking":{"$ref":"#/components/schemas/Booking"}},"example":{"booking":{"booking_ref":"JNK-A0AUR2","booking_reference":"JNK-A0AUR2","status":"completed","type":"flight","last_name":"Doe","total_paid":{"value":31840,"currency":"USD","decimal_places":2,"display":"USD 318.40"},"created_at":"2026-05-20T09:14:00Z","calendar":{"files":[{"method":"REQUEST","filename":"jinko-JNK-A0AUR2.ics","content_type":"text/calendar; charset=utf-8; method=REQUEST","content":"BEGIN:VCALENDAR\\r\\nVERSION:2.0\\r\\n…\\r\\nEND:VCALENDAR\\r\\n"}]}}}},"GetBookingRequest":{"type":"object","properties":{"booking_ref":{"type":"string","minLength":1,"description":"Jinko booking reference. Call get_booking first, then select its booked_item_id for servicing.","example":"JNK-A0AUR2"},"last_name":{"type":"string","minLength":1,"description":"Required for guest lookup. A credential that owns the booking may omit it; ownership is checked by the platform.","example":"Doe"},"intent":{"$ref":"#/components/schemas/IntentInput"}},"required":["booking_ref"]},"SupportLevel":{"type":"string","enum":["AUTO","MANUAL_REQUIRED","UNSUPPORTED"],"description":"How this action can be carried out. `AUTO` — the platform completes it end to end through the API. `MANUAL_REQUIRED` — it is possible, but a Jinko agent has to act; call the API to raise it and expect a delay rather than an immediate result. `UNSUPPORTED` — the provider or fare does not allow it at all; nothing you send will change that. Do not treat MANUAL_REQUIRED as a failure, and do not retry UNSUPPORTED. On the refund check `manual_reason` says why it is not `AUTO`, on the exchange shop `reason`."},"RefundCheckResponse":{"type":"object","properties":{"seat_refunds":{"type":"array","items":{"$ref":"#/components/schemas/SeatRefund"},"description":"Separate refunds for carrier-settled paid seats. A paid seat is refunded once the airline refunds Jinko; these amounts are not part of the quoted fare refund."},"is_refundable":{"type":"boolean","description":"Whether the fare that was sold is refundable under its fare rules: the same answer the price check gave. A non-refundable ticket can still be given back in full while it is inside its void window; read `is_voidable` for that.","example":true},"is_voidable":{"type":"boolean","description":"True when committing would VOID the ticket outright — the document is still inside its void window, so the original charge is reversed in full instead of a fare-rule refund being computed. This check returns eligibility only; use flight_refund_preview for customer-facing amounts. Time-sensitive — read it from a live check, never from stored booking data.","example":false},"is_automatable":{"type":"boolean","example":true},"support_level":{"$ref":"#/components/schemas/SupportLevel"},"manual_reason":{"type":"string","description":"Why the refund is not automatic or not available, in words the customer can be shown; never the supplier’s wording. Present whenever `support_level` is not `AUTO`. With `UNSUPPORTED` it is the sentence a `flight_refund_preview` refusal (409 `not_cancellable`) carries for the same booking. With `MANUAL_REQUIRED`, where the reason would be the supplier’s own words, it reads “This request needs manual handling by Jinko support.”","example":"fare rules require agent review"},"reason_code":{"type":"string","description":"Why the refund is not automatic or not available, as a code to route on: `NON_REFUNDABLE_FARE`, `EXCHANGED_TICKET`, `DOCUMENT_RETIRED`, `CARRIER_NOT_AUTHORIZED`, `NOT_CANCELLABLE` (a refusal the supplier gave no code for). When `support_level` is `UNSUPPORTED`, it is always present and is the same code a `flight_refund_preview` refusal (409 `not_cancellable`) carries for this booking.","example":"NON_REFUNDABLE_FARE"},"expires_at":{"type":"string","example":"2026-06-02T00:00:00Z"},"warnings":{"type":"array","items":{"type":"string"},"description":"Notes about the booking and its documents, each said once, in words the customer can be shown. A note the supplier sends as free text without a code is left out."}}},"FlightServicingConflictCode":{"type":"string","enum":["quote_drift","QUOTE_EXPIRED","active_operation_exists","not_cancellable","funds_unavailable","superseded","manual_required","penalty_exceeds_sell","multi_currency_basis"],"description":"Which refusal this is. `manual_required` — the quote needs a Jinko agent and you did not send `manual_ok: true`; resend the same commit with it to hand the operation over. It can also mean the supplier’s totals use inconsistent currencies or decimal places, or the cancellation’s booking fee cannot be established or the booking has already had a completed exchange. These cases require a Jinko operator to complete the cancellation; contact Jinko support with the booking reference. `quote_drift` — your acknowledged figure differs from the still-live quote’s refund. `retry_quote_id` is that same quote’s handle and `current.refund` its figure. Show it to the customer, then commit again with that handle and figure. `QUOTE_EXPIRED` — the quote stopped binding; take a fresh one. `active_operation_exists` — an operation is already running on this booking; poll `active_operation` when present instead of starting a second. `not_cancellable` — usually the airline refuses to refund this ticket as it stands (a non-refundable fare, or every document already voided or refunded), with its reason in `error.message`. When `reason_code` is `already_cancelled`, Jinko already recorded a successful cancellation for this booking; this is not a supplier answer. The preview answers it too, so it is not a commit-only case. `booking_not_active` means the booking is no longer active; `error.message` is the platform’s sentence. `funds_unavailable` — the original payment cannot cover the refund yet; `shortfall` is how much is missing and `blocking` names the refunds that have to settle first. `superseded` — the item this quote named is no longer the live one with the airline (an exchange moved it); preview again; there is no `retry_quote_id`. The last two arrive only on a commit against a quote that already answered `commitable: false`, and repeat that quote’s own `not_commitable_reason`: `penalty_exceeds_sell` — the penalty is at least what the customer paid, so there is nothing to refund; `multi_currency_basis` — the airline prices the refund in a currency the customer was not charged in, so no single figure exists and a Jinko agent has to settle it. In every case NOTHING was sent to the airline."},"FlightServicingConflictResponse":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"CONFLICT"},"message":{"type":"string"},"doc_url":{"type":"string"}},"required":["code","message"]},"code":{"$ref":"#/components/schemas/FlightServicingConflictCode"},"reason_code":{"type":"string","pattern":"^[A-Za-z_]{1,64}$","description":"Machine-readable reason for the servicing refusal. `already_cancelled` means this booking already has a successful cancellation recorded by Jinko; it is not a supplier answer. `booking_not_active` means get_booking returns a booking whose status is failed (for example, after payment), expired_quote, pending or exchange_partial_failure. Refund and hotel servicing routes refuse it with `code: not_cancellable` and a platform sentence in `error.message`, to a caller who may service the booking; any other caller gets the same 404 as for an unknown booking.","example":"already_cancelled"},"retry_quote_id":{"type":"string","description":"The quote to commit against when your acknowledged figure differs from a still-live quote’s refund (`quote_drift`). This is the same `quote_id`, not a new one. Show the customer `current.refund`, then commit again with this `quote_id` and that figure. Absent on `QUOTE_EXPIRED`, `superseded`, or a hotel `quote_drift` because the supplier’s policy moved since the quote. Preview again in those cases.","example":"svq_01J8AB4N7GLY3Q0D"},"requote":{"type":"string","description":"Deprecated: use `retry_quote_id` instead. Same value.","example":"svq_01J8AB4N7GLY3Q0D","deprecated":true},"current":{"type":"object","properties":{"refund":{"$ref":"#/components/schemas/Money"}},"description":"The quote’s refund, on `quote_drift`. When `retry_quote_id` is present, show this figure to the customer, then commit again with that handle and this figure."},"active_operation":{"type":"string","description":"The operation already running on this booking, on `active_operation_exists`. Present when the running operation belongs to this booking and can be polled with the status route; it may be absent otherwise. Poll it with the status route for its domain — POST /v1/hotel_cancel_status or POST /v1/flight_refund_status.","example":"svc_01J7ZR5Q2KME8V4T"},"shortfall":{"allOf":[{"$ref":"#/components/schemas/Money"},{"description":"How much of the refund could not be reserved, on `funds_unavailable`. The refusal is about money still moving on the original payment, not about this booking, so this is the figure that says how much has to clear before a commit can succeed."}]},"blocking":{"type":"array","items":{"type":"string"},"description":"What is holding that money, on `funds_unavailable`: the refunds already in flight against the same payment, which have to settle before this one can be reserved. Opaque handles — read them as identity, do not parse them. This is what to wait on; without it, \"retry later\" has nothing behind it.","example":["re_3UBIxK2eZvKYlo2C"]}},"required":["error","code"]},"RefundCheckRequest":{"type":"object","properties":{"booking_ref":{"type":"string","minLength":1,"description":"Jinko booking reference. Call get_booking first, then select its booked_item_id for servicing.","example":"JNK-A0AUR2"},"booked_item_id":{"type":"string","pattern":"^itm_[a-z2-7]+$(?![\\s\\S])","description":"The booked item, from get_booking items[].booked_item_id. Preserved across exchanges. Omit only when the booking has exactly one item of the requested domain; otherwise a 409 returns items to choose from.","example":"itm_aaaaaaaaaaaaafhuxsdxpbzrbfz5hm3a"},"last_name":{"type":"string","minLength":1,"description":"Required for guest lookup. A credential that owns the booking may omit it; ownership is checked by the platform.","example":"Doe"},"order_id":{"type":"string","minLength":1,"description":"Deprecated: name the booking with booking_ref and booked_item_id from get_booking. Names the booking instead of booking_ref, never beside it, and needs a credential that owns the booking. Accepted until 2027-04-30; a request that sends it is answered with `Deprecation` and `Sunset` headers.","deprecated":true},"provider":{"type":"string","description":"Deprecated: omit it. The platform uses the supplier recorded with the booking. Accepted until 2027-04-30; a request that sends it is answered with `Deprecation` and `Sunset` headers.","deprecated":true},"intent":{"$ref":"#/components/schemas/IntentInput"}},"example":{"booking_ref":"JNK-A0AUR2","last_name":"Doe"}},"ServicingState":{"type":"string","enum":["in_progress","attention_required","succeeded","failed"],"description":"Where the cancellation has got to. `in_progress` — running; keep polling. `attention_required` — stalled on something a person at Jinko has to resolve (`reason` names it); keep polling, and do not report it to the customer as a failure, because the booking may already be cancelled at the supplier. `succeeded` and `failed` are terminal: the supplier outcome and the money are both settled and nothing further will change."},"FlightOperationKind":{"type":"string","enum":["cancel","void"],"description":"What the platform would do, or did. `cancel` — refund the ticket under the fare rules, less the penalty. `void` — the ticket is inside its void window, so it is voided and the original charge is reversed in full, penalty-free. The platform decides from the live state of the documents; the caller cannot ask for one."},"RefundCommitResponse":{"type":"object","properties":{"refund_status":{"type":"string","enum":["PENDING","IN_PROGRESS","SUCCEEDED","FAILED","MANUAL_REQUIRED","UNCONFIRMED","CANCELLED"],"description":"The refund's processing status. `PENDING` and `IN_PROGRESS` mean it is still running — poll `operation`. `MANUAL_REQUIRED` means a Jinko agent has it: raised, parked, NOT failed. `UNCONFIRMED` means the airline has not settled its answer yet. `SUCCEEDED`, `FAILED` and `CANCELLED` are terminal. `state` beside it reports the same operation in the platform vocabulary; either is enough to decide whether to keep polling.","example":"IN_PROGRESS"},"operation":{"type":"string","description":"The refund this call started (\"svc_…\"). Refunding at the airline and returning the money to the customer's card are separate steps, so this call answering does not mean both finished — poll the handle with POST /v1/flight_refund_status until `state` is terminal.","example":"svc_01J7ZR5Q2KME8V4T"},"state":{"$ref":"#/components/schemas/ServicingState"},"operation_kind":{"$ref":"#/components/schemas/FlightOperationKind"},"refund_amount":{"allOf":[{"$ref":"#/components/schemas/Money"},{"description":"THE CUSTOMER FIGURE: what goes back to the payment method that paid for this ticket — what they paid, less the penalty, or the whole charge when the ticket was voided (`operation_kind: void`). All amounts are customer-facing only. Absent is not zero."}]},"refund_reference":{"type":"string","example":"svc_agqsfzamchoagwznss4z3gwobm5tpxx3r4n5tfrngy"},"warnings":{"type":"array","items":{"type":"string"}}}},"RefundCommitRequest":{"type":"object","properties":{"booking_ref":{"type":"string","minLength":1,"description":"Jinko booking reference. Call get_booking first, then select its booked_item_id for servicing.","example":"JNK-A0AUR2"},"booked_item_id":{"type":"string","pattern":"^itm_[a-z2-7]+$(?![\\s\\S])","description":"The booked item, from get_booking items[].booked_item_id. Preserved across exchanges. Omit only when the booking has exactly one item of the requested domain; otherwise a 409 returns items to choose from.","example":"itm_aaaaaaaaaaaaafhuxsdxpbzrbfz5hm3a"},"last_name":{"type":"string","minLength":1,"description":"Required for guest lookup. A credential that owns the booking may omit it; ownership is checked by the platform.","example":"Doe"},"order_id":{"type":"string","minLength":1,"description":"Deprecated: name the booking with booking_ref and booked_item_id from get_booking. Names the booking instead of booking_ref, never beside it, and needs a credential that owns the booking. Accepted until 2027-04-30; a request that sends it is answered with `Deprecation` and `Sunset` headers.","deprecated":true},"provider":{"type":"string","description":"Deprecated: omit it. The platform uses the supplier recorded with the booking. Accepted until 2027-04-30; a request that sends it is answered with `Deprecation` and `Sunset` headers.","deprecated":true},"intent":{"$ref":"#/components/schemas/IntentInput"}},"example":{"booking_ref":"JNK-A0AUR2","last_name":"Doe"}},"RefundStatusResponse":{"type":"object","properties":{"refund_status":{"type":"string","description":"Where the refund has got to. Current refunds use `PENDING`, `IN_PROGRESS`, `SUCCEEDED`, `FAILED`, `MANUAL_REQUIRED`, `UNCONFIRMED`, or `CANCELLED`. Older refunds may report other status values, which is why this field is not published as a closed list. Treat an unrecognised value as \"still running\" and poll `state`, which is one vocabulary for both.","example":"SUCCEEDED"},"operation":{"type":"string","description":"The refund being reported (\"svc_…\"), for refunds started on the servicing path. POST /v1/flight_refund_status reads the same operation and adds where the money itself has got to.","example":"svc_01J7ZR5Q2KME8V4T"},"state":{"$ref":"#/components/schemas/ServicingState"},"refund_amount":{"allOf":[{"$ref":"#/components/schemas/Money"},{"description":"THE CUSTOMER FIGURE: what goes back to the payment method — the settled figure once the refund has settled, the quoted one before. Absent is not zero."}]},"refund_reference":{"type":"string","example":"svc_agqsfzamchoagwznss4z3gwobm5tpxx3r4n5tfrngy"},"warnings":{"type":"array","items":{"type":"string"}}}},"RefundStatusRequest":{"type":"object","properties":{"booking_ref":{"type":"string","minLength":1,"description":"Jinko booking reference. Call get_booking first, then select its booked_item_id for servicing.","example":"JNK-A0AUR2"},"booked_item_id":{"type":"string","pattern":"^itm_[a-z2-7]+$(?![\\s\\S])","description":"The booked item, from get_booking items[].booked_item_id. Preserved across exchanges. Omit only when the booking has exactly one item of the requested domain; otherwise a 409 returns items to choose from.","example":"itm_aaaaaaaaaaaaafhuxsdxpbzrbfz5hm3a"},"last_name":{"type":"string","minLength":1,"description":"Required for guest lookup. A credential that owns the booking may omit it; ownership is checked by the platform.","example":"Doe"},"order_id":{"type":"string","minLength":1,"description":"Deprecated: name the booking with booking_ref and booked_item_id from get_booking. Names the booking instead of booking_ref, never beside it, and needs a credential that owns the booking. Accepted until 2027-04-30; a request that sends it is answered with `Deprecation` and `Sunset` headers.","deprecated":true},"provider":{"type":"string","description":"Deprecated: omit it. The platform uses the supplier recorded with the booking. Accepted until 2027-04-30; a request that sends it is answered with `Deprecation` and `Sunset` headers.","deprecated":true},"intent":{"$ref":"#/components/schemas/IntentInput"},"refund_reference":{"type":"string"}},"example":{"booking_ref":"JNK-A0AUR2","last_name":"Doe"}},"ExchangeOfferSegment":{"type":"object","properties":{"airline":{"type":"string","description":"IATA code of the MARKETING carrier — whose flight number this is.","example":"AA"},"operating_carrier":{"type":"string","description":"IATA code of the carrier that flies the aircraft. Differs from `airline` on a codeshare.","example":"BA"},"flight_number":{"type":"string","description":"Provider flight number, forwarded unchanged. Depending on the upstream provider it may be a bare number such as `460` or a composed designator such as `AS460`; the carrier code is also carried separately when known.","example":"6957"},"departure_airport":{"type":"string","example":"MIA"},"departure_time":{"type":"string","description":"Local wall-clock time at the airport, `YYYY-MM-DDTHH:MM:SS` with no UTC offset (e.g. `2026-11-24T19:35:00`). Do not read it as UTC.","example":"2026-11-24T19:35:00"},"arrival_airport":{"type":"string","example":"LHR"},"arrival_time":{"type":"string","description":"Local wall-clock time at the airport, `YYYY-MM-DDTHH:MM:SS` with no UTC offset (e.g. `2026-11-24T19:35:00`). Do not read it as UTC.","example":"2026-11-25T09:05:00"},"duration_minutes":{"type":"number","example":530},"cabin_name":{"type":"string","description":"The cabin of the fare on this segment, as the airline names it.","example":"Economy"},"fare_brand_key":{"type":"string","description":"The fare brand on this segment, as the supplier keys it. The offer-level `fare_brand_code` / `fare_brand_name` are the ones to show.","example":"MAINSL"},"fare_basis_code":{"type":"string","description":"The fare basis code the airline prices this segment with.","example":"OVAIZNB3"},"rbd":{"type":"string","description":"The booking class (reservation booking designator) on this segment.","example":"O"}}},"ExchangeOfferLeg":{"type":"object","properties":{"stops":{"type":"integer","example":0},"duration_minutes":{"type":"number","example":530},"segments":{"type":"array","items":{"$ref":"#/components/schemas/ExchangeOfferSegment"},"description":"The flown segments of this journey, in travel order."}}},"ExchangeOffer":{"type":"object","properties":{"offer_id":{"type":"string","description":"The handle POST /v1/exchange_price and /v1/exchange_commit take.","example":"exch_offer_3c1a9f"},"description":{"type":"string","example":"AA100, 23 Sep, JFK-DFW-SJC"},"estimated_payment_outcome":{"type":"string","description":"What the money is expected to do — additional payment due, a refund, or even. An ESTIMATE from the shop step; POST /v1/exchange_price is the binding figure.","example":"additional_payment_due"},"estimated_amount":{"$ref":"#/components/schemas/Money"},"non_refundable_amount":{"$ref":"#/components/schemas/Money"},"validating_carrier":{"type":"string","example":"AA"},"fare_brand_code":{"type":"string","description":"The airline’s code for the fare brand this alternative sells. One itinerary can come back as several offers — one per brand — on the same flight and booking class, differing only in brand, fare basis and price; this and `fare_brand_name` are what tell them apart. Absent when the airline states no single brand for the offer.","example":"MAINSL"},"fare_brand_name":{"type":"string","description":"The fare brand’s display name, as the airline states it. Show this to the customer rather than `fare_brand_code`. Absent when the airline states no single brand.","example":"MAIN SELECT"},"legs":{"type":"array","items":{"$ref":"#/components/schemas/ExchangeOfferLeg"},"description":"The itinerary this alternative puts the traveller on, one entry per journey in travel order. Absent when the supplier returned no itinerary for the offer; `description` is then all that is known."},"fare_basis_codes":{"type":"array","items":{"type":"string"},"description":"The fare basis codes of the alternative, in segment order.","example":["OVAIZNB3"]},"booking_classes":{"type":"array","items":{"type":"string"},"description":"The booking classes of the alternative, in segment order.","example":["O"]},"cabin":{"type":"string","example":"Economy"}}},"ExchangeShopResponse":{"type":"object","properties":{"support_level":{"$ref":"#/components/schemas/SupportLevel"},"reason_code":{"type":"string","description":"Why the exchange is not automatic, as a Jinko code to route on, never a supplier’s. Present whenever `support_level` is not `AUTO`, absent when it is. Today `NOT_EXCHANGEABLE` with `support_level: UNSUPPORTED` — the booking cannot be exchanged online — and `MANUAL_REQUIRED` with `support_level: MANUAL_REQUIRED` — a Jinko agent has to carry out the exchange.","example":"NOT_EXCHANGEABLE"},"reason":{"type":"string","description":"Why the exchange is not automatic, in words the customer can be shown. Present whenever `support_level` is not `AUTO`, absent when it is. It is a fixed Jinko sentence and never the supplier’s wording: “This booking cannot be exchanged online. Contact Jinko support.” with `NOT_EXCHANGEABLE`, “This request needs manual handling by Jinko support.” with `MANUAL_REQUIRED`.","example":"This booking cannot be exchanged online. Contact Jinko support."},"offers":{"type":"array","items":{"$ref":"#/components/schemas/ExchangeOffer"}},"warnings":{"type":"array","items":{"type":"string"},"description":"Notes about the booking and its documents, each said once, in words the customer can be shown. A note the supplier sends as free text without a code is left out."}}},"ExchangeLegSelection":{"type":"object","properties":{"origin_location":{"type":"object","properties":{"code":{"type":"string"},"type":{"type":"string","enum":["city","airport"]}},"required":["code","type"]},"destination_location":{"type":"object","properties":{"code":{"type":"string"},"type":{"type":"string","enum":["city","airport"]}},"required":["code","type"]},"departure_date":{"type":"string","format":"date","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"The new departure date for this booked leg (YYYY-MM-DD)."}},"required":["origin_location","destination_location","departure_date"]},"ExchangeShopRequest":{"type":"object","properties":{"booking_ref":{"type":"string","minLength":1,"description":"Jinko booking reference. Call get_booking first, then select its booked_item_id for servicing.","example":"JNK-A0AUR2"},"booked_item_id":{"type":"string","pattern":"^itm_[a-z2-7]+$(?![\\s\\S])","description":"The booked item, from get_booking items[].booked_item_id. Preserved across exchanges. Omit only when the booking has exactly one item of the requested domain; otherwise a 409 returns items to choose from.","example":"itm_aaaaaaaaaaaaafhuxsdxpbzrbfz5hm3a"},"last_name":{"type":"string","minLength":1,"description":"Required for guest lookup. A credential that owns the booking may omit it; ownership is checked by the platform.","example":"Doe"},"order_id":{"type":"string","minLength":1,"description":"Deprecated: name the booking with booking_ref and booked_item_id from get_booking. Names the booking instead of booking_ref, never beside it, and needs a credential that owns the booking. Accepted until 2027-04-30; a request that sends it is answered with `Deprecation` and `Sunset` headers.","deprecated":true},"provider":{"type":"string","description":"Deprecated: omit it. The platform uses the supplier recorded with the booking. Accepted until 2027-04-30; a request that sends it is answered with `Deprecation` and `Sunset` headers.","deprecated":true},"intent":{"$ref":"#/components/schemas/IntentInput"},"segments_to_exchange":{"type":"array","items":{"$ref":"#/components/schemas/ExchangeLegSelection"},"minItems":1,"description":"The legs to change. Each entry is matched to a booked leg by its origin and destination and gives the new departure date. Booked legs you do not list are kept unchanged: every offer is the whole itinerary, with the kept legs as booked. Name each leg by its booked airport codes (`type: airport`): the first departure and the last arrival airport of that journey, as get_booking lists the leg — not a connection airport. A city code cannot be matched and is refused. An entry that matches no booked leg is refused with 400 `INVALID_SEGMENTS_TO_EXCHANGE`; a leg that has already departed with 400 `LEG_ALREADY_DEPARTED`; a date before today at the departure airport with 400 `DEPARTURE_DATE_IN_PAST`; new dates that would put the legs out of order with 400 `LEG_DATES_OUT_OF_ORDER`. Each refusal lists the booked legs. Some suppliers cannot keep a leg yet; for those bookings a request that names only some of the legs is refused with 400 `PARTIAL_EXCHANGE_UNSUPPORTED` — name every leg to reshop the whole itinerary."},"preferred_departure_date":{"type":"string","description":"Legacy. Moves the FIRST leg of the booking to this date and reshops every leg, so it cannot change only a later leg (a return) and can replace the flights of a leg you meant to keep. Use `segments_to_exchange` instead."}},"example":{"booking_ref":"JNK-A0AUR2","last_name":"Doe"}},"ExchangeCustomerAmounts":{"type":"object","properties":{"original_fare":{"allOf":[{"$ref":"#/components/schemas/MoneyValue"},{"description":"What the customer paid for the ticket being exchanged: the fare charge on the booking, after any earlier exchange and refund, excluding paid seats and bags (an exchange does not re-price them)."}]},"new_fare":{"allOf":[{"$ref":"#/components/schemas/MoneyValue"},{"description":"The new ticket's value to the customer, derived so the figures add up: `new_fare = original_fare + total_due - total_refund - penalty_amount - residual_value`."}]},"penalty_amount":{"allOf":[{"$ref":"#/components/schemas/MoneyValue"},{"description":"The airline's change fee, already inside `total_due` (or taken off `total_refund`). Absent when there is no airline change fee: absent means none, not unknown."}]},"total_due":{"allOf":[{"$ref":"#/components/schemas/MoneyValue"},{"description":"Exactly what the customer is charged for this exchange. Present when `payment_outcome` is `ADD_COLLECT` (above 0) or `EVEN` (0). Supplier service fees are never part of it."}]},"total_refund":{"allOf":[{"$ref":"#/components/schemas/MoneyValue"},{"description":"Exactly what goes back to the customer's original payment. Present when `payment_outcome` is `REFUND`."}]},"residual_value":{"allOf":[{"$ref":"#/components/schemas/MoneyValue"},{"description":"The part of the original fare the airline keeps as a credit document (EMD) in the traveller's name after an exchange to a cheaper fare. It is not refunded to the card by this exchange and is not part of total_due or total_refund. Present only when the airline reported one. The figures add up as `original_fare + total_due - total_refund = new_fare + penalty_amount + residual_value`."}]}},"required":["original_fare","new_fare"],"description":"Read the exchange here: the customer's own figures, all in the booking's charge currency. Absent when the platform cannot state them; the legacy fields beside it are then all there is. They add up to the cent: `original_fare + total_due - total_refund = new_fare + penalty_amount + residual_value`, an absent figure counting as 0. `total_due` and `total_refund` are exactly what is charged to or refunded on the customer's payment, the same figures as the top-level ones; supplier service fees are never part of them. Baggage prices in `new_fare_details` are airline information for buying bags; they are not part of `total_due`."},"ExchangePriceResponse":{"type":"object","properties":{"payment_outcome":{"type":"string","description":"What the customer's money does: `ADD_COLLECT` — they pay `total_due`; `REFUND` — they get `total_refund` back; `EVEN` — nothing moves. It is the customer's outcome, which can differ from the supplier's. The list may grow: handle a value you do not know.","example":"ADD_COLLECT"},"customer_amounts":{"$ref":"#/components/schemas/ExchangeCustomerAmounts"},"fare_difference":{"allOf":[{"$ref":"#/components/schemas/Money"},{"description":"Deprecated: use `customer_amounts` instead. The supplier's base-fare difference between the new and the original ticket, converted to the booking's charge currency at the rate pinned when the booking was priced. It does not reconcile with the other figures: `new_fare - original_fare` need not equal it, and it is not what the customer pays — `total_due` / `total_refund` are.","deprecated":true}]},"penalty_amount":{"allOf":[{"$ref":"#/components/schemas/Money"},{"description":"Deprecated: use `customer_amounts.penalty_amount` instead. The change fee as the supplier states it, converted at the booking's pinned rate. On some bookings it is the supplier's own booking change fee, which Jinko absorbs: it is not part of `total_due`, and adding it to a fare difference overstates what the customer pays.","deprecated":true}]},"original_fare":{"allOf":[{"$ref":"#/components/schemas/Money"},{"description":"Deprecated: use `customer_amounts.original_fare` instead. The supplier's value of the ticket being exchanged, converted at the booking's pinned rate. It is not necessarily what the customer paid.","deprecated":true}]},"new_fare":{"allOf":[{"$ref":"#/components/schemas/Money"},{"description":"Deprecated: use `customer_amounts.new_fare` instead. The supplier's price of the new fare, converted at the booking's pinned rate. The legacy figures do not add up to it.","deprecated":true}]},"total_due":{"allOf":[{"$ref":"#/components/schemas/Money"},{"description":"Exactly what the customer is charged for this exchange, in the booking's charge currency. Supplier service fees are never part of it."}]},"total_refund":{"allOf":[{"$ref":"#/components/schemas/Money"},{"description":"Exactly what goes back to the customer's original payment, in the booking's charge currency."}]},"new_fare_details":{"type":"object","properties":{"fare_brand_name":{"type":"string","example":"MAIN CABIN FLEXIBLE"},"fare_brand_code":{"type":"string","example":"MAINFL"},"fare_basis_codes":{"type":"array","items":{"type":"string"},"description":"The new fare’s basis codes, in segment order."},"endorsements":{"type":"string","description":"The ticket endorsement text, verbatim — the source of the two booleans below.","example":"USD281.86 NONREFUNDABLE/NONREF/FAREDIF/CXL BY FLT TIME OR NOVALUE"},"is_refundable":{"type":"boolean","description":"Whether the new fare is refundable. Absent when the fare does not say."},"is_changeable":{"type":"boolean","description":"Whether the new fare can be changed. Absent when the fare does not say."},"refund_policy":{"type":"string","description":"The refund rule as a display string.","example":"Non-refundable"},"change_policy":{"type":"string","description":"The change rule as a display string.","example":"Changeable (conditions apply)"},"checked_baggage":{"type":"object","properties":{"pieces":{"type":"integer","description":"Pieces included. Absent when the fare states none or states zero; `description` then says which (\"No checked bags included\" is a stated zero)."},"weight":{"type":"number"},"weight_unit":{"type":"string","example":"KG"},"dimensions":{"type":"string"},"description":{"type":"string","example":"1 bag, 23kg included"}},"description":"The checked baggage the new fare includes. Absent when the fare states none."},"carry_on_baggage":{"type":"object","properties":{"pieces":{"type":"integer","description":"Pieces included. Absent when the fare states none or states zero; `description` then says which (\"No checked bags included\" is a stated zero)."},"weight":{"type":"number"},"weight_unit":{"type":"string","example":"KG"},"dimensions":{"type":"string"},"description":{"type":"string","example":"1 bag, 23kg included"}},"description":"The carry-on baggage the new fare includes. Absent when the fare states none."},"extra_baggage_options":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"pieces":{"type":"integer"},"weight":{"type":"number"},"weight_unit":{"type":"string"},"is_included":{"type":"boolean"},"price_per_bag":{"$ref":"#/components/schemas/Money"},"total_price":{"$ref":"#/components/schemas/Money"},"description":{"type":"string"},"segment_id":{"type":"string"},"journey_id":{"type":"string"}}}},"extra_carry_on_options":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"pieces":{"type":"integer"},"weight":{"type":"number"},"weight_unit":{"type":"string"},"is_included":{"type":"boolean"},"price_per_bag":{"$ref":"#/components/schemas/Money"},"total_price":{"$ref":"#/components/schemas/Money"},"description":{"type":"string"},"segment_id":{"type":"string"},"journey_id":{"type":"string"}}}}}},"fare_change_notice":{"type":"object","properties":{"original_brand":{"type":"string","example":"MAIN SELECT"},"new_brand":{"type":"string","example":"MAIN CABIN"},"refundability_changed":{"type":"boolean","description":"True only when both fares state whether they are refundable and the two differ. An unstated side leaves it false."},"text":{"type":"string","description":"The notice to show the customer before they pay."}}},"quote_id":{"type":"string","description":"This price’s quote. Pass it as `quote_id` to exchange_commit when committing the same offer after a refused price, to request manual handling.","example":"exch_7a1c93f0"},"session_reference":{"type":"string","description":"Deprecated: use `quote_id` instead. Same value.","example":"exch_7a1c93f0","deprecated":true},"expires_at":{"type":"string","example":"2026-06-01T13:04:56Z"},"warnings":{"type":"array","items":{"type":"string"},"description":"Notes about the booking and its documents, each said once, in words the customer can be shown. A note the supplier sends as free text without a code is left out."}}},"ExchangePriceConflictCode":{"type":"string","enum":["funds_unavailable","multi_currency_basis","manual_required","not_exchangeable"],"description":"Which refusal this is. `not_exchangeable` — the booking cannot be exchanged as it stands: no ticket or flight is left to change, or the fare does not permit the change; do not retry, and committing the offer does not send it to support. Its `error.message` is “This booking cannot be exchanged online. Contact Jinko support.” `funds_unavailable` — the exchange would refund more than the original payment can still refund. `multi_currency_basis` — the exchange cannot be priced in the currency the booking was paid in. `manual_required` — the booking’s charge or the payment the refund would go to could not be read; the supplier’s price components cannot be identified or do not add up to its total; an airline change fee is charged outside the exchange total; or the supplier’s totals use inconsistent currencies or decimal places. `error.message` states the case in figures where there are any. In every case nothing was exchanged or charged. Jinko support can complete `funds_unavailable`, `multi_currency_basis` and `manual_required`. To request manual handling, commit the same offer: the request then goes to Jinko support. This applies to those three codes only; committing a `not_exchangeable` offer is refused again with the same code."},"ExchangePriceConflictResponse":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"CONFLICT"},"message":{"type":"string"},"doc_url":{"type":"string"}},"required":["code","message"]},"code":{"$ref":"#/components/schemas/ExchangePriceConflictCode"}},"required":["error","code"]},"ExchangePriceRequest":{"type":"object","properties":{"booking_ref":{"type":"string","minLength":1,"description":"Jinko booking reference. Call get_booking first, then select its booked_item_id for servicing.","example":"JNK-A0AUR2"},"booked_item_id":{"type":"string","pattern":"^itm_[a-z2-7]+$(?![\\s\\S])","description":"The booked item, from get_booking items[].booked_item_id. Preserved across exchanges. Omit only when the booking has exactly one item of the requested domain; otherwise a 409 returns items to choose from.","example":"itm_aaaaaaaaaaaaafhuxsdxpbzrbfz5hm3a"},"last_name":{"type":"string","minLength":1,"description":"Required for guest lookup. A credential that owns the booking may omit it; ownership is checked by the platform.","example":"Doe"},"order_id":{"type":"string","minLength":1,"description":"Deprecated: name the booking with booking_ref and booked_item_id from get_booking. Names the booking instead of booking_ref, never beside it, and needs a credential that owns the booking. Accepted until 2027-04-30; a request that sends it is answered with `Deprecation` and `Sunset` headers.","deprecated":true},"provider":{"type":"string","description":"Deprecated: omit it. The platform uses the supplier recorded with the booking. Accepted until 2027-04-30; a request that sends it is answered with `Deprecation` and `Sunset` headers.","deprecated":true},"intent":{"$ref":"#/components/schemas/IntentInput"},"offer_id":{"type":"string","minLength":1}},"required":["offer_id"],"example":{"booking_ref":"JNK-A0AUR2","last_name":"Doe","offer_id":"exch_offer_3c1a9f"}},"ExchangeCommitResponse":{"type":"object","properties":{"airline_locator":{"type":"string","description":"Deprecated: read the item’s `supplier_references` on POST /v1/get_booking, which carries each airline’s record locator with the airline that issued it. Airline record locator for the current itinerary.","example":"ABC123","deprecated":true},"booking_ref":{"type":"string","example":"JNK-A0AUR2"},"booking_reference":{"type":"string","description":"Deprecated: use `booking_ref` instead. Same value.","example":"JNK-A0AUR2","deprecated":true},"new_ticket_numbers":{"type":"array","items":{"type":"string"},"example":["0012345678901"]},"status":{"type":"string","enum":["confirmed","pending","partial_failure","failed"],"description":"A commit answers `pending`: the exchange is carried out after this call, whatever the payment outcome. Follow it with exchange_status until it reads `confirmed`, `partial_failure` or `failed`.","example":"pending"},"payment_outcome":{"type":"string","enum":["EVEN","ADD_COLLECT","REFUND"],"description":"`ADD_COLLECT` — the customer pays the difference first: send them to `checkout_url`; the exchange runs only after that payment authorizes. `EVEN` — nothing to pay. `REFUND` — the difference goes back to the original payment method once the exchange completes.","example":"ADD_COLLECT"},"checkout_url":{"type":"string","description":"Present when `payment_outcome` is `ADD_COLLECT`: the page where the customer pays the difference. Nothing is exchanged until that payment authorizes. exchange_status returns the same page while the exchange waits for it.","example":"https://app.gojinko.com/checkout?t=eyJyIjoiY2FydCIs…"},"stripe_checkout_session_url":{"type":"string","description":"Deprecated: use `checkout_url` instead. The same payment page, under a provider’s name.","deprecated":true},"warnings":{"type":"array","items":{"type":"string"},"description":"Notes about the booking and its documents, each said once, in words the customer can be shown. A note the supplier sends as free text without a code is left out."}}},"ExchangeCommitConflictCode":{"type":"string","enum":["funds_unavailable","multi_currency_basis","manual_required","exchange_in_progress","not_exchangeable"],"description":"Which refusal this is. `funds_unavailable`, `multi_currency_basis`, `manual_required` — as on exchange_price; the request has gone to Jinko support (`manual_handling`). `exchange_in_progress` — this booking already has an exchange waiting for the customer’s payment or running: nothing new was started; exchange_status reports its progress and, while it waits for payment, its `checkout_url`. `not_exchangeable` — as on exchange_price: the booking cannot be exchanged as it stands, so nothing was started and nothing goes to Jinko support; do not retry."},"ExchangeCommitConflictResponse":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"CONFLICT"},"message":{"type":"string"},"doc_url":{"type":"string"}},"required":["code","message"]},"code":{"$ref":"#/components/schemas/ExchangeCommitConflictCode"},"manual_handling":{"type":"object","properties":{"status":{"type":"string","enum":["pending"],"description":"`pending` — the request is with Jinko support, who will complete the exchange and refund the customer; nothing else to do. Follow it on `get_booking` at `items[].servicing.exchange`.","example":"pending"}},"required":["status"],"description":"Present on the three refusals that send the request to Jinko support."}},"required":["error","code"]},"ExchangeCommitRequest":{"type":"object","properties":{"booking_ref":{"type":"string","minLength":1,"description":"Jinko booking reference. Call get_booking first, then select its booked_item_id for servicing.","example":"JNK-A0AUR2"},"booked_item_id":{"type":"string","pattern":"^itm_[a-z2-7]+$(?![\\s\\S])","description":"The booked item, from get_booking items[].booked_item_id. Preserved across exchanges. Omit only when the booking has exactly one item of the requested domain; otherwise a 409 returns items to choose from.","example":"itm_aaaaaaaaaaaaafhuxsdxpbzrbfz5hm3a"},"last_name":{"type":"string","minLength":1,"description":"Required for guest lookup. A credential that owns the booking may omit it; ownership is checked by the platform.","example":"Doe"},"order_id":{"type":"string","minLength":1,"description":"Deprecated: name the booking with booking_ref and booked_item_id from get_booking. Names the booking instead of booking_ref, never beside it, and needs a credential that owns the booking. Accepted until 2027-04-30; a request that sends it is answered with `Deprecation` and `Sunset` headers.","deprecated":true},"provider":{"type":"string","description":"Deprecated: omit it. The platform uses the supplier recorded with the booking. Accepted until 2027-04-30; a request that sends it is answered with `Deprecation` and `Sunset` headers.","deprecated":true},"intent":{"$ref":"#/components/schemas/IntentInput"},"offer_id":{"type":"string","minLength":1},"quote_id":{"type":"string","description":"The `quote_id` exchange_price returned. Not required. Optional after a refused exchange price, when committing the same offer to request manual handling."}},"required":["offer_id"],"example":{"booking_ref":"JNK-A0AUR2","last_name":"Doe","offer_id":"exch_offer_3c1a9f","quote_id":"exch_7a1c93f0"}},"ExchangeStatusResponse":{"type":"object","properties":{"airline_locator":{"type":"string","description":"Deprecated: read the item’s `supplier_references` on POST /v1/get_booking, which carries each airline’s record locator with the airline that issued it. Airline record locator for the current itinerary.","example":"ABC123","deprecated":true},"booking_ref":{"type":"string","example":"JNK-A0AUR2"},"booking_reference":{"type":"string","description":"Deprecated: use `booking_ref` instead. Same value.","example":"JNK-A0AUR2","deprecated":true},"status":{"type":"string","enum":["confirmed","pending","partial_failure","failed"],"description":"`pending` until the exchange is carried out — including while it waits for the customer’s payment, and while a Jinko agent has it (`manual_handling`).","example":"confirmed"},"new_ticket_numbers":{"type":"array","items":{"type":"string"},"example":["0012345678901"]},"confirmed_payment_outcome":{"type":"string","example":"paid"},"checkout_url":{"type":"string","description":"Present while the exchange waits for the customer to pay the difference: the same page exchange_commit returned. The exchange runs once that payment authorizes.","example":"https://app.gojinko.com/checkout?t=eyJyIjoiY2FydCIs…"},"manual_handling":{"type":"object","properties":{"status":{"type":"string","enum":["pending"],"description":"`pending` — a Jinko agent has the exchange and will complete or fail it.","example":"pending"}},"required":["status"],"description":"Present while the exchange is with a Jinko agent: it could not be completed automatically. `status` stays `PENDING` and polling does not move it; the agent does. Absent once the exchange is carried out or failed."},"warnings":{"type":"array","items":{"type":"string"},"description":"Notes about the booking and its documents, each said once, in words the customer can be shown. A note the supplier sends as free text without a code is left out."}}},"ExchangeStatusRequest":{"type":"object","properties":{"booking_ref":{"type":"string","minLength":1,"description":"Jinko booking reference. Call get_booking first, then select its booked_item_id for servicing.","example":"JNK-A0AUR2"},"booked_item_id":{"type":"string","pattern":"^itm_[a-z2-7]+$(?![\\s\\S])","description":"The booked item, from get_booking items[].booked_item_id. Preserved across exchanges. Omit only when the booking has exactly one item of the requested domain; otherwise a 409 returns items to choose from.","example":"itm_aaaaaaaaaaaaafhuxsdxpbzrbfz5hm3a"},"last_name":{"type":"string","minLength":1,"description":"Required for guest lookup. A credential that owns the booking may omit it; ownership is checked by the platform.","example":"Doe"},"order_id":{"type":"string","minLength":1,"description":"Deprecated: name the booking with booking_ref and booked_item_id from get_booking. Names the booking instead of booking_ref, never beside it, and needs a credential that owns the booking. Accepted until 2027-04-30; a request that sends it is answered with `Deprecation` and `Sunset` headers.","deprecated":true},"provider":{"type":"string","description":"Deprecated: omit it. The platform uses the supplier recorded with the booking. Accepted until 2027-04-30; a request that sends it is answered with `Deprecation` and `Sunset` headers.","deprecated":true},"intent":{"$ref":"#/components/schemas/IntentInput"}},"example":{"booking_ref":"JNK-A0AUR2","last_name":"Doe"}},"HotelCancelResponse":{"type":"object","properties":{"booking_ref":{"type":"string","example":"JNK-A0AUR2"},"booking_reference":{"type":"string","description":"Deprecated: use `booking_ref` instead. Same value.","example":"JNK-A0AUR2","deprecated":true},"confirmation_number":{"type":"string","description":"Deprecated: read the item’s `supplier_references` on POST /v1/get_booking, which names who issued each reference. Customer-facing hotel confirmation number, when supplied by the hotel.","deprecated":true},"status":{"type":"string","example":"cancelled"},"operation":{"type":"string","description":"The cancellation this call started (\"svc_…\"). Cancelling the booking at the supplier and returning the money are separate steps, so this call answering does not mean both finished — poll the handle with POST /v1/hotel_cancel_status until `state` is terminal.","example":"svc_01J7ZR5Q2KME8V4T"},"state":{"$ref":"#/components/schemas/ServicingState"},"refund_amount":{"allOf":[{"$ref":"#/components/schemas/Money"},{"description":"THE CUSTOMER FIGURE: what goes back to the payment method that paid for this booking — what the customer paid for it, less `penalty_amount`. Up to contract version 0.3.0 this field carried the SUPPLIER's net refund instead, which on a booking sold at a margin is a smaller number. Only customer-facing amounts are published. Absent when no refund was scheduled — absent is not zero."}]},"customer_refund_amount":{"allOf":[{"$ref":"#/components/schemas/Money"},{"description":"Deprecated alias of `refund_amount`, carrying the same value. It existed because `refund_amount` used to be the supplier figure; now that `refund_amount` IS the customer figure the alias is redundant. Read `refund_amount` — this field is removed in the next contract version.","deprecated":true}]},"penalty_amount":{"anyOf":[{"$ref":"#/components/schemas/Money"},{"type":"object","properties":{"fee_known":{"type":"boolean","enum":[false]}},"required":["fee_known"]}],"description":"The cancellation penalty withheld from the customer, on the same basis as `refund_amount`: what they paid, less this, is what they get back. When unknown, the amount is omitted; a fee_known: false marker may remain."},"cancelled_at":{"type":"string","example":"2026-06-01T12:34:56Z"},"idempotent":{"type":"boolean","example":false}}},"HotelCancelRequest":{"type":"object","properties":{"booking_ref":{"type":"string","minLength":1,"description":"Jinko booking reference. Call get_booking first, then select its booked_item_id for servicing.","example":"JNK-A0AUR2"},"booked_item_id":{"type":"string","pattern":"^itm_[a-z2-7]+$(?![\\s\\S])","description":"The booked item, from get_booking items[].booked_item_id. Preserved across exchanges. Omit only when the booking has exactly one item of the requested domain; otherwise a 409 returns items to choose from.","example":"itm_aaaaaaaaaaaaafhuxsdxpbzrbfz5hm3a"},"last_name":{"type":"string","minLength":1,"description":"Required for guest lookup. A credential that owns the booking may omit it; ownership is checked by the platform.","example":"Doe"},"provider_booking_id":{"type":"string","minLength":1,"description":"Deprecated: name the booking with booking_ref and booked_item_id from get_booking. Names the booking instead of booking_ref and last_name, never beside it, and needs a credential that owns the booking. Accepted until 2027-04-30; a request that sends it is answered with `Deprecation` and `Sunset` headers.","deprecated":true},"provider":{"type":"string","description":"Deprecated: omit it. The platform uses the supplier recorded with the booking. Accepted until 2027-04-30; a request that sends it is answered with `Deprecation` and `Sunset` headers.","deprecated":true},"intent":{"$ref":"#/components/schemas/IntentInput"}}},"ServicingSupportLevel":{"type":"string","enum":["AUTO","MANUAL_REQUIRED"],"description":"How this cancellation would be carried out. `AUTO` — the platform completes it end to end. `MANUAL_REQUIRED` — it is possible, but a Jinko agent has to act; expect a delay rather than an immediate result, and do not treat it as a failure; `manual_reason` says why. There is no \"unsupported\" level: a supplier that cannot be cancelled through the API arrives as `commitable: false` with `not_commitable_reason: provider_unsupported`, and a booking the supplier will not cancel as it stands arrives as a 409 `not_cancellable` — from the preview as well as the commit — with `error.message` “This booking cannot be cancelled online. Contact Jinko support.”"},"HotelCancelPreviewResponse":{"type":"object","properties":{"quote_id":{"type":"string","description":"This quote. Pass it as `quote_id` to POST /v1/hotel_cancel_commit — it binds the commit to the figures below. Opaque; the format may evolve.","example":"svq_01J7ZR3M8FKX2P9C"},"quote":{"type":"string","description":"Deprecated: use `quote_id` instead. Same value.","example":"svq_01J7ZR3M8FKX2P9C","deprecated":true},"state":{"type":"string","description":"Lifecycle of the QUOTE, not of a cancellation — nothing has been cancelled by this call. `completed` means the figures are final until `expires_at`.","example":"completed"},"commitable":{"type":"boolean","description":"Whether a commit against this quote would be accepted right now. When false, `not_commitable_reason` says why and committing is pointless.","example":true},"support_level":{"$ref":"#/components/schemas/ServicingSupportLevel"},"manual_reason":{"type":"string","description":"Why a person has to act, in words the customer can be shown. Present with `support_level: MANUAL_REQUIRED`. Where the reason would be the supplier’s own words, it reads “This request needs manual handling by Jinko support.” `not_commitable_reason` is the code to branch on; this is the sentence to show.","example":"the supplier's cancellation policy names no fee in force for this booking, so the fee could not be established"},"expires_at":{"type":"string","description":"When this quote stops binding. Committing after it answers 409 `QUOTE_EXPIRED`; take a fresh quote and show the customer the new figure before committing again.","example":"2026-09-03T12:15:00Z"},"booked_item_id":{"type":"string","description":"The booked item this quote would cancel — the `booked_item_id` get_booking returns. A booking holding several items is quoted and cancelled one item at a time.","example":"itm_7f2c9a4e8b1d"},"item":{"type":"string","description":"Deprecated: use `booked_item_id` instead. Same value.","example":"itm_7f2c9a4e8b1d","deprecated":true},"refund":{"type":"object","properties":{"amount":{"type":"number","description":"Deprecated: use `amount_money` instead. Alternative to `value` on some endpoints (the two never appear together); its scale depends on decimal_places. When this object carries decimal_places, amount is an INTEGER in minor units — divide by 10 ** decimal_places (e.g. select_ancillaries total_with_ancillaries). When there is no decimal_places field, amount is a decimal in MAJOR units, safe to display as-is (e.g. trip and checkout totals).","deprecated":true},"value":{"type":"integer","description":"Deprecated: use `amount_money` instead. Integer amount in MINOR units, always paired with decimal_places — divide by 10 ** decimal_places to display. Example: value 15977 with decimal_places 2 is 159.77 USD. Rendering this field directly shows prices 100x too high for 2-decimal currencies.","example":41250,"deprecated":true},"currency":{"type":"string","description":"Deprecated: use `amount_money` instead. ISO 4217 currency code.","example":"USD","deprecated":true},"decimal_places":{"type":"integer","description":"Deprecated: use `amount_money` instead. Scale of the integer value/amount: display = integer / 10 ** decimal_places. Always sent alongside minor-unit amounts. Usually the ISO 4217 digits of the currency (2 for USD/EUR/GBP, 0 for JPY/KRW, 3 for BHD/JOD/KWD) but currency-converted prices can carry a different scale — ALWAYS use the decimal_places sent with the amount, never a hardcoded 2. Only if the field is genuinely absent on a value-shaped object, fall back to the ISO digits for the currency.","example":2,"deprecated":true},"display":{"type":"string","description":"Deprecated: use `amount_money` instead. The same figure as a string ready to show: the ISO 4217 code, a space, then the amount, e.g. \"USD 159.77\". With decimal_places it is written at exactly that scale (value 1561500, JPY, decimal_places 2 is \"JPY 15615.00\"); without, at the ISO digits of the currency. Show this; compute with the number. Absent when the figure is not money (a cancellation step expressed as a percent or a number of nights) or could not be read unambiguously.","example":"USD 412.50","deprecated":true},"amount_money":{"allOf":[{"$ref":"#/components/schemas/MoneyValue"},{"description":"The refund figure. Absent when the figure is not known."}]},"basis":{"type":"string","description":"How the figure was computed. `sell_minus_penalty` — what the customer paid for this item, less the penalty. `original_charge` — the entire charge is reversed, penalty-free (a void). The platform chooses; the caller cannot ask for one.","example":"sell_minus_penalty"}},"description":"THE CUSTOMER FIGURE: what would go back to the payment method, on the basis named in `basis`. Show this one. Absent is not zero."},"penalty":{"anyOf":[{"type":"object","properties":{"amount":{"type":"number","description":"Deprecated: use `amount_money` instead. Alternative to `value` on some endpoints (the two never appear together); its scale depends on decimal_places. When this object carries decimal_places, amount is an INTEGER in minor units — divide by 10 ** decimal_places (e.g. select_ancillaries total_with_ancillaries). When there is no decimal_places field, amount is a decimal in MAJOR units, safe to display as-is (e.g. trip and checkout totals).","deprecated":true},"value":{"type":"integer","description":"Deprecated: use `amount_money` instead. Integer amount in MINOR units, always paired with decimal_places — divide by 10 ** decimal_places to display. Example: value 15977 with decimal_places 2 is 159.77 USD. Rendering this field directly shows prices 100x too high for 2-decimal currencies.","example":41250,"deprecated":true},"currency":{"type":"string","description":"Deprecated: use `amount_money` instead. ISO 4217 currency code.","example":"USD","deprecated":true},"decimal_places":{"type":"integer","description":"Deprecated: use `amount_money` instead. Scale of the integer value/amount: display = integer / 10 ** decimal_places. Always sent alongside minor-unit amounts. Usually the ISO 4217 digits of the currency (2 for USD/EUR/GBP, 0 for JPY/KRW, 3 for BHD/JOD/KWD) but currency-converted prices can carry a different scale — ALWAYS use the decimal_places sent with the amount, never a hardcoded 2. Only if the field is genuinely absent on a value-shaped object, fall back to the ISO digits for the currency.","example":2,"deprecated":true},"display":{"type":"string","description":"Deprecated: use `amount_money` instead. The same figure as a string ready to show: the ISO 4217 code, a space, then the amount, e.g. \"USD 159.77\". With decimal_places it is written at exactly that scale (value 1561500, JPY, decimal_places 2 is \"JPY 15615.00\"); without, at the ISO digits of the currency. Show this; compute with the number. Absent when the figure is not money (a cancellation step expressed as a percent or a number of nights) or could not be read unambiguously.","example":"USD 412.50","deprecated":true},"amount_money":{"allOf":[{"$ref":"#/components/schemas/MoneyValue"},{"description":"The penalty figure. Present only when `fee_known` is true; absent when the fee is unknown, which is not zero."}]},"fee_known":{"type":"boolean","description":"false means the penalty could not be established — UNKNOWN, not zero. Render it as \"we will confirm the fee\", never as free cancellation, and expect the final figure on the operation status.","example":true}}},{"type":"object","properties":{"fee_known":{"type":"boolean","enum":[false]}},"required":["fee_known"]}],"description":"What the customer would forfeit. When unknown, the amount is omitted; a fee_known: false marker may remain. Absent is not zero. Read `fee_known` before showing it."},"policy":{"type":"object","properties":{"free_cancel_until":{"type":"string","nullable":true,"description":"The last instant at which cancelling costs nothing. `null` means there is no free window — either the booking never had one or it has passed.","example":"2026-09-01T00:00:00Z"},"is_refundable_now":{"type":"boolean","example":true},"tiers":{"type":"array","items":{"type":"object","properties":{"from":{"type":"string","description":"When this step starts applying.","example":"2026-09-01T00:00:00Z"},"amount":{"$ref":"#/components/schemas/Money"},"fraction":{"type":"number","description":"The share of the paid price withheld from `from` onwards, when the supplier states the step as a proportion rather than a sum. A step carries `amount` or `fraction`, not both.","example":0.5}}},"description":"The supplier's policy steps in time order. Informational: the figure that binds is `penalty`, computed for right now."}},"description":"The supplier's cancellation policy as it stands, for explaining the figures to the customer."},"not_commitable_reason":{"type":"string","description":"Why `commitable` is false. Today: `penalty_exceeds_sell` (the penalty is at least what the customer paid), `multi_currency_basis` (the supplier prices the fee in a currency the customer was not charged in, so no single refund figure exists). A fee the supplier’s policy does not establish (`penalty.fee_known: false`) is `support_level: MANUAL_REQUIRED` with no reason code — read `manual_reason`. Two refusals are never values here: a booking the supplier will not cancel as it stands is 409 `not_cancellable` from this preview itself, and a supplier with no cancellation route is 422 `provider_unsupported`. The contract reserves further values (`provider_unsupported`, `funds_unavailable`, `insufficient_time_to_converge`) and new reasons may be added, so treat an unrecognised value as \"not right now\".","example":"penalty_exceeds_sell"}}},"ServicingConflictCode":{"type":"string","enum":["quote_drift","QUOTE_EXPIRED","active_operation_exists","not_cancellable","funds_unavailable","superseded"],"description":"Which refusal this is. `quote_drift` — your acknowledged figure differs from the still-live quote’s refund. `retry_quote_id` is that same quote’s handle and `current.refund` its figure. Show it to the customer, then commit again with that handle and figure. If the supplier’s policy moved since the quote, `quote_drift` has no `retry_quote_id`; preview again. `QUOTE_EXPIRED` — the quote stopped binding; take a fresh one. `active_operation_exists` — one cancellation is already running on this booking; poll `active_operation` when present instead of starting a second. `not_cancellable` — usually the supplier refuses to cancel this booking as it stands (already cancelled, never confirmed), with its reason in `error.message`. When `reason_code` is `already_cancelled`, Jinko already recorded a successful cancellation for this booking; this is not a supplier answer. The preview answers it too, so it is not a commit-only case. `booking_not_active` means the booking is no longer active; `error.message` is the platform’s sentence. `funds_unavailable` — the original payment cannot cover the refund yet; `shortfall` is how much is missing and `blocking` names the refunds that have to settle first. `superseded` — the item this quote named is no longer the live one with the supplier (an exchange moved it); preview again; there is no `retry_quote_id`. In every case NOTHING was cancelled."},"ServicingConflictResponse":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"CONFLICT"},"message":{"type":"string"},"doc_url":{"type":"string"}},"required":["code","message"]},"code":{"$ref":"#/components/schemas/ServicingConflictCode"},"reason_code":{"type":"string","pattern":"^[A-Za-z_]{1,64}$","description":"Machine-readable reason for the servicing refusal. `already_cancelled` means this booking already has a successful cancellation recorded by Jinko; it is not a supplier answer. `booking_not_active` means get_booking returns a booking whose status is failed (for example, after payment), expired_quote, pending or exchange_partial_failure. Refund and hotel servicing routes refuse it with `code: not_cancellable` and a platform sentence in `error.message`, to a caller who may service the booking; any other caller gets the same 404 as for an unknown booking.","example":"already_cancelled"},"retry_quote_id":{"type":"string","description":"The quote to commit against when your acknowledged figure differs from a still-live quote’s refund (`quote_drift`). This is the same `quote_id`, not a new one. Show the customer `current.refund`, then commit again with this `quote_id` and that figure. Absent on `QUOTE_EXPIRED`, `superseded`, or a hotel `quote_drift` because the supplier’s policy moved since the quote. Preview again in those cases.","example":"svq_01J8AB4N7GLY3Q0D"},"requote":{"type":"string","description":"Deprecated: use `retry_quote_id` instead. Same value.","example":"svq_01J8AB4N7GLY3Q0D","deprecated":true},"current":{"type":"object","properties":{"refund":{"$ref":"#/components/schemas/Money"}},"description":"The quote’s refund, on `quote_drift`. When `retry_quote_id` is present, show this figure to the customer, then commit again with that handle and this figure."},"active_operation":{"type":"string","description":"The operation already running on this booking, on `active_operation_exists`. Present when the running operation belongs to this booking and can be polled with the status route; it may be absent otherwise. Poll it with the status route for its domain — POST /v1/hotel_cancel_status or POST /v1/flight_refund_status.","example":"svc_01J7ZR5Q2KME8V4T"},"shortfall":{"allOf":[{"$ref":"#/components/schemas/Money"},{"description":"How much of the refund could not be reserved, on `funds_unavailable`. The refusal is about money still moving on the original payment, not about this booking, so this is the figure that says how much has to clear before a commit can succeed."}]},"blocking":{"type":"array","items":{"type":"string"},"description":"What is holding that money, on `funds_unavailable`: the refunds already in flight against the same payment, which have to settle before this one can be reserved. Opaque handles — read them as identity, do not parse them. This is what to wait on; without it, \"retry later\" has nothing behind it.","example":["re_3UBIxK2eZvKYlo2C"]}},"required":["error","code"]},"HotelCancelPreviewRequest":{"type":"object","properties":{"booking_ref":{"type":"string","minLength":1,"description":"Jinko booking reference. Call get_booking first, then select its booked_item_id for servicing.","example":"JNK-A0AUR2"},"booked_item_id":{"type":"string","pattern":"^itm_[a-z2-7]+$(?![\\s\\S])","description":"The booked item, from get_booking items[].booked_item_id. Preserved across exchanges. Omit only when the booking has exactly one item of the requested domain; otherwise a 409 returns items to choose from.","example":"itm_aaaaaaaaaaaaafhuxsdxpbzrbfz5hm3a"},"last_name":{"type":"string","minLength":1,"description":"Required for guest lookup. A credential that owns the booking may omit it; ownership is checked by the platform.","example":"Doe"},"provider_reference":{"type":"string","minLength":1,"description":"Deprecated: name the booking with booking_ref and booked_item_id from get_booking. Names the booking instead of booking_ref and last_name, never beside it, and needs a credential that owns the booking. Accepted until 2027-04-30; a request that sends it is answered with `Deprecation` and `Sunset` headers.","deprecated":true},"intent":{"$ref":"#/components/schemas/IntentInput"}},"example":{"booking_ref":"JNK-H1ZK90","last_name":"Doe"}},"HotelCancelCommitResponse":{"type":"object","properties":{"operation":{"type":"string","description":"The cancellation this call started (\"svc_…\"). Poll it with POST /v1/hotel_cancel_status; it is the only handle that reports how the supplier call and the refund ended.","example":"svc_01J7ZR5Q2KME8V4T"},"state":{"$ref":"#/components/schemas/ServicingState"}}},"HotelCancelCommitRequest":{"type":"object","properties":{"booking_ref":{"type":"string","minLength":1,"description":"Jinko booking reference. Call get_booking first, then select its booked_item_id for servicing.","example":"JNK-A0AUR2"},"booked_item_id":{"type":"string","pattern":"^itm_[a-z2-7]+$(?![\\s\\S])","description":"The booked item, from get_booking items[].booked_item_id. Preserved across exchanges. Omit only when the booking has exactly one item of the requested domain; otherwise a 409 returns items to choose from.","example":"itm_aaaaaaaaaaaaafhuxsdxpbzrbfz5hm3a"},"last_name":{"type":"string","minLength":1,"description":"Required for guest lookup. A credential that owns the booking may omit it; ownership is checked by the platform.","example":"Doe"},"provider_reference":{"type":"string","minLength":1,"description":"Deprecated: name the booking with booking_ref and booked_item_id from get_booking. Names the booking instead of booking_ref and last_name, never beside it, and needs a credential that owns the booking. Accepted until 2027-04-30; a request that sends it is answered with `Deprecation` and `Sunset` headers.","deprecated":true},"intent":{"$ref":"#/components/schemas/IntentInput"},"quote_id":{"type":"string","description":"The `quote_id` from a preceding POST /v1/hotel_cancel_preview. Binds this commit to the figures that quote reported.","example":"svq_01J7ZR3M8FKX2P9C"},"acknowledged":{"type":"object","properties":{"value":{"type":"integer","description":"Integer amount in MINOR units, always paired with decimal_places — divide by 10 ** decimal_places to display. Example: value 15977 with decimal_places 2 is 159.77 USD. Rendering this field directly shows prices 100x too high for 2-decimal currencies.","example":25000},"currency":{"type":"string","minLength":1,"description":"ISO 4217 currency code.","example":"USD"},"decimal_places":{"type":"integer","description":"Scale of the integer value/amount: display = integer / 10 ** decimal_places. Always sent alongside minor-unit amounts. Usually the ISO 4217 digits of the currency (2 for USD/EUR/GBP, 0 for JPY/KRW, 3 for BHD/JOD/KWD) but currency-converted prices can carry a different scale — ALWAYS use the decimal_places sent with the amount, never a hardcoded 2. Only if the field is genuinely absent on a value-shaped object, fall back to the ISO digits for the currency.","example":2}},"required":["value","currency","decimal_places"],"description":"The refund the customer was shown — copy the preview's `refund.amount_money` across. Copying the whole `refund` object, as older integrations do, is still accepted. All three of `value` (minor units), `currency` and `decimal_places` are REQUIRED: the platform compares this against the refund as it stands, and a figure whose scale is unstated cannot be compared. `amount` is not accepted in its place. Any other key, such as `display` or `basis`, is ignored. A mismatch against a still-live quote’s refund is answered 409 `quote_drift`. `retry_quote_id` is that same quote’s handle and `current.refund` its figure. Show it to the customer, then commit again with that handle and figure. Nothing is cancelled."}},"required":["quote_id","acknowledged"],"example":{"booking_ref":"JNK-H1ZK90","last_name":"Doe","quote_id":"svq_01J7ZR3M8FKX2P9C","acknowledged":{"value":25000,"currency":"USD","decimal_places":2}}},"HotelCancelStatusResponse":{"type":"object","properties":{"operation":{"type":"string","example":"svc_01J7ZR5Q2KME8V4T"},"state":{"$ref":"#/components/schemas/ServicingState"},"reason":{"type":"string","description":"With `state: attention_required`, why the operation is stalled: the value names what Jinko has to resolve, and needs nothing from the caller. With `state: failed`, why it ended: `provider_refused` (the supplier refused before changing anything), `provider_did_not_apply` (the supplier’s record shows it was not applied), `not_sent` (it ended before anything was sent to the supplier), `QUOTE_EXPIRED` (the quote it was committed on no longer stood), `superseded` or `not_cancellable`. Absent on a failed operation that recorded none.","example":"settlement_review"},"message":{"type":"string","description":"A sentence about why a failed operation ended, where there is one: what the supplier’s refusal means, or that its price changed after the quote. Present only with `state: failed`; it never states the supplier’s figures.","example":"the fare rules do not allow a refund of this ticket"},"park_deadline":{"type":"string","description":"When a parked operation stops waiting for Jinko and is settled or failed. Present with `state: attention_required`; it is how long \"keep polling\" lasts.","example":"2026-09-10T12:15:00Z"},"next_run_at":{"type":"string","description":"When the platform next drives this operation by itself. Informational — polling sooner does not make it run sooner.","example":"2026-09-03T12:20:00Z"},"provider":{"type":"object","properties":{"state":{"type":"string","description":"The supplier's own view of the booking: `confirmed` (still standing), `cancelled`, or `pending` (the supplier has not settled it yet).","example":"cancelled"},"references":{"type":"object","properties":{"cancellation_reference":{"type":"string","description":"The reference the supplier issued for the cancellation or refund itself, for example the ticket number a void settles or the supplier’s refund id. It is never the supplier’s identifier for the booking, and is absent when the supplier issued none.","example":"cxl_2f90a1c3"}}}},"description":"The supplier's record. It can say `cancelled` while the money is still moving — that is the normal middle of a cancellation, not a discrepancy."},"money":{"type":"object","properties":{"amount":{"type":"number","description":"Deprecated: use `amount_money` instead. Alternative to `value` on some endpoints (the two never appear together); its scale depends on decimal_places. When this object carries decimal_places, amount is an INTEGER in minor units — divide by 10 ** decimal_places (e.g. select_ancillaries total_with_ancillaries). When there is no decimal_places field, amount is a decimal in MAJOR units, safe to display as-is (e.g. trip and checkout totals).","deprecated":true},"value":{"type":"integer","description":"Deprecated: use `amount_money` instead. Integer amount in MINOR units, always paired with decimal_places — divide by 10 ** decimal_places to display. Example: value 15977 with decimal_places 2 is 159.77 USD. Rendering this field directly shows prices 100x too high for 2-decimal currencies.","example":41250,"deprecated":true},"currency":{"type":"string","description":"Deprecated: use `amount_money` instead. ISO 4217 currency code.","example":"USD","deprecated":true},"decimal_places":{"type":"integer","description":"Deprecated: use `amount_money` instead. Scale of the integer value/amount: display = integer / 10 ** decimal_places. Always sent alongside minor-unit amounts. Usually the ISO 4217 digits of the currency (2 for USD/EUR/GBP, 0 for JPY/KRW, 3 for BHD/JOD/KWD) but currency-converted prices can carry a different scale — ALWAYS use the decimal_places sent with the amount, never a hardcoded 2. Only if the field is genuinely absent on a value-shaped object, fall back to the ISO digits for the currency.","example":2,"deprecated":true},"display":{"type":"string","description":"Deprecated: use `amount_money` instead. The same figure as a string ready to show: the ISO 4217 code, a space, then the amount, e.g. \"USD 159.77\". With decimal_places it is written at exactly that scale (value 1561500, JPY, decimal_places 2 is \"JPY 15615.00\"); without, at the ISO digits of the currency. Show this; compute with the number. Absent when the figure is not money (a cancellation step expressed as a percent or a number of nights) or could not be read unambiguously.","example":"USD 412.50","deprecated":true},"amount_money":{"allOf":[{"$ref":"#/components/schemas/MoneyValue"},{"description":"The figure being moved."}]},"direction":{"type":"string","description":"Which way the money moves. `refund` on a cancellation.","example":"refund"},"vehicle_state":{"type":"string","description":"How far the money itself has got, independently of the supplier: `reserved` (earmarked, nothing sent), `refund_pending` (submitted to the payment provider), `payout_initiated` and `payout_settled` (paid out to a third party), `paid` (it has reached the customer), `released` (earmark dropped, nothing owed), `refund_review` (a person at Jinko has to release it). Only `paid` and `payout_settled` mean the customer has the money.","example":"paid"},"stripe_reference":{"type":"string","description":"Deprecated. An internal payment reference.","example":"re_3UBIxK2eZvKYlo2C","deprecated":true}},"description":"The refund and where it has got to. Read `vehicle_state` before telling a customer they have been refunded."}}},"HotelCancelStatusRequest":{"type":"object","properties":{"booking_ref":{"type":"string","minLength":1,"description":"Jinko booking reference. Call get_booking first, then select its booked_item_id for servicing.","example":"JNK-A0AUR2"},"booked_item_id":{"type":"string","pattern":"^itm_[a-z2-7]+$(?![\\s\\S])","description":"The booked item, from get_booking items[].booked_item_id. Preserved across exchanges. Omit only when the booking has exactly one item of the requested domain; otherwise a 409 returns items to choose from.","example":"itm_aaaaaaaaaaaaafhuxsdxpbzrbfz5hm3a"},"last_name":{"type":"string","minLength":1,"description":"Required for guest lookup. A credential that owns the booking may omit it; ownership is checked by the platform.","example":"Doe"},"provider_reference":{"type":"string","minLength":1,"description":"Deprecated: name the booking with booking_ref and booked_item_id from get_booking. Names the booking instead of booking_ref and last_name, never beside it, and needs a credential that owns the booking. Accepted until 2027-04-30; a request that sends it is answered with `Deprecation` and `Sunset` headers.","deprecated":true},"intent":{"$ref":"#/components/schemas/IntentInput"},"operation":{"type":"string","description":"The \"svc_…\" handle from a commit, or from POST /v1/hotel_cancel.","example":"svc_01J7ZR5Q2KME8V4T"}},"required":["operation"],"example":{"booking_ref":"JNK-H1ZK90","last_name":"Doe","operation":"svc_01J7ZR5Q2KME8V4T"}},"FlightServicingSupportLevel":{"type":"string","enum":["AUTO","AUTO_VOID","MANUAL_REQUIRED"],"description":"How this refund would be carried out. `AUTO` — the platform refunds the ticket end to end under the fare rules. `AUTO_VOID` — the ticket is still inside the airline's void window, so the platform voids it and the ENTIRE charge is reversed, penalty-free; there is no supplier figure to report on a void. `MANUAL_REQUIRED` — it is possible, but a Jinko agent has to act (`manual_reason` says why): the commit is refused unless you send `manual_ok: true`, which hands it to that agent. Do not treat MANUAL_REQUIRED as a failure. Time-sensitive: a void window closes, so read the level from a live preview."},"FlightDocument":{"type":"object","properties":{"number":{"type":"string","description":"The 13-digit ticket or EMD number.","example":"0012345678901"},"type":{"type":"string","enum":["TKT","EMD"],"description":"What the document is. `TKT` — the flight ticket itself. `EMD` — an electronic miscellaneous document, which is how an ancillary (a bag, a seat) is issued."},"state":{"type":"string","enum":["ACTIVE","REFUNDED","VOIDED","INACTIVE","PENDING","UNKNOWN"],"description":"Where the document itself has got to. Every document on a preview is `ACTIVE` — that is what makes the quote possible. On a status read the values are the outcome per document: `REFUNDED`, `VOIDED`, `INACTIVE` (the airline dropped it), `PENDING` (the airline has not settled it) or `UNKNOWN` (not yet observed).","example":"ACTIVE"},"recoverable":{"type":"boolean","description":"Whether this document’s value can be recovered through this path. `false` on every EMD: the platform has no EMD refund or void operation, so an ancillary issued as one is not returned by this operation and a person has to recover it.","example":false}}},"FlightRefundPreviewResponse":{"type":"object","properties":{"seat_refunds":{"type":"array","items":{"$ref":"#/components/schemas/SeatRefund"},"description":"Separate refunds for carrier-settled paid seats. A paid seat is refunded once the airline refunds Jinko; these amounts are not part of the quoted fare refund."},"quote_id":{"type":"string","description":"This quote. Pass it as `quote_id` to POST /v1/flight_refund_commit — it binds the commit to the figures below. Opaque; the format may evolve.","example":"svq_01J7ZR3M8FKX2P9C"},"quote":{"type":"string","description":"Deprecated: use `quote_id` instead. Same value.","example":"svq_01J7ZR3M8FKX2P9C","deprecated":true},"state":{"type":"string","description":"Lifecycle of the QUOTE, not of a refund — nothing has been refunded or voided by this call. `completed` means the figures are final until `expires_at`.","example":"completed"},"commitable":{"type":"boolean","description":"Whether a commit against this quote would be accepted as it stands. A MANUAL_REQUIRED quote is never commitable and is still SUBMITTABLE: send the commit with `manual_ok: true` to hand it to a Jinko agent. Otherwise, when false, `not_commitable_reason` says why and committing is pointless.","example":true},"support_level":{"$ref":"#/components/schemas/FlightServicingSupportLevel"},"manual_reason":{"type":"string","description":"Why a person has to act, in words the customer can be shown. Present with `support_level: MANUAL_REQUIRED`. Where the reason would be the supplier’s own words, it reads “This request needs manual handling by Jinko support.”","example":"fare rules require agent review"},"operation_kind":{"allOf":[{"$ref":"#/components/schemas/FlightOperationKind"},{"description":"What this quote would do — refund the ticket under the fare rules (`cancel`) or void it (`void`). On a void the whole charge comes back and the penalty is zero. The status read reports the operation that actually ran under this same name, so the two compare directly. The handle that binds this quote is `quote`; `operation` is the \"svc_…\" handle the COMMIT answers, and it never appears on a quote."}]},"settlement_basis":{"type":"string","description":"How the refund is settled. `sell_minus_penalty` — what the customer paid for this ticket, less the penalty (a cancellation). `original_charge` — the whole charge is reversed, penalty-free (a void). It follows `operation_kind`, which the platform derives; the caller cannot ask for one.","example":"sell_minus_penalty"},"expires_at":{"type":"string","description":"When this quote stops binding. Committing after it answers 409 `QUOTE_EXPIRED`; take a fresh quote and show the customer the new figure before committing again.","example":"2026-09-03T12:15:00Z"},"booked_item_id":{"type":"string","description":"The booked item this quote covers — the `booked_item_id` get_booking returns. A booking holding several items is quoted and refunded one item at a time; within an item, every ticket issued for it is covered together.","example":"itm_7f2c9a4e8b1d"},"item":{"type":"string","description":"Deprecated: use `booked_item_id` instead. Same value.","example":"itm_7f2c9a4e8b1d","deprecated":true},"refund":{"type":"object","properties":{"amount":{"type":"number","description":"Deprecated: use `amount_money` instead. Alternative to `value` on some endpoints (the two never appear together); its scale depends on decimal_places. When this object carries decimal_places, amount is an INTEGER in minor units — divide by 10 ** decimal_places (e.g. select_ancillaries total_with_ancillaries). When there is no decimal_places field, amount is a decimal in MAJOR units, safe to display as-is (e.g. trip and checkout totals).","deprecated":true},"value":{"type":"integer","description":"Deprecated: use `amount_money` instead. Integer amount in MINOR units, always paired with decimal_places — divide by 10 ** decimal_places to display. Example: value 15977 with decimal_places 2 is 159.77 USD. Rendering this field directly shows prices 100x too high for 2-decimal currencies.","example":41250,"deprecated":true},"currency":{"type":"string","description":"Deprecated: use `amount_money` instead. ISO 4217 currency code.","example":"USD","deprecated":true},"decimal_places":{"type":"integer","description":"Deprecated: use `amount_money` instead. Scale of the integer value/amount: display = integer / 10 ** decimal_places. Always sent alongside minor-unit amounts. Usually the ISO 4217 digits of the currency (2 for USD/EUR/GBP, 0 for JPY/KRW, 3 for BHD/JOD/KWD) but currency-converted prices can carry a different scale — ALWAYS use the decimal_places sent with the amount, never a hardcoded 2. Only if the field is genuinely absent on a value-shaped object, fall back to the ISO digits for the currency.","example":2,"deprecated":true},"display":{"type":"string","description":"Deprecated: use `amount_money` instead. The same figure as a string ready to show: the ISO 4217 code, a space, then the amount, e.g. \"USD 159.77\". With decimal_places it is written at exactly that scale (value 1561500, JPY, decimal_places 2 is \"JPY 15615.00\"); without, at the ISO digits of the currency. Show this; compute with the number. Absent when the figure is not money (a cancellation step expressed as a percent or a number of nights) or could not be read unambiguously.","example":"USD 412.50","deprecated":true},"amount_money":{"allOf":[{"$ref":"#/components/schemas/MoneyValue"},{"description":"The refund figure. Absent when the figure is not known."}]},"basis":{"type":"string","description":"How the figure was computed. `sell_minus_penalty` — what the customer paid for this item, less the penalty. `original_charge` — the entire charge is reversed, penalty-free (a void). The platform chooses; the caller cannot ask for one.","example":"sell_minus_penalty"}},"description":"THE CUSTOMER FIGURE: what would go back to the payment method, on the basis named in `basis` — what they paid less the penalty, or the entire charge on a void. Show this one. Absent is not zero: a quote whose penalty is unknown carries no refund figure at all."},"penalty":{"type":"object","properties":{"amount":{"type":"number","description":"Deprecated: use `amount_money` instead. Alternative to `value` on some endpoints (the two never appear together); its scale depends on decimal_places. When this object carries decimal_places, amount is an INTEGER in minor units — divide by 10 ** decimal_places (e.g. select_ancillaries total_with_ancillaries). When there is no decimal_places field, amount is a decimal in MAJOR units, safe to display as-is (e.g. trip and checkout totals).","deprecated":true},"value":{"type":"integer","description":"Deprecated: use `amount_money` instead. Integer amount in MINOR units, always paired with decimal_places — divide by 10 ** decimal_places to display. Example: value 15977 with decimal_places 2 is 159.77 USD. Rendering this field directly shows prices 100x too high for 2-decimal currencies.","example":41250,"deprecated":true},"currency":{"type":"string","description":"Deprecated: use `amount_money` instead. ISO 4217 currency code.","example":"USD","deprecated":true},"decimal_places":{"type":"integer","description":"Deprecated: use `amount_money` instead. Scale of the integer value/amount: display = integer / 10 ** decimal_places. Always sent alongside minor-unit amounts. Usually the ISO 4217 digits of the currency (2 for USD/EUR/GBP, 0 for JPY/KRW, 3 for BHD/JOD/KWD) but currency-converted prices can carry a different scale — ALWAYS use the decimal_places sent with the amount, never a hardcoded 2. Only if the field is genuinely absent on a value-shaped object, fall back to the ISO digits for the currency.","example":2,"deprecated":true},"display":{"type":"string","description":"Deprecated: use `amount_money` instead. The same figure as a string ready to show: the ISO 4217 code, a space, then the amount, e.g. \"USD 159.77\". With decimal_places it is written at exactly that scale (value 1561500, JPY, decimal_places 2 is \"JPY 15615.00\"); without, at the ISO digits of the currency. Show this; compute with the number. Absent when the figure is not money (a cancellation step expressed as a percent or a number of nights) or could not be read unambiguously.","example":"USD 412.50","deprecated":true},"amount_money":{"allOf":[{"$ref":"#/components/schemas/MoneyValue"},{"description":"The penalty figure. Present only when `fee_known` is true; absent when the fee is unknown, which is not zero."}]},"fee_known":{"type":"boolean","description":"false means the penalty could not be established — UNKNOWN, not zero. Render it as \"we will confirm the fee\", never as free cancellation, and expect the final figure on the operation status.","example":true},"breakdown":{"type":"object","properties":{"airline_fee":{"allOf":[{"$ref":"#/components/schemas/MoneyValue"},{"description":"The airline’s cancellation fee, taxes on the fee included. This is the figure a fare rule advertises."}]},"non_refundable_taxes":{"allOf":[{"$ref":"#/components/schemas/MoneyValue"},{"description":"The taxes and carrier surcharges the airline does not give back, summed. On a ticket refunded after part of it was used, this includes the taxes of the flights already flown."}]},"used_fare":{"allOf":[{"$ref":"#/components/schemas/MoneyValue"},{"description":"The fare of the flights already flown, on a ticket that is refunded after part of it was used."}]}},"description":"How the supplier itemises the penalty, in the same currency: the airline’s cancellation fee, the taxes it does not give back, and the fare of flights already flown. Present when the supplier states at least one of them; a member the supplier does not state is absent, and a stated zero is a zero. The penalty figure is what is withheld; the members explain it and may not add up to it exactly (each is converted and rounded on its own, and a supplier may withhold something it does not itemise)."}},"description":"What the customer would forfeit, in the currency they were charged. Zero on a void. May be omitted when unknown; absent is not zero. Read `fee_known` before showing it — false means UNKNOWN, not free."},"documents":{"type":"array","items":{"$ref":"#/components/schemas/FlightDocument"},"description":"Every document this operation covers, each of them active right now. Ticket subsets are not offered: the operation takes the whole item."},"ancillary_recoverable":{"type":"boolean","description":"Whether the ancillaries bought with this ticket come back with it. `false` when any document is an EMD — the platform cannot refund or void one, so that value has to be recovered by a person, and the quote is MANUAL_REQUIRED. Tell the customer before they commit, not after.","example":true},"not_commitable_reason":{"type":"string","description":"Why `commitable` is false. Today: `manual_required` (a Jinko agent has to act — resubmit with `manual_ok: true`; `manual_reason` says why), `penalty_exceeds_sell` (the penalty is at least what the customer paid), `multi_currency_basis` (the airline prices the refund in a currency the customer was not charged in, so no single figure exists). Two refusals are never values here: a fare the airline will not refund is 409 `not_cancellable` from this preview itself, and an airline with no refund route is 422 `provider_unsupported`. The contract reserves further values (`provider_unsupported`, `funds_unavailable`, `insufficient_time_to_converge`) and new reasons may be added, so treat an unrecognised value as \"not right now\".","example":"manual_required"}}},"FlightRefundPreviewRequest":{"type":"object","properties":{"booking_ref":{"type":"string","minLength":1,"description":"Jinko booking reference. Call get_booking first, then select its booked_item_id for servicing.","example":"JNK-A0AUR2"},"booked_item_id":{"type":"string","pattern":"^itm_[a-z2-7]+$(?![\\s\\S])","description":"The booked item, from get_booking items[].booked_item_id. Preserved across exchanges. Omit only when the booking has exactly one item of the requested domain; otherwise a 409 returns items to choose from.","example":"itm_aaaaaaaaaaaaafhuxsdxpbzrbfz5hm3a"},"last_name":{"type":"string","minLength":1,"description":"Required for guest lookup. A credential that owns the booking may omit it; ownership is checked by the platform.","example":"Doe"},"provider_reference":{"type":"string","minLength":1,"description":"Deprecated: name the booking with booking_ref and booked_item_id from get_booking. Names the booking instead of booking_ref and last_name, never beside it, and needs a credential that owns the booking. Accepted until 2027-04-30; a request that sends it is answered with `Deprecation` and `Sunset` headers.","deprecated":true},"intent":{"$ref":"#/components/schemas/IntentInput"}},"example":{"booking_ref":"JNK-A0AUR2","last_name":"Doe"}},"FlightRefundCommitResponse":{"type":"object","properties":{"operation":{"type":"string","description":"The operation this call started (\"svc_…\"). Poll it with POST /v1/flight_refund_status; it is the only handle that reports how the airline call and the refund ended.","example":"svc_01J7ZR5Q2KME8V4T"},"state":{"$ref":"#/components/schemas/ServicingState"},"reason":{"type":"string","description":"Why the operation is parked, when `state` is `attention_required`. On a commit that is `manual_required`: you sent `manual_ok: true`, a Jinko agent now has it, and nothing has been sent to the airline. Not a failure — poll the operation.","example":"manual_required"}}},"FlightRefundCommitRequest":{"type":"object","properties":{"booking_ref":{"type":"string","minLength":1,"description":"Jinko booking reference. Call get_booking first, then select its booked_item_id for servicing.","example":"JNK-A0AUR2"},"booked_item_id":{"type":"string","pattern":"^itm_[a-z2-7]+$(?![\\s\\S])","description":"The booked item, from get_booking items[].booked_item_id. Preserved across exchanges. Omit only when the booking has exactly one item of the requested domain; otherwise a 409 returns items to choose from.","example":"itm_aaaaaaaaaaaaafhuxsdxpbzrbfz5hm3a"},"last_name":{"type":"string","minLength":1,"description":"Required for guest lookup. A credential that owns the booking may omit it; ownership is checked by the platform.","example":"Doe"},"provider_reference":{"type":"string","minLength":1,"description":"Deprecated: name the booking with booking_ref and booked_item_id from get_booking. Names the booking instead of booking_ref and last_name, never beside it, and needs a credential that owns the booking. Accepted until 2027-04-30; a request that sends it is answered with `Deprecation` and `Sunset` headers.","deprecated":true},"intent":{"$ref":"#/components/schemas/IntentInput"},"quote_id":{"type":"string","description":"The `quote_id` from a preceding POST /v1/flight_refund_preview. Binds this commit to the figures that quote reported.","example":"svq_01J7ZR3M8FKX2P9C"},"acknowledged":{"type":"object","properties":{"value":{"type":"integer","description":"Integer amount in MINOR units, always paired with decimal_places — divide by 10 ** decimal_places to display. Example: value 15977 with decimal_places 2 is 159.77 USD. Rendering this field directly shows prices 100x too high for 2-decimal currencies.","example":25000},"currency":{"type":"string","minLength":1,"description":"ISO 4217 currency code.","example":"USD"},"decimal_places":{"type":"integer","description":"Scale of the integer value/amount: display = integer / 10 ** decimal_places. Always sent alongside minor-unit amounts. Usually the ISO 4217 digits of the currency (2 for USD/EUR/GBP, 0 for JPY/KRW, 3 for BHD/JOD/KWD) but currency-converted prices can carry a different scale — ALWAYS use the decimal_places sent with the amount, never a hardcoded 2. Only if the field is genuinely absent on a value-shaped object, fall back to the ISO digits for the currency.","example":2}},"required":["value","currency","decimal_places"],"description":"The refund the customer was shown — copy the preview's `refund.amount_money` across. Copying the whole `refund` object, as older integrations do, is still accepted. REQUIRED whenever the preview carried a `refund`, which is every quote that names a figure. It may be omitted in exactly one case: a MANUAL_REQUIRED quote that carries no `refund` at all because the penalty is unknown (`penalty.fee_known: false`), submitted with `manual_ok: true` — there is no figure to acknowledge, and inventing one would have the customer agree to a number nobody computed. Omitting it anywhere else is refused by the platform, not here. When present, all three of `value` (minor units), `currency` and `decimal_places` are required: the platform compares this against the refund as it stands, and a figure whose scale is unstated cannot be compared. `amount` is not accepted in its place. Any other key, such as `display` or `basis`, is ignored. A mismatch against a still-live quote’s refund is answered 409 `quote_drift`. `retry_quote_id` is that same quote’s handle and `current.refund` its figure. Show it to the customer, then commit again with that handle and figure. Nothing is sent to the airline."},"manual_ok":{"type":"boolean","description":"Send `true` to submit a quote whose `support_level` is MANUAL_REQUIRED. The operation is then raised for a Jinko agent and answers `state: attention_required` with `reason: manual_required` — NOTHING is sent to the airline by this call, and the agent settles or fails it. Without the flag such a commit is refused 409 `manual_required`, so a customer is never put in an agent queue unasked. On an AUTO or AUTO_VOID quote the flag changes nothing. This is also the one submission that may carry no `acknowledged` figure — when the quote itself named none, because the penalty is unknown.","example":false}},"required":["quote_id"],"example":{"booking_ref":"JNK-A0AUR2","last_name":"Doe","quote_id":"svq_01J7ZR3M8FKX2P9C","acknowledged":{"value":25000,"currency":"USD","decimal_places":2}}},"FlightRefundStatusResponse":{"type":"object","properties":{"operation":{"type":"string","example":"svc_01J7ZR5Q2KME8V4T"},"state":{"$ref":"#/components/schemas/ServicingState"},"reason":{"type":"string","description":"With `state: attention_required`, why the operation is stalled: the value names what Jinko has to resolve, and needs nothing from the caller. With `state: failed`, why it ended: `provider_refused` (the supplier refused before changing anything), `provider_did_not_apply` (the supplier’s record shows it was not applied), `not_sent` (it ended before anything was sent to the supplier), `QUOTE_EXPIRED` (the quote it was committed on no longer stood), `superseded` or `not_cancellable`. Absent on a failed operation that recorded none.","example":"settlement_review"},"message":{"type":"string","description":"A sentence about why a failed operation ended, where there is one: what the supplier’s refusal means, or that its price changed after the quote. Present only with `state: failed`; it never states the supplier’s figures.","example":"the fare rules do not allow a refund of this ticket"},"park_deadline":{"type":"string","description":"When a parked operation stops waiting for Jinko and is settled or failed. Present with `state: attention_required`; it is how long \"keep polling\" lasts.","example":"2026-09-10T12:15:00Z"},"next_run_at":{"type":"string","description":"When the platform next drives this operation by itself. Informational — polling sooner does not make it run sooner.","example":"2026-09-03T12:20:00Z"},"provider":{"type":"object","properties":{"state":{"type":"string","description":"The supplier's own view of the booking: `confirmed` (still standing), `cancelled`, or `pending` (the supplier has not settled it yet).","example":"cancelled"},"references":{"type":"object","properties":{"cancellation_reference":{"type":"string","description":"The reference the supplier issued for the cancellation or refund itself, for example the ticket number a void settles or the supplier’s refund id. It is never the supplier’s identifier for the booking, and is absent when the supplier issued none.","example":"cxl_2f90a1c3"}}},"documents":{"type":"array","items":{"$ref":"#/components/schemas/FlightDocument"},"description":"What each document the operation covers was last observed to be. Absent until the first probe. This is the proof of what happened: an operation has done its work when every document it covers has reached a terminal state, not when a refund record appears."}},"description":"The airline's record. It can show the tickets refunded while the money is still moving — that is the normal middle of a refund, not a discrepancy."},"money":{"type":"object","properties":{"amount":{"type":"number","description":"Deprecated: use `amount_money` instead. Alternative to `value` on some endpoints (the two never appear together); its scale depends on decimal_places. When this object carries decimal_places, amount is an INTEGER in minor units — divide by 10 ** decimal_places (e.g. select_ancillaries total_with_ancillaries). When there is no decimal_places field, amount is a decimal in MAJOR units, safe to display as-is (e.g. trip and checkout totals).","deprecated":true},"value":{"type":"integer","description":"Deprecated: use `amount_money` instead. Integer amount in MINOR units, always paired with decimal_places — divide by 10 ** decimal_places to display. Example: value 15977 with decimal_places 2 is 159.77 USD. Rendering this field directly shows prices 100x too high for 2-decimal currencies.","example":41250,"deprecated":true},"currency":{"type":"string","description":"Deprecated: use `amount_money` instead. ISO 4217 currency code.","example":"USD","deprecated":true},"decimal_places":{"type":"integer","description":"Deprecated: use `amount_money` instead. Scale of the integer value/amount: display = integer / 10 ** decimal_places. Always sent alongside minor-unit amounts. Usually the ISO 4217 digits of the currency (2 for USD/EUR/GBP, 0 for JPY/KRW, 3 for BHD/JOD/KWD) but currency-converted prices can carry a different scale — ALWAYS use the decimal_places sent with the amount, never a hardcoded 2. Only if the field is genuinely absent on a value-shaped object, fall back to the ISO digits for the currency.","example":2,"deprecated":true},"display":{"type":"string","description":"Deprecated: use `amount_money` instead. The same figure as a string ready to show: the ISO 4217 code, a space, then the amount, e.g. \"USD 159.77\". With decimal_places it is written at exactly that scale (value 1561500, JPY, decimal_places 2 is \"JPY 15615.00\"); without, at the ISO digits of the currency. Show this; compute with the number. Absent when the figure is not money (a cancellation step expressed as a percent or a number of nights) or could not be read unambiguously.","example":"USD 412.50","deprecated":true},"amount_money":{"allOf":[{"$ref":"#/components/schemas/MoneyValue"},{"description":"The figure being moved."}]},"direction":{"type":"string","description":"Which way the money moves. `refund` on a cancellation.","example":"refund"},"vehicle_state":{"type":"string","description":"How far the money itself has got, independently of the supplier: `reserved` (earmarked, nothing sent), `refund_pending` (submitted to the payment provider), `payout_initiated` and `payout_settled` (paid out to a third party), `paid` (it has reached the customer), `released` (earmark dropped, nothing owed), `refund_review` (a person at Jinko has to release it). Only `paid` and `payout_settled` mean the customer has the money.","example":"paid"},"stripe_reference":{"type":"string","description":"Deprecated. An internal payment reference.","example":"re_3UBIxK2eZvKYlo2C","deprecated":true}},"description":"The refund and where it has got to. Read `vehicle_state` before telling a customer they have been refunded."},"operation_kind":{"allOf":[{"$ref":"#/components/schemas/FlightOperationKind"},{"description":"Which operation ran — a refund under the fare rules (`cancel`) or a void of the original charge (`void`). The same field the preview quoted, so what was quoted and what ran compare directly."}]}}},"FlightRefundStatusRequest":{"type":"object","properties":{"booking_ref":{"type":"string","minLength":1,"description":"Jinko booking reference. Call get_booking first, then select its booked_item_id for servicing.","example":"JNK-A0AUR2"},"booked_item_id":{"type":"string","pattern":"^itm_[a-z2-7]+$(?![\\s\\S])","description":"The booked item, from get_booking items[].booked_item_id. Preserved across exchanges. Omit only when the booking has exactly one item of the requested domain; otherwise a 409 returns items to choose from.","example":"itm_aaaaaaaaaaaaafhuxsdxpbzrbfz5hm3a"},"last_name":{"type":"string","minLength":1,"description":"Required for guest lookup. A credential that owns the booking may omit it; ownership is checked by the platform.","example":"Doe"},"provider_reference":{"type":"string","minLength":1,"description":"Deprecated: name the booking with booking_ref and booked_item_id from get_booking. Names the booking instead of booking_ref and last_name, never beside it, and needs a credential that owns the booking. Accepted until 2027-04-30; a request that sends it is answered with `Deprecation` and `Sunset` headers.","deprecated":true},"intent":{"$ref":"#/components/schemas/IntentInput"},"operation":{"type":"string","description":"The \"svc_…\" handle from a commit, or from POST /v1/refund_commit.","example":"svc_01J7ZR5Q2KME8V4T"}},"required":["operation"],"example":{"booking_ref":"JNK-A0AUR2","last_name":"Doe","operation":"svc_01J7ZR5Q2KME8V4T"}},"WebhookCreatedResponse":{"type":"object","properties":{"id":{"type":"number","example":42},"url":{"type":"string","example":"https://partner.example.com/jinko/webhooks"},"events":{"type":"array","items":{"type":"string"},"example":["booking.completed"]},"secret":{"type":"string","description":"HMAC signing secret. Returned ONCE — store it securely; later reads show only a prefix.","example":"whsec_3f9a…"},"status":{"type":"string","example":"active"},"created_at":{"type":"string","example":"2026-06-02T08:00:00Z"}},"required":["id","url","events","secret","status","created_at"]},"CreateWebhookRequest":{"type":"object","properties":{"url":{"type":"string","format":"uri","example":"https://partner.example.com/jinko/webhooks"},"events":{"type":"array","items":{"$ref":"#/components/schemas/WebhookEvent"},"minItems":1,"example":["booking.completed","booking.failed","booking.partial"]}},"required":["url","events"],"example":{"url":"https://partner.example.com/jinko/webhooks","events":["booking.completed","booking.failed","booking.partial"]}},"WebhookListItem":{"type":"object","properties":{"id":{"type":"number","example":42},"url":{"type":"string","example":"https://partner.example.com/jinko/webhooks"},"events":{"type":"array","items":{"type":"string"},"example":["booking.completed","booking.failed"]},"status":{"type":"string","example":"active"},"secret_prefix":{"type":"string","example":"whsec_3f…"},"created_at":{"type":"string","example":"2026-06-02T08:00:00Z"},"updated_at":{"type":"string","example":"2026-06-02T08:00:00Z"}},"required":["id","url","events","status","secret_prefix","created_at","updated_at"]},"WebhookListResponse":{"type":"object","properties":{"webhooks":{"type":"array","items":{"$ref":"#/components/schemas/WebhookListItem"}}},"required":["webhooks"]},"WebhookDelivery":{"type":"object","properties":{"id":{"type":"number"},"event_id":{"type":"string","example":"evt_1234_booking.completed"},"event_type":{"type":"string","example":"booking.completed"},"booking_ref":{"type":"string","example":"JNK-A7B3X9"},"status":{"type":"string","example":"delivered"},"attempt_count":{"type":"number","example":1},"last_http_status":{"type":"number","example":200},"delivered_at":{"type":"string"},"created_at":{"type":"string"}}},"WebhookDetailResponse":{"type":"object","properties":{"webhook":{"$ref":"#/components/schemas/WebhookListItem"},"recent_deliveries":{"type":"array","items":{"$ref":"#/components/schemas/WebhookDelivery"}}},"required":["webhook"]},"WebhookDeleteResponse":{"type":"object","properties":{"message":{"type":"string","example":"webhook deleted"}},"required":["message"]},"WebhookTestResponse":{"type":"object","properties":{"message":{"type":"string","example":"test event enqueued"},"delivery_id":{"type":"number","example":101},"event_id":{"type":"string","example":"evt_test_42_1780000000"}},"required":["message"]},"WebhookDeliveryDetail":{"type":"object","properties":{"id":{"type":"number"},"event_id":{"type":"string","example":"evt_1234_booking.completed"},"event_type":{"type":"string","example":"booking.completed"},"booking_ref":{"type":"string","nullable":true,"example":"JNK-A7B3X9"},"status":{"type":"string","description":"`pending` — queued for delivery; `delivered` — delivered successfully; `failed` — a retry is still scheduled; `exhausted` — the last attempt failed and exhausted_at is set.","example":"exhausted"},"attempt_count":{"type":"number","example":1},"last_http_status":{"type":"number","nullable":true,"example":200},"delivered_at":{"type":"string","nullable":true},"created_at":{"type":"string"},"last_error":{"type":"string","nullable":true,"example":"non-2xx response: 503"},"updated_at":{"type":"string","example":"2026-10-08T11:20:40Z"},"exhausted_at":{"type":"string","nullable":true,"example":"2026-10-08T11:20:40Z"},"replay_count":{"type":"integer","example":0},"payload":{"allOf":[{"$ref":"#/components/schemas/WebhookEventPayload"},{"nullable":true}]}},"required":["last_error","updated_at","exhausted_at","replay_count","payload"],"description":"One event delivery to a webhook subscription, including its attempts and replay count. `payload` is the exact envelope sent, or null after it is purged 30 days after the delivery's last attempt or replay.","example":{"id":981,"event_id":"evt_4471_booking.completed","event_type":"booking.completed","booking_ref":"JNK-8F3D21","status":"exhausted","attempt_count":8,"last_http_status":503,"last_error":"non-2xx response: 503","created_at":"2026-10-08T10:02:11Z","updated_at":"2026-10-08T11:20:40Z","delivered_at":null,"exhausted_at":"2026-10-08T11:20:40Z","replay_count":0,"payload":{"event":"booking.completed","event_id":"evt_4471_booking.completed","booking_ref":"JNK-8F3D21","status":"confirmed","occurred_at":"2026-10-08T10:02:11Z","livemode":true}}},"WebhookDeliveryListResponse":{"type":"object","properties":{"deliveries":{"type":"array","items":{"$ref":"#/components/schemas/WebhookDeliveryDetail"}},"next_cursor":{"type":"string","nullable":true,"example":null}},"required":["deliveries","next_cursor"]},"WebhookReplayResponse":{"type":"object","properties":{"delivery":{"$ref":"#/components/schemas/WebhookDeliveryDetail"}},"required":["delivery"]},"WebhookReplayConflictResponse":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["CONFLICT"]},"message":{"type":"string"},"doc_url":{"type":"string"}},"required":["code","message"]},"code":{"type":"string","enum":["delivery_in_progress","payload_purged"],"description":"The replay refusal reason. Absent for an unrecognised conflict."}},"required":["error"]}},"parameters":{}},"paths":{"/health":{"get":{"tags":["system"],"summary":"Service health","responses":{"200":{"description":"Service is up","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HealthResponse"}}}}}}},"/v1/flight_price_advice":{"post":{"tags":["Discovery"],"summary":"Accept a flight with its current price for price advice","description":"Accepts a flight with its current price and returns price advice. Available results compare the fare with typical fares for comparable flights and provide fixed 1-day and 3-day price forecasts, each with a forecasted price and estimated probabilities of an increase, decrease, or no change. Initial numeric coverage is one-adult, nonstop, USD; one-way, and round-trip with return_date; other structurally valid inputs return a reasoned unavailable result. The deterministic summary can be displayed without an LLM. The endpoint requires a verified jnk_ API key and does not search, monitor, quote, or book flights. When the platform refuses the inputs, the answer is 400 or 422 `BAD_REQUEST` with the platform’s sentence; a 502 is a technical failure (`UPSTREAM_ERROR`).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlightPriceAdviceRequest"}}}},"responses":{"200":{"description":"Computed flight price advice or a reasoned unavailable/not-ready result","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlightPriceAdviceResponse"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"Malformed JSON in request body.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"AUTH_REQUIRED","message":"Invalid or expired API key.","doc_url":"https://docs.gojinko.com/authentication/api-keys"}}}}},"402":{"description":"Payment required — organization balance exhausted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"PAYMENT_REQUIRED","message":"Insufficient balance — this call costs $0.0150 and your organization has $0.0000 available. Top up at https://dashboard.gojinko.com/developers/billing/topup","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"A required field is missing or invalid.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"429":{"description":"Rate limit or quota exceeded","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"502":{"description":"The travel provider rejected the request, or an upstream call failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_REJECTED","message":"The request was declined for this offer. Try another offer.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"503":{"description":"No travel provider can serve the request right now","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_UNAVAILABLE","message":"The service is temporarily unavailable. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"504":{"description":"The travel provider did not answer in time","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_TIMEOUT","message":"The request timed out before a result was available. Retry the request.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}}}}},"/v1/hotel_ai_search":{"post":{"tags":["Hotels"],"summary":"Search hotels from one sentence, with follow-ups","description":"Send the traveller's request in one sentence. The answer comes within about 20 seconds with `200`: the hotels, or a question to answer in the same conversation. When the search takes longer, the answer is `202` with the early reply and a `Location` to read until the task ends. Continue the conversation with its `context_id`.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"servers":[{"url":"https://api.gojinko.com","description":"Production"}],"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":200,"description":"Makes a retried message safe: a retry with the same key answers the first message again instead of sending a second one. Keys are scoped to the caller, not to the conversation, and the retried body is not compared: a different message needs a new key. 1–200 characters."},"required":false,"name":"Idempotency-Key","in":"header"},{"schema":{"type":"string","maxLength":200,"description":"`wait=N` (RFC 7240): answer after at most N seconds, never more than 20, with `202` and the early reply if the task has not ended by then. `wait=0` answers at once. Without it the wait is 20 seconds. The wait applied is returned in `Preference-Applied`.","example":"wait=0"},"required":false,"name":"Prefer","in":"header"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HotelAiSearchRequest"}}}},"responses":{"200":{"description":"The task ended this turn; or, with `Accept: text/event-stream`, the turn as it happens.","headers":{"Preference-Applied":{"schema":{"type":"string"},"description":"The wait applied, as `wait=<seconds>`, when the request sent `Prefer: wait=N`."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HotelAiSearchAnswer"}},"text/event-stream":{"schema":{"type":"string","description":"Server-Sent Events, one per task event: `id` is its sequence number (send it back as `Last-Event-ID` to resume), `event` is `task.status` (`status` `submitted` or `working`), `agent.message` (the early reply first), `hotels.page`, or `task.completed` / `task.input_required` / `task.failed` / `task.canceled` / `task.expired` for the status that ends the turn; `data` is JSON and carries `task_id` and `context_id`. The stream ends with the turn, or after 45 seconds: resume it with the `Last-Event-ID` of the last event received."}}}},"202":{"description":"The search is still running: the early reply. Read `Location` after Retry-After.","headers":{"Location":{"schema":{"type":"string"},"description":"The task to read for the final answer."},"Retry-After":{"schema":{"type":"string"},"description":"Seconds to wait before reading the task again."},"Preference-Applied":{"schema":{"type":"string"},"description":"The wait applied, as `wait=<seconds>`, when the request sent `Prefer: wait=N`."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HotelAiSearchAnswer"}}}},"400":{"description":"Bad request: malformed JSON, or an invalid header such as a reserved `X-End-User-Id`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"Malformed JSON in request body"}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"AUTH_REQUIRED","message":"Invalid or expired API key.","doc_url":"https://docs.gojinko.com/authentication/api-keys"}}}}},"403":{"description":"The key is valid but not granted the `search_live` scope, or the account is limited to cached searches. **`FORBIDDEN`** — The credential is valid, and this request is refused anyway. Do not rotate the key — it is working. Check that it is entitled to this operation, and on this environment.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"FORBIDDEN","message":"This API key is not granted the \"search_live\" scope required by this endpoint"}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"NOT_FOUND","message":"Resource not found.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"410":{"description":"The resource this request names no longer exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"GONE","message":"The resource no longer exists.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"A required field is missing or invalid.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"429":{"description":"Rate limit or quota exceeded","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"502":{"description":"The travel provider rejected the request, or an upstream call failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_REJECTED","message":"The request was declined for this offer. Try another offer.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"503":{"description":"No travel provider can serve the request right now","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_UNAVAILABLE","message":"The service is temporarily unavailable. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"504":{"description":"The travel provider did not answer in time","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_TIMEOUT","message":"The request timed out before a result was available. Retry the request.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}}}}},"/v1/hotel_ai_search/tasks/{task_id}":{"get":{"tags":["Hotels"],"summary":"Read a hotel AI search task","description":"The task's answer now: `working` with the early reply, or the final answer.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"servers":[{"url":"https://api.gojinko.com","description":"Production"}],"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":100,"example":"atk_example"},"required":true,"name":"task_id","in":"path"}],"responses":{"200":{"description":"The task.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HotelAiSearchAnswer"}}}},"400":{"description":"Bad request: an invalid header, such as a reserved `X-End-User-Id` (`user_…`, `org_…`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"X-End-User-Id must be an opaque, tenant-scoped id; WorkOS-shaped ids (user_…, org_…) are not allowed."}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"AUTH_REQUIRED","message":"Invalid or expired API key.","doc_url":"https://docs.gojinko.com/authentication/api-keys"}}}}},"403":{"description":"The key is valid but not granted the `search_live` scope, or the account is limited to cached searches. **`FORBIDDEN`** — The credential is valid, and this request is refused anyway. Do not rotate the key — it is working. Check that it is entitled to this operation, and on this environment.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"FORBIDDEN","message":"This API key is not granted the \"search_live\" scope required by this endpoint"}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"NOT_FOUND","message":"Resource not found.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"A required field is missing or invalid.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"429":{"description":"Rate limit or quota exceeded","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"502":{"description":"The travel provider rejected the request, or an upstream call failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_REJECTED","message":"The request was declined for this offer. Try another offer.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"503":{"description":"No travel provider can serve the request right now","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_UNAVAILABLE","message":"The service is temporarily unavailable. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"504":{"description":"The travel provider did not answer in time","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_TIMEOUT","message":"The request timed out before a result was available. Retry the request.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}}}}},"/v1/hotel_ai_search/tasks/{task_id}/events":{"get":{"tags":["Hotels"],"summary":"Stream a hotel AI search task's events","description":"The task's events as Server-Sent Events, from the start or after `Last-Event-ID`. The stream ends when the task ends its turn, or after 45 seconds: reconnect with the `Last-Event-ID` of the last event received.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"servers":[{"url":"https://api.gojinko.com","description":"Production"}],"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":100,"example":"atk_example"},"required":true,"name":"task_id","in":"path"},{"schema":{"type":"string","maxLength":20,"description":"The `id` of the last event received.","example":"7"},"required":false,"name":"Last-Event-ID","in":"header"}],"responses":{"200":{"description":"The events.","content":{"text/event-stream":{"schema":{"type":"string","description":"Server-Sent Events, one per task event: `id` is its sequence number (send it back as `Last-Event-ID` to resume), `event` is `task.status` (`status` `submitted` or `working`), `agent.message` (the early reply first), `hotels.page`, or `task.completed` / `task.input_required` / `task.failed` / `task.canceled` / `task.expired` for the status that ends the turn; `data` is JSON and carries `task_id` and `context_id`. The stream ends with the turn, or after 45 seconds: resume it with the `Last-Event-ID` of the last event received."}}}},"400":{"description":"Bad request: an invalid header, such as a reserved `X-End-User-Id` (`user_…`, `org_…`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"X-End-User-Id must be an opaque, tenant-scoped id; WorkOS-shaped ids (user_…, org_…) are not allowed."}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"AUTH_REQUIRED","message":"Invalid or expired API key.","doc_url":"https://docs.gojinko.com/authentication/api-keys"}}}}},"403":{"description":"The key is valid but not granted the `search_live` scope, or the account is limited to cached searches. **`FORBIDDEN`** — The credential is valid, and this request is refused anyway. Do not rotate the key — it is working. Check that it is entitled to this operation, and on this environment.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"FORBIDDEN","message":"This API key is not granted the \"search_live\" scope required by this endpoint"}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"NOT_FOUND","message":"Resource not found.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"A required field is missing or invalid.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"429":{"description":"Rate limit or quota exceeded","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"502":{"description":"The travel provider rejected the request, or an upstream call failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_REJECTED","message":"The request was declined for this offer. Try another offer.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"503":{"description":"No travel provider can serve the request right now","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_UNAVAILABLE","message":"The service is temporarily unavailable. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"504":{"description":"The travel provider did not answer in time","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_TIMEOUT","message":"The request timed out before a result was available. Retry the request.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}}}}},"/v1/hotel_ai_search/tasks/{task_id}:cancel":{"post":{"tags":["Hotels"],"summary":"Cancel a hotel AI search task","description":"Cancels a task that has not ended. A task that already ended answers `409` `CONFLICT`.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"servers":[{"url":"https://api.gojinko.com","description":"Production"}],"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":100,"example":"atk_example"},"required":true,"name":"task_id","in":"path"}],"responses":{"200":{"description":"The task, now `canceled`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HotelAiSearchAnswer"}}}},"400":{"description":"Bad request: an invalid header, such as a reserved `X-End-User-Id` (`user_…`, `org_…`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"X-End-User-Id must be an opaque, tenant-scoped id; WorkOS-shaped ids (user_…, org_…) are not allowed."}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"AUTH_REQUIRED","message":"Invalid or expired API key.","doc_url":"https://docs.gojinko.com/authentication/api-keys"}}}}},"403":{"description":"The key is valid but not granted the `search_live` scope, or the account is limited to cached searches. **`FORBIDDEN`** — The credential is valid, and this request is refused anyway. Do not rotate the key — it is working. Check that it is entitled to this operation, and on this environment.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"FORBIDDEN","message":"This API key is not granted the \"search_live\" scope required by this endpoint"}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"NOT_FOUND","message":"Resource not found.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"409":{"description":"The operation conflicts with the current state of the resource","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"CONFLICT","message":"The operation conflicts with the current state of the resource.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"A required field is missing or invalid.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"429":{"description":"Rate limit or quota exceeded","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"502":{"description":"The travel provider rejected the request, or an upstream call failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_REJECTED","message":"The request was declined for this offer. Try another offer.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"503":{"description":"No travel provider can serve the request right now","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_UNAVAILABLE","message":"The service is temporarily unavailable. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"504":{"description":"The travel provider did not answer in time","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_TIMEOUT","message":"The request timed out before a result was available. Retry the request.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}}}}},"/v1/find_destination":{"post":{"tags":["Discovery"],"summary":"Discover travel destinations accessible from your departure airports","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FindDestinationRequest"}}}},"responses":{"200":{"description":"Matching destinations","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FindDestinationResponse"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"Malformed JSON in request body.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"AUTH_REQUIRED","message":"Invalid or expired API key.","doc_url":"https://docs.gojinko.com/authentication/api-keys"}}}}},"402":{"description":"Payment required — organization balance exhausted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"PAYMENT_REQUIRED","message":"Insufficient balance — this call costs $0.0150 and your organization has $0.0000 available. Top up at https://dashboard.gojinko.com/developers/billing/topup","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"NOT_FOUND","message":"Resource not found.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"409":{"description":"The operation conflicts with the current state of the resource","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"CONFLICT","message":"The operation conflicts with the current state of the resource.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"410":{"description":"The resource this request names no longer exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"GONE","message":"The resource no longer exists.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"A required field is missing or invalid.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"429":{"description":"Rate limit or quota exceeded","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"502":{"description":"The travel provider rejected the request, or an upstream call failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_REJECTED","message":"The request was declined for this offer. Try another offer.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"503":{"description":"No travel provider can serve the request right now","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_UNAVAILABLE","message":"The service is temporarily unavailable. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"504":{"description":"The travel provider did not answer in time","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_TIMEOUT","message":"The request timed out before a result was available. Retry the request.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}}}}},"/v1/flight_calendar":{"post":{"tags":["Discovery"],"summary":"Search flights between a known origin and destination for flexible dates","description":"`limit` caps the globally sorted itinerary list across the full matching date range; it does not reserve one result for each date pair. With the default `sort_by: lowest`, a limited response contains the cheapest itineraries overall, so an omitted date pair does not by itself mean that date is missing from the cache. Use `find_dates` for date-pair coverage.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlightDiscoveryRequest"}}}},"responses":{"200":{"description":"Matching itineraries","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlightCalendarResponse"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"Malformed JSON in request body.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"AUTH_REQUIRED","message":"Invalid or expired API key.","doc_url":"https://docs.gojinko.com/authentication/api-keys"}}}}},"402":{"description":"Payment required — organization balance exhausted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"PAYMENT_REQUIRED","message":"Insufficient balance — this call costs $0.0150 and your organization has $0.0000 available. Top up at https://dashboard.gojinko.com/developers/billing/topup","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"NOT_FOUND","message":"Resource not found.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"409":{"description":"The operation conflicts with the current state of the resource","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"CONFLICT","message":"The operation conflicts with the current state of the resource.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"410":{"description":"The resource this request names no longer exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"GONE","message":"The resource no longer exists.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"A required field is missing or invalid.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"429":{"description":"Rate limit or quota exceeded","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"502":{"description":"The travel provider rejected the request, or an upstream call failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_REJECTED","message":"The request was declined for this offer. Try another offer.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"503":{"description":"No travel provider can serve the request right now","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_UNAVAILABLE","message":"The service is temporarily unavailable. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"504":{"description":"The travel provider did not answer in time","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_TIMEOUT","message":"The request timed out before a result was available. Retry the request.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}}}}},"/v1/find_dates":{"post":{"tags":["Discovery"],"summary":"Best date options for a route — the cheapest itinerary per date pair, spread across the window","description":"Takes the same request as flight_calendar and returns one cheapest itinerary per (departure, return) date pair, for up to `limit` date pairs (default 20) spread across the requested departure window, so you see a diverse set of date options rather than near-adjacent cheapest days. Cache-backed; never live-prices.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlightDiscoveryRequest"}}}},"responses":{"200":{"description":"Best date options (one itinerary per date-pair)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlightCalendarResponse"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"Malformed JSON in request body.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"AUTH_REQUIRED","message":"Invalid or expired API key.","doc_url":"https://docs.gojinko.com/authentication/api-keys"}}}}},"402":{"description":"Payment required — organization balance exhausted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"PAYMENT_REQUIRED","message":"Insufficient balance — this call costs $0.0150 and your organization has $0.0000 available. Top up at https://dashboard.gojinko.com/developers/billing/topup","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"NOT_FOUND","message":"Resource not found.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"409":{"description":"The operation conflicts with the current state of the resource","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"CONFLICT","message":"The operation conflicts with the current state of the resource.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"410":{"description":"The resource this request names no longer exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"GONE","message":"The resource no longer exists.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"A required field is missing or invalid.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"429":{"description":"Rate limit or quota exceeded","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"502":{"description":"The travel provider rejected the request, or an upstream call failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_REJECTED","message":"The request was declined for this offer. Try another offer.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"503":{"description":"No travel provider can serve the request right now","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_UNAVAILABLE","message":"The service is temporarily unavailable. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"504":{"description":"The travel provider did not answer in time","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_TIMEOUT","message":"The request timed out before a result was available. Retry the request.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}}}}},"/v1/lowest_fare":{"post":{"tags":["Discovery"],"summary":"Cheapest fares for a fixed route + date — up to ten itineraries, cheapest first","description":"Takes the same request as flight_calendar but, for a fixed origin, destination, and date, returns up to ten itineraries sorted cheapest-first; `limit` is not applied on this route. The fixed-date counterpart of flight_calendar. Cache-backed; never live-prices.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlightDiscoveryRequest"}}}},"responses":{"200":{"description":"Cheapest itineraries for the route + date, lowest-first","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlightCalendarResponse"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"Malformed JSON in request body.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"AUTH_REQUIRED","message":"Invalid or expired API key.","doc_url":"https://docs.gojinko.com/authentication/api-keys"}}}}},"402":{"description":"Payment required — organization balance exhausted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"PAYMENT_REQUIRED","message":"Insufficient balance — this call costs $0.0150 and your organization has $0.0000 available. Top up at https://dashboard.gojinko.com/developers/billing/topup","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"NOT_FOUND","message":"Resource not found.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"409":{"description":"The operation conflicts with the current state of the resource","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"CONFLICT","message":"The operation conflicts with the current state of the resource.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"410":{"description":"The resource this request names no longer exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"GONE","message":"The resource no longer exists.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"A required field is missing or invalid.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"429":{"description":"Rate limit or quota exceeded","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"502":{"description":"The travel provider rejected the request, or an upstream call failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_REJECTED","message":"The request was declined for this offer. Try another offer.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"503":{"description":"No travel provider can serve the request right now","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_UNAVAILABLE","message":"The service is temporarily unavailable. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"504":{"description":"The travel provider did not answer in time","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_TIMEOUT","message":"The request timed out before a result was available. Retry the request.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}}}}},"/v1/flight_search":{"post":{"tags":["Pricing & booking"],"summary":"Get live flight pricing — search by route, or re-price a known offer","description":"Two modes: **search** — provide `origin` + `destination` + `departure_date` to price a route; or **price-check** — provide only `offer_token` to re-price a specific offer from a discovery endpoint. Exactly one mode per request.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlightSearchRequest"}}}},"responses":{"200":{"description":"Priced offers. Every filter that narrows the results (cabin, stops, price, carriers, times, connections, fare conditions, aircraft) is applied exactly as sent: no returned offer breaks one. Options that widen a search (alternate or nearby airports) are searched where available. A search that completes and matches nothing is also a 200: `offers` is empty, `result` is `no_matches` and the body carries a recovery `instruction`. A search that could not be completed answers an error (`UPSTREAM_REJECTED`, `UPSTREAM_ERROR`, `UPSTREAM_UNAVAILABLE` or `UPSTREAM_TIMEOUT`) rather than an empty list. Offers that were found are returned even when part of the search could not be completed.","headers":{"X-Jinko-Result":{"deprecated":true,"required":false,"description":"Deprecated: read `result` in the body. Sent for empty searches with result no_matches.","schema":{"type":"string","example":"no_matches"}},"X-Jinko-Instruction":{"deprecated":true,"required":false,"description":"Deprecated: read `instruction` in the body. Sent for empty searches with result no_matches.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlightSearchResponse"}}}},"400":{"description":"The request was malformed or failed validation (search and price check alike). When a supplier declined the request, `error.message` is “The request was declined for these inputs. Check the dates, places and travellers, then retry.” **`BAD_REQUEST`** — The request was malformed or failed validation. Fix the request as the message describes; retrying it unchanged cannot succeed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"The request was declined for these inputs. Check the dates, places and travellers, then retry."}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"AUTH_REQUIRED","message":"Invalid or expired API key.","doc_url":"https://docs.gojinko.com/authentication/api-keys"}}}}},"402":{"description":"Payment required — organization balance exhausted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"PAYMENT_REQUIRED","message":"Insufficient balance — this call costs $0.0150 and your organization has $0.0000 available. Top up at https://dashboard.gojinko.com/developers/billing/topup","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"NOT_FOUND","message":"Resource not found.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"409":{"description":"The operation conflicts with the current state of the resource","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"CONFLICT","message":"The operation conflicts with the current state of the resource.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"410":{"description":"The resource this request names no longer exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"GONE","message":"The resource no longer exists.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"A required field is missing or invalid.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"429":{"description":"Rate limit or quota exceeded","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"502":{"description":"The travel provider rejected the request, or an upstream call failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_REJECTED","message":"The request was declined for this offer. Try another offer.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"503":{"description":"No travel provider can serve the request right now","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_UNAVAILABLE","message":"The service is temporarily unavailable. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"504":{"description":"The travel provider did not answer in time","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_TIMEOUT","message":"The request timed out before a result was available. Retry the request.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}}}}},"/v1/price_monitoring":{"post":{"tags":["Discovery"],"summary":"Monitor price for a specific origin, destination, and dates pair","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PriceMonitoringRequest"}}}},"responses":{"200":{"description":"Cheapest cached itinerary, or stale on a cache miss","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PriceMonitoringResponse"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"Malformed JSON in request body.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"AUTH_REQUIRED","message":"Invalid or expired API key.","doc_url":"https://docs.gojinko.com/authentication/api-keys"}}}}},"402":{"description":"Payment required — organization balance exhausted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"PAYMENT_REQUIRED","message":"Insufficient balance — this call costs $0.0150 and your organization has $0.0000 available. Top up at https://dashboard.gojinko.com/developers/billing/topup","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"NOT_FOUND","message":"Resource not found.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"409":{"description":"The operation conflicts with the current state of the resource","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"CONFLICT","message":"The operation conflicts with the current state of the resource.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"410":{"description":"The resource this request names no longer exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"GONE","message":"The resource no longer exists.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"A required field is missing or invalid.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"429":{"description":"Rate limit or quota exceeded","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"502":{"description":"The travel provider rejected the request, or an upstream call failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_REJECTED","message":"The request was declined for this offer. Try another offer.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"503":{"description":"No travel provider can serve the request right now","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_UNAVAILABLE","message":"The service is temporarily unavailable. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"504":{"description":"The travel provider did not answer in time","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_TIMEOUT","message":"The request timed out before a result was available. Retry the request.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}}}}},"/v1/flight_schedule":{"post":{"tags":["Discovery"],"summary":"Flights observed between two places on exact dates (no price)","description":"Returns the trips the flight catalog has observed for one origin, one destination and an exact date pair. `trip_type` says which kind: `oneway` (no `return_date`) or `roundtrip` (`return_date` required); a round trip is an outbound + inbound pairing that was actually observed together, never a combination built from two one-ways. The answer carries no price, and `last_seen` is when a fare search last observed the trip — not a statement that it can be booked. Results are paged: send `pagination.next_page_token` back as `page_token` until it is empty.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlightScheduleRequest"}}}},"responses":{"200":{"description":"Observed trips; `trips` is empty when nothing matches","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlightScheduleResponse"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"Malformed JSON in request body.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"AUTH_REQUIRED","message":"Invalid or expired API key.","doc_url":"https://docs.gojinko.com/authentication/api-keys"}}}}},"402":{"description":"Payment required — organization balance exhausted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"PAYMENT_REQUIRED","message":"Insufficient balance — this call costs $0.0150 and your organization has $0.0000 available. Top up at https://dashboard.gojinko.com/developers/billing/topup","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"NOT_FOUND","message":"Resource not found.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"409":{"description":"The operation conflicts with the current state of the resource","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"CONFLICT","message":"The operation conflicts with the current state of the resource.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"410":{"description":"The resource this request names no longer exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"GONE","message":"The resource no longer exists.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"A required field is missing or invalid.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"429":{"description":"Rate limit or quota exceeded","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"502":{"description":"The travel provider rejected the request, or an upstream call failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_REJECTED","message":"The request was declined for this offer. Try another offer.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"503":{"description":"No travel provider can serve the request right now","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_UNAVAILABLE","message":"The service is temporarily unavailable. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"504":{"description":"The travel provider did not answer in time","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_TIMEOUT","message":"The request timed out before a result was available. Retry the request.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}}}}},"/v1/trip":{"post":{"tags":["Pricing & booking"],"summary":"Create and manage a trip: add or remove items and set travelers","description":"Creates or edits a trip without starting pricing. A new item has no price until it is successfully quoted. Call POST /v1/checkout to start pricing and wait for a checkout result. Polling GET /v1/trip/{trip_id} after creation does not start pricing.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TripRequest"}}}},"responses":{"200":{"description":"The trip","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TripResponse"}}}},"400":{"description":"The request was refused. Invalid traveller names at `upsert_travelers` are refused with `BAD_REQUEST` and `error.field` naming the traveller. **`BAD_REQUEST`** — The request was malformed or failed validation. Fix the request as the message describes; retrying it unchanged cannot succeed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"AUTH_REQUIRED","message":"Invalid or expired API key.","doc_url":"https://docs.gojinko.com/authentication/api-keys"}}}}},"402":{"description":"Payment required — organization balance exhausted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"PAYMENT_REQUIRED","message":"Insufficient balance — this call costs $0.0150 and your organization has $0.0000 available. Top up at https://dashboard.gojinko.com/developers/billing/topup","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"NOT_FOUND","message":"Resource not found.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"409":{"description":"The trip refused the write, and nothing was changed — `updated_at` is untouched. **`TRIP_EXPIRED`** — The trip lapsed (24 hours without activity) and no longer accepts writes. Start a new trip and add the items again. A lapsed trip cannot be revived. **`TRIP_STATE_CONFLICT`** — The trip is in a state that refuses this operation: payment already started (`fulfilling`, `fulfilled`, `partially_fulfilled`), or it is `cancelled` or `failed`. Read `status` and act on it: read the trip to find the booking that already exists, or start a new trip. Nothing was written. **`CONFLICT`** — The resource's current state refuses the operation. Re-read the resource and act on what it says — never retry blindly.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/TripStateResponse"},{"$ref":"#/components/schemas/ErrorResponse"}]},"example":{"error":{"code":"TRIP_EXPIRED","message":"This trip expired after 24 hours without activity and cannot be modified.","doc_url":"https://docs.gojinko.com/concepts/errors"},"status":"expired"}}}},"410":{"description":"The offer named in the request is no longer available, or another resource the request names is gone. Tell the two apart by `error.code`. **`OFFER_EXPIRED`** — The search offer is no longer usable: its token expired, or the supplier rejected it as expired or missing during pricing. No payable quote was established. Search again, replace the unavailable item using the latest search result, and check out promptly. Show that the flight offer is no longer available; do not describe this as an established quote expiring (QUOTE_EXPIRED). **`GONE`** — The resource this request names existed and no longer does. Re-acquire it — search again, or check out again to re-quote — then retry with the new identifier. Repeating this request with the same one cannot succeed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request failed validation. Tell the causes apart by `error.code`. **`BAD_REQUEST`** — The request was malformed or failed validation. Fix the request as the message describes; retrying it unchanged cannot succeed. **`INVALID_PHONE_NUMBER`** — A phone number is not in international format: it has no leading `+` and country code, is not a possible number, or carries an extension. Nothing was written. Resend the number with a leading `+` and country code, for example `+12025550147`. Spaces, hyphens, dots and parentheses are accepted and removed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"INVALID_PHONE_NUMBER","message":"phone number must be in international format with a leading + and country code, for example +12025550147","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"429":{"description":"Rate limit or quota exceeded","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"502":{"description":"The travel provider rejected the request, or an upstream call failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_REJECTED","message":"The request was declined for this offer. Try another offer.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"503":{"description":"No travel provider can serve the request right now","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_UNAVAILABLE","message":"The service is temporarily unavailable. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"504":{"description":"The travel provider did not answer in time","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_TIMEOUT","message":"The request timed out before a result was available. Retry the request.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}}}}},"/v1/trip/{trip_id}":{"get":{"tags":["Pricing & booking"],"summary":"Fetch the full lifecycle state of a trip — items, travelers, quote, fulfillment, and any booking references","description":"Reads the latest trip state without starting pricing. After checkout fails, quote.status can be failed, with failure_reason and failure_message when available. A failed quote has no payment expires_at. Never treat a missing item price or total_amount as zero.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"trip_id","in":"path"}],"responses":{"200":{"description":"The trip","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetTripResponse"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"Malformed JSON in request body.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"AUTH_REQUIRED","message":"Invalid or expired API key.","doc_url":"https://docs.gojinko.com/authentication/api-keys"}}}}},"402":{"description":"Payment required — organization balance exhausted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"PAYMENT_REQUIRED","message":"Insufficient balance — this call costs $0.0150 and your organization has $0.0000 available. Top up at https://dashboard.gojinko.com/developers/billing/topup","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"NOT_FOUND","message":"Resource not found.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"409":{"description":"The operation conflicts with the current state of the resource","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"CONFLICT","message":"The operation conflicts with the current state of the resource.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"410":{"description":"The resource this request names no longer exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"GONE","message":"The resource no longer exists.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"A required field is missing or invalid.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"429":{"description":"Rate limit or quota exceeded","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"502":{"description":"The travel provider rejected the request, or an upstream call failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_REJECTED","message":"The request was declined for this offer. Try another offer.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"503":{"description":"No travel provider can serve the request right now","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_UNAVAILABLE","message":"The service is temporarily unavailable. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"504":{"description":"The travel provider did not answer in time","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_TIMEOUT","message":"The request timed out before a result was available. Retry the request.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}}}}},"/v1/trip/{trip_id}/ancillaries":{"get":{"tags":["Pricing & booking"],"summary":"List purchasable ancillaries for a trip (no checkout)","description":"Returns the available ancillaries for a trip without generating a checkout URL. A price quote runs automatically to retrieve the ancillary catalog and is reused while valid.\n\nResponds **200** with `status: \"ready\"` and `items[]` when the quote is complete. If the quote is still being priced it responds **202** with `status: \"pricing\"` and a `Retry-After` header — poll the same URL until it returns 200. Never returns a checkout URL.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"trip_id","in":"path"}],"responses":{"200":{"description":"Ancillaries ready: `status: \"ready\"` with `items[]`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TripAncillariesResponse"}}}},"202":{"description":"The quote is still being priced: `status: \"pricing\"`. Poll the same URL after Retry-After.","headers":{"Retry-After":{"schema":{"type":"string"},"description":"Polling delay supplied by the service."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TripAncillariesResponse"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"Malformed JSON in request body.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"AUTH_REQUIRED","message":"Invalid or expired API key.","doc_url":"https://docs.gojinko.com/authentication/api-keys"}}}}},"402":{"description":"Payment required — organization balance exhausted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"PAYMENT_REQUIRED","message":"Insufficient balance — this call costs $0.0150 and your organization has $0.0000 available. Top up at https://dashboard.gojinko.com/developers/billing/topup","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"NOT_FOUND","message":"Resource not found.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"409":{"description":"The operation conflicts with the current state of the resource","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"CONFLICT","message":"The operation conflicts with the current state of the resource.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"410":{"description":"The resource this request names no longer exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"GONE","message":"The resource no longer exists.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"A required field is missing or invalid.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"429":{"description":"Rate limit or quota exceeded","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"502":{"description":"The travel provider rejected the request, or an upstream call failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_REJECTED","message":"The request was declined for this offer. Try another offer.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"503":{"description":"No travel provider can serve the request right now","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_UNAVAILABLE","message":"The service is temporarily unavailable. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"504":{"description":"The travel provider did not answer in time","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_TIMEOUT","message":"The request timed out before a result was available. Retry the request.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}}}}},"/v1/select_ancillaries":{"post":{"tags":["Pricing & booking"],"summary":"Select ancillaries (baggage, seats, meals) for a trip item","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AncillaryRequest"}}}},"responses":{"200":{"description":"Updated ancillary selection","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AncillaryResponse"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"Malformed JSON in request body.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"AUTH_REQUIRED","message":"Invalid or expired API key.","doc_url":"https://docs.gojinko.com/authentication/api-keys"}}}}},"402":{"description":"Payment required — organization balance exhausted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"PAYMENT_REQUIRED","message":"Insufficient balance — this call costs $0.0150 and your organization has $0.0000 available. Top up at https://dashboard.gojinko.com/developers/billing/topup","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"NOT_FOUND","message":"Resource not found.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"409":{"description":"The operation conflicts with the current state of the resource","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"CONFLICT","message":"The operation conflicts with the current state of the resource.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"410":{"description":"The resource this request names no longer exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"GONE","message":"The resource no longer exists.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"A required field is missing or invalid.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"429":{"description":"Rate limit or quota exceeded","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"502":{"description":"The travel provider rejected the request, or an upstream call failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_REJECTED","message":"The request was declined for this offer. Try another offer.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"503":{"description":"No travel provider can serve the request right now","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_UNAVAILABLE","message":"The service is temporarily unavailable. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"504":{"description":"The travel provider did not answer in time","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_TIMEOUT","message":"The request timed out before a result was available. Retry the request.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}}}}},"/v1/checkout":{"post":{"tags":["Pricing & booking"],"summary":"Checkout a trip — returns a checkout URL the user opens to pay, plus the Shared Payment Token params an agent can use to pay programmatically","description":"Starts pricing when no valid quote exists and waits server-side for up to about 55 seconds. If the quote is still being priced, answers 503 QUOTE_PENDING with Retry-After; calling checkout again picks up the same quote. A successful response contains the quoted prices and checkout details; no GET trip polling is required first. A supplier offer rejected as expired or missing during pricing returns 410 OFFER_EXPIRED, even when no quote has ever succeeded. Search again and replace the item. QUOTE_EXPIRED instead describes an established quote that can no longer be used: its deadline passed, or the trip's travelers changed after it was priced (`reason: travelers_changed`). Call checkout again to re-quote.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckoutRequest"}}}},"responses":{"200":{"description":"Checkout result: the priced trip and how to pay for it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckoutResponse"}}}},"400":{"description":"The request was refused. Invalid traveller names are refused with `BAD_REQUEST` and `error.field` naming the traveller. **`BAD_REQUEST`** — The request was malformed or failed validation. Fix the request as the message describes; retrying it unchanged cannot succeed. **`CURRENCY_UNSUPPORTED`** — The requested currency is not supported for this operation. Re-run the search in a supported currency. Agent payment supports `USD` or `EUR`, depending on the partner's Stripe account binding; a trip in a currency the partner does not accept carries no `agent_spt_params`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"AUTH_REQUIRED","message":"Invalid or expired API key.","doc_url":"https://docs.gojinko.com/authentication/api-keys"}}}}},"402":{"description":"Payment required — organization balance exhausted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"PAYMENT_REQUIRED","message":"Insufficient balance — this call costs $0.0150 and your organization has $0.0000 available. Top up at https://dashboard.gojinko.com/developers/billing/topup","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"NOT_FOUND","message":"Resource not found.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"409":{"description":"The checkout could not proceed against the current state of the trip. Tell the causes apart by `error.code`. **`CONFLICT`** — The resource's current state refuses the operation. Re-read the resource and act on what it says — never retry blindly. **`TRIP_EXPIRED`** — The trip lapsed (24 hours without activity) and no longer accepts writes. Start a new trip and add the items again. A lapsed trip cannot be revived. **`TRIP_STATE_CONFLICT`** — The trip is in a state that refuses this operation: payment already started (`fulfilling`, `fulfilled`, `partially_fulfilled`), or it is `cancelled` or `failed`. Read `status` and act on it: read the trip to find the booking that already exists, or start a new trip. Nothing was written. **`OFFER_UNAVAILABLE`** — The provider can no longer quote this offer. Emitted today when the hotel supplier has no rate for your occupancy at quote time, and at checkout when a flight fare cannot be priced for the trip's travellers: the fare is not sold for them, or the airline declines to price it. Search again for the occupancy or the travellers you intend to book and pick an offer that covers them; this one cannot be quoted at any price.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/TripStateResponse"},{"$ref":"#/components/schemas/ErrorResponse"}]},"example":{"error":{"code":"CONFLICT","message":"The checkout could not proceed against the current state of the trip.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"410":{"description":"The selected offer could not be priced, the established quote can no longer be used (`QUOTE_EXPIRED`, with `reason: travelers_changed` when the trip's travelers changed after it was priced), or another resource the request names is gone. Tell the causes apart by `error.code`. **`OFFER_EXPIRED`** — The search offer is no longer usable: its token expired, or the supplier rejected it as expired or missing during pricing. No payable quote was established. Search again, replace the unavailable item using the latest search result, and check out promptly. Show that the flight offer is no longer available; do not describe this as an established quote expiring (QUOTE_EXPIRED). **`GONE`** — The resource this request names existed and no longer does. Re-acquire it — search again, or check out again to re-quote — then retry with the new identifier. Repeating this request with the same one cannot succeed. **`QUOTE_EXPIRED`** — The quote can no longer be paid, so payment was refused before any payment object was created: either it passed its `expires_at`, or the trip's travelers changed after it was priced (`reason: travelers_changed`; `expires_at` is then the refusal instant). A typed payment refused this way also carries `payment_error` and `recovery`. Call `POST /v1/checkout` again on the same trip — it re-quotes — then mint a new Shared Payment Token against the new `agent_spt_params` and submit that against the new `quote_id`. Nothing was charged.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/QuoteExpiredResponse"},{"$ref":"#/components/schemas/ErrorResponse"}]}}}},"422":{"description":"The trip is not complete enough to check out. Tell the causes apart by `error.code`. **`BAD_REQUEST`** — The request was malformed or failed validation. Fix the request as the message describes; retrying it unchanged cannot succeed. **`MISSING_CUSTOMER_DETAILS`** — The trip is missing details the booking needs: no travelers, a contact without both an email and a phone, or passenger documents a carrier requires (see `required_attributes` on the trip item). Send `upsert_travelers` with the travelers, a contact, and the fields named in `missing_fields`, then check out again.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/MissingCustomerDetailsResponse"},{"$ref":"#/components/schemas/ErrorResponse"}]},"example":{"error":{"code":"MISSING_CUSTOMER_DETAILS","message":"the provider requires details this booking has not collected — flight: passport_number, passport_expiry, passport_country, nationality","doc_url":"https://docs.gojinko.com/concepts/errors"},"missing_fields":["nationality","passport_country","passport_expiry","passport_number"],"missing_by_domain":{"flight":["passport_number","passport_expiry","passport_country","nationality"]}}}}},"429":{"description":"Rate limit or quota exceeded","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"502":{"description":"The travel provider rejected the request, or an upstream call failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_REJECTED","message":"The request was declined for this offer. Try another offer.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"503":{"description":"The quote is still being priced, or an upstream service is unavailable. **`QUOTE_PENDING`** — The trip's quote is still being priced. Call `POST /v1/checkout` again after the interval in `Retry-After`; the call picks up the same quote and answers with the checkout once it is priced. **`UPSTREAM_UNAVAILABLE`** — No provider can serve the request right now, for a reason that is not the request. Retry unchanged after the interval in `Retry-After`.","headers":{"Retry-After":{"description":"Seconds to wait before calling checkout again to pick up the same quote.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"retry_after_seconds":{"type":"integer","minimum":0,"exclusiveMinimum":true},"quote_id":{"type":"string","description":"The quote being priced; checkout called again picks up the same one.","example":"2097152"},"quoted_cart_id":{"type":"number","description":"Deprecated: use `quote_id` instead. The same quote as an integer.","deprecated":true}}}]}}}},"504":{"description":"The upstream service did not answer before the deadline. **`UPSTREAM_TIMEOUT`** — A travel provider did not answer in time. Retry a read unchanged. Before repeating a request that changes something, read the resource's state because the operation may still have completed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/book":{"post":{"tags":["Pricing & booking"],"summary":"DEPRECATED — use /checkout. Alias retained for backward compatibility.","description":"Deprecated alias of `POST /v1/checkout`, kept so existing integrators keep working through the rename. Every response carries `Deprecation: true`, `Sunset: Fri, 30 Apr 2027 23:59:59 GMT` and `Link: </v1/checkout>; rel=\"successor-version\"`, so the removal date is readable from your own logs. Behaviour is identical to `/v1/checkout` until then; switch the path and nothing else changes. Pricing is synchronous: it waits for success or failure, and can return 410 OFFER_EXPIRED when a supplier offer cannot be priced, before any valid quote exists. See https://docs.gojinko.com/concepts/versioning.","deprecated":true,"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckoutRequest"}}}},"responses":{"200":{"description":"Checkout result: the priced trip and how to pay for it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckoutResponse"}}}},"400":{"description":"The request was refused. Invalid traveller names are refused with `BAD_REQUEST` and `error.field` naming the traveller. **`BAD_REQUEST`** — The request was malformed or failed validation. Fix the request as the message describes; retrying it unchanged cannot succeed. **`CURRENCY_UNSUPPORTED`** — The requested currency is not supported for this operation. Re-run the search in a supported currency. Agent payment supports `USD` or `EUR`, depending on the partner's Stripe account binding; a trip in a currency the partner does not accept carries no `agent_spt_params`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"AUTH_REQUIRED","message":"Invalid or expired API key.","doc_url":"https://docs.gojinko.com/authentication/api-keys"}}}}},"402":{"description":"Payment required — organization balance exhausted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"PAYMENT_REQUIRED","message":"Insufficient balance — this call costs $0.0150 and your organization has $0.0000 available. Top up at https://dashboard.gojinko.com/developers/billing/topup","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"NOT_FOUND","message":"Resource not found.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"409":{"description":"The checkout could not proceed against the current state of the trip. Tell the causes apart by `error.code`. **`CONFLICT`** — The resource's current state refuses the operation. Re-read the resource and act on what it says — never retry blindly. **`TRIP_EXPIRED`** — The trip lapsed (24 hours without activity) and no longer accepts writes. Start a new trip and add the items again. A lapsed trip cannot be revived. **`TRIP_STATE_CONFLICT`** — The trip is in a state that refuses this operation: payment already started (`fulfilling`, `fulfilled`, `partially_fulfilled`), or it is `cancelled` or `failed`. Read `status` and act on it: read the trip to find the booking that already exists, or start a new trip. Nothing was written. **`OFFER_UNAVAILABLE`** — The provider can no longer quote this offer. Emitted today when the hotel supplier has no rate for your occupancy at quote time, and at checkout when a flight fare cannot be priced for the trip's travellers: the fare is not sold for them, or the airline declines to price it. Search again for the occupancy or the travellers you intend to book and pick an offer that covers them; this one cannot be quoted at any price.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/TripStateResponse"},{"$ref":"#/components/schemas/ErrorResponse"}]},"example":{"error":{"code":"CONFLICT","message":"The checkout could not proceed against the current state of the trip.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"410":{"description":"The selected offer could not be priced, the established quote can no longer be used (`QUOTE_EXPIRED`, with `reason: travelers_changed` when the trip's travelers changed after it was priced), or another resource the request names is gone. Tell the causes apart by `error.code`. **`OFFER_EXPIRED`** — The search offer is no longer usable: its token expired, or the supplier rejected it as expired or missing during pricing. No payable quote was established. Search again, replace the unavailable item using the latest search result, and check out promptly. Show that the flight offer is no longer available; do not describe this as an established quote expiring (QUOTE_EXPIRED). **`GONE`** — The resource this request names existed and no longer does. Re-acquire it — search again, or check out again to re-quote — then retry with the new identifier. Repeating this request with the same one cannot succeed. **`QUOTE_EXPIRED`** — The quote can no longer be paid, so payment was refused before any payment object was created: either it passed its `expires_at`, or the trip's travelers changed after it was priced (`reason: travelers_changed`; `expires_at` is then the refusal instant). A typed payment refused this way also carries `payment_error` and `recovery`. Call `POST /v1/checkout` again on the same trip — it re-quotes — then mint a new Shared Payment Token against the new `agent_spt_params` and submit that against the new `quote_id`. Nothing was charged.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/QuoteExpiredResponse"},{"$ref":"#/components/schemas/ErrorResponse"}]}}}},"422":{"description":"The trip is not complete enough to check out. Tell the causes apart by `error.code`. **`BAD_REQUEST`** — The request was malformed or failed validation. Fix the request as the message describes; retrying it unchanged cannot succeed. **`MISSING_CUSTOMER_DETAILS`** — The trip is missing details the booking needs: no travelers, a contact without both an email and a phone, or passenger documents a carrier requires (see `required_attributes` on the trip item). Send `upsert_travelers` with the travelers, a contact, and the fields named in `missing_fields`, then check out again.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/MissingCustomerDetailsResponse"},{"$ref":"#/components/schemas/ErrorResponse"}]},"example":{"error":{"code":"MISSING_CUSTOMER_DETAILS","message":"the provider requires details this booking has not collected — flight: passport_number, passport_expiry, passport_country, nationality","doc_url":"https://docs.gojinko.com/concepts/errors"},"missing_fields":["nationality","passport_country","passport_expiry","passport_number"],"missing_by_domain":{"flight":["passport_number","passport_expiry","passport_country","nationality"]}}}}},"429":{"description":"Rate limit or quota exceeded","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"502":{"description":"The travel provider rejected the request, or an upstream call failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_REJECTED","message":"The request was declined for this offer. Try another offer.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"503":{"description":"The quote is still being priced, or an upstream service is unavailable. **`QUOTE_PENDING`** — The trip's quote is still being priced. Call `POST /v1/checkout` again after the interval in `Retry-After`; the call picks up the same quote and answers with the checkout once it is priced. **`UPSTREAM_UNAVAILABLE`** — No provider can serve the request right now, for a reason that is not the request. Retry unchanged after the interval in `Retry-After`.","headers":{"Retry-After":{"description":"Seconds to wait before calling checkout again to pick up the same quote.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"retry_after_seconds":{"type":"integer","minimum":0,"exclusiveMinimum":true},"quote_id":{"type":"string","description":"The quote being priced; checkout called again picks up the same one.","example":"2097152"},"quoted_cart_id":{"type":"number","description":"Deprecated: use `quote_id` instead. The same quote as an integer.","deprecated":true}}}]}}}},"504":{"description":"The upstream service did not answer before the deadline. **`UPSTREAM_TIMEOUT`** — A travel provider did not answer in time. Retry a read unchanged. Before repeating a request that changes something, read the resource's state because the operation may still have completed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/agent_payment/submit":{"post":{"tags":["Pricing & booking"],"summary":"Submit a typed SPT payment with Idempotency-Key, or a legacy flat Shared Payment Token","description":"Typed submissions require a jnk_ API key via X-API-Key (ApiKeyAuth) or Authorization: Bearer (BearerAuth), a tenant-bound booking scope, quote_id and Idempotency-Key, and return a PaymentAttemptOutcome (200 known outcome / 202 pending). OAuth/JWT bearer tokens are supported only for the legacy flat token body, which keeps its existing authorization response. Request bodies are limited to 16 KiB.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":255,"pattern":"^[ -~]+$(?![\\s\\S])","description":"Required on a typed submission (a body with `payment`); without it the request is refused with 400 `CREDENTIAL_FORMAT_INVALID`. Ignored on the legacy body. 1–255 printable ASCII characters. Reuse only for the same payment submission: the same key with the same body replays the first outcome, with a different body it is refused."},"required":false,"name":"Idempotency-Key","in":"header"}],"requestBody":{"content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/TypedAgentPaymentSubmitRequest"},{"$ref":"#/components/schemas/AgentPaymentSubmitRequest"}]}}}},"responses":{"200":{"description":"Legacy authorization result or typed payment outcome","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/PaymentAttemptOutcome"},{"$ref":"#/components/schemas/AgentPaymentSubmitResponse"}]}}}},"202":{"description":"Payment accepted and pending; poll the attempt after Retry-After.","headers":{"Retry-After":{"schema":{"type":"string"},"description":"Polling delay supplied by the service."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentAttemptOutcome"}}}},"400":{"description":"Payment credential format or validity refusal. **`CREDENTIAL_FORMAT_INVALID`** — The typed payment body or Idempotency-Key has an invalid format. Correct the named field and resubmit with a valid Idempotency-Key. **`PAYMENT_CREDENTIAL_INVALID`** — The payment credential is invalid. Follow recovery.action and supply a replacement credential when required. **`BAD_REQUEST`** — The request was malformed or failed validation. Fix the request as the message describes; retrying it unchanged cannot succeed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentRefusalResponse"}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"AUTH_REQUIRED","message":"Invalid or expired API key.","doc_url":"https://docs.gojinko.com/authentication/api-keys"}}}}},"402":{"description":"Payment required — organization balance exhausted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"PAYMENT_REQUIRED","message":"Insufficient balance — this call costs $0.0150 and your organization has $0.0000 available. Top up at https://dashboard.gojinko.com/developers/billing/topup","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"403":{"description":"This payment type is not enabled or access is forbidden. **`PAYMENT_TYPE_NOT_ENABLED`** — This payment type is not enabled for the caller. Choose a type advertised in accepted_payment_types or contact support. **`FORBIDDEN`** — The credential is valid, and this request is refused anyway. Do not rotate the key — it is working. Check that it is entitled to this operation, and on this environment.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentRefusalResponse"}}}},"404":{"description":"The trip, or the `quote_id` among its quotes, was not found. Nothing was charged. **`NOT_FOUND`** — No resource with that identifier is visible to this key. Check the identifier. A trip or booking belongs to the key that created it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Payment submission conflicts with an existing attempt. **`TRIP_OWNED_BY_OTHER_PAYMENT`** — Another payment attempt owns this trip. Read the existing attempt and follow its recovery action before paying again. **`IDEMPOTENCY_KEY_REUSED`** — The idempotency key was already used for a different submission. Reuse the original request with this key, or use a new key for a new submission. **`ATTEMPT_IN_PROGRESS`** — A payment attempt is already in progress. Poll the existing attempt and follow recovery.action. **`ATTEMPT_TERMINAL`** — This payment attempt has reached a terminal state. Read its outcome and follow recovery.action before starting another attempt.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentRefusalResponse"}}}},"410":{"description":"The locked quote has expired, so nothing was redeemed and no payment object was created — or the resource the request names is gone. Tell the two apart by `error.code`. **`QUOTE_EXPIRED`** — The quote can no longer be paid, so payment was refused before any payment object was created: either it passed its `expires_at`, or the trip's travelers changed after it was priced (`reason: travelers_changed`; `expires_at` is then the refusal instant). A typed payment refused this way also carries `payment_error` and `recovery`. Call `POST /v1/checkout` again on the same trip — it re-quotes — then mint a new Shared Payment Token against the new `agent_spt_params` and submit that against the new `quote_id`. Nothing was charged. **`GONE`** — The resource this request names existed and no longer does. Re-acquire it — search again, or check out again to re-quote — then retry with the new identifier. Repeating this request with the same one cannot succeed.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/QuoteExpiredResponse"},{"$ref":"#/components/schemas/ErrorResponse"},{"$ref":"#/components/schemas/PaymentRefusalResponse"}]},"example":{"error":{"code":"QUOTE_EXPIRED","message":"The quote behind this checkout expired at 2026-09-04T12:39:56Z.","doc_url":"https://docs.gojinko.com/concepts/errors"},"expires_at":"2026-09-04T12:39:56Z","quote_id":"2097152","reason":"travelers_changed"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"A required field is missing or invalid.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"429":{"description":"Rate limit or quota exceeded","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"502":{"description":"The travel provider rejected the request, or an upstream call failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_REJECTED","message":"The request was declined for this offer. Try another offer.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"503":{"description":"Payment temporarily unavailable. **`TEMPORARILY_UNAVAILABLE`** — Payment submission is temporarily unavailable. Wait for Retry-After when provided and follow recovery.action.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentRefusalResponse"}}}},"504":{"description":"The travel provider did not answer in time","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_TIMEOUT","message":"The request timed out before a result was available. Retry the request.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}}}}},"/v1/trip/{trip_id}/payment_attempts/{payment_attempt_id}":{"get":{"tags":["Pricing & booking"],"summary":"Read a payment attempt outcome","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"trip_id","in":"path"},{"schema":{"type":"string"},"required":true,"name":"payment_attempt_id","in":"path"}],"responses":{"200":{"description":"Payment attempt outcome","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentAttemptOutcome"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"Malformed JSON in request body.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"AUTH_REQUIRED","message":"Invalid or expired API key.","doc_url":"https://docs.gojinko.com/authentication/api-keys"}}}}},"402":{"description":"Payment required — organization balance exhausted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"PAYMENT_REQUIRED","message":"Insufficient balance — this call costs $0.0150 and your organization has $0.0000 available. Top up at https://dashboard.gojinko.com/developers/billing/topup","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"403":{"description":"Payment attempt access forbidden. **`FORBIDDEN`** — The credential is valid, and this request is refused anyway. Do not rotate the key — it is working. Check that it is entitled to this operation, and on this environment.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"NOT_FOUND","message":"Resource not found.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"409":{"description":"The operation conflicts with the current state of the resource","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"CONFLICT","message":"The operation conflicts with the current state of the resource.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"410":{"description":"The resource this request names no longer exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"GONE","message":"The resource no longer exists.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"A required field is missing or invalid.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"429":{"description":"Rate limit or quota exceeded","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"502":{"description":"The travel provider rejected the request, or an upstream call failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_REJECTED","message":"The request was declined for this offer. Try another offer.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"503":{"description":"No travel provider can serve the request right now","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_UNAVAILABLE","message":"The service is temporarily unavailable. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"504":{"description":"The travel provider did not answer in time","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_TIMEOUT","message":"The request timed out before a result was available. Retry the request.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}}}}},"/v1/hotel_search":{"post":{"tags":["Hotels"],"summary":"Search live hotel inventory and rates","description":"The search is described by the JSON body alone. The two query parameters earlier contract versions accepted to switch hotel inventory sources on or off are deprecated: they are accepted and ignored until 2027-04-30, and the platform chooses which inventory to search. A request that sends one is answered with the `Deprecation`, `Sunset` and `Link` headers.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HotelSearchRequest"}}}},"responses":{"200":{"description":"Matching hotels","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HotelSearchResponse"}}}},"400":{"description":"The request was malformed or failed validation. When a supplier declined the request, `error.message` is “The request was declined for these inputs. Check the dates, places and travellers, then retry.” **`BAD_REQUEST`** — The request was malformed or failed validation. Fix the request as the message describes; retrying it unchanged cannot succeed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"The request was declined for these inputs. Check the dates, places and travellers, then retry."}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"AUTH_REQUIRED","message":"Invalid or expired API key.","doc_url":"https://docs.gojinko.com/authentication/api-keys"}}}}},"402":{"description":"Payment required — organization balance exhausted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"PAYMENT_REQUIRED","message":"Insufficient balance — this call costs $0.0150 and your organization has $0.0000 available. Top up at https://dashboard.gojinko.com/developers/billing/topup","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"NOT_FOUND","message":"Resource not found.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"409":{"description":"The operation conflicts with the current state of the resource","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"CONFLICT","message":"The operation conflicts with the current state of the resource.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"410":{"description":"The resource this request names no longer exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"GONE","message":"The resource no longer exists.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"422":{"description":"The request failed validation, or named a destination the platform could not confidently resolve. Tell the causes apart by `error.code`. **`BAD_REQUEST`** — The request was malformed or failed validation. Fix the request as the message describes; retrying it unchanged cannot succeed. **`HOTEL_NAME_LOW_CONFIDENCE`** — A `hotel_name` lookup found no exact match, only close candidates none of which clearly wins. Read `error.top_candidates` (closest first). If the top one is an obvious match, retry with its `hotel_id`; if several fit, ask the user which one they meant — never say there is no such hotel. **`DESTINATION_LOW_CONFIDENCE`** — A `city_name` + `country_code` search found no exact catalog city, only close candidates none of which clearly wins. Read `error.top_candidates` (closest first). If the top one is an obvious typo fix, confirm and retry with `error.suggested_retry.destination`; if several fit, ask the user which city they meant — never say there are no hotels in the requested city.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/HotelNameLowConfidenceResponse"},{"$ref":"#/components/schemas/DestinationLowConfidenceResponse"},{"$ref":"#/components/schemas/ErrorResponse"}]},"example":{"error":{"code":"DESTINATION_LOW_CONFIDENCE","message":"no confident city match for \"Sain Malo\" in FR (closest: \"Saint Malo\", FR at 0.75)","top_candidates":[{"city":"Saint Malo","country_code":"FR","hotel_count":326,"score":0.75}],"suggested_retry":{"destination":{"city_name":"Saint Malo","country_code":"FR"},"rationale":"the closest catalog city to the requested name; retry with it, or ask the user when the candidates differ"}}}}}},"429":{"description":"Rate limit or quota exceeded","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"502":{"description":"The travel provider rejected the request, or an upstream call failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_REJECTED","message":"The request was declined for this offer. Try another offer.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"503":{"description":"No travel provider can serve the request right now","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_UNAVAILABLE","message":"The service is temporarily unavailable. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"504":{"description":"The travel provider did not answer in time","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_TIMEOUT","message":"The request timed out before a result was available. Retry the request.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}}}}},"/v1/hotel_details/{hotel_id}":{"get":{"tags":["Hotels"],"summary":"Fetch rich metadata (gallery, facilities, policies, per-room details) for a single hotel","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"The `hotel_id` of a hotel from hotel_search, passed exactly as received and URL-encoded as one path segment. It is opaque: do not parse or build it.","example":"lp1d2c3"},"required":true,"name":"hotel_id","in":"path"},{"schema":{"type":"string"},"required":false,"name":"checkin","in":"query"},{"schema":{"type":"string"},"required":false,"name":"checkout","in":"query"}],"responses":{"200":{"description":"Hotel detail","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HotelDetailsResponse"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"Malformed JSON in request body.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"AUTH_REQUIRED","message":"Invalid or expired API key.","doc_url":"https://docs.gojinko.com/authentication/api-keys"}}}}},"402":{"description":"Payment required — organization balance exhausted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"PAYMENT_REQUIRED","message":"Insufficient balance — this call costs $0.0150 and your organization has $0.0000 available. Top up at https://dashboard.gojinko.com/developers/billing/topup","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"NOT_FOUND","message":"Resource not found.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"409":{"description":"The operation conflicts with the current state of the resource","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"CONFLICT","message":"The operation conflicts with the current state of the resource.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"410":{"description":"The resource this request names no longer exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"GONE","message":"The resource no longer exists.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"A required field is missing or invalid.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"429":{"description":"Rate limit or quota exceeded","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"502":{"description":"The travel provider rejected the request, or an upstream call failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_REJECTED","message":"The request was declined for this offer. Try another offer.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"503":{"description":"No travel provider can serve the request right now","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_UNAVAILABLE","message":"The service is temporarily unavailable. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"504":{"description":"The travel provider did not answer in time","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_TIMEOUT","message":"The request timed out before a result was available. Retry the request.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}}}}},"/v1/hotel_reviews/{hotel_id}":{"get":{"tags":["Hotels"],"summary":"Guest review summary and recent reviews for a single hotel","description":"Use after hotel_search or hotel_details, when the user wants to know what guests say about a hotel or how it scores on cleanliness, location or staff. Returns the overall guest score and review count, a summary of what guests praise and criticise, a score per category, and a page of recent reviews, newest first: headline, score, pros/cons, date and language for each. The review source is not attributed.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"The `hotel_id` of a hotel from hotel_search, or the `id` hotel_details returns, passed exactly as received and URL-encoded as one path segment. It is opaque: do not parse or build it.","example":"lp1d2c3"},"required":true,"name":"hotel_id","in":"path"},{"schema":{"type":"integer","minimum":1,"maximum":50,"description":"Reviews per page, 1 to 50. Default 10.","example":10},"required":false,"name":"limit","in":"query"},{"schema":{"type":"integer","nullable":true,"minimum":0,"maximum":2147483647,"description":"Reviews to skip before this page. Default 0.","example":0},"required":false,"name":"offset","in":"query"}],"responses":{"200":{"description":"Hotel reviews","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HotelReviewsResponse"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"Malformed JSON in request body.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"AUTH_REQUIRED","message":"Invalid or expired API key.","doc_url":"https://docs.gojinko.com/authentication/api-keys"}}}}},"402":{"description":"Payment required — organization balance exhausted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"PAYMENT_REQUIRED","message":"Insufficient balance — this call costs $0.0150 and your organization has $0.0000 available. Top up at https://dashboard.gojinko.com/developers/billing/topup","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"NOT_FOUND","message":"Resource not found.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"409":{"description":"The operation conflicts with the current state of the resource","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"CONFLICT","message":"The operation conflicts with the current state of the resource.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"410":{"description":"The resource this request names no longer exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"GONE","message":"The resource no longer exists.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"A required field is missing or invalid.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"429":{"description":"Rate limit or quota exceeded","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"502":{"description":"The travel provider rejected the request, or an upstream call failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_REJECTED","message":"The request was declined for this offer. Try another offer.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"503":{"description":"No travel provider can serve the request right now","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_UNAVAILABLE","message":"The service is temporarily unavailable. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"504":{"description":"The travel provider did not answer in time","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_TIMEOUT","message":"The request timed out before a result was available. Retry the request.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}}}}},"/v1/best_price_guarantee":{"post":{"tags":["Hotels"],"summary":"Best Price Guarantee: ask for a hotel offer at a lower price found elsewhere","description":"Submit a hotel offer from `POST /v1/hotel_search` with the lower price the traveller found for the same stay on another site: the link, a screenshot address and the total payable at booking, in the currency of the offer. The submission is reviewed in the background and this call answers `pending` with an `id`, or the result when `wait_seconds` let it finish in time. The answer carries the state of the request in `status`: `pending` while the review runs, then `offered` with the new offer, or `declined` with the reason. A decline is a 200: it is an answer, not a failure of the call. An offered result is a NEW offer id to add to a trip; the submitted offer is left as it was. Jinko enables Best Price Guarantee per account: a key it is not enabled for is answered HTTP 200 `status: declined` with `decline.reason: feature_disabled`.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BestPriceGuaranteeRequest"}}}},"responses":{"200":{"description":"The state of the request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BestPriceGuaranteeResponse"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"Malformed JSON in request body.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"AUTH_REQUIRED","message":"Invalid or expired API key.","doc_url":"https://docs.gojinko.com/authentication/api-keys"}}}}},"402":{"description":"Payment required — organization balance exhausted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"PAYMENT_REQUIRED","message":"Insufficient balance — this call costs $0.0150 and your organization has $0.0000 available. Top up at https://dashboard.gojinko.com/developers/billing/topup","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"NOT_FOUND","message":"Resource not found.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"409":{"description":"The operation conflicts with the current state of the resource","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"CONFLICT","message":"The operation conflicts with the current state of the resource.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"410":{"description":"The resource this request names no longer exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"GONE","message":"The resource no longer exists.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"A required field is missing or invalid.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"429":{"description":"Rate limit or quota exceeded","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"502":{"description":"The travel provider rejected the request, or an upstream call failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_REJECTED","message":"The request was declined for this offer. Try another offer.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"503":{"description":"No travel provider can serve the request right now","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_UNAVAILABLE","message":"The service is temporarily unavailable. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"504":{"description":"The travel provider did not answer in time","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_TIMEOUT","message":"The request timed out before a result was available. Retry the request.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}}}}},"/v1/best_price_guarantee/{id}":{"get":{"tags":["Hotels"],"summary":"Best Price Guarantee: read the result of a request","description":"Read a request submitted with `POST /v1/best_price_guarantee`. The answer carries the state of the request in `status`: `pending` while the review runs, then `offered` with the new offer, or `declined` with the reason. A decline is a 200: it is an answer, not a failure of the call. A request of another key's tenant is answered 404, the same as one that does not exist.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"ngo_mfrggzdfmztwq2lknnwg23tpob"},"required":true,"name":"id","in":"path"},{"schema":{"type":"integer","nullable":true,"minimum":0,"maximum":20,"description":"How long this call waits for a request that is still pending, 0–20 seconds (default 0).","example":15},"required":false,"name":"wait_seconds","in":"query"}],"responses":{"200":{"description":"The state of the request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BestPriceGuaranteeResponse"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"Malformed JSON in request body.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"AUTH_REQUIRED","message":"Invalid or expired API key.","doc_url":"https://docs.gojinko.com/authentication/api-keys"}}}}},"402":{"description":"Payment required — organization balance exhausted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"PAYMENT_REQUIRED","message":"Insufficient balance — this call costs $0.0150 and your organization has $0.0000 available. Top up at https://dashboard.gojinko.com/developers/billing/topup","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"NOT_FOUND","message":"Resource not found.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"409":{"description":"The operation conflicts with the current state of the resource","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"CONFLICT","message":"The operation conflicts with the current state of the resource.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"410":{"description":"The resource this request names no longer exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"GONE","message":"The resource no longer exists.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"A required field is missing or invalid.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"429":{"description":"Rate limit or quota exceeded","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"502":{"description":"The travel provider rejected the request, or an upstream call failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_REJECTED","message":"The request was declined for this offer. Try another offer.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"503":{"description":"No travel provider can serve the request right now","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_UNAVAILABLE","message":"The service is temporarily unavailable. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"504":{"description":"The travel provider did not answer in time","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_TIMEOUT","message":"The request timed out before a result was available. Retry the request.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}}}}},"/v1/car_search":{"post":{"tags":["Cars"],"summary":"Search live car rental offers","description":"Search live car rental availability. `pick_up` names exactly one of an `airport_code` (IATA), a free-text `place` (resolved server-side — ambiguous text returns `candidates` to retry with), or `geo` coordinates (round-trip only). Date-times are branch-local `YYYY-MM-DDTHH:MM:SS` with no timezone. Omit `drop_off` to return the car to the pick-up branch, setting `drop_off_date_time`. Pass each offer's `trip_item_token` verbatim to `POST /v1/trip` as `add_item.trip_item_token` to add the rental to a trip, then check out as normal. `price.pay_now` is the only amount Jinko charges — `due_at_desk` is collected by the rental desk locally.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CarSearchRequest"}}}},"responses":{"200":{"description":"Bookable car rental offers","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CarSearchResponse"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"Malformed JSON in request body.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"AUTH_REQUIRED","message":"Invalid or expired API key.","doc_url":"https://docs.gojinko.com/authentication/api-keys"}}}}},"402":{"description":"Payment required — organization balance exhausted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"PAYMENT_REQUIRED","message":"Insufficient balance — this call costs $0.0150 and your organization has $0.0000 available. Top up at https://dashboard.gojinko.com/developers/billing/topup","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"NOT_FOUND","message":"Resource not found.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"409":{"description":"The operation conflicts with the current state of the resource","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"CONFLICT","message":"The operation conflicts with the current state of the resource.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"410":{"description":"The resource this request names no longer exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"GONE","message":"The resource no longer exists.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"422":{"description":"The request failed validation, or the rental supplier declined the search. When a supplier declined the request, `error.message` is “The rental request was declined. Try another vehicle, location or time.” **`BAD_REQUEST`** — The request was malformed or failed validation. Fix the request as the message describes; retrying it unchanged cannot succeed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"The rental request was declined. Try another vehicle, location or time."}}}}},"429":{"description":"Rate limit or quota exceeded","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"502":{"description":"The travel provider rejected the request, or an upstream call failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_REJECTED","message":"The request was declined for this offer. Try another offer.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"503":{"description":"No travel provider can serve the request right now","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_UNAVAILABLE","message":"The service is temporarily unavailable. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"504":{"description":"The travel provider did not answer in time","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_TIMEOUT","message":"The request timed out before a result was available. Retry the request.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}}}}},"/v1/car_cancel_preview":{"post":{"tags":["Cars"],"summary":"Preview the cost of cancelling a car rental booking","description":"Quote what cancelling would cost right now: the fee in force and the refund it would leave. Nothing is cancelled. `fee_known: false` means the fee could not be established — render it as \"we will confirm the fee\", never as free cancellation. The returned `quote_id` is required by car_cancel_commit, and binds until `expires_at` where one is given. `cancellable: false` with `manual_required: true` means the cancellation cannot be completed online (fee unknown, rental changed after payment, fee in another currency or above the rental) and `refund_review_reason` says why: it is not a dead end — once the customer agrees, commit it with `manual_ok: true` and a Jinko agent completes it. `cancellable: false` without that flag is a rental that cannot be cancelled at all (already cancelled, or over), carrying the cancellation that stands where there is one. Guest-authenticated: booking_ref + last_name; a wrong pair returns 404 rather than confirming the booking's existence.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CarCancelPreviewRequest"}}}},"responses":{"200":{"description":"Cancellation quote","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CarCancelPreviewResponse"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"Malformed JSON in request body.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"AUTH_REQUIRED","message":"Invalid or expired API key.","doc_url":"https://docs.gojinko.com/authentication/api-keys"}}}}},"402":{"description":"Payment required — organization balance exhausted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"PAYMENT_REQUIRED","message":"Insufficient balance — this call costs $0.0150 and your organization has $0.0000 available. Top up at https://dashboard.gojinko.com/developers/billing/topup","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"404":{"description":"Unknown booking_ref + last_name pair.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"NOT_FOUND","message":"booking not found"}}}}},"409":{"description":"An exchange or another cancellation is already in flight for this rental, so no quote can be given: `code` is `active_operation_exists`, as a sibling of `error`, and `active_operation` names the cancellation running where it is one. Wait for it to finish (car_cancel_status) and preview again. A 409 with no `code` beside `error` is not this case — the reference matched more than one booking.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/CarServicingConflictResponse"},{"$ref":"#/components/schemas/BookingSelectionConflict"}]},"example":{"error":{"code":"CONFLICT","message":"an exchange is in progress for this rental; wait for it to finish before cancelling"},"code":"active_operation_exists"}}}},"410":{"description":"The resource this request names no longer exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"GONE","message":"The resource no longer exists.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"A required field is missing or invalid.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"429":{"description":"Rate limit or quota exceeded","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"502":{"description":"The travel provider rejected the request, or an upstream call failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_REJECTED","message":"The request was declined for this offer. Try another offer.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"503":{"description":"No travel provider can serve the request right now","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_UNAVAILABLE","message":"The service is temporarily unavailable. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"504":{"description":"The travel provider did not answer in time","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_TIMEOUT","message":"The request timed out before a result was available. Retry the request.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}}}}},"/v1/car_cancel_commit":{"post":{"tags":["Cars"],"summary":"Cancel a car rental booking","description":"Cancel the booking at the rental provider and refund the customer's payment minus the cancellation fee. Requires the `cancellation_id` from a preceding preview, binding the commit to the fee the customer saw. Safe to call twice — a committed cancellation is reported as it stands rather than repeated. `state: confirmed` is done; `state: pending` is recorded but not finished — the platform is still completing it, or a Jinko agent has it — so read car_cancel_status rather than committing again. `refund_pending_review: true` means the refund needs a person before it is issued. A preview that answered `manual_required: true` is committed with `manual_ok: true`, once the customer agrees: the cancellation is then recorded for a Jinko agent (`state: pending`, `refund_pending_review: true`) and nothing is sent to the rental company by this call. Without the flag that commit is refused 409 `manual_required`. The other 409 refusals are `QUOTE_EXPIRED` (preview again), `quote_drift` (the refund moved; commit against the `retry_quote_id` it names after showing the customer `current.refund`), `active_operation_exists`, `not_cancellable` and `funds_unavailable` — see `CarServicingConflictCode`. In every one of them nothing was cancelled.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CarCancelCommitRequest"}}}},"responses":{"200":{"description":"Cancellation result","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CarCancelCommitResponse"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"Malformed JSON in request body.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"AUTH_REQUIRED","message":"Invalid or expired API key.","doc_url":"https://docs.gojinko.com/authentication/api-keys"}}}}},"402":{"description":"Payment required — organization balance exhausted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"PAYMENT_REQUIRED","message":"Insufficient balance — this call costs $0.0150 and your organization has $0.0000 available. Top up at https://dashboard.gojinko.com/developers/billing/topup","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"404":{"description":"Unknown booking_ref + last_name pair.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"NOT_FOUND","message":"booking not found"}}}}},"409":{"description":"The commit cannot run as quoted; nothing was cancelled and no money moved. `code` says which case, as a sibling of `error` — see `CarServicingConflictCode` for the closed list and what each one asks of you. A 409 whose `code` is outside that list is NOT a servicing refusal: it arrives as the plain error envelope, with no `code` beside it.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/CarServicingConflictResponse"},{"$ref":"#/components/schemas/BookingSelectionConflict"}]},"example":{"error":{"code":"CONFLICT","message":"this quote cannot be committed"},"code":"manual_required"}}}},"410":{"description":"The resource this request names no longer exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"GONE","message":"The resource no longer exists.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"A required field is missing or invalid.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"429":{"description":"Rate limit or quota exceeded","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"502":{"description":"The travel provider rejected the request, or an upstream call failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_REJECTED","message":"The request was declined for this offer. Try another offer.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"503":{"description":"No travel provider can serve the request right now","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_UNAVAILABLE","message":"The service is temporarily unavailable. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"504":{"description":"The travel provider did not answer in time","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_TIMEOUT","message":"The request timed out before a result was available. Retry the request.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}}}}},"/v1/car_cancel_status":{"post":{"tags":["Cars"],"summary":"Check the status of a car rental cancellation","description":"Report the latest cancellation attempt for the booking, whatever state it reached — including `refund_pending_review`, where the booking is cancelled but the refund awaits a human. Read-only. A 404 means either an unknown booking_ref + last_name pair or that no cancellation attempt exists yet for this booking — run car_cancel_preview first.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CarCancelStatusRequest"}}}},"responses":{"200":{"description":"Latest cancellation state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CarCancelStatusResponse"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"Malformed JSON in request body.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"AUTH_REQUIRED","message":"Invalid or expired API key.","doc_url":"https://docs.gojinko.com/authentication/api-keys"}}}}},"402":{"description":"Payment required — organization balance exhausted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"PAYMENT_REQUIRED","message":"Insufficient balance — this call costs $0.0150 and your organization has $0.0000 available. Top up at https://dashboard.gojinko.com/developers/billing/topup","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"404":{"description":"Unknown booking_ref + last_name pair, or no cancellation attempt exists yet for this booking.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"NOT_FOUND","message":"booking not found"}}}}},"409":{"description":"Several booked car items match. Select a booked_item_id from items and repeat with booking_ref and booked_item_id.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"$ref":"#/components/schemas/BookingSelectionConflict"}]}}}},"410":{"description":"The resource this request names no longer exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"GONE","message":"The resource no longer exists.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"A required field is missing or invalid.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"429":{"description":"Rate limit or quota exceeded","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"502":{"description":"The travel provider rejected the request, or an upstream call failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_REJECTED","message":"The request was declined for this offer. Try another offer.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"503":{"description":"No travel provider can serve the request right now","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_UNAVAILABLE","message":"The service is temporarily unavailable. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"504":{"description":"The travel provider did not answer in time","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_TIMEOUT","message":"The request timed out before a result was available. Retry the request.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}}}}},"/v1/destination":{"get":{"tags":["Demand"],"summary":"Rank the top destinations by travel demand","description":"Ranks destinations by current demand share (a point-in-time snapshot). Optional `destination_country` (a `Country:<code>` selector, allowed only with `level=city`) narrows the ranking to that country’s destination cities.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Origin selector `<Level>:<Code>` (e.g. `Country:US`), or `global` for all origins. Required for the destination endpoints.","example":"Country:US"},"required":false,"name":"origin","in":"query"},{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Start of the departure window (YYYY-MM-DD). Supply together with departure_date_to; omit both for no departure-date filter (all departure dates).","example":"2026-07-01"},"required":false,"name":"departure_date_from","in":"query"},{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"End of the departure window (YYYY-MM-DD). Supply together with departure_date_from; omit both for no departure-date filter (all departure dates).","example":"2026-07-31"},"required":false,"name":"departure_date_to","in":"query"},{"schema":{"type":"string","enum":["Solo","Couple","Group","Family"],"example":"Couple"},"required":false,"name":"traveler_type","in":"query"},{"schema":{"type":"string","enum":["oneway","roundtrip"],"example":"roundtrip"},"required":false,"name":"trip_type","in":"query"},{"schema":{"type":"integer","nullable":true,"minimum":0,"description":"Minimum trip length in nights (return_date − dep_date). Round-trips only.","example":5},"required":false,"name":"trip_duration_min","in":"query"},{"schema":{"type":"integer","nullable":true,"minimum":0,"description":"Maximum trip length in nights (must be >= trip_duration_min).","example":9},"required":false,"name":"trip_duration_max","in":"query"},{"schema":{"type":"integer","nullable":true,"minimum":0,"description":"Minimum advance-purchase window in days (dep_date − search_date): keep demand from searches made at least this many days before departure.","example":7},"required":false,"name":"search_window_min","in":"query"},{"schema":{"type":"integer","nullable":true,"minimum":0,"description":"Maximum advance-purchase window in days (must be >= search_window_min).","example":30},"required":false,"name":"search_window_max","in":"query"},{"schema":{"type":"integer","minimum":2,"maximum":90,"description":"Number of most-recent days of search activity aggregated into the demand level (min 2, default 7, max 90 — a 1-day window is the current day, whose data is incomplete; data is kept for 90 days).","example":30},"required":false,"name":"snapshot_window_days","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":200,"default":20,"description":"Maximum number of result rows. Default 20, max 200.","example":20},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","enum":["country","city"],"description":"Ranked output granularity: country (default) or city. Applies to destination/market (not audience).","example":"city"},"required":false,"name":"level","in":"query"},{"schema":{"type":"string","description":"Destination country selector `Country:<code>` (e.g. `Country:FR`). Optional and allowed ONLY together with level=city: it scopes the ranked destinations to a single country so that country’s cities are ranked. Must be a country — a City selector is rejected.","example":"Country:FR"},"required":false,"name":"destination_country","in":"query"}],"responses":{"200":{"description":"Destination demand ranking","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DestinationResponse"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"Malformed JSON in request body.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"AUTH_REQUIRED","message":"Invalid or expired API key.","doc_url":"https://docs.gojinko.com/authentication/api-keys"}}}}},"402":{"description":"Payment required — organization balance exhausted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"PAYMENT_REQUIRED","message":"Insufficient balance — this call costs $0.0150 and your organization has $0.0000 available. Top up at https://dashboard.gojinko.com/developers/billing/topup","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"NOT_FOUND","message":"Resource not found.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"409":{"description":"The operation conflicts with the current state of the resource","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"CONFLICT","message":"The operation conflicts with the current state of the resource.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"410":{"description":"The resource this request names no longer exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"GONE","message":"The resource no longer exists.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"A required field is missing or invalid.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"429":{"description":"Rate limit or quota exceeded","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"502":{"description":"The travel provider rejected the request, or an upstream call failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_REJECTED","message":"The request was declined for this offer. Try another offer.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"503":{"description":"No travel provider can serve the request right now","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_UNAVAILABLE","message":"The service is temporarily unavailable. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"504":{"description":"The travel provider did not answer in time","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_TIMEOUT","message":"The request timed out before a result was available. Retry the request.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}}}}},"/v1/market":{"get":{"tags":["Demand"],"summary":"Rank origin markets feeding a destination","description":"Ranks origin markets by current demand share (a point-in-time snapshot). Requires `destination`. Optional `origin_country` (a `Country:<code>` selector, allowed only with `level=city`) narrows the ranking to that country’s origin cities.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Destination selector `<Level>:<Code>`. Required for this endpoint.","example":"City:PAR"},"required":true,"name":"destination","in":"query"},{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Start of the departure window (YYYY-MM-DD). Supply together with departure_date_to; omit both for no departure-date filter (all departure dates).","example":"2026-07-01"},"required":false,"name":"departure_date_from","in":"query"},{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"End of the departure window (YYYY-MM-DD). Supply together with departure_date_from; omit both for no departure-date filter (all departure dates).","example":"2026-07-31"},"required":false,"name":"departure_date_to","in":"query"},{"schema":{"type":"string","enum":["Solo","Couple","Group","Family"],"example":"Couple"},"required":false,"name":"traveler_type","in":"query"},{"schema":{"type":"string","enum":["oneway","roundtrip"],"example":"roundtrip"},"required":false,"name":"trip_type","in":"query"},{"schema":{"type":"integer","nullable":true,"minimum":0,"description":"Minimum trip length in nights (return_date − dep_date). Round-trips only.","example":5},"required":false,"name":"trip_duration_min","in":"query"},{"schema":{"type":"integer","nullable":true,"minimum":0,"description":"Maximum trip length in nights (must be >= trip_duration_min).","example":9},"required":false,"name":"trip_duration_max","in":"query"},{"schema":{"type":"integer","nullable":true,"minimum":0,"description":"Minimum advance-purchase window in days (dep_date − search_date): keep demand from searches made at least this many days before departure.","example":7},"required":false,"name":"search_window_min","in":"query"},{"schema":{"type":"integer","nullable":true,"minimum":0,"description":"Maximum advance-purchase window in days (must be >= search_window_min).","example":30},"required":false,"name":"search_window_max","in":"query"},{"schema":{"type":"integer","minimum":2,"maximum":90,"description":"Number of most-recent days of search activity aggregated into the demand level (min 2, default 7, max 90 — a 1-day window is the current day, whose data is incomplete; data is kept for 90 days).","example":30},"required":false,"name":"snapshot_window_days","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":200,"default":20,"description":"Maximum number of result rows. Default 20, max 200.","example":20},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","enum":["country","city"],"description":"Ranked output granularity: country (default) or city. Applies to destination/market (not audience).","example":"city"},"required":false,"name":"level","in":"query"},{"schema":{"type":"string","description":"Origin country selector `Country:<code>` (e.g. `Country:US`). Optional and allowed ONLY together with level=city: it scopes the ranked origin markets to a single country so that country’s cities are ranked. Must be a country — a City selector is rejected.","example":"Country:US"},"required":false,"name":"origin_country","in":"query"}],"responses":{"200":{"description":"Market demand ranking","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MarketResponse"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"Malformed JSON in request body.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"AUTH_REQUIRED","message":"Invalid or expired API key.","doc_url":"https://docs.gojinko.com/authentication/api-keys"}}}}},"402":{"description":"Payment required — organization balance exhausted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"PAYMENT_REQUIRED","message":"Insufficient balance — this call costs $0.0150 and your organization has $0.0000 available. Top up at https://dashboard.gojinko.com/developers/billing/topup","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"NOT_FOUND","message":"Resource not found.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"409":{"description":"The operation conflicts with the current state of the resource","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"CONFLICT","message":"The operation conflicts with the current state of the resource.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"410":{"description":"The resource this request names no longer exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"GONE","message":"The resource no longer exists.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"A required field is missing or invalid.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"429":{"description":"Rate limit or quota exceeded","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"502":{"description":"The travel provider rejected the request, or an upstream call failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_REJECTED","message":"The request was declined for this offer. Try another offer.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"503":{"description":"No travel provider can serve the request right now","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_UNAVAILABLE","message":"The service is temporarily unavailable. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"504":{"description":"The travel provider did not answer in time","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_TIMEOUT","message":"The request timed out before a result was available. Retry the request.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}}}}},"/v1/audience":{"get":{"tags":["Demand"],"summary":"Rank traveler audiences for a destination","description":"Ranks traveler audiences by current demand share (a point-in-time snapshot). Requires `destination`.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Origin selector `<Level>:<Code>` (e.g. `Country:US`), or `global` for all origins. Required for the destination endpoints.","example":"Country:US"},"required":false,"name":"origin","in":"query"},{"schema":{"type":"string","minLength":1,"description":"Destination selector `<Level>:<Code>`. Required for this endpoint.","example":"City:PAR"},"required":true,"name":"destination","in":"query"},{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Start of the departure window (YYYY-MM-DD). Supply together with departure_date_to; omit both for no departure-date filter (all departure dates).","example":"2026-07-01"},"required":false,"name":"departure_date_from","in":"query"},{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"End of the departure window (YYYY-MM-DD). Supply together with departure_date_from; omit both for no departure-date filter (all departure dates).","example":"2026-07-31"},"required":false,"name":"departure_date_to","in":"query"},{"schema":{"type":"string","enum":["Solo","Couple","Group","Family"],"example":"Couple"},"required":false,"name":"traveler_type","in":"query"},{"schema":{"type":"string","enum":["oneway","roundtrip"],"example":"roundtrip"},"required":false,"name":"trip_type","in":"query"},{"schema":{"type":"integer","nullable":true,"minimum":0,"description":"Minimum trip length in nights (return_date − dep_date). Round-trips only.","example":5},"required":false,"name":"trip_duration_min","in":"query"},{"schema":{"type":"integer","nullable":true,"minimum":0,"description":"Maximum trip length in nights (must be >= trip_duration_min).","example":9},"required":false,"name":"trip_duration_max","in":"query"},{"schema":{"type":"integer","nullable":true,"minimum":0,"description":"Minimum advance-purchase window in days (dep_date − search_date): keep demand from searches made at least this many days before departure.","example":7},"required":false,"name":"search_window_min","in":"query"},{"schema":{"type":"integer","nullable":true,"minimum":0,"description":"Maximum advance-purchase window in days (must be >= search_window_min).","example":30},"required":false,"name":"search_window_max","in":"query"},{"schema":{"type":"integer","minimum":2,"maximum":90,"description":"Number of most-recent days of search activity aggregated into the demand level (min 2, default 7, max 90 — a 1-day window is the current day, whose data is incomplete; data is kept for 90 days).","example":30},"required":false,"name":"snapshot_window_days","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":200,"default":20,"description":"Maximum number of result rows. Default 20, max 200.","example":20},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"Audience demand ranking","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AudienceResponse"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"Malformed JSON in request body.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"AUTH_REQUIRED","message":"Invalid or expired API key.","doc_url":"https://docs.gojinko.com/authentication/api-keys"}}}}},"402":{"description":"Payment required — organization balance exhausted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"PAYMENT_REQUIRED","message":"Insufficient balance — this call costs $0.0150 and your organization has $0.0000 available. Top up at https://dashboard.gojinko.com/developers/billing/topup","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"NOT_FOUND","message":"Resource not found.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"409":{"description":"The operation conflicts with the current state of the resource","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"CONFLICT","message":"The operation conflicts with the current state of the resource.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"410":{"description":"The resource this request names no longer exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"GONE","message":"The resource no longer exists.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"A required field is missing or invalid.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"429":{"description":"Rate limit or quota exceeded","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"502":{"description":"The travel provider rejected the request, or an upstream call failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_REJECTED","message":"The request was declined for this offer. Try another offer.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"503":{"description":"No travel provider can serve the request right now","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_UNAVAILABLE","message":"The service is temporarily unavailable. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"504":{"description":"The travel provider did not answer in time","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_TIMEOUT","message":"The request timed out before a result was available. Retry the request.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}}}}},"/v1/trend":{"get":{"tags":["Demand"],"summary":"Demand time series for explicit origin/destination pairs","description":"Deep dive: returns one de-sampled search-volume series per (origin, destination) pair. v1 allows a list on only one side (a single origin OR a single destination).","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Comma-separated origin selectors `<Level>:<Code>` (e.g. `Country:US,Country:CA`). v1: at most one of origins/destinations may have more than one entry.","example":"Country:US"},"required":true,"name":"origins","in":"query"},{"schema":{"type":"string","minLength":1,"description":"Comma-separated destination selectors `<Level>:<Code>` (e.g. `Country:FR`).","example":"Country:FR,Country:DE,Country:GB"},"required":true,"name":"destinations","in":"query"},{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Start of the departure window (YYYY-MM-DD). Supply together with departure_date_to; omit both (recommended) for no departure-date filter — each bucket then counts searches for all departure dates. A window anchored at the query date counts only long-advance-purchase searches in old buckets and near-departure searches in recent ones, ramping the curve artificially.","example":"2026-07-01"},"required":false,"name":"departure_date_from","in":"query"},{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"End of the departure window (YYYY-MM-DD). Supply together with departure_date_from; omit both for no departure-date filter.","example":"2026-07-31"},"required":false,"name":"departure_date_to","in":"query"},{"schema":{"type":"string","enum":["Solo","Couple","Group","Family"],"example":"Couple"},"required":false,"name":"traveler_type","in":"query"},{"schema":{"type":"string","enum":["oneway","roundtrip"],"example":"roundtrip"},"required":false,"name":"trip_type","in":"query"},{"schema":{"type":"integer","nullable":true,"minimum":0,"example":5},"required":false,"name":"trip_duration_min","in":"query"},{"schema":{"type":"integer","nullable":true,"minimum":0,"example":9},"required":false,"name":"trip_duration_max","in":"query"},{"schema":{"type":"integer","nullable":true,"minimum":0,"example":7},"required":false,"name":"search_window_min","in":"query"},{"schema":{"type":"integer","nullable":true,"minimum":0,"example":30},"required":false,"name":"search_window_max","in":"query"},{"schema":{"type":"integer","minimum":2,"description":"Number of buckets N, in the chosen granularity unit (min 2; max depends on granularity: day <= 60, week <= 52, month <= 12; default 12). The series spans the last N complete buckets, excluding the in-progress one (day: last N days ending yesterday; week: last N full Monday-Sunday weeks; month: last N full calendar months).","example":12},"required":false,"name":"trend_window","in":"query"},{"schema":{"type":"string","enum":["day","week","month"],"description":"Bucket size for the returned series: week (default), day, or month.","example":"week"},"required":false,"name":"granularity","in":"query"}],"responses":{"200":{"description":"Per-pair demand time series","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrendResponse"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"Malformed JSON in request body.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"AUTH_REQUIRED","message":"Invalid or expired API key.","doc_url":"https://docs.gojinko.com/authentication/api-keys"}}}}},"402":{"description":"Payment required — organization balance exhausted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"PAYMENT_REQUIRED","message":"Insufficient balance — this call costs $0.0150 and your organization has $0.0000 available. Top up at https://dashboard.gojinko.com/developers/billing/topup","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"NOT_FOUND","message":"Resource not found.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"409":{"description":"The operation conflicts with the current state of the resource","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"CONFLICT","message":"The operation conflicts with the current state of the resource.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"410":{"description":"The resource this request names no longer exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"GONE","message":"The resource no longer exists.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"A required field is missing or invalid.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"429":{"description":"Rate limit or quota exceeded","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"502":{"description":"The travel provider rejected the request, or an upstream call failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_REJECTED","message":"The request was declined for this offer. Try another offer.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"503":{"description":"No travel provider can serve the request right now","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_UNAVAILABLE","message":"The service is temporarily unavailable. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"504":{"description":"The travel provider did not answer in time","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_TIMEOUT","message":"The request timed out before a result was available. Retry the request.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}}}}},"/v1/destination/trend":{"get":{"tags":["Demand"],"summary":"Demand time series for the top-N destinations","description":"Ranks destinations reachable from origin by within-window growth, then returns each top-N destination’s series. Optional `destination_country` (a `Country:<code>` selector, allowed only with `level=city`) narrows the ranking to that country’s destination cities.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Origin selector `<Level>:<Code>` (e.g. `Country:US`), or `global` for all origins. Required for the destination endpoints.","example":"Country:US"},"required":false,"name":"origin","in":"query"},{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Start of the departure window (YYYY-MM-DD). Supply together with departure_date_to; omit both (recommended) for no departure-date filter — each bucket then counts searches for all departure dates. A window anchored at the query date counts only long-advance-purchase searches in old buckets and near-departure searches in recent ones, ramping the curve artificially.","example":"2026-07-01"},"required":false,"name":"departure_date_from","in":"query"},{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"End of the departure window (YYYY-MM-DD). Supply together with departure_date_from; omit both for no departure-date filter.","example":"2026-07-31"},"required":false,"name":"departure_date_to","in":"query"},{"schema":{"type":"string","enum":["Solo","Couple","Group","Family"],"example":"Couple"},"required":false,"name":"traveler_type","in":"query"},{"schema":{"type":"string","enum":["oneway","roundtrip"],"example":"roundtrip"},"required":false,"name":"trip_type","in":"query"},{"schema":{"type":"integer","nullable":true,"minimum":0,"example":5},"required":false,"name":"trip_duration_min","in":"query"},{"schema":{"type":"integer","nullable":true,"minimum":0,"example":9},"required":false,"name":"trip_duration_max","in":"query"},{"schema":{"type":"integer","nullable":true,"minimum":0,"example":7},"required":false,"name":"search_window_min","in":"query"},{"schema":{"type":"integer","nullable":true,"minimum":0,"example":30},"required":false,"name":"search_window_max","in":"query"},{"schema":{"type":"integer","minimum":2,"description":"Number of buckets N, in the chosen granularity unit (min 2; max depends on granularity: day <= 60, week <= 52, month <= 12; default 12). The series spans the last N complete buckets, excluding the in-progress one (day: last N days ending yesterday; week: last N full Monday-Sunday weeks; month: last N full calendar months).","example":12},"required":false,"name":"trend_window","in":"query"},{"schema":{"type":"string","enum":["day","week","month"],"description":"Bucket size for the returned series: week (default), day, or month.","example":"week"},"required":false,"name":"granularity","in":"query"},{"schema":{"type":"string","enum":["country","city"],"description":"Ranked output granularity: country (default) or city. Applies to destination/market (not audience).","example":"city"},"required":false,"name":"level","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":50,"default":10,"description":"Maximum number of series (top-N keys). Default 10, max 50.","example":10},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","description":"Destination country selector `Country:<code>` (e.g. `Country:FR`). Optional and allowed ONLY together with level=city: it scopes the ranked destinations to a single country so that country’s cities are ranked. Must be a country — a City selector is rejected.","example":"Country:FR"},"required":false,"name":"destination_country","in":"query"}],"responses":{"200":{"description":"Top-N destination time series","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrendResponse"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"Malformed JSON in request body.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"AUTH_REQUIRED","message":"Invalid or expired API key.","doc_url":"https://docs.gojinko.com/authentication/api-keys"}}}}},"402":{"description":"Payment required — organization balance exhausted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"PAYMENT_REQUIRED","message":"Insufficient balance — this call costs $0.0150 and your organization has $0.0000 available. Top up at https://dashboard.gojinko.com/developers/billing/topup","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"NOT_FOUND","message":"Resource not found.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"409":{"description":"The operation conflicts with the current state of the resource","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"CONFLICT","message":"The operation conflicts with the current state of the resource.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"410":{"description":"The resource this request names no longer exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"GONE","message":"The resource no longer exists.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"A required field is missing or invalid.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"429":{"description":"Rate limit or quota exceeded","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"502":{"description":"The travel provider rejected the request, or an upstream call failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_REJECTED","message":"The request was declined for this offer. Try another offer.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"503":{"description":"No travel provider can serve the request right now","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_UNAVAILABLE","message":"The service is temporarily unavailable. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"504":{"description":"The travel provider did not answer in time","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_TIMEOUT","message":"The request timed out before a result was available. Retry the request.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}}}}},"/v1/market/trend":{"get":{"tags":["Demand"],"summary":"Demand time series for the top-N source markets","description":"Ranks source markets feeding the destination by within-window growth, then returns each top-N market’s series. Requires `destination`. Optional `origin_country` (a `Country:<code>` selector, allowed only with `level=city`) narrows the ranking to that country’s origin cities.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Destination selector `<Level>:<Code>`. Required for this endpoint.","example":"City:PAR"},"required":true,"name":"destination","in":"query"},{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Start of the departure window (YYYY-MM-DD). Supply together with departure_date_to; omit both (recommended) for no departure-date filter — each bucket then counts searches for all departure dates. A window anchored at the query date counts only long-advance-purchase searches in old buckets and near-departure searches in recent ones, ramping the curve artificially.","example":"2026-07-01"},"required":false,"name":"departure_date_from","in":"query"},{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"End of the departure window (YYYY-MM-DD). Supply together with departure_date_from; omit both for no departure-date filter.","example":"2026-07-31"},"required":false,"name":"departure_date_to","in":"query"},{"schema":{"type":"string","enum":["Solo","Couple","Group","Family"],"example":"Couple"},"required":false,"name":"traveler_type","in":"query"},{"schema":{"type":"string","enum":["oneway","roundtrip"],"example":"roundtrip"},"required":false,"name":"trip_type","in":"query"},{"schema":{"type":"integer","nullable":true,"minimum":0,"example":5},"required":false,"name":"trip_duration_min","in":"query"},{"schema":{"type":"integer","nullable":true,"minimum":0,"example":9},"required":false,"name":"trip_duration_max","in":"query"},{"schema":{"type":"integer","nullable":true,"minimum":0,"example":7},"required":false,"name":"search_window_min","in":"query"},{"schema":{"type":"integer","nullable":true,"minimum":0,"example":30},"required":false,"name":"search_window_max","in":"query"},{"schema":{"type":"integer","minimum":2,"description":"Number of buckets N, in the chosen granularity unit (min 2; max depends on granularity: day <= 60, week <= 52, month <= 12; default 12). The series spans the last N complete buckets, excluding the in-progress one (day: last N days ending yesterday; week: last N full Monday-Sunday weeks; month: last N full calendar months).","example":12},"required":false,"name":"trend_window","in":"query"},{"schema":{"type":"string","enum":["day","week","month"],"description":"Bucket size for the returned series: week (default), day, or month.","example":"week"},"required":false,"name":"granularity","in":"query"},{"schema":{"type":"string","enum":["country","city"],"description":"Ranked output granularity: country (default) or city. Applies to destination/market (not audience).","example":"city"},"required":false,"name":"level","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":50,"default":10,"description":"Maximum number of series (top-N keys). Default 10, max 50.","example":10},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","description":"Origin country selector `Country:<code>` (e.g. `Country:US`). Optional and allowed ONLY together with level=city: it scopes the ranked origin markets to a single country so that country’s cities are ranked. Must be a country — a City selector is rejected.","example":"Country:US"},"required":false,"name":"origin_country","in":"query"}],"responses":{"200":{"description":"Top-N market time series","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrendResponse"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"Malformed JSON in request body.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"AUTH_REQUIRED","message":"Invalid or expired API key.","doc_url":"https://docs.gojinko.com/authentication/api-keys"}}}}},"402":{"description":"Payment required — organization balance exhausted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"PAYMENT_REQUIRED","message":"Insufficient balance — this call costs $0.0150 and your organization has $0.0000 available. Top up at https://dashboard.gojinko.com/developers/billing/topup","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"NOT_FOUND","message":"Resource not found.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"409":{"description":"The operation conflicts with the current state of the resource","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"CONFLICT","message":"The operation conflicts with the current state of the resource.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"410":{"description":"The resource this request names no longer exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"GONE","message":"The resource no longer exists.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"A required field is missing or invalid.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"429":{"description":"Rate limit or quota exceeded","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"502":{"description":"The travel provider rejected the request, or an upstream call failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_REJECTED","message":"The request was declined for this offer. Try another offer.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"503":{"description":"No travel provider can serve the request right now","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_UNAVAILABLE","message":"The service is temporarily unavailable. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"504":{"description":"The travel provider did not answer in time","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_TIMEOUT","message":"The request timed out before a result was available. Retry the request.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}}}}},"/v1/audience/trend":{"get":{"tags":["Demand"],"summary":"Demand time series per traveler-type segment","description":"Returns a series for each traveler-type segment (excluding Unknown) for the given destination. Requires `destination`.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Origin selector `<Level>:<Code>` (e.g. `Country:US`), or `global` for all origins. Required for the destination endpoints.","example":"Country:US"},"required":false,"name":"origin","in":"query"},{"schema":{"type":"string","minLength":1,"description":"Destination selector `<Level>:<Code>`. Required for this endpoint.","example":"City:PAR"},"required":true,"name":"destination","in":"query"},{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Start of the departure window (YYYY-MM-DD). Supply together with departure_date_to; omit both (recommended) for no departure-date filter — each bucket then counts searches for all departure dates. A window anchored at the query date counts only long-advance-purchase searches in old buckets and near-departure searches in recent ones, ramping the curve artificially.","example":"2026-07-01"},"required":false,"name":"departure_date_from","in":"query"},{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"End of the departure window (YYYY-MM-DD). Supply together with departure_date_from; omit both for no departure-date filter.","example":"2026-07-31"},"required":false,"name":"departure_date_to","in":"query"},{"schema":{"type":"string","enum":["Solo","Couple","Group","Family"],"example":"Couple"},"required":false,"name":"traveler_type","in":"query"},{"schema":{"type":"string","enum":["oneway","roundtrip"],"example":"roundtrip"},"required":false,"name":"trip_type","in":"query"},{"schema":{"type":"integer","nullable":true,"minimum":0,"example":5},"required":false,"name":"trip_duration_min","in":"query"},{"schema":{"type":"integer","nullable":true,"minimum":0,"example":9},"required":false,"name":"trip_duration_max","in":"query"},{"schema":{"type":"integer","nullable":true,"minimum":0,"example":7},"required":false,"name":"search_window_min","in":"query"},{"schema":{"type":"integer","nullable":true,"minimum":0,"example":30},"required":false,"name":"search_window_max","in":"query"},{"schema":{"type":"integer","minimum":2,"description":"Number of buckets N, in the chosen granularity unit (min 2; max depends on granularity: day <= 60, week <= 52, month <= 12; default 12). The series spans the last N complete buckets, excluding the in-progress one (day: last N days ending yesterday; week: last N full Monday-Sunday weeks; month: last N full calendar months).","example":12},"required":false,"name":"trend_window","in":"query"},{"schema":{"type":"string","enum":["day","week","month"],"description":"Bucket size for the returned series: week (default), day, or month.","example":"week"},"required":false,"name":"granularity","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":50,"default":10,"description":"Maximum number of series (top-N keys). Default 10, max 50.","example":10},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"Per-segment time series","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrendResponse"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"Malformed JSON in request body.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"AUTH_REQUIRED","message":"Invalid or expired API key.","doc_url":"https://docs.gojinko.com/authentication/api-keys"}}}}},"402":{"description":"Payment required — organization balance exhausted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"PAYMENT_REQUIRED","message":"Insufficient balance — this call costs $0.0150 and your organization has $0.0000 available. Top up at https://dashboard.gojinko.com/developers/billing/topup","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"NOT_FOUND","message":"Resource not found.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"409":{"description":"The operation conflicts with the current state of the resource","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"CONFLICT","message":"The operation conflicts with the current state of the resource.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"410":{"description":"The resource this request names no longer exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"GONE","message":"The resource no longer exists.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"A required field is missing or invalid.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"429":{"description":"Rate limit or quota exceeded","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"502":{"description":"The travel provider rejected the request, or an upstream call failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_REJECTED","message":"The request was declined for this offer. Try another offer.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"503":{"description":"No travel provider can serve the request right now","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_UNAVAILABLE","message":"The service is temporarily unavailable. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"504":{"description":"The travel provider did not answer in time","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_TIMEOUT","message":"The request timed out before a result was available. Retry the request.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}}}}},"/v1/get_booking":{"post":{"tags":["Post-booking"],"summary":"Retrieve booked item IDs and per-item servicing eligibility using the Jinko booking reference","description":"Call get_booking first. Use booking_ref and the selected booked_item_id for every servicing action. last_name is required for guest lookup; an owning credential may omit it. The deprecated `provider`, `order_id`, `provider_reference` and `provider_booking_id` are accepted until 2027-04-30 where the request declares them; a request that sends one is answered with `Deprecation` and `Sunset` headers. A 409 item_selection_required returns safe items to choose from.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetBookingRequest"}}}},"responses":{"200":{"description":"Retrieve booked item IDs and per-item servicing eligibility using the Jinko booking reference","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetBookingResponse"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"Malformed JSON in request body.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"AUTH_REQUIRED","message":"Invalid or expired API key.","doc_url":"https://docs.gojinko.com/authentication/api-keys"}}}}},"402":{"description":"Payment required — organization balance exhausted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"PAYMENT_REQUIRED","message":"Insufficient balance — this call costs $0.0150 and your organization has $0.0000 available. Top up at https://dashboard.gojinko.com/developers/billing/topup","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"NOT_FOUND","message":"Resource not found.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"409":{"description":"Choose booked_item_id when several booked items match; other conflicts report the current resource state.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"$ref":"#/components/schemas/BookingSelectionConflict"}]}}}},"410":{"description":"The resource this request names no longer exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"GONE","message":"The resource no longer exists.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"A required field is missing or invalid.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"429":{"description":"Rate limit or quota exceeded","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"502":{"description":"The travel provider rejected the request, or an upstream call failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_REJECTED","message":"The request was declined for this offer. Try another offer.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"503":{"description":"No travel provider can serve the request right now","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_UNAVAILABLE","message":"The service is temporarily unavailable. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"504":{"description":"The travel provider did not answer in time","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_TIMEOUT","message":"The request timed out before a result was available. Retry the request.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}}}}},"/v1/refund_check":{"post":{"tags":["Post-booking"],"summary":"DEPRECATED — use /v1/flight_refund_preview. Check refund eligibility only; use flight_refund_preview for customer-facing amounts","deprecated":true,"description":"Call get_booking first. Use booking_ref and the selected booked_item_id for every servicing action. last_name is required for guest lookup; an owning credential may omit it. The deprecated `provider`, `order_id`, `provider_reference` and `provider_booking_id` are accepted until 2027-04-30 where the request declares them; a request that sends one is answered with `Deprecation` and `Sunset` headers. A 409 item_selection_required returns safe items to choose from.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefundCheckRequest"}}}},"responses":{"200":{"description":"Check refund eligibility only; use flight_refund_preview for customer-facing amounts","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefundCheckResponse"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"Malformed JSON in request body.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"AUTH_REQUIRED","message":"Invalid or expired API key.","doc_url":"https://docs.gojinko.com/authentication/api-keys"}}}}},"402":{"description":"Payment required — organization balance exhausted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"PAYMENT_REQUIRED","message":"Insufficient balance — this call costs $0.0150 and your organization has $0.0000 available. Top up at https://dashboard.gojinko.com/developers/billing/topup","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"NOT_FOUND","message":"Resource not found.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"409":{"description":"Servicing refusal or booking item selection required. A booking returned by get_booking with status failed (for example, after payment), expired_quote, pending or exchange_partial_failure is refused with `code: not_cancellable`, `reason_code: booking_not_active` and a platform sentence in `error.message`, when the caller may service the booking. Any other caller gets the same 404 as for an unknown booking.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"$ref":"#/components/schemas/FlightServicingConflictResponse"},{"$ref":"#/components/schemas/BookingSelectionConflict"}]}}}},"410":{"description":"The resource this request names no longer exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"GONE","message":"The resource no longer exists.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"A required field is missing or invalid.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"429":{"description":"Rate limit or quota exceeded","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"502":{"description":"The travel provider rejected the request, or an upstream call failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_REJECTED","message":"The request was declined for this offer. Try another offer.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"503":{"description":"No travel provider can serve the request right now","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_UNAVAILABLE","message":"The service is temporarily unavailable. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"504":{"description":"The travel provider did not answer in time","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_TIMEOUT","message":"The request timed out before a result was available. Retry the request.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}}}}},"/v1/refund_commit":{"post":{"tags":["Post-booking"],"summary":"DEPRECATED — use /v1/flight_refund_commit. Initiate a refund for a booked flight","deprecated":true,"description":"Call get_booking first. Use booking_ref and the selected booked_item_id for every servicing action. last_name is required for guest lookup; an owning credential may omit it. The deprecated `provider`, `order_id`, `provider_reference` and `provider_booking_id` are accepted until 2027-04-30 where the request declares them; a request that sends one is answered with `Deprecation` and `Sunset` headers. A 409 item_selection_required returns safe items to choose from.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefundCommitRequest"}}}},"responses":{"200":{"description":"Initiate a refund for a booked flight","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefundCommitResponse"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"Malformed JSON in request body.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"AUTH_REQUIRED","message":"Invalid or expired API key.","doc_url":"https://docs.gojinko.com/authentication/api-keys"}}}}},"402":{"description":"Payment required — organization balance exhausted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"PAYMENT_REQUIRED","message":"Insufficient balance — this call costs $0.0150 and your organization has $0.0000 available. Top up at https://dashboard.gojinko.com/developers/billing/topup","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"NOT_FOUND","message":"Resource not found.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"409":{"description":"Servicing refusal or booking item selection required. A booking returned by get_booking with status failed (for example, after payment), expired_quote, pending or exchange_partial_failure is refused with `code: not_cancellable`, `reason_code: booking_not_active` and a platform sentence in `error.message`, when the caller may service the booking. Any other caller gets the same 404 as for an unknown booking.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"$ref":"#/components/schemas/FlightServicingConflictResponse"},{"$ref":"#/components/schemas/BookingSelectionConflict"}]}}}},"410":{"description":"The resource this request names no longer exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"GONE","message":"The resource no longer exists.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"A required field is missing or invalid.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"429":{"description":"Rate limit or quota exceeded","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"502":{"description":"The travel provider rejected the request, or an upstream call failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_REJECTED","message":"The request was declined for this offer. Try another offer.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"503":{"description":"No travel provider can serve the request right now","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_UNAVAILABLE","message":"The service is temporarily unavailable. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"504":{"description":"The travel provider did not answer in time","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_TIMEOUT","message":"The request timed out before a result was available. Retry the request.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}}}}},"/v1/refund_status":{"post":{"tags":["Post-booking"],"summary":"DEPRECATED — use /v1/flight_refund_status. Check the status of a refund","deprecated":true,"description":"Call get_booking first. Use booking_ref and the selected booked_item_id for every servicing action. last_name is required for guest lookup; an owning credential may omit it. The deprecated `provider`, `order_id`, `provider_reference` and `provider_booking_id` are accepted until 2027-04-30 where the request declares them; a request that sends one is answered with `Deprecation` and `Sunset` headers. A 409 item_selection_required returns safe items to choose from.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefundStatusRequest"}}}},"responses":{"200":{"description":"Check the status of a refund","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefundStatusResponse"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"Malformed JSON in request body.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"AUTH_REQUIRED","message":"Invalid or expired API key.","doc_url":"https://docs.gojinko.com/authentication/api-keys"}}}}},"402":{"description":"Payment required — organization balance exhausted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"PAYMENT_REQUIRED","message":"Insufficient balance — this call costs $0.0150 and your organization has $0.0000 available. Top up at https://dashboard.gojinko.com/developers/billing/topup","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"404":{"description":"No refund exists for this booking, or the booking itself could not be read.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"NOT_FOUND","message":"no refund found for this booking"}}}}},"409":{"description":"Choose booked_item_id when several booked items match; other conflicts report the current resource state.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"$ref":"#/components/schemas/BookingSelectionConflict"}]}}}},"410":{"description":"The resource this request names no longer exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"GONE","message":"The resource no longer exists.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"A required field is missing or invalid.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"429":{"description":"Rate limit or quota exceeded","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"502":{"description":"The travel provider rejected the request, or an upstream call failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_REJECTED","message":"The request was declined for this offer. Try another offer.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"503":{"description":"No travel provider can serve the request right now","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_UNAVAILABLE","message":"The service is temporarily unavailable. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"504":{"description":"The travel provider did not answer in time","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_TIMEOUT","message":"The request timed out before a result was available. Retry the request.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}}}}},"/v1/exchange_shop":{"post":{"tags":["Post-booking"],"summary":"Shop for flight exchange alternatives","description":"Call get_booking first. Use booking_ref and the selected booked_item_id for every servicing action. last_name is required for guest lookup; an owning credential may omit it. The deprecated `provider`, `order_id`, `provider_reference` and `provider_booking_id` are accepted until 2027-04-30 where the request declares them; a request that sends one is answered with `Deprecation` and `Sunset` headers. A 409 item_selection_required returns safe items to choose from.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExchangeShopRequest"}}}},"responses":{"200":{"description":"Shop for flight exchange alternatives","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExchangeShopResponse"}}}},"400":{"description":"A leg selection the booked itinerary or the supplier refuses. When the booked itinerary refuses it, the message lists the booked legs; when the supplier refuses it, the message is “This exchange cannot be processed as requested. Change the selected flights or contact Jinko support.” **`INVALID_SEGMENTS_TO_EXCHANGE`** — An exchange selection does not match a booked leg. Read the booking and select each leg by its booked origin and destination. **`PARTIAL_EXCHANGE_UNSUPPORTED`** — The supplier cannot keep unselected legs unchanged for this booking. Name every booked leg in segments_to_exchange to reshop the whole itinerary. **`LEG_ALREADY_DEPARTED`** — An exchange names a booked leg whose first flight has already departed. Change only legs that have not departed; the message lists the booked legs. **`DEPARTURE_DATE_IN_PAST`** — An exchange asks for a departure date before today at the departure airport. Choose a departure date from today onwards, in the departure airport’s local time. **`LEG_DATES_OUT_OF_ORDER`** — The new dates would put the legs of the itinerary out of order. Pick dates that keep each leg after the one before it, or change both legs in one request; the message lists the booked legs. **`INVALID_EXCHANGE_REQUEST`** — The supplier refused the exchange request itself. Change the selected flights, or contact Jinko support. The refusal does not say which part of the request was refused.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"AUTH_REQUIRED","message":"Invalid or expired API key.","doc_url":"https://docs.gojinko.com/authentication/api-keys"}}}}},"402":{"description":"Payment required — organization balance exhausted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"PAYMENT_REQUIRED","message":"Insufficient balance — this call costs $0.0150 and your organization has $0.0000 available. Top up at https://dashboard.gojinko.com/developers/billing/topup","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"NOT_FOUND","message":"Resource not found.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"409":{"description":"Choose booked_item_id when several booked items match (item_selection_required). Otherwise the booking cannot be exchanged as it stands. **`NOT_EXCHANGEABLE`** — The booking cannot be exchanged as it stands: no ticket or flight is left to change, or the fare does not permit the change. Do not retry the same exchange. Contact Jinko support if the customer still needs a change.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"$ref":"#/components/schemas/BookingSelectionConflict"}]}}}},"410":{"description":"The resource this request names no longer exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"GONE","message":"The resource no longer exists.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"A required field is missing or invalid.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"429":{"description":"Rate limit or quota exceeded","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"502":{"description":"The travel provider rejected the request, or an upstream call failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_REJECTED","message":"The request was declined for this offer. Try another offer.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"503":{"description":"No travel provider can serve the request right now","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_UNAVAILABLE","message":"The service is temporarily unavailable. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"504":{"description":"The travel provider did not answer in time","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_TIMEOUT","message":"The request timed out before a result was available. Retry the request.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}}}}},"/v1/exchange_price":{"post":{"tags":["Post-booking"],"summary":"Price a specific exchange offer","description":"Call get_booking first. Use booking_ref and the selected booked_item_id for every servicing action. last_name is required for guest lookup; an owning credential may omit it. The deprecated `provider`, `order_id`, `provider_reference` and `provider_booking_id` are accepted until 2027-04-30 where the request declares them; a request that sends one is answered with `Deprecation` and `Sunset` headers. A 409 item_selection_required returns safe items to choose from.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExchangePriceRequest"}}}},"responses":{"200":{"description":"Price a specific exchange offer","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExchangePriceResponse"}}}},"400":{"description":"The supplier refused the request itself (an expired or malformed offer, a leg selection it cannot serve), with the message “This exchange cannot be processed as requested. Change the selected flights or contact Jinko support.” Nothing was priced; shop again. **`INVALID_EXCHANGE_REQUEST`** — The supplier refused the exchange request itself. Change the selected flights, or contact Jinko support. The refusal does not say which part of the request was refused. **`INVALID_SEGMENTS_TO_EXCHANGE`** — An exchange selection does not match a booked leg. Read the booking and select each leg by its booked origin and destination. **`PARTIAL_EXCHANGE_UNSUPPORTED`** — The supplier cannot keep unselected legs unchanged for this booking. Name every booked leg in segments_to_exchange to reshop the whole itinerary.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"AUTH_REQUIRED","message":"Invalid or expired API key.","doc_url":"https://docs.gojinko.com/authentication/api-keys"}}}}},"402":{"description":"Payment required — organization balance exhausted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"PAYMENT_REQUIRED","message":"Insufficient balance — this call costs $0.0150 and your organization has $0.0000 available. Top up at https://dashboard.gojinko.com/developers/billing/topup","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"NOT_FOUND","message":"Resource not found.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"409":{"description":"The exchange cannot be carried out automatically, so no price is given; nothing was exchanged or charged. `code` says why, as a sibling of `error` — see `ExchangePriceConflictCode` — and `error.message` states it in figures where there are any. For `funds_unavailable`, `multi_currency_basis` and `manual_required`, committing the same offer sends the request to Jinko support, who will complete it and refund the customer; `not_exchangeable` is not sent to support. Choose `item_id` when several booked items match. A 409 whose `code` is outside that list is not one of these refusals: it arrives as the plain error envelope.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ExchangePriceConflictResponse"},{"$ref":"#/components/schemas/BookingSelectionConflict"},{"$ref":"#/components/schemas/ErrorResponse"}]},"example":{"error":{"code":"CONFLICT","message":"this exchange would refund 1050.00 USD, and only 789.00 is still refundable on the original payment. It can't be refunded automatically. Confirming this exchange sends it to Jinko support, who will complete it and refund you."},"code":"funds_unavailable"}}}},"410":{"description":"The resource this request names no longer exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"GONE","message":"The resource no longer exists.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"A required field is missing or invalid.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"429":{"description":"Rate limit or quota exceeded","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"502":{"description":"The travel provider rejected the request, or an upstream call failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_REJECTED","message":"The request was declined for this offer. Try another offer.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"503":{"description":"No travel provider can serve the request right now","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_UNAVAILABLE","message":"The service is temporarily unavailable. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"504":{"description":"The travel provider did not answer in time","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_TIMEOUT","message":"The request timed out before a result was available. Retry the request.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}}}}},"/v1/exchange_commit":{"post":{"tags":["Post-booking"],"summary":"Commit a flight exchange","description":"Call get_booking first. Use booking_ref and the selected booked_item_id for every servicing action. last_name is required for guest lookup; an owning credential may omit it. The deprecated `provider`, `order_id`, `provider_reference` and `provider_booking_id` are accepted until 2027-04-30 where the request declares them; a request that sends one is answered with `Deprecation` and `Sunset` headers. A 409 item_selection_required returns safe items to choose from.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExchangeCommitRequest"}}}},"responses":{"200":{"description":"Commit a flight exchange","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExchangeCommitResponse"}}}},"400":{"description":"The supplier refused the request when the commit priced the offer again (an expired or malformed offer, a leg selection it cannot serve), with the message “This exchange cannot be processed as requested. Change the selected flights or contact Jinko support.” Nothing was exchanged or charged; shop again. **`INVALID_EXCHANGE_REQUEST`** — The supplier refused the exchange request itself. Change the selected flights, or contact Jinko support. The refusal does not say which part of the request was refused. **`INVALID_SEGMENTS_TO_EXCHANGE`** — An exchange selection does not match a booked leg. Read the booking and select each leg by its booked origin and destination. **`PARTIAL_EXCHANGE_UNSUPPORTED`** — The supplier cannot keep unselected legs unchanged for this booking. Name every booked leg in segments_to_exchange to reshop the whole itinerary.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"AUTH_REQUIRED","message":"Invalid or expired API key.","doc_url":"https://docs.gojinko.com/authentication/api-keys"}}}}},"402":{"description":"Payment required — organization balance exhausted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"PAYMENT_REQUIRED","message":"Insufficient balance — this call costs $0.0150 and your organization has $0.0000 available. Top up at https://dashboard.gojinko.com/developers/billing/topup","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"NOT_FOUND","message":"Resource not found.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"409":{"description":"Nothing was exchanged or charged by this call. `code` says why — see `ExchangeCommitConflictCode`. On `funds_unavailable`, `multi_currency_basis` and `manual_required` the exchange can't be refunded automatically, so the request has been sent to Jinko support, who will complete it and refund the customer; follow it on `get_booking` at `items[].servicing.exchange`. On `exchange_in_progress` an exchange for this booking is already waiting for payment or running: call exchange_status for its progress and its `checkout_url`. On `not_exchangeable` the booking cannot be exchanged as it stands (no ticket or flight left to change, or a fare that does not permit it); nothing is sent to support. Choose `item_id` when several booked items match. A 409 whose `code` is outside that list arrives as the plain error envelope.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ExchangeCommitConflictResponse"},{"$ref":"#/components/schemas/BookingSelectionConflict"},{"$ref":"#/components/schemas/ErrorResponse"}]},"example":{"error":{"code":"CONFLICT","message":"This exchange can't be refunded automatically, so it has been sent to Jinko support. They will complete it and refund you; there is nothing else to do."},"code":"funds_unavailable","manual_handling":{"status":"pending"}}}}},"410":{"description":"The resource this request names no longer exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"GONE","message":"The resource no longer exists.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"A required field is missing or invalid.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"429":{"description":"Rate limit or quota exceeded","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"502":{"description":"The travel provider rejected the request, or an upstream call failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_REJECTED","message":"The request was declined for this offer. Try another offer.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"503":{"description":"No travel provider can serve the request right now","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_UNAVAILABLE","message":"The service is temporarily unavailable. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"504":{"description":"The travel provider did not answer in time","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_TIMEOUT","message":"The request timed out before a result was available. Retry the request.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}}}}},"/v1/exchange_status":{"post":{"tags":["Post-booking"],"summary":"Check the status of an exchange","description":"Call get_booking first. Use booking_ref and the selected booked_item_id for every servicing action. last_name is required for guest lookup; an owning credential may omit it. The deprecated `provider`, `order_id`, `provider_reference` and `provider_booking_id` are accepted until 2027-04-30 where the request declares them; a request that sends one is answered with `Deprecation` and `Sunset` headers. A 409 item_selection_required returns safe items to choose from.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExchangeStatusRequest"}}}},"responses":{"200":{"description":"Check the status of an exchange","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExchangeStatusResponse"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"Malformed JSON in request body.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"AUTH_REQUIRED","message":"Invalid or expired API key.","doc_url":"https://docs.gojinko.com/authentication/api-keys"}}}}},"402":{"description":"Payment required — organization balance exhausted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"PAYMENT_REQUIRED","message":"Insufficient balance — this call costs $0.0150 and your organization has $0.0000 available. Top up at https://dashboard.gojinko.com/developers/billing/topup","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"NOT_FOUND","message":"Resource not found.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"409":{"description":"Choose booked_item_id when several booked items match; other conflicts report the current resource state.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"$ref":"#/components/schemas/BookingSelectionConflict"}]}}}},"410":{"description":"The resource this request names no longer exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"GONE","message":"The resource no longer exists.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"A required field is missing or invalid.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"429":{"description":"Rate limit or quota exceeded","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"502":{"description":"The travel provider rejected the request, or an upstream call failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_REJECTED","message":"The request was declined for this offer. Try another offer.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"503":{"description":"No travel provider can serve the request right now","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_UNAVAILABLE","message":"The service is temporarily unavailable. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"504":{"description":"The travel provider did not answer in time","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_TIMEOUT","message":"The request timed out before a result was available. Retry the request.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}}}}},"/v1/hotel_cancel":{"post":{"tags":["Post-booking"],"summary":"DEPRECATED — use /v1/hotel_cancel_preview and /v1/hotel_cancel_commit. Cancel a hotel booking and get a refund when eligible. Preview and commit in one call — POST /v1/hotel_cancel_preview plus /v1/hotel_cancel_commit is the two-step form, which shows the customer the refund before anything is cancelled. `refund_amount` is what the CUSTOMER gets back; all amounts are customer-facing only. Returning the money outlives this call: poll the `operation` handle with POST /v1/hotel_cancel_status. Every call here needs API authentication (an API key or a Bearer token); this is about which mode identifies the booking. Call get_booking first, then send booking_ref and the selected booked_item_id. Guests must send last_name; an owning credential may omit it. The deprecated `provider`, `order_id`, `provider_reference` and `provider_booking_id` are accepted until 2027-04-30 where the request declares them; a request that sends one is answered with `Deprecation` and `Sunset` headers.","deprecated":true,"description":"Call get_booking first. Use booking_ref and the selected booked_item_id for every servicing action. last_name is required for guest lookup; an owning credential may omit it. The deprecated `provider`, `order_id`, `provider_reference` and `provider_booking_id` are accepted until 2027-04-30 where the request declares them; a request that sends one is answered with `Deprecation` and `Sunset` headers. A 409 item_selection_required returns safe items to choose from.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HotelCancelRequest"}}}},"responses":{"200":{"description":"Cancel a hotel booking and get a refund when eligible. Preview and commit in one call — POST /v1/hotel_cancel_preview plus /v1/hotel_cancel_commit is the two-step form, which shows the customer the refund before anything is cancelled. `refund_amount` is what the CUSTOMER gets back; all amounts are customer-facing only. Returning the money outlives this call: poll the `operation` handle with POST /v1/hotel_cancel_status. Every call here needs API authentication (an API key or a Bearer token); this is about which mode identifies the booking. Call get_booking first, then send booking_ref and the selected booked_item_id. Guests must send last_name; an owning credential may omit it. The deprecated `provider`, `order_id`, `provider_reference` and `provider_booking_id` are accepted until 2027-04-30 where the request declares them; a request that sends one is answered with `Deprecation` and `Sunset` headers.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HotelCancelResponse"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"Malformed JSON in request body.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"AUTH_REQUIRED","message":"Invalid or expired API key.","doc_url":"https://docs.gojinko.com/authentication/api-keys"}}}}},"402":{"description":"Payment required — organization balance exhausted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"PAYMENT_REQUIRED","message":"Insufficient balance — this call costs $0.0150 and your organization has $0.0000 available. Top up at https://dashboard.gojinko.com/developers/billing/topup","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"NOT_FOUND","message":"Resource not found.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"409":{"description":"Choose booked_item_id when several booked items match; other conflicts report the current resource state.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"$ref":"#/components/schemas/BookingSelectionConflict"}]}}}},"410":{"description":"The resource this request names no longer exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"GONE","message":"The resource no longer exists.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"A required field is missing or invalid.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"429":{"description":"Rate limit or quota exceeded","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"502":{"description":"The travel provider rejected the request, or an upstream call failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_REJECTED","message":"The request was declined for this offer. Try another offer.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"503":{"description":"No travel provider can serve the request right now","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_UNAVAILABLE","message":"The service is temporarily unavailable. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"504":{"description":"The travel provider did not answer in time","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_TIMEOUT","message":"The request timed out before a result was available. Retry the request.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}}}}},"/v1/hotel_cancel_preview":{"post":{"tags":["Post-booking"],"summary":"Preview what cancelling a hotel booking would cost","description":"Quote what cancelling would return to the customer right now: the refund, the penalty in force, and the policy behind them. NOTHING is cancelled. `refund` is the customer figure — what they paid for the item less the penalty. All amounts are customer-facing only. `commitable: false` means a commit would be refused and `not_commitable_reason` says why. The handle this returns is named `quote`: it is what POST /v1/hotel_cancel_commit consumes, and it stops binding at `expires_at`. Every call here needs API authentication; this is about which mode identifies the booking. Call get_booking first, then send booking_ref and the selected booked_item_id. An owning credential identifies the booking without last_name; guests must supply it. The deprecated `provider`, `order_id`, `provider_reference` and `provider_booking_id` are accepted until 2027-04-30 where the request declares them; a request that sends one is answered with `Deprecation` and `Sunset` headers.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HotelCancelPreviewRequest"}}}},"responses":{"200":{"description":"What cancelling would cost","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HotelCancelPreviewResponse"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"Malformed JSON in request body.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"AUTH_REQUIRED","message":"Invalid or expired API key.","doc_url":"https://docs.gojinko.com/authentication/api-keys"}}}}},"402":{"description":"Payment required — organization balance exhausted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"PAYMENT_REQUIRED","message":"Insufficient balance — this call costs $0.0150 and your organization has $0.0000 available. Top up at https://dashboard.gojinko.com/developers/billing/topup","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"404":{"description":"Unknown booking, or one this credential may not read.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"NOT_FOUND","message":"booking not found"}}}}},"409":{"description":"The supplier will not cancel this booking as it stands — it is already cancelled, or was never confirmed. A hotel booking that is no longer active is also refused with `reason_code: booking_not_active` when the caller may service it (any other caller gets the same 404 as for an unknown booking). `code` is `not_cancellable`, as a sibling of `error`. `error.message` is “This booking cannot be cancelled online. Contact Jinko support.” when the supplier refused, or the platform’s sentence when the booking is no longer active. There is no quote to show and nothing to commit. Another operation already in flight on the item (an exchange, a refund or a cancellation) is refused the same way, with `code: active_operation_exists` and `active_operation` naming it: follow it with the status route and preview again once it has finished. A 409 with no `code` beside `error` is not this case — the reference matched more than one booking.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ServicingConflictResponse"},{"$ref":"#/components/schemas/BookingSelectionConflict"}]},"example":{"error":{"code":"CONFLICT","message":"This booking cannot be cancelled online. Contact Jinko support."},"code":"not_cancellable"}}}},"410":{"description":"The resource this request names no longer exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"GONE","message":"The resource no longer exists.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"A required field is missing or invalid.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"429":{"description":"Rate limit or quota exceeded","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"502":{"description":"The travel provider rejected the request, or an upstream call failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_REJECTED","message":"The request was declined for this offer. Try another offer.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"503":{"description":"No travel provider can serve the request right now","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_UNAVAILABLE","message":"The service is temporarily unavailable. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"504":{"description":"The travel provider did not answer in time","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_TIMEOUT","message":"The request timed out before a result was available. Retry the request.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}}}}},"/v1/hotel_cancel_commit":{"post":{"tags":["Post-booking"],"summary":"Cancel a hotel booking against a quote the customer accepted","description":"Start the cancellation the preview described. The body is the `quote` handle and the `acknowledged` figure PLUS the same booking_ref, item_id and last_name the preview took: ownership is re-checked here rather than carried by the handle, so they have to be sent again. `acknowledged` is the refund the customer was shown, copied from the preview as it arrived (`value` and `decimal_places` included): if your figure differs from the still-live quote’s refund, the call is refused with 409 `quote_drift`. `retry_quote_id` is that same quote’s handle and `current.refund` its figure. Show it to the customer, then commit again with that handle and figure. Nothing is cancelled. If the supplier’s policy moved since the quote, `quote_drift` has no `retry_quote_id`; preview again. The other refusals are 409 `QUOTE_EXPIRED` (run hotel_cancel_preview again), `active_operation_exists` (poll the `active_operation` when present instead of starting a second one), `not_cancellable` and `funds_unavailable`. Success does NOT mean the money has moved: cancelling at the supplier and refunding the customer are separate steps, so the answer is an `operation` handle to poll with POST /v1/hotel_cancel_status.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HotelCancelCommitRequest"}}}},"responses":{"200":{"description":"The cancellation was accepted and is running","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HotelCancelCommitResponse"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"Malformed JSON in request body.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"AUTH_REQUIRED","message":"Invalid or expired API key.","doc_url":"https://docs.gojinko.com/authentication/api-keys"}}}}},"402":{"description":"Payment required — organization balance exhausted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"PAYMENT_REQUIRED","message":"Insufficient balance — this call costs $0.0150 and your organization has $0.0000 available. Top up at https://dashboard.gojinko.com/developers/billing/topup","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"404":{"description":"Unknown booking, or one this credential may not read.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"NOT_FOUND","message":"booking not found"}}}}},"409":{"description":"The commit cannot run as quoted; nothing was cancelled. `code` says which case, as a sibling of `error` — see `ServicingConflictCode` for the closed list and what each one asks of you. A booking that is no longer active is refused with `code: not_cancellable`, `reason_code: booking_not_active` and a platform sentence in `error.message`, when the caller may service it. A 409 whose `code` is outside that list is NOT a servicing refusal: it arrives as the plain error envelope, with no `code` beside it.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ServicingConflictResponse"},{"$ref":"#/components/schemas/BookingSelectionConflict"}]},"example":{"error":{"code":"CONFLICT","message":"Your acknowledged figure differs from the quote’s refund; nothing was cancelled. Show the customer `current.refund`, then commit again with `retry_quote_id` (the same quote handle) and that figure."},"code":"quote_drift","requote":"svq_01J8AB4N7GLY3Q0D","current":{"refund":{"value":20500,"currency":"USD","decimal_places":2}}}}}},"410":{"description":"The resource this request names no longer exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"GONE","message":"The resource no longer exists.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"A required field is missing or invalid.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"429":{"description":"Rate limit or quota exceeded","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"502":{"description":"The travel provider rejected the request, or an upstream call failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_REJECTED","message":"The request was declined for this offer. Try another offer.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"503":{"description":"No travel provider can serve the request right now","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_UNAVAILABLE","message":"The service is temporarily unavailable. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"504":{"description":"The travel provider did not answer in time","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_TIMEOUT","message":"The request timed out before a result was available. Retry the request.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}}}}},"/v1/hotel_cancel_status":{"post":{"tags":["Post-booking"],"summary":"Check how a hotel cancellation ended","description":"Report where one cancellation has got to, named by the `operation` handle a commit (or POST /v1/hotel_cancel) returned. Read-only. `state` is the platform view — keep polling while it is `in_progress` or `attention_required`, both of which mean the operation is still alive. `provider` is what the supplier says about the booking and `money` is where the refund itself has got to; the two move independently, so a booking can read `cancelled` while the money is still in flight. Do not tell a customer they have been refunded until `money.vehicle_state` says `paid`. Same auth modes as hotel_cancel_preview.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HotelCancelStatusRequest"}}}},"responses":{"200":{"description":"Where the cancellation has got to","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HotelCancelStatusResponse"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"Malformed JSON in request body.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"AUTH_REQUIRED","message":"Invalid or expired API key.","doc_url":"https://docs.gojinko.com/authentication/api-keys"}}}}},"402":{"description":"Payment required — organization balance exhausted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"PAYMENT_REQUIRED","message":"Insufficient balance — this call costs $0.0150 and your organization has $0.0000 available. Top up at https://dashboard.gojinko.com/developers/billing/topup","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"404":{"description":"Unknown operation or booking, or one this credential may not read.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"NOT_FOUND","message":"booking not found"}}}}},"409":{"description":"Several booked items match. Select a booked_item_id from items and repeat with booking_ref and booked_item_id.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"$ref":"#/components/schemas/BookingSelectionConflict"}]}}}},"410":{"description":"The resource this request names no longer exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"GONE","message":"The resource no longer exists.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"A required field is missing or invalid.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"429":{"description":"Rate limit or quota exceeded","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"502":{"description":"The travel provider rejected the request, or an upstream call failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_REJECTED","message":"The request was declined for this offer. Try another offer.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"503":{"description":"No travel provider can serve the request right now","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_UNAVAILABLE","message":"The service is temporarily unavailable. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"504":{"description":"The travel provider did not answer in time","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_TIMEOUT","message":"The request timed out before a result was available. Retry the request.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}}}}},"/v1/flight_refund_preview":{"post":{"tags":["Post-booking"],"summary":"Preview what cancelling a booked flight (every ticket on the item) would return to the customer","description":"Quote what the customer would get back for this flight item, every ticket on it, right now: the refund, the penalty in force, and every document the operation would cover. NOTHING is sent to the airline. `refund` is what the customer paid less the penalty. All amounts are customer-facing only. The platform decides WHICH operation this is and reports it as `operation_kind`: a ticket still inside its void window is `void` — the entire charge is reversed, penalty-free — and anything else is `cancel`, a refund under the fare rules. You cannot ask for one; read what you are given, because the void window closes with time. `commitable: false` with `support_level: MANUAL_REQUIRED` is not a dead end: the commit takes `manual_ok: true` and hands the operation to a Jinko agent. The handle this returns is named `quote`: it is what POST /v1/flight_refund_commit consumes, and it stops binding at `expires_at`. Every call here needs API authentication; this is about which mode identifies the booking. Call get_booking first, then send booking_ref and the selected booked_item_id. Guests must send last_name; owning credentials may omit it. The deprecated `provider`, `order_id`, `provider_reference` and `provider_booking_id` are accepted until 2027-04-30 where the request declares them; a request that sends one is answered with `Deprecation` and `Sunset` headers.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlightRefundPreviewRequest"}}}},"responses":{"200":{"description":"What cancelling the flight item would return","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlightRefundPreviewResponse"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"Malformed JSON in request body.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"AUTH_REQUIRED","message":"Invalid or expired API key.","doc_url":"https://docs.gojinko.com/authentication/api-keys"}}}}},"402":{"description":"Payment required — organization balance exhausted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"PAYMENT_REQUIRED","message":"Insufficient balance — this call costs $0.0150 and your organization has $0.0000 available. Top up at https://dashboard.gojinko.com/developers/billing/topup","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"404":{"description":"Unknown booking, or one this credential may not read.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"NOT_FOUND","message":"booking not found"}}}}},"409":{"description":"The airline will not refund this item as it stands — a non-refundable fare, or every document already voided or refunded. A booking that is no longer active is also refused with `reason_code: booking_not_active` when the caller may service it (any other caller gets the same 404 as for an unknown booking). `code` is `not_cancellable`, as a sibling of `error`. `error.message` is “This booking cannot be cancelled online. Contact Jinko support.” when the airline refused, or the platform’s sentence when the booking is no longer active. There is no quote to show, nothing to commit, and `manual_ok` does not apply. Another operation already in flight on the item (an exchange, a refund or a cancellation) is refused the same way, with `code: active_operation_exists` and `active_operation` naming it: follow it with the status route and preview again once it has finished. A manual request no Jinko agent has picked up yet does not block a preview. A 409 with no `code` beside `error` is not this case — the reference matched more than one booking, or the booking holds several flights and `item_id` named none.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/FlightServicingConflictResponse"},{"$ref":"#/components/schemas/BookingSelectionConflict"}]},"example":{"error":{"code":"CONFLICT","message":"This booking cannot be cancelled online. Contact Jinko support."},"code":"not_cancellable"}}}},"410":{"description":"The resource this request names no longer exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"GONE","message":"The resource no longer exists.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"A required field is missing or invalid.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"429":{"description":"Rate limit or quota exceeded","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"502":{"description":"The travel provider rejected the request, or an upstream call failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_REJECTED","message":"The request was declined for this offer. Try another offer.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"503":{"description":"No travel provider can serve the request right now","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_UNAVAILABLE","message":"The service is temporarily unavailable. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"504":{"description":"The travel provider did not answer in time","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_TIMEOUT","message":"The request timed out before a result was available. Retry the request.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}}}}},"/v1/flight_refund_commit":{"post":{"tags":["Post-booking"],"summary":"Refund or void a booked flight (every ticket on the item) against a quote the customer accepted","description":"Start the operation the preview described — a refund, or a void of the original charge, whichever the preview reported as `operation_kind`. The body is the `quote` handle and the `acknowledged` figure PLUS the same auth mode the preview took: ownership is re-checked here rather than carried by the handle, so the mode has to be sent again. `acknowledged` is the refund the customer was shown, copied from the preview as it arrived (`value` and `decimal_places` included): if your figure differs from the still-live quote’s refund, the call is refused with 409 `quote_drift`. `retry_quote_id` is that same quote’s handle and `current.refund` its figure. Show it to the customer, then commit again with that handle and figure. Nothing is sent to the airline. A MANUAL_REQUIRED quote needs `manual_ok: true`: the operation is then raised for a Jinko agent and answers `attention_required` with `reason: manual_required`, again without the airline being called. Committing one without the flag is refused 409 `manual_required`. That submission is also the one case where `acknowledged` may be left out: a MANUAL_REQUIRED quote whose penalty is unknown carries no `refund` to acknowledge, and there is nothing to compare against. Whenever the preview DID name a refund, send it. The other refusals are 409 `QUOTE_EXPIRED` (preview again), `active_operation_exists` (poll the `active_operation` when present instead of starting a second one), `not_cancellable`, `funds_unavailable`, `superseded`, and — when the preview already said `commitable: false` and you committed anyway — that quote’s own reason answered back as the code: `penalty_exceeds_sell` or `multi_currency_basis`. Success does NOT mean the money has moved: giving the tickets back at the airline and returning the money to the customer are separate steps, so the answer is an `operation` handle to poll with POST /v1/flight_refund_status.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlightRefundCommitRequest"}}}},"responses":{"200":{"description":"The refund or void was accepted and is running","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlightRefundCommitResponse"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"Malformed JSON in request body.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"AUTH_REQUIRED","message":"Invalid or expired API key.","doc_url":"https://docs.gojinko.com/authentication/api-keys"}}}}},"402":{"description":"Payment required — organization balance exhausted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"PAYMENT_REQUIRED","message":"Insufficient balance — this call costs $0.0150 and your organization has $0.0000 available. Top up at https://dashboard.gojinko.com/developers/billing/topup","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"404":{"description":"Unknown booking, or one this credential may not read.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"NOT_FOUND","message":"booking not found"}}}}},"409":{"description":"The commit cannot run as quoted; nothing was sent to the airline. `code` says which case, as a sibling of `error` — see `FlightServicingConflictCode` for the closed list and what each one asks of you. A booking that is no longer active is refused with `code: not_cancellable`, `reason_code: booking_not_active` and a platform sentence in `error.message`, when the caller may service it. A 409 whose `code` is outside that list is NOT a servicing refusal: it arrives as the plain error envelope, with no `code` beside it.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/FlightServicingConflictResponse"},{"$ref":"#/components/schemas/BookingSelectionConflict"}]},"example":{"error":{"code":"CONFLICT","message":"This refund cannot be carried out automatically and nothing was sent to the airline. The quote said `support_level: MANUAL_REQUIRED`; commit again with `manual_ok: true` to hand it to a Jinko agent, then poll the operation."},"code":"manual_required"}}}},"410":{"description":"The resource this request names no longer exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"GONE","message":"The resource no longer exists.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"A required field is missing or invalid.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"429":{"description":"Rate limit or quota exceeded","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"502":{"description":"The travel provider rejected the request, or an upstream call failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_REJECTED","message":"The request was declined for this offer. Try another offer.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"503":{"description":"No travel provider can serve the request right now","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_UNAVAILABLE","message":"The service is temporarily unavailable. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"504":{"description":"The travel provider did not answer in time","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_TIMEOUT","message":"The request timed out before a result was available. Retry the request.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}}}}},"/v1/flight_refund_status":{"post":{"tags":["Post-booking"],"summary":"Check how a flight refund or void ended","description":"Report where one refund or void has got to, named by the `operation` handle a commit (or POST /v1/refund_commit) returned. Read-only. `state` is the platform view — keep polling while it is `in_progress` or `attention_required`, both of which mean the operation is still alive; `attention_required` means a Jinko agent has it, and `park_deadline` says how long that lasts. `operation_kind` says which operation actually ran. `provider` is what the airline says, including what became of each ticket and EMD — an operation has done its work when every document it covers has reached a terminal state. `money` is where the refund itself has got to; the two move independently, so the tickets can read refunded while the money is still in flight. Do not tell a customer they have been refunded until `money.vehicle_state` says `paid`. Same auth modes as flight_refund_preview.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlightRefundStatusRequest"}}}},"responses":{"200":{"description":"Where the refund or void has got to","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlightRefundStatusResponse"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"Malformed JSON in request body.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"AUTH_REQUIRED","message":"Invalid or expired API key.","doc_url":"https://docs.gojinko.com/authentication/api-keys"}}}}},"402":{"description":"Payment required — organization balance exhausted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"PAYMENT_REQUIRED","message":"Insufficient balance — this call costs $0.0150 and your organization has $0.0000 available. Top up at https://dashboard.gojinko.com/developers/billing/topup","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"404":{"description":"Unknown operation or booking, or one this credential may not read.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"NOT_FOUND","message":"booking not found"}}}}},"409":{"description":"Several booked items match. Select a booked_item_id from items and repeat with booking_ref and booked_item_id.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"$ref":"#/components/schemas/BookingSelectionConflict"}]}}}},"410":{"description":"The resource this request names no longer exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"GONE","message":"The resource no longer exists.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"A required field is missing or invalid.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"429":{"description":"Rate limit or quota exceeded","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"502":{"description":"The travel provider rejected the request, or an upstream call failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_REJECTED","message":"The request was declined for this offer. Try another offer.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"503":{"description":"No travel provider can serve the request right now","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_UNAVAILABLE","message":"The service is temporarily unavailable. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"504":{"description":"The travel provider did not answer in time","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_TIMEOUT","message":"The request timed out before a result was available. Retry the request.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}}}}},"/v1/webhooks":{"post":{"tags":["Webhooks"],"summary":"Register a webhook endpoint for booking lifecycle events","description":"Subscribe a callback URL to `booking.processing`, `booking.completed`, `booking.failed`, `booking.partial`, `servicing.completed`, `servicing.exchange_confirmed`, `servicing.ticket_issued`, or `servicing.failed`. The response includes the HMAC signing secret ONCE. Deliveries are signed with `X-Jinko-Signature: sha256=<hex>` over `\"<X-Jinko-Timestamp>.<raw body>\"`.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateWebhookRequest"}}}},"responses":{"201":{"description":"Webhook registered (secret shown once)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookCreatedResponse"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"Malformed JSON in request body.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"AUTH_REQUIRED","message":"Invalid or expired API key.","doc_url":"https://docs.gojinko.com/authentication/api-keys"}}}}},"402":{"description":"Payment required — organization balance exhausted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"PAYMENT_REQUIRED","message":"Insufficient balance — this call costs $0.0150 and your organization has $0.0000 available. Top up at https://dashboard.gojinko.com/developers/billing/topup","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"NOT_FOUND","message":"Resource not found.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"409":{"description":"The operation conflicts with the current state of the resource","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"CONFLICT","message":"The operation conflicts with the current state of the resource.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"410":{"description":"The resource this request names no longer exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"GONE","message":"The resource no longer exists.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"A required field is missing or invalid.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"429":{"description":"Rate limit or quota exceeded","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"502":{"description":"The travel provider rejected the request, or an upstream call failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_REJECTED","message":"The request was declined for this offer. Try another offer.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"503":{"description":"No travel provider can serve the request right now","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_UNAVAILABLE","message":"The service is temporarily unavailable. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"504":{"description":"The travel provider did not answer in time","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_TIMEOUT","message":"The request timed out before a result was available. Retry the request.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}}}},"get":{"tags":["Webhooks"],"summary":"List the caller's webhook subscriptions","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"responses":{"200":{"description":"Webhook subscriptions (secret masked)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookListResponse"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"Malformed JSON in request body.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"AUTH_REQUIRED","message":"Invalid or expired API key.","doc_url":"https://docs.gojinko.com/authentication/api-keys"}}}}},"402":{"description":"Payment required — organization balance exhausted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"PAYMENT_REQUIRED","message":"Insufficient balance — this call costs $0.0150 and your organization has $0.0000 available. Top up at https://dashboard.gojinko.com/developers/billing/topup","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"NOT_FOUND","message":"Resource not found.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"409":{"description":"The operation conflicts with the current state of the resource","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"CONFLICT","message":"The operation conflicts with the current state of the resource.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"410":{"description":"The resource this request names no longer exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"GONE","message":"The resource no longer exists.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"A required field is missing or invalid.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"429":{"description":"Rate limit or quota exceeded","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"502":{"description":"The travel provider rejected the request, or an upstream call failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_REJECTED","message":"The request was declined for this offer. Try another offer.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"503":{"description":"No travel provider can serve the request right now","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_UNAVAILABLE","message":"The service is temporarily unavailable. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"504":{"description":"The travel provider did not answer in time","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_TIMEOUT","message":"The request timed out before a result was available. Retry the request.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}}}}},"/v1/webhooks/{id}":{"get":{"tags":["Webhooks"],"summary":"Get a webhook subscription and its recent deliveries","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"42"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Webhook subscription + recent deliveries","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookDetailResponse"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"Malformed JSON in request body.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"AUTH_REQUIRED","message":"Invalid or expired API key.","doc_url":"https://docs.gojinko.com/authentication/api-keys"}}}}},"402":{"description":"Payment required — organization balance exhausted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"PAYMENT_REQUIRED","message":"Insufficient balance — this call costs $0.0150 and your organization has $0.0000 available. Top up at https://dashboard.gojinko.com/developers/billing/topup","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"NOT_FOUND","message":"Resource not found.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"409":{"description":"The operation conflicts with the current state of the resource","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"CONFLICT","message":"The operation conflicts with the current state of the resource.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"410":{"description":"The resource this request names no longer exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"GONE","message":"The resource no longer exists.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"A required field is missing or invalid.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"429":{"description":"Rate limit or quota exceeded","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"502":{"description":"The travel provider rejected the request, or an upstream call failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_REJECTED","message":"The request was declined for this offer. Try another offer.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"503":{"description":"No travel provider can serve the request right now","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_UNAVAILABLE","message":"The service is temporarily unavailable. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"504":{"description":"The travel provider did not answer in time","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_TIMEOUT","message":"The request timed out before a result was available. Retry the request.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}}}},"delete":{"tags":["Webhooks"],"summary":"Delete a webhook subscription","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"42"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Webhook deleted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookDeleteResponse"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"Malformed JSON in request body.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"AUTH_REQUIRED","message":"Invalid or expired API key.","doc_url":"https://docs.gojinko.com/authentication/api-keys"}}}}},"402":{"description":"Payment required — organization balance exhausted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"PAYMENT_REQUIRED","message":"Insufficient balance — this call costs $0.0150 and your organization has $0.0000 available. Top up at https://dashboard.gojinko.com/developers/billing/topup","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"NOT_FOUND","message":"Resource not found.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"409":{"description":"The operation conflicts with the current state of the resource","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"CONFLICT","message":"The operation conflicts with the current state of the resource.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"410":{"description":"The resource this request names no longer exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"GONE","message":"The resource no longer exists.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"A required field is missing or invalid.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"429":{"description":"Rate limit or quota exceeded","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"502":{"description":"The travel provider rejected the request, or an upstream call failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_REJECTED","message":"The request was declined for this offer. Try another offer.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"503":{"description":"No travel provider can serve the request right now","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_UNAVAILABLE","message":"The service is temporarily unavailable. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"504":{"description":"The travel provider did not answer in time","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_TIMEOUT","message":"The request timed out before a result was available. Retry the request.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}}}}},"/v1/webhooks/{id}/test":{"post":{"tags":["Webhooks"],"summary":"Send a sample signed event to validate the endpoint","description":"Delivers a `livemode: false` sample event so you can verify signature + connectivity without a real booking.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"42"},"required":true,"name":"id","in":"path"}],"responses":{"202":{"description":"Test event enqueued","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookTestResponse"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"Malformed JSON in request body.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"AUTH_REQUIRED","message":"Invalid or expired API key.","doc_url":"https://docs.gojinko.com/authentication/api-keys"}}}}},"402":{"description":"Payment required — organization balance exhausted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"PAYMENT_REQUIRED","message":"Insufficient balance — this call costs $0.0150 and your organization has $0.0000 available. Top up at https://dashboard.gojinko.com/developers/billing/topup","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"NOT_FOUND","message":"Resource not found.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"409":{"description":"The operation conflicts with the current state of the resource","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"CONFLICT","message":"The operation conflicts with the current state of the resource.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"410":{"description":"The resource this request names no longer exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"GONE","message":"The resource no longer exists.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"A required field is missing or invalid.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"429":{"description":"Rate limit or quota exceeded","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"502":{"description":"The travel provider rejected the request, or an upstream call failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_REJECTED","message":"The request was declined for this offer. Try another offer.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"503":{"description":"No travel provider can serve the request right now","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_UNAVAILABLE","message":"The service is temporarily unavailable. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"504":{"description":"The travel provider did not answer in time","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_TIMEOUT","message":"The request timed out before a result was available. Retry the request.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}}}}},"/v1/webhooks/{id}/deliveries":{"get":{"tags":["Webhooks"],"summary":"List deliveries for a webhook subscription","description":"Lists event deliveries to a subscription you own, newest first. Each row records one event delivery and its attempts. `payload` is the exact envelope sent, or null after it is purged 30 days after the delivery's last attempt or replay. Use next_cursor to fetch the next page.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"42"},"required":true,"name":"id","in":"path"},{"schema":{"type":"integer","minimum":1,"maximum":100,"description":"Maximum rows to return. Defaults to 20 when omitted.","example":20},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","description":"Opaque pagination cursor."},"required":false,"name":"cursor","in":"query"},{"schema":{"type":"string","enum":["pending","delivered","failed","exhausted"]},"required":false,"name":"status","in":"query"},{"schema":{"type":"string","example":"booking.completed"},"required":false,"name":"event_type","in":"query"}],"responses":{"200":{"description":"Webhook deliveries, newest first","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookDeliveryListResponse"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"Malformed JSON in request body.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"AUTH_REQUIRED","message":"Invalid or expired API key.","doc_url":"https://docs.gojinko.com/authentication/api-keys"}}}}},"402":{"description":"Payment required — organization balance exhausted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"PAYMENT_REQUIRED","message":"Insufficient balance — this call costs $0.0150 and your organization has $0.0000 available. Top up at https://dashboard.gojinko.com/developers/billing/topup","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"NOT_FOUND","message":"Resource not found.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"409":{"description":"The operation conflicts with the current state of the resource","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"CONFLICT","message":"The operation conflicts with the current state of the resource.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"410":{"description":"The resource this request names no longer exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"GONE","message":"The resource no longer exists.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"A required field is missing or invalid.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"429":{"description":"Rate limit or quota exceeded","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"502":{"description":"The travel provider rejected the request, or an upstream call failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_REJECTED","message":"The request was declined for this offer. Try another offer.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"503":{"description":"No travel provider can serve the request right now","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_UNAVAILABLE","message":"The service is temporarily unavailable. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"504":{"description":"The travel provider did not answer in time","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_TIMEOUT","message":"The request timed out before a result was available. Retry the request.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}}}}},"/v1/webhooks/{id}/deliveries/{delivery_id}/replay":{"post":{"tags":["Webhooks"],"summary":"Replay a webhook delivery","description":"Replays a delivered or exhausted event delivery for a subscription you own. Sets its status to pending, resets attempt_count to 0, increments replay_count, and enqueues a fresh delivery of the same payload with a new signature timestamp. The replay keeps the same event_id, so your idempotency key still applies. Pending or failed deliveries are still being delivered or retried and cannot be replayed. Payloads are purged 30 days after the delivery's last attempt or replay; a delivery whose payload was purged can no longer be replayed.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"42"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","example":"981"},"required":true,"name":"delivery_id","in":"path"}],"responses":{"202":{"description":"Webhook delivery replay enqueued","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookReplayResponse"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"Malformed JSON in request body.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"AUTH_REQUIRED","message":"Invalid or expired API key.","doc_url":"https://docs.gojinko.com/authentication/api-keys"}}}}},"402":{"description":"Payment required — organization balance exhausted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"PAYMENT_REQUIRED","message":"Insufficient balance — this call costs $0.0150 and your organization has $0.0000 available. Top up at https://dashboard.gojinko.com/developers/billing/topup","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"NOT_FOUND","message":"Resource not found.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"409":{"description":"CONFLICT: delivery_in_progress while delivery or retries are pending, or payload_purged once the payload is purged, 30 days after the delivery's last attempt or replay. The replay reason is in code beside error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookReplayConflictResponse"}}}},"410":{"description":"The resource this request names no longer exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"GONE","message":"The resource no longer exists.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"BAD_REQUEST","message":"A required field is missing or invalid.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"429":{"description":"Rate limit or quota exceeded","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"502":{"description":"The travel provider rejected the request, or an upstream call failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_REJECTED","message":"The request was declined for this offer. Try another offer.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"503":{"description":"No travel provider can serve the request right now","headers":{"Retry-After":{"description":"Seconds to wait before retrying, forwarded verbatim from the upstream service. Absent when the upstream named no interval.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_UNAVAILABLE","message":"The service is temporarily unavailable. Retry after the Retry-After delay.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}},"504":{"description":"The travel provider did not answer in time","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"UPSTREAM_TIMEOUT","message":"The request timed out before a result was available. Retry the request.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}}}}}}}