{"openapi":"3.0.0","info":{"title":"Jinko Public API","version":"0.38.1","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. WorkOS-shaped 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."},"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\"."},"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}}},"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":"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"},"aircraft":{"type":"string","description":"IATA aircraft type code for the equipment scheduled on this segment.","example":"32Q"},"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. 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. A separately named `*_local` field is 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. 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. A separately named `*_local` field is 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. Always sent, including when it equals `airline` — so a value differing from `airline` is a codeshare the passenger must be told about, and equality is a positive statement, not a gap. Absent only when the provider disclosed no operating carrier at all.","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. A separately named `*_local` field is 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. A separately named `*_local` field is 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."}}},"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 cart’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."},"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"},"booking_reference":{"type":"string","description":"The supplier confirmation for this item, when available on a completed item. Absent on a failed item. The Jinko reference is the event-level `booking_ref`.","example":"XM9L2K"},"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 a booking_reference."},"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"},"item":{"type":"integer","description":"The booked item the operation acted on — `item_id` on get_booking.","example":9182},"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":"Failure code selecting the email copy; specific copy exists for price_changed and offer_not_available. Omitted when unclassified. May differ from data.failure_reason."},"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_not_available","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_not_available 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."},"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":{"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":"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."},"item":{"type":"integer","description":"Original booking item serviced; omitted when unknown."},"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."},"item":{"type":"integer","description":"Original booking item serviced; omitted when unknown."},"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."},"item":{"type":"integer","description":"Original booking item serviced; omitted when unknown."},"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":"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"},"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":"Stable per occurrence: `evt_<fulfillment_cart_id>_<event>` for a booking event, and `evt_<fulfillment_cart_id>_<event>_<occurrence_key>` for a servicing event, which can happen more than once on one booking (each cancellation has its own id). `<fulfillment_cart_id>` identifies the booking. Deliveries are deduplicated on it, and a retry or a replay repeats it — key your own idempotency off this. Treat it as opaque: do not parse it.","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"]},"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},"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,"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"]},"summary":{"type":"string","minLength":1},"context":{"$ref":"#/components/schemas/FlightPriceAdviceContext"},"price_assessment":{"oneOf":[{"type":"object","properties":{"status":{"type":"string","enum":["available"]},"reason_code":{"nullable":true},"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-v1-[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}},"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":{"nullable":true},"typical_price":{"nullable":true},"typical_range":{"nullable":true},"percentile":{"nullable":true},"difference_pct":{"nullable":true},"comparison_basis":{"type":"string","enum":["historical_comparable_low_fares"]},"evidence":{"nullable":true}},"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":{"nullable":true},"horizons":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"horizon_days":{"type":"number","enum":[1]},"status":{"type":"string","enum":["available"]},"reason_code":{"nullable":true},"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":{"nullable":true},"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":{"nullable":true},"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":{"nullable":true},"forecasted_price":{"type":"number","nullable":true,"not":{"type":"number"}},"probabilities":{"type":"object","properties":{"increase":{"nullable":true},"decrease":{"nullable":true},"unchanged":{"nullable":true}},"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":{"nullable":true},"forecasted_price":{"type":"number","nullable":true,"not":{"type":"number"}},"probabilities":{"type":"object","properties":{"increase":{"nullable":true},"decrease":{"nullable":true},"unchanged":{"nullable":true}},"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":{"nullable":true},"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":{"nullable":true},"forecasted_price":{"type":"number","nullable":true,"not":{"type":"number"}},"probabilities":{"type":"object","properties":{"increase":{"nullable":true},"decrease":{"nullable":true},"unchanged":{"nullable":true}},"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":{"nullable":true},"forecasted_price":{"type":"number","nullable":true,"not":{"type":"number"}},"probabilities":{"type":"object","properties":{"increase":{"nullable":true},"decrease":{"nullable":true},"unchanged":{"nullable":true}},"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"}],"discriminator":{"propertyName":"policy","mapping":{"expected_fare":"#/components/schemas/FlightPriceAdviceExpectedFareRecommendationBasis","modal_direction":"#/components/schemas/FlightPriceAdviceModalDirectionRecommendationBasis"}}},"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"},{"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":"Schema 3.2 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":["credential_format_invalid","payment_type_not_enabled","payment_credential_invalid","trip_owned_by_other_payment","idempotency_key_reused","attempt_in_progress","quote_expired","attempt_terminal","temporarily_unavailable","AUTH_REQUIRED","PAYMENT_REQUIRED","RATE_LIMITED","BAD_REQUEST","FORBIDDEN","NOT_FOUND","CONFLICT","GONE","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"},"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"]},"stops":{"type":"number","description":"Actual number of flight connections: 0 for nonstop, 1, or 2. Omit when unknown.","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":"DEV contract preview input. Structural trip type, optional actual connection count, and cabin values are validated; this preview does not imply statistics coverage."},"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":{"id":{"type":"string","description":"Opaque identifier of this cached itinerary. It is the offer token: pass it unchanged as `offer_token` to `POST /v1/flight_search` to price-check this itinerary. The response has no separate `offer_token` field.","example":"es-VVN8QUZ8MjAyNzA2MTU6QUY6Nzo6WXwyMDI3MDYyMjpBRjo2OjpZ"},"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":{"$ref":"#/components/schemas/Amount"},"cabin_class":{"type":"string","example":"economy"},"outbound":{"allOf":[{"$ref":"#/components/schemas/FlightLeg"},{"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/FlightLeg"},{"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":["id","origin","destination"]},"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":{"$ref":"#/components/schemas/Itinerary"},"flights":{"type":"array","items":{"$ref":"#/components/schemas/Itinerary"}}},"required":["flights"]},"FindDestinationResponse":{"type":"object","properties":{"destinations":{"type":"array","items":{"$ref":"#/components/schemas/DestinationSuggestion"}},"total":{"type":"number","example":12},"next_page_token":{"type":"string","example":"eyJvZmZzZXQiOjIwfQ=="}},"required":["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"],"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","example":false},"adults":{"type":"integer","minimum":0,"example":1},"children":{"type":"integer","minimum":0},"infants_in_lap":{"type":"integer","minimum":0},"infants_in_seat":{"type":"integer","minimum":0},"youth":{"type":"integer","minimum":0},"student":{"type":"integer","minimum":0},"ages":{"type":"array","items":{"type":"integer","minimum":0}},"residency_country":{"type":"string","minLength":2,"maxLength":2},"max_total":{"type":"number","minimum":0,"exclusiveMinimum":true,"example":800},"currency":{"type":"string","example":"USD"},"locale":{"type":"string","example":"en-US"},"limit":{"type":"integer","minimum":1,"maximum":100,"default":20,"description":"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. Use `find_dates` for one cheapest itinerary per date pair spread across the requested window."},"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","adults":1,"max_total":800,"currency":"USD","flights_per_destination":1}},"FlightCalendarResponse":{"type":"object","properties":{"itineraries":{"type":"array","items":{"$ref":"#/components/schemas/Itinerary"}},"next_page_token":{"type":"string","example":"eyJvZmZzZXQiOjIwfQ=="}},"required":["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","example":false},"adults":{"type":"integer","minimum":0,"example":1},"children":{"type":"integer","minimum":0},"infants_in_lap":{"type":"integer","minimum":0},"infants_in_seat":{"type":"integer","minimum":0},"youth":{"type":"integer","minimum":0},"student":{"type":"integer","minimum":0},"ages":{"type":"array","items":{"type":"integer","minimum":0}},"residency_country":{"type":"string","minLength":2,"maxLength":2},"max_total":{"type":"number","minimum":0,"exclusiveMinimum":true,"example":800},"currency":{"type":"string","example":"USD"},"locale":{"type":"string","example":"en-US"},"limit":{"type":"integer","minimum":1,"maximum":100,"default":20,"description":"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. Use `find_dates` for one cheapest itinerary per date pair spread across the requested window."},"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","adults":1,"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."},"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","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":"The carrier’s own prose. Present on fares with NO free allowance too, so its presence is not evidence a bag is included.","example":"1 checked bag up to 23 kg"}},"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."},"Fare":{"type":"object","properties":{"trip_item_token":{"type":"string","example":"tit_5f8c1d3e9a"},"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 30-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:03: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","example":"Main Cabin"},"total_price":{"$ref":"#/components/schemas/Money"},"price_per_person":{"$ref":"#/components/schemas/Money"},"included_baggage":{"$ref":"#/components/schemas/CheckedBaggage"},"carry_on_baggage":{"$ref":"#/components/schemas/CheckedBaggage"},"is_changeable":{"type":"boolean","description":"Whether the fare allows a voluntary change — the same predicate as the `changeable_only` filter. `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 before departure, with or without a fee — the same predicate as the `refundable_only` filter. `false` is an answer: the fare rules did not confirm a refund is allowed.","example":false},"checked_bag_included":{"type":"boolean","description":"Whether the price already includes at least one free checked bag — the same predicate as the `checked_bag_included` filter: `included_baggage.pieces` is 1 or more. `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","example":"off_3a9f1b27c4"},"provider":{"type":"string","example":"provider_a"},"provider_name":{"type":"string","example":"Example provider"},"origin":{"type":"object","properties":{"code":{"type":"string"},"name":{"type":"string"}},"example":{"code":"JFK","name":"New York John F. Kennedy"}},"destination":{"type":"object","properties":{"code":{"type":"string"},"name":{"type":"string"}},"example":{"code":"LAX","name":"Los Angeles Intl"}},"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"]},"FlightProviderStatus":{"type":"object","properties":{"provider":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9._-]*$","description":"Stable provider identifier using ASCII letters, digits, `.`, `_`, or `-`. Operator labels, PCC descriptions, credentials, and raw provider error details are excluded.","example":"provider_a"},"reason":{"type":"object","properties":{"code":{"type":"string","description":"Open provider or connector error code."},"message":{"type":"string","maxLength":300}},"required":["code","message"],"description":"Present only when status is error."},"status":{"type":"string","enum":["success","empty","error"],"description":"Provider outcome: `success` returned eligible offers, `empty` completed successfully with none, and `error` could not complete. Empty is a successful provider response, not a failure.","example":"success"},"offer_count":{"type":"integer","minimum":0,"description":"Eligible offers returned by this provider before the global result limit is applied. This can exceed the number of that provider’s offers visible in `offers`.","example":12}},"required":["provider","status","offer_count"]},"FilterEnforcement":{"type":"object","properties":{"name":{"type":"string","description":"The filter name reported in `applied_filters`.","example":"refundable_only"},"enforced_by":{"type":"string","enum":["provider","post_filter"]},"exhaustive":{"type":"boolean","description":"Present only on `post_filter` entries. True when filtering the returned offers cannot have missed a matching one. Today that is only `max_price`, and only when the returned offers went past the cap: providers return the cheapest offers first, so every cheaper one is already here. False means a wider search may find more."},"note":{"type":"string","description":"A short human-readable explanation, when available."}},"required":["name","enforced_by"]},"UnappliedFilter":{"type":"object","properties":{"name":{"type":"string","description":"The request field that was not enforced. Request field names, with one exception: the alternate-airport lists are reported per side as `origin` and `destination`, not under the field name they were sent with. A list sent against a city anchor is reported here for that reason.","example":"aircraft_types"},"reason":{"type":"string","description":"Why it could not be enforced.","example":"not supported by the provider that returned these fares"}},"required":["name","reason"]},"FlightSearchResponse":{"type":"object","properties":{"offers":{"type":"array","items":{"$ref":"#/components/schemas/FlightOffer"}},"provider_statuses":{"type":"array","items":{"$ref":"#/components/schemas/FlightProviderStatus"},"description":"Completeness of the attempted provider set, with exactly one entry per provider in deterministic order. Present when provider visibility is available; otherwise absent. Empty is a successful provider response; an all-`empty` array is still a successful search with no offers."},"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},"applied_filters":{"type":"array","items":{"type":"string"},"description":"The requested filters these results DO honor. Empty when the request carried no filters. Entries are request field names, with one exception: `origin_alternate_airports` / `destination_alternate_airports` are reported per side as `origin` and `destination`.","example":["max_stops","checked_bag_included"]},"filter_enforcement":{"type":"array","items":{"$ref":"#/components/schemas/FilterEnforcement"},"description":"Who enforced each filter in `applied_filters`, in the same order. `provider`: the filter was part of the provider search, so the provider only returned matching itineraries. `post_filter`: the platform removed non-matching offers from what the provider returned; when `exhaustive` is false, matching itineraries the provider did not return may exist, so a wider search could find more. Absent when the platform did not report it; never invented.","example":[{"name":"refundable_only","enforced_by":"provider"},{"name":"max_price","enforced_by":"post_filter","exhaustive":true}]},"include_carriers_widened":{"type":"boolean","description":"True when a search with `include_carriers` matched no itineraries and was automatically re-run without that filter. `include_carriers` appears in `unapplied_filters` with the reason, and itineraries containing a segment marketed by a requested carrier come first. Absent otherwise.","example":true},"unapplied_filters":{"type":"array","items":{"$ref":"#/components/schemas/UnappliedFilter"},"description":"The requested filters these results do NOT honor, each with a reason. A filter listed here was not applied to the offers above — post-filter them yourself, or tell the user the constraint could not be met.","example":[]},"result":{"type":"string","enum":["no_matches","provider_error"],"description":"Search mode only. `no_matches` means every provider answered but nothing matched; `applied_filters` / `unapplied_filters` are then empty. `provider_error` means at least one provider failed and nothing was returned: retry rather than change the filters. `offers` is empty for either result. Absent when offers were found."},"instruction":{"type":"string","description":"Present with `result: no_matches` or `result: provider_error`: what to tell the user or do next.","example":"Search completed successfully, but no offers matched the current filters. Ask the user whether they want to adjust the filters before searching again."}},"required":["offers","applied_filters","unapplied_filters"]},"FlightSearchErrorResponse":{"type":"object","properties":{"error":{"type":"object","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","quote_expired","attempt_terminal","temporarily_unavailable","AUTH_REQUIRED","PAYMENT_REQUIRED","RATE_LIMITED","BAD_REQUEST","FORBIDDEN","NOT_FOUND","CONFLICT","GONE","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"},"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"]},"provider_statuses":{"type":"array","items":{"$ref":"#/components/schemas/FlightProviderStatus"},"description":"Provider completeness captured before a flight-search failure. Exactly one entry per attempted provider in deterministic order. Counts are measured before the global result limit. Optional reason is present only when status is error."}},"required":["error"]},"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.","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.","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}$"},"return_date":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"trip_type":{"type":"string","enum":["oneway","roundtrip"]},"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 explicit cabin is reported in `applied_filters` with `enforced_by: post_filter` and `exhaustive: false`; an omitted or `null` cabin is not reported as applied.","example":"economy"},"direct_only":{"type":"boolean","description":"DEPRECATED — send `max_stops: 0` instead. Still accepted: `true` is folded into `max_stops: 0` before the search runs and only `max_stops` reaches the platform, so `applied_filters` / `unapplied_filters` always name `max_stops`, never `direct_only`. 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. Sabre-only effect; TravelFusion behavior is unchanged.","example":true},"max_price":{"type":"number","minimum":0,"exclusiveMinimum":true,"description":"Drop fares whose total price (all passengers) exceeds this cap, in `currency`, major units. When the platform cannot convert to `currency` it reports `max_price` in `unapplied_filters` instead of guessing.","example":800},"include_carriers":{"type":"array","items":{"type":"string","pattern":"^[A-Za-z0-9]{2}$"},"description":"Keep only itineraries marketed by these IATA carriers. Every segment must be marketed by a listed carrier (codeshares count by marketing carrier); if no itineraries match, the search is automatically re-run without this filter as described by `include_carriers_widened`.","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`.","example":60},"connection_time_max_minutes":{"type":"integer","minimum":0,"description":"Longest acceptable layover, in minutes, applied to every connection of every leg.","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. Each returned fare carries the resolved flag as `is_refundable`.","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. Each returned fare carries the resolved flag as `is_changeable`.","example":true},"checked_bag_included":{"type":"boolean","description":"Keep only fares whose price already includes a checked bag. Each returned fare carries the resolved flag as `checked_bag_included`.","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 when at least one connection is one of them. Non-stop itineraries have no connection to check and are kept.","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.","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`. TAKES EFFECT ONLY WITH AN AIRPORT ANCHOR. A city anchor — `origin_type: \"city\"`, or an `origin` code the platform resolves to a city — is accepted and searched as the city it is, but this list is then ignored and comes back in `unapplied_filters` with the reason, since a city already searches its whole metro area. On a round trip the platform mirrors the list onto the return leg, so it also covers where the return lands. Reported as `origin` in `applied_filters` / `unapplied_filters` — one entry per side, never under this field name. Sabre honours the list natively; TravelFusion cannot (single-station location) and reports it unapplied.","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`. TAKES EFFECT ONLY WITH AN AIRPORT ANCHOR. A city anchor — `destination_type: \"city\"`, or a `destination` code the platform resolves to a city — is accepted and searched as the city it is, but this list is then ignored and comes back in `unapplied_filters` with the reason, since a city already searches its whole metro area. On a round trip the platform mirrors the list onto the return leg, so it also covers where the return departs from. Reported as `destination` in `applied_filters` / `unapplied_filters` — one entry per side, never under this field name. Sabre honours the list natively; TravelFusion cannot (single-station location) and reports it unapplied.","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.","example":true},"same_turnaround_airport_only":{"type":"boolean","description":"Keep only round trips whose return departs from the airport the outbound arrived at.","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 its alternatives). 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+)."},"children":{"type":"integer","minimum":0,"description":"Child travelers (2–11)."},"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"},"locale":{"type":"string"},"offer_token":{"type":"string"},"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"}},"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"}}},"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":{"$ref":"#/components/schemas/MonitoredMoney"},"outbound_carrier":{"$ref":"#/components/schemas/MonitoredCarrier"},"inbound_carrier":{"$ref":"#/components/schemas/MonitoredCarrier"},"outbound_stops":{"type":"number","example":0},"inbound_stops":{"type":"number","example":1},"outbound_duration_minutes":{"type":"number","example":470},"inbound_duration_minutes":{"type":"number","example":520},"outbound_departure_local":{"type":"string","description":"Local wall-clock time at the airport, with NO UTC offset (e.g. `2026-07-15T10:15:00`). Unlike the datetimes on a flight-search leg, this is NOT an instant: it cannot be compared across timezones, and parsing it as UTC is wrong by the airport’s offset. Show it as written, or re-shop the offer for a datetime you can compute with.","example":"2026-07-15T10:15:00"},"outbound_arrival_local":{"type":"string","description":"Local wall-clock time at the airport, with NO UTC offset (e.g. `2026-07-15T10:15:00`). Unlike the datetimes on a flight-search leg, this is NOT an instant: it cannot be compared across timezones, and parsing it as UTC is wrong by the airport’s offset. Show it as written, or re-shop the offer for a datetime you can compute with.","example":"2026-07-15T13:05:00"},"inbound_departure_local":{"type":"string","description":"Local wall-clock time at the airport, with NO UTC offset (e.g. `2026-07-15T10:15:00`). Unlike the datetimes on a flight-search leg, this is NOT an instant: it cannot be compared across timezones, and parsing it as UTC is wrong by the airport’s offset. Show it as written, or re-shop the offer for a datetime you can compute with.","example":"2026-07-22T17:40:00"},"inbound_arrival_local":{"type":"string","description":"Local wall-clock time at the airport, with NO UTC offset (e.g. `2026-07-15T10:15:00`). Unlike the datetimes on a flight-search leg, this is NOT an instant: it cannot be compared across timezones, and parsing it as UTC is wrong by the airport’s offset. Show it as written, or re-shop the offer for a datetime you can compute with.","example":"2026-07-23T07:25:00"},"cabin_class":{"type":"string","example":"economy"},"fare_brand":{"type":"string","example":"Main"}}},"PriceMonitoringResponse":{"type":"object","properties":{"status":{"type":"string","example":"ok"},"monitored_at":{"type":"string","example":"2026-06-01T12:34:56Z"},"flight":{"$ref":"#/components/schemas/MonitoredFlight"},"request_echo":{"nullable":true}},"required":["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"},"max_price":{"type":"number","minimum":0,"exclusiveMinimum":true},"include_carriers":{"type":"array","items":{"type":"string"}},"exclude_carriers":{"type":"array","items":{"type":"string"}},"adults":{"type":"integer","minimum":1,"default":1,"description":"Adult travelers (12+)."},"children":{"type":"integer","minimum":0,"description":"Child travelers (2–11)."},"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"},"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}},"Occupancy":{"type":"object","properties":{"adults":{"type":"integer","minimum":1},"children_ages":{"type":"array","items":{"type":"integer"}}},"required":["adults"],"description":"The party one room is requested or priced for: the number of adults and the age of each child."},"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}}},"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."}]}}},"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, as the supplier states it, when this step is a percentage. 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}}},"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":{"type":"string"}},"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."},"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":"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"},"trip_item_token":{"type":"string","description":"The flight handle `trip(add_item)` took, echoed verbatim — the itinerary id and the fare joined by a colon. Flight items only, and absent when the fare half was never resolved (a caller-supplied snapshot skips the resolver); read the 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"},"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."}]}}},"TripTraveler":{"type":"object","properties":{"pax_ref_id":{"type":"string","description":"The reference to send as `pax_ref_id` when selecting a per-passenger ancillary: `pax_<N>`, the traveler's 1-based position in `travelers`.","example":"pax_1"},"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"},"passenger_type":{"type":"string","example":"ADULT"}},"description":"A traveler stored on the trip. The optional identity extras that were sent on write (frequent_flyer, known_traveler_number, redress_number, passport fields) are echoed back alongside these when present."},"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. Item-level progress after payment is in `fulfillment.status` (GET /v1/trip/{trip_id}); this field summarises it. `draft`: being built or priced, nothing paid. `fulfilling`: payment was submitted and booking started; read `fulfillment.status` for progress. Then exactly one outcome: `fulfilled` (`fulfillment.status` `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). Required for flights; optional for hotel-only trips."},"gender":{"type":"string","enum":["MALE","FEMALE"],"description":"Required for flights; optional for hotel-only trips."},"passenger_type":{"type":"string","enum":["ADULT","CHILD","INFANT"]},"nationality":{"type":"string"},"passport_number":{"type":"string"},"passport_expiry":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"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","passenger_type"]},"Contact":{"type":"object","properties":{"email":{"type":"string","format":"email","description":"Contact email for booking confirmations."},"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 cart, but required before booking."},"TripRequest":{"type":"object","properties":{"trip_id":{"type":"string"},"add_item":{"type":"object","properties":{"trip_item_token":{"type":"string"}},"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":"tok_flt_9f3b2a17"}}},"PaymentAttemptOutcome":{"type":"object","properties":{"payment_attempt_id":{"type":"string"},"trip_id":{"type":"string"},"quoted_cart_id":{"type":"integer"},"fulfillment_cart_id":{"type":"integer"},"payment_status":{"type":"string","enum":["not_started","pending","requires_authentication","authorized","captured","declined","cancelling","cancelled","expired","failed"]},"booking_status":{"type":"string"},"failure_reason":{"type":"string","nullable":true,"description":"Why the booking failed, when it failed after payment and the cause is known: `offer_not_available`, `price_changed`, `booking_not_created` (the supplier holds no booking; nothing was booked and no money was taken), `booking_cancelled_at_provider`. Null otherwise.","example":"booking_not_created"},"items":{"type":"array","items":{"type":"object","properties":{"item_id":{"type":"integer"},"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_not_available`, `price_changed`, `booking_not_created` (the supplier holds no booking; nothing was booked and no money was taken), `booking_cancelled_at_provider`. Null otherwise.","example":"booking_not_created"}},"required":["item_id","outcome"]}},"booking_ref":{"type":"string"},"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","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"]},"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"},"authentication":{"type":"object","properties":{"url":{"type":"string"},"expires_at":{"type":"string","format":"date-time"}},"required":["url","expires_at"]},"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","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","example":"anc_bag_20kg"},"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. TravelFusion option groups 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"},"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","example":"anc_bag_20kg"},"pax_ref_id":{"type":"string","description":"The passenger the selection applies to; absent on a booking-level ancillary.","example":"pax_1"},"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. PENDING means the airline outcome is unresolved: booking may still be in progress, or reconciliation may be needed after booking. After booking, the seat price remains captured and is not refunded while PENDING. CONFIRMED means the seat is assigned and any required seat payment is confirmed. FAILED means the airline could not assign the seat or complete its purchase; the seat price is captured then refunded, so it is not kept."},"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":{"type":"string"},"description":"How the rate can be settled.","example":["NUITEE_PAY"]},"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":"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"},"trip_item_token":{"type":"string","description":"The flight handle `trip(add_item)` took, echoed verbatim — the itinerary id and the fare joined by a colon. Flight items only, and absent when the fare half was never resolved (a caller-supplied snapshot skips the resolver); read the 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"},"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"},"booking_reference":{"type":"string","description":"The supplier's confirmation reference for this item (airline record locator, hotel confirmation number). This is NOT the handle `POST /v1/get_booking` takes — use the trip-level `booking_ref` for that.","example":"8Q69NYQTT"},"pnr":{"type":"string","description":"The supplier's record locator or confirmation number for this item, when the provider returned one (airline PNR for flights, hotel confirmation for hotels, reservation number for car/ground).","example":"XM9L2K"},"provider_status":{"type":"string","description":"The provider outcome for this item. An entry can also represent a failed attempt.","example":"CONFIRMED"},"offer_id":{"type":"string","description":"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"},"trip_item_token":{"type":"string","description":"The flight handle `trip(add_item)` took, echoed verbatim — the itinerary id and the fare joined by a colon. Flight items only, and absent when the fare half was never resolved (a caller-supplied snapshot skips the resolver); read the 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"}}},"GetTripResponse":{"type":"object","properties":{"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. Item-level progress after payment is in `fulfillment.status` (GET /v1/trip/{trip_id}); this field summarises it. `draft`: being built or priced, nothing paid. `fulfilling`: payment was submitted and booking started; read `fulfillment.status` for progress. Then exactly one outcome: `fulfilled` (`fulfillment.status` `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":{"quoted_cart_id":{"type":"number"},"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."},"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 (also available as /v1/book) starts it and waits for success or failure. Reading the trip does not trigger pricing. A failed quote has no payable price.","example":{"quoted_cart_id":2097152,"status":"quoted","expires_at":"2026-06-01T13:04:56Z"}},"fulfillment":{"type":"object","properties":{"fulfillment_cart_id":{"type":"number"},"status":{"type":"string","description":"Progress of the booking that started when payment was submitted; the trip-level `status` 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_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 a fulfillment cart exists for the trip — i.e. once fulfillment has been scheduled, which can be before payment completes (a hosted checkout still in progress, or an agent submit that fell back to `checkout_url`, leaves the cart `awaiting_payment`). Presence does not mean paid: read `fulfillment.status`. 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 provider booking reference or provider status, including items that reached a provider and failed. Read `provider_status` for the outcome. 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"},"quoted_cart_id":{"type":"number"},"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"},"quoted_item_id":{"type":"number","example":8810231},"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"},"category":{"type":"string","description":"The server resolves the category from the offer."},"pax_ref_id":{"type":"string","description":"Use the traveler's `pax_ref_id` 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_bag_20kg","pax_ref_id":"pax_1","quantity":1},{"offer_id":"seat_6020_0","pax_ref_id":"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 cart 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 cart 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 cart total — scope the Shared Payment Token to at most `value` (minor units) in `currency`, the cart 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 cart currency: `USD` or `EUR`, depending on the partner's Stripe account binding. **Absent when the partner does not accept SPT for this cart 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":{"quoted_cart_id":{"type":"integer"},"accepted_payment_types":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["spt","card"]},"card_kinds":{"type":"array","items":{"type":"string","enum":["customer_card","vcc"]}},"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","example":"cs_1048576"},"checkout_url":{"type":"string","example":"https://app.gojinko.com/checkout?t=<signed token>"},"status":{"type":"string","description":"Checkout session state — \"ready\", \"pending\" or \"failed\".","example":"ready"},"expires_at":{"type":"string","description":"When the quote behind this checkout stops being payable (RFC 3339). Roughly fifteen minutes from the call — 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 Stripe-hosted checkout session is 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":["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"},"quoted_cart_id":{"type":"number","description":"The quote that was refused — the same `quoted_cart_id` the trip and checkout responses carry, so a caller holding several can tell which one this refusal is about.","example":2097152},"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"]},"CheckoutRequest":{"type":"object","properties":{"trip_id":{"type":"string","example":"trip_1048576"},"intent":{"$ref":"#/components/schemas/IntentInput"}},"required":["trip_id"]},"AgentPaymentSubmitResponse":{"type":"object","properties":{"fulfillment_cart_id":{"type":"integer"},"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.","example":"JNK-XEVGW5"},"checkout_url":{"type":"string","description":"Hosted-checkout fallback, present only on a 3DS step-up / decline."},"recap_token":{"type":"string"}}},"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","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"]},"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"},"quoted_cart_id":{"type":"number","description":"The quote that was refused — the same `quoted_cart_id` the trip and checkout responses carry, so a caller holding several can tell which one this refusal is about.","example":2097152},"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"},"quoted_cart_id":{"type":"integer"},"payment":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","enum":["spt"]},"shared_payment_token":{"type":"string","minLength":1}},"required":["type","shared_payment_token"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","enum":["card"]},"card_kind":{"type":"string","enum":["customer_card","vcc"]},"number":{"type":"string","description":"12–19 digits after removing spaces, with a valid Luhn checksum. Forwarded unchanged."},"exp_month":{"type":"integer","minimum":1,"maximum":12},"exp_year":{"type":"integer","description":"Four-digit expiry year, at least the current UTC year.","minimum":1000,"maximum":9999},"cvc":{"type":"string","pattern":"^[0-9]{3,4}$(?![\\s\\S])"},"billing_details":{"type":"object","properties":{"name":{"type":"string"},"email":{"type":"string","format":"email"},"phone":{"type":"string"},"address":{"type":"object","properties":{"line1":{"type":"string"},"line2":{"type":"string"},"city":{"type":"string"},"state":{"type":"string"},"postal_code":{"type":"string"},"country":{"type":"string"}},"additionalProperties":false}},"additionalProperties":false}},"required":["type","card_kind","number","exp_month","exp_year","cvc"],"additionalProperties":false}]}},"required":["trip_id","quoted_cart_id","payment"],"additionalProperties":false},"AgentPaymentSubmitRequest":{"type":"object","properties":{"trip_id":{"type":"string","example":"trip_1048576"},"shared_payment_token":{"type":"string","example":"spt_1Nq8L2eZvKYlo2C0"},"intent":{"$ref":"#/components/schemas/IntentInput"}},"required":["trip_id","shared_payment_token"]},"HotelIntentFacts":{"type":"object","properties":{"source":{"type":"string","enum":["hotel_catalog"],"description":"The source of the normalized facts supplied to intent ranking."},"name":{"type":"string","description":"Normalized catalog hotel name supplied to intent ranking."},"description":{"type":"string","description":"Normalized catalog description supplied to intent ranking."},"facility_labels":{"type":"array","items":{"type":"string"},"description":"Normalized catalog facility labels supplied to intent ranking."},"name_truncated":{"type":"boolean","description":"True when the catalog name was shortened before intent ranking."},"description_truncated":{"type":"boolean","description":"True when the catalog description was shortened before intent ranking."},"facility_labels_truncated":{"type":"boolean","description":"True when the facility label list was shortened before intent ranking."},"omitted_facts_status":{"type":"string","enum":["unknown"],"description":"Present as `unknown` when the catalog cannot determine omitted facts."}},"required":["source","name","description","facility_labels"],"description":"Normalized hotel-catalog facts actually supplied to intent ranking for this hotel. Present only after successful intent scoring. These are scoring inputs, not model rationale or proof that the hotel satisfies each requested feature."},"HotelBed":{"type":"object","properties":{"bed_type":{"type":"string"},"count":{"type":"integer","minimum":1}},"required":["bed_type","count"]},"HotelRatePromotion":{"type":"object","properties":{"code":{"type":"string"},"name":{"type":"string"},"remark":{"type":"string"}}},"HotelRate":{"type":"object","properties":{"offer_id":{"type":"string","description":"Legacy rooms[].rates[] compatibility token. New integrations use the hotel-level `offers[].offer_id`; add that canonical offer token 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":{"type":"string"},"description":"How the rate can be settled.","example":["NUITEE_PAY"]},"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. Multi-room searches return this as an empty array.","deprecated":true}}},"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."},"rate_id":{"type":"string"},"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":{"type":"string"}},"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":"The single token accepted by `trip(add_item)`. Add it once even when `room_rates` contains several occupancies."},"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":{"type":"string"},"description":"How the rate can be settled.","example":["NUITEE_PAY"]},"promotions":{"type":"array","items":{"$ref":"#/components/schemas/HotelRatePromotion"}},"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","room_rates"]},"Hotel":{"type":"object","properties":{"hotel_id":{"type":"string","example":"lp1d2c3"},"hotel_ref":{"type":"string","description":"Provider-qualified property reference. Use this for hotel_details because native hotel_id values can overlap across providers.","example":"nuitee:lp1d2c3"},"provider":{"type":"string","example":"nuitee"},"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","example":1243},"main_photo":{"type":"string","description":"Full-size hero image URL.","example":"https://static.cupid.travel/hotels/main.jpg"},"thumbnail":{"type":"string","description":"Small preview image URL.","example":"https://static.cupid.travel/hotels/thumb.jpg"},"latitude":{"type":"number","example":48.8566},"longitude":{"type":"number","example":2.3522},"distance_km":{"type":"number","minimum":0,"description":"Distance from the internal search center in kilometers."},"recommendation_score":{"type":"number","minimum":0,"maximum":1,"description":"Relative ranking score combining intent match and distance; not a calibrated recommendation probability."},"intent_match_probability":{"type":"number","minimum":0,"maximum":1,"description":"Model probability for the matched evidence label; absent for distance-only ranking."},"intent_confidence":{"type":"number","minimum":0,"maximum":1,"description":"Confidence in the selected evidence label, including unknown or contradicted; not the recommendation score."},"intent_evidence_status":{"type":"string","enum":["matched","contradicted","unknown"]},"intent_facts":{"$ref":"#/components/schemas/HotelIntentFacts"},"rooms":{"type":"array","items":{"$ref":"#/components/schemas/HotelRoom"}},"offers":{"type":"array","items":{"$ref":"#/components/schemas/HotelOffer"},"description":"Bookable whole-stay offers. Choose one `offer_id` and add it 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`."},"HotelSearchScope":{"type":"object","properties":{"plan_kind":{"type":"string","enum":["specific_hotels","geo_snapshot","ranked_ids","multi_provider"]},"provider":{"type":"string"},"coverage":{"type":"string","enum":["requested_hotels","snapshot_unknown","candidate_pool","provider_scopes"]},"snapshot_size":{"type":"integer","minimum":0},"candidate_count":{"type":"integer","minimum":0},"coverage_unknown":{"type":"boolean"},"truncated":{"type":"boolean"},"providers":{"type":"array","items":{"type":"object","properties":{"provider":{"type":"string"},"plan_kind":{"type":"string","enum":["specific_hotels","geo_snapshot","ranked_ids"]},"coverage":{"type":"string","enum":["requested_hotels","snapshot_unknown","candidate_pool"]},"snapshot_size":{"type":"integer","minimum":0},"candidate_count":{"type":"integer","minimum":0},"coverage_unknown":{"type":"boolean"},"truncated":{"type":"boolean"}},"required":["provider","plan_kind","coverage","coverage_unknown"]}}},"required":["plan_kind","provider","coverage","coverage_unknown"],"description":"Provider and finite result scope frozen by this search session. Optional counts describe the fixed snapshot or ranked candidate pool."},"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 is display/cart association only."},"BudgetFilterMeta":{"type":"object","properties":{"max_budget_per_night":{"type":"number","description":"Deprecated: use `max_budget_per_night_money` instead. The budget the request set, per night per room, in MAJOR units.","example":150,"deprecated":true},"currency":{"type":"string","description":"Deprecated: use `max_budget_per_night_money` instead. The currency the budget was compared in. Absent when the request named none.","example":"EUR","deprecated":true},"max_budget_per_night_money":{"allOf":[{"$ref":"#/components/schemas/MoneyValue"},{"description":"The budget the request set, per night per room. Absent when the request named no currency: the budget was then compared in the supplier’s default currency, which the response does not name."}]},"nights":{"type":"integer","example":3},"pages_fetched":{"type":"integer","example":2},"catalog_exhausted":{"type":"boolean","description":"True when the destination ran out of candidate hotels before the fill target."},"hotels_dropped":{"type":"integer","example":34}},"description":"How the max_budget_per_night filter shaped the response (present only when set)."},"DestinationResolution":{"type":"object","properties":{"shape":{"type":"string","enum":["city","coordinates","place_id","query","hotel_name","hotel_ids"],"description":"Which destination selector the request used.","example":"city"},"match":{"type":"string","enum":["exact","fuzzy","provider_matched","fallback_string"],"description":"`exact` — the destination was found verbatim: a city catalog group, 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; on hotel_name, a lookup scoring < 1.0 — the response also carries a warning naming the substitution. `provider_matched` — a `query`/`place_id` 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, place_id, 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":"coordinates shape: the radius actually applied, absent when the supplier default was used and is unknown to the platform."},"catalog_hotel_count":{"type":"integer","example":41},"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},"place_id":{"type":"string"},"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`, or `resolved_city` differs from `requested_city`. `exact` and `provider_matched` need no header line of their own."},"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"],"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":"Stable opaque identifier for the fixed search session.","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_scope":{"$ref":"#/components/schemas/HotelSearchScope"},"search_context":{"$ref":"#/components/schemas/HotelSearchContext"},"warnings":{"type":"array","items":{"type":"string"},"description":"Non-fatal advisories, e.g. budget-filter outcomes or ignored filters."},"budget_filter":{"$ref":"#/components/schemas/BudgetFilterMeta"},"internal_search":{"type":"object","properties":{"applied":{"type":"boolean"},"reason":{"type":"string"},"strategy":{"type":"string"},"candidates_recalled":{"type":"integer"},"hotels_queried":{"type":"integer"},"stop_reason":{"type":"string"},"partial":{"type":"boolean"},"candidate_pool_remaining":{"type":"integer"},"catalog_has_more":{"type":"boolean"},"ranking_model":{"type":"string"},"ranking_requests":{"type":"integer"}},"required":["applied","partial","candidate_pool_remaining","catalog_has_more"],"description":"Present when internal search was requested. applied=false gives the legacy fallback reason. Candidate pool remaining and catalog_has_more describe unsearched inventory; neither is a continuation cursor or proof of no availability."},"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"}},"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"},"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_id to a trip once."},"adults":{"type":"integer","minimum":1},"children":{"type":"array","items":{"type":"integer"}},"rooms":{"type":"integer","minimum":1},"ai_search":{"type":"boolean","description":"Development preview: opt in to internal catalog recall, intent.user_intent ranking and batched availability checks. Default false preserves the existing flow. Supports Nuitee single- and multi-room destination searches by coordinates or city + country, with offset zero."},"currency":{"type":"string"},"guest_nationality":{"type":"string","minLength":2,"maxLength":2},"trip_id":{"type":"string","minLength":1,"description":"Optional trip/cart association echoed in the frozen search context for display continuity. It is not authentication or authorization evidence."},"min_rating":{"type":"number"},"min_star_rating":{"type":"integer","minimum":1,"maximum":5},"star_rating":{"type":"integer","minimum":1,"maximum":5},"max_star_rating":{"type":"integer","minimum":1,"maximum":5},"min_reviews":{"type":"integer"},"max_results":{"type":"integer","description":"Desired result count for the initial search. A fixed ranked-ID search returns every priced hotel from its current supplier batch, so that page can exceed this value; use next_handle for the next batch. Default 50."},"offset":{"type":"integer","minimum":0,"description":"Pagination offset — skip this many hotels at the upstream search. Default 0."},"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. Legacy destination searches may scan deeper when too few hotels fit. A fixed ranked-ID session evaluates one 100-ID supplier batch per handle, returns every priced hotel from that batch, and does not fetch another batch to fill max_results.","example":150},"hotel_type_ids":{"type":"array","items":{"type":"string"}},"chain_ids":{"type":"array","items":{"type":"string"}},"facility_ids":{"type":"array","items":{"type":"string"}},"intent":{"$ref":"#/components/schemas/IntentInput"}},"required":["checkin","checkout"]}],"example":{"city_name":"Paris","country_code":"fr","checkin":"2026-09-01","checkout":"2026-09-04","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"}}},"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"]},"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},"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","example":"htl_8f21a9c0"},"hotel_ref":{"type":"string","example":"nuitee:htl_8f21a9c0"},"provider":{"type":"string","example":"nuitee"},"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},"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"]},"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."}}},"GroundStation":{"type":"object","properties":{"code":{"type":"string"},"name":{"type":"string"},"city_name":{"type":"string"},"country_code":{"type":"string"}}},"GroundCarrier":{"type":"object","properties":{"code":{"type":"string","example":"HEXR"},"trade_name":{"type":"string","example":"Heathrow Express"},"legal_name":{"type":"string"},"logo_url":{"type":"string"},"transport_mode":{"type":"string"},"website":{"type":"string"}}},"GroundAmount":{"type":"object","properties":{"value":{"type":"number","description":"Minor units (e.g. pence)."},"currency":{"type":"string"},"decimal_places":{"type":"integer"},"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."}},"required":["value","currency"]},"GroundFare":{"type":"object","properties":{"fare_class":{"type":"string"},"fare_name":{"type":"string"},"total_price":{"$ref":"#/components/schemas/GroundAmount"},"refundable":{"type":"boolean","nullable":true},"exchangeable":{"type":"boolean","nullable":true},"conditions":{"type":"array","items":{"type":"string"}}}},"GroundConnection":{"type":"object","properties":{"id":{"type":"string","description":"The trip item token. Pass verbatim to POST /v1/trip as trip_item_token to add this journey to a cart.","example":"HEXR-GBLONLPB-GBLONLHB-2026-08-28T04:34-2026-08-28T04:59"},"departure_station":{"$ref":"#/components/schemas/GroundStation"},"arrival_station":{"$ref":"#/components/schemas/GroundStation"},"departure_time":{"type":"string"},"arrival_time":{"type":"string"},"duration_minutes":{"type":"number"},"transport_mode":{"type":"string","description":"train / bus / ferry, when the carrier reports it."},"marketing_carrier":{"$ref":"#/components/schemas/GroundCarrier"},"operating_carrier":{"$ref":"#/components/schemas/GroundCarrier"},"fares":{"type":"array","items":{"$ref":"#/components/schemas/GroundFare"}},"from_amount":{"allOf":[{"$ref":"#/components/schemas/GroundAmount"},{"description":"Cheapest fare, for \"from X\" display."}]}},"required":["id"]},"GroundSearchResponse":{"type":"object","properties":{"connections":{"type":"array","items":{"$ref":"#/components/schemas/GroundConnection"}}}},"GroundPassengerGroup":{"type":"object","properties":{"pax":{"type":"integer","minimum":1,"description":"Number of travelers in this group.","example":1},"max_age":{"type":"integer","minimum":0,"description":"Upper age bound for the group. 0-15 prices as a child; omit for an adult."}},"required":["pax"]},"GroundSearchRequest":{"type":"object","properties":{"departure_stations":{"type":"array","items":{"type":"string"},"description":"Distribusion station codes.","example":["GBLONLPB"]},"departure_city":{"type":"string","description":"ISO-country + city code (GBLON = London), NOT IATA.","example":"GBLON"},"arrival_stations":{"type":"array","items":{"type":"string"},"example":["GBLONLHB"]},"arrival_city":{"type":"string","example":"GBLON"},"departure_date":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","example":"2026-08-28"},"return_date":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Round trip. Each leg is priced separately."},"passengers":{"type":"array","items":{"$ref":"#/components/schemas/GroundPassengerGroup"},"description":"Defaults to one adult when omitted."},"carrier_codes":{"type":"array","items":{"type":"string"},"description":"Restrict to specific carriers.","example":["HEXR"]},"currency":{"type":"string","minLength":3,"maxLength":3,"example":"GBP"},"locale":{"type":"string","example":"en"},"intent":{"$ref":"#/components/schemas/IntentInput"}},"required":["departure_date"],"description":"Supply a departure selector (`departure_stations` OR `departure_city`) AND an arrival selector (`arrival_stations` OR `arrival_city`). These either/or rules are enforced at runtime but cannot be expressed in JSON Schema, so they do not appear in `required`."},"CarImage":{"type":"object","properties":{"url":{"type":"string","example":"https://cdn.autoeurope.com/images/vehicles/optimized/small/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":{"offer_id":{"type":"string","description":"The trip item token. Pass verbatim to POST /v1/trip as trip_item_token to add this rental to a cart.","example":"car_9f3b2a17c4e8d501"},"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 offer_id stops being addable to a trip. Re-search after this.","example":"2026-09-12T10:30:00Z"}},"required":["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":{"cancellation_id":{"type":"string","description":"Opaque reference of this preview (\"ccl_…\"). Pass 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"},"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. On a `cancellable: false` preview of a rental that cannot be cancelled at all, the supplier's own reason.","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); `requote` is a fresh preview reference and `current.refund` the figure now — show it to the customer and commit against `requote`. `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"},"requote":{"type":"string","description":"A fresh preview reference, on `quote_drift`. Show the customer the figure in `current.refund`, then commit against this reference as you would one from car_cancel_preview.","example":"ccl_01J8AB4N7GLY3Q0DXK2M5R9VWT"},"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":{"item_id":{"type":"integer","minimum":0,"exclusiveMinimum":true,"maximum":9007199254740991,"description":"Stable booked item ID from get_booking items[].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":42},"domain":{"type":"string"},"summary":{"type":"string"},"status":{"type":"string"},"booking_reference":{"type":"string"},"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 an item_id from items (the same safe projection as get_booking) and repeat the request with booking_ref and item_id."},"CarCancelPreviewRequest":{"type":"object","properties":{"booking_ref":{"type":"string","minLength":1,"description":"Jinko booking reference. Call get_booking first, then select its item_id for servicing.","example":"JNK-A0AUR2"},"item_id":{"type":"integer","minimum":0,"exclusiveMinimum":true,"maximum":9007199254740991,"description":"Stable booked item ID from get_booking items[].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":42},"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":{"cancellation_id":{"type":"string","example":"ccl_2f7c9a4e8b1d4c6f9e3a5b7d1c2e4f6a"},"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"},"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 item_id for servicing.","example":"JNK-A0AUR2"},"item_id":{"type":"integer","minimum":0,"exclusiveMinimum":true,"maximum":9007199254740991,"description":"Stable booked item ID from get_booking items[].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":42},"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"},"cancellation_id":{"type":"string","minLength":1,"description":"The \"ccl_…\" reference 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","cancellation_id"],"example":{"booking_ref":"JNK-8PT9VS","last_name":"Doe","cancellation_id":"ccl_2f7c9a4e8b1d4c6f9e3a5b7d1c2e4f6a"}},"CarCancelStatusResponse":{"type":"object","properties":{"cancellation_id":{"type":"string","example":"ccl_2f7c9a4e8b1d4c6f9e3a5b7d1c2e4f6a"},"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"},"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 item_id for servicing.","example":"JNK-A0AUR2"},"item_id":{"type":"integer","minimum":0,"exclusiveMinimum":true,"maximum":9007199254740991,"description":"Stable booked item ID from get_booking items[].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":42},"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","example":"re_3UBIxK2eZvKYlo2C"}}}},"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"},"pax_ref_id":{"type":"string","example":"pax_1"},"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"],"description":"Booked ancillary outcomes carry seats only.","example":"seat"},"seat_number":{"type":"string","example":"12B"},"pax_ref_id":{"type":"string","example":"pax_1"},"segment_ref_ids":{"type":"array","items":{"type":"string"},"example":["seg_1"]},"status":{"type":"string","enum":["CONFIRMED","PENDING","FAILED"],"description":"CONFIRMED means the ancillary was assigned and any required payment confirmed. PENDING means the outcome is unresolved; its price remains captured pending reconciliation. FAILED means it could not be fulfilled; its price stays listed here but is not kept."},"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":"A seat selected for this booked item. Price is omitted for an included/free seat; otherwise it uses the item confirmation’s total_paid money shape, in the cart currency. Booking payment totals are unchanged by this list."},"Booking":{"type":"object","properties":{"booking_reference":{"type":"string","description":"The Jinko booking reference of this booking — the same value you passed as `booking_ref`. Hand it to cancel, refund and exchange as `booking_ref`.","example":"JNK-XEVGW5"},"status":{"type":"string","example":"confirmed"},"failure_reason":{"type":"string","nullable":true,"description":"Why the booking failed, when it failed after payment and the cause is known: `offer_not_available`, `price_changed`, `booking_not_created` (the supplier holds no booking; nothing was booked and no money was taken), `booking_cancelled_at_provider`. Null otherwise.","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":{"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","example":"re_3UBIxK2eZvKYlo2C"}}}}},"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."},"failure_reason":{"type":"string","nullable":true,"description":"Why the booking failed, when it failed after payment and the cause is known: `offer_not_available`, `price_changed`, `booking_not_created` (the supplier holds no booking; nothing was booked and no money was taken), `booking_cancelled_at_provider`. Null otherwise.","example":"booking_not_created"},"booked_ancillaries":{"type":"array","items":{"$ref":"#/components/schemas/BookedAncillary"}},"confirmation":{"type":"object","properties":{"confirmation_number":{"type":"string"},"product_summary":{"type":"string"},"total_paid":{"$ref":"#/components/schemas/Money"},"details":{"type":"object","properties":{"flight":{"type":"object","properties":{"pnr":{"type":"string"},"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":"Each airline's own record locator, one per carrier. A codeshare or interline booking has more than one; `pnr` is the validating carrier's."},"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"}}},"refund_policy":{"type":"string"},"change_policy":{"type":"string"},"carry_on_baggage":{"type":"string"},"checked_baggage":{"type":"string"}}},"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"}}}}}}}}}},"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."},"GetBookingResponse":{"type":"object","properties":{"booking":{"$ref":"#/components/schemas/Booking"}},"example":{"booking":{"booking_reference":"JNK-A0AUR2","status":"confirmed","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 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 (`manual_reason` says why). `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."},"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","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 a person has to act. Present with `support_level: MANUAL_REQUIRED`.","example":"fare rules require agent review"},"expires_at":{"type":"string","example":"2026-06-02T00:00:00Z"},"warnings":{"type":"array","items":{"type":"string"}}}},"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. `quote_drift` — the refund moved since the quote you acknowledged; `requote` is a fresh quote handle and `current.refund` the figure now. `quote_expired` — the quote stopped binding; take a fresh one. `active_operation_exists` — an operation is already running on this booking; poll `active_operation` 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. `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); quote again with `requote`. 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.","example":"already_cancelled"},"requote":{"type":"string","description":"A fresh quote handle, on the refusals that give you one (`quote_drift`, `superseded`). Show the customer the figure it reports, then commit against it.","example":"svq_01J8AB4N7GLY3Q0D"},"current":{"type":"object","properties":{"refund":{"$ref":"#/components/schemas/Money"}},"description":"The customer refund as it stands now, on `quote_drift`. This is what the customer would get if they accepted the new quote."},"active_operation":{"type":"string","description":"The operation already running on this booking, on `active_operation_exists`. 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 item_id for servicing.","example":"JNK-A0AUR2"},"item_id":{"type":"integer","minimum":0,"exclusiveMinimum":true,"maximum":9007199254740991,"description":"Stable booked item ID from get_booking items[].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":42},"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 provider handle retained for existing API callers. Use booking_ref and item_id from get_booking instead. Requires a credential owning the booking.","deprecated":true},"provider":{"type":"string"},"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":"rfnd_3c91f0a2"},"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 item_id for servicing.","example":"JNK-A0AUR2"},"item_id":{"type":"integer","minimum":0,"exclusiveMinimum":true,"maximum":9007199254740991,"description":"Stable booked item ID from get_booking items[].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":42},"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 provider handle retained for existing API callers. Use booking_ref and item_id from get_booking instead. Requires a credential owning the booking.","deprecated":true},"provider":{"type":"string"},"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"},"provider_status":{"type":"string","description":"The airline's own raw status word for the refund, verbatim.","example":"COMPLETED"},"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":"rfnd_3c91f0a2"},"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 item_id for servicing.","example":"JNK-A0AUR2"},"item_id":{"type":"integer","minimum":0,"exclusiveMinimum":true,"maximum":9007199254740991,"description":"Stable booked item ID from get_booking items[].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":42},"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 provider handle retained for existing API callers. Use booking_ref and item_id from get_booking instead. Requires a credential owning the booking.","deprecated":true},"provider":{"type":"string"},"intent":{"$ref":"#/components/schemas/IntentInput"},"refund_reference":{"type":"string"}},"example":{"booking_ref":"JNK-A0AUR2","last_name":"Doe"}},"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"}}},"ExchangeShopResponse":{"type":"object","properties":{"support_level":{"$ref":"#/components/schemas/SupportLevel"},"offers":{"type":"array","items":{"$ref":"#/components/schemas/ExchangeOffer"}},"warnings":{"type":"array","items":{"type":"string"}}}},"ExchangeShopRequest":{"type":"object","properties":{"booking_ref":{"type":"string","minLength":1,"description":"Jinko booking reference. Call get_booking first, then select its item_id for servicing.","example":"JNK-A0AUR2"},"item_id":{"type":"integer","minimum":0,"exclusiveMinimum":true,"maximum":9007199254740991,"description":"Stable booked item ID from get_booking items[].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":42},"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 provider handle retained for existing API callers. Use booking_ref and item_id from get_booking instead. Requires a credential owning the booking.","deprecated":true},"provider":{"type":"string"},"intent":{"$ref":"#/components/schemas/IntentInput"},"segments_to_exchange":{"type":"array","items":{"nullable":true}},"preferred_departure_date":{"type":"string"}},"example":{"booking_ref":"JNK-A0AUR2","last_name":"Doe"}},"ExchangePriceResponse":{"type":"object","properties":{"payment_outcome":{"type":"string","example":"additional_payment_due"},"fare_difference":{"$ref":"#/components/schemas/Money"},"penalty_amount":{"$ref":"#/components/schemas/Money"},"original_fare":{"$ref":"#/components/schemas/Money"},"new_fare":{"$ref":"#/components/schemas/Money"},"total_due":{"$ref":"#/components/schemas/Money"},"total_refund":{"$ref":"#/components/schemas/Money"},"new_fare_details":{"type":"object","properties":{"extra_baggage_options":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"pieces":{"type":"integer"},"weight":{"type":"number"},"weight_unit":{"type":"string"},"price_per_bag":{"$ref":"#/components/schemas/Money"},"total_price":{"$ref":"#/components/schemas/Money"},"description":{"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"},"price_per_bag":{"$ref":"#/components/schemas/Money"},"total_price":{"$ref":"#/components/schemas/Money"},"description":{"type":"string"}}}}}},"session_reference":{"type":"string","example":"exch_7a1c93f0"},"expires_at":{"type":"string","example":"2026-06-01T13:04:56Z"},"warnings":{"type":"array","items":{"type":"string"}}}},"ExchangePriceConflictCode":{"type":"string","enum":["funds_unavailable","multi_currency_basis","manual_required"],"description":"Which refusal this is. `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. `error.message` states the case in figures where there are any. In every case nothing was exchanged or charged. To request manual handling, commit the same offer: the request then goes to Jinko support."},"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 item_id for servicing.","example":"JNK-A0AUR2"},"item_id":{"type":"integer","minimum":0,"exclusiveMinimum":true,"maximum":9007199254740991,"description":"Stable booked item ID from get_booking items[].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":42},"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 provider handle retained for existing API callers. Use booking_ref and item_id from get_booking instead. Requires a credential owning the booking.","deprecated":true},"provider":{"type":"string"},"intent":{"$ref":"#/components/schemas/IntentInput"},"offer_id":{"type":"string"}},"required":["offer_id"],"example":{"booking_ref":"JNK-A0AUR2","last_name":"Doe","offer_id":"exch_offer_3c1a9f"}},"ExchangeCommitResponse":{"type":"object","properties":{"exchange_reference":{"type":"string","example":"exch_7a1c93f0"},"airline_locator":{"type":"string","description":"Airline record locator for the current itinerary.","example":"ABC123"},"booking_reference":{"type":"string","example":"JNK-A0AUR2"},"new_ticket_numbers":{"type":"array","items":{"type":"string"},"example":["0012345678901"]},"status":{"type":"string","example":"exchanged"},"payment_outcome":{"type":"string","example":"paid"},"warnings":{"type":"array","items":{"type":"string"}}}},"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/ExchangePriceConflictCode"},"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"]}},"required":["error","code","manual_handling"]},"ExchangeCommitRequest":{"type":"object","properties":{"booking_ref":{"type":"string","minLength":1,"description":"Jinko booking reference. Call get_booking first, then select its item_id for servicing.","example":"JNK-A0AUR2"},"item_id":{"type":"integer","minimum":0,"exclusiveMinimum":true,"maximum":9007199254740991,"description":"Stable booked item ID from get_booking items[].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":42},"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 provider handle retained for existing API callers. Use booking_ref and item_id from get_booking instead. Requires a credential owning the booking.","deprecated":true},"provider":{"type":"string"},"intent":{"$ref":"#/components/schemas/IntentInput"},"offer_id":{"type":"string"},"session_reference":{"type":"string","description":"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","session_reference":"exch_7a1c93f0"}},"ExchangeStatusResponse":{"type":"object","properties":{"airline_locator":{"type":"string","example":"ABC123"},"booking_reference":{"type":"string","example":"JNK-A0AUR2"},"status":{"type":"string","example":"exchanged"},"new_ticket_numbers":{"type":"array","items":{"type":"string"},"example":["0012345678901"]},"confirmed_payment_outcome":{"type":"string","example":"paid"},"warnings":{"type":"array","items":{"type":"string"}}}},"ExchangeStatusRequest":{"type":"object","properties":{"booking_ref":{"type":"string","minLength":1,"description":"Jinko booking reference. Call get_booking first, then select its item_id for servicing.","example":"JNK-A0AUR2"},"item_id":{"type":"integer","minimum":0,"exclusiveMinimum":true,"maximum":9007199254740991,"description":"Stable booked item ID from get_booking items[].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":42},"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 provider handle retained for existing API callers. Use booking_ref and item_id from get_booking instead. Requires a credential owning the booking.","deprecated":true},"provider":{"type":"string"},"intent":{"$ref":"#/components/schemas/IntentInput"}},"example":{"booking_ref":"JNK-A0AUR2","last_name":"Doe"}},"HotelCancelResponse":{"type":"object","properties":{"booking_reference":{"type":"string","example":"JNK-A0AUR2"},"confirmation_number":{"type":"string","description":"Customer-facing hotel confirmation number, when supplied by the hotel."},"provider":{"type":"string","example":"nuitee"},"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 item_id for servicing.","example":"JNK-A0AUR2"},"item_id":{"type":"integer","minimum":0,"exclusiveMinimum":true,"maximum":9007199254740991,"description":"Stable booked item ID from get_booking items[].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":42},"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 provider handle retained for existing API callers. Use booking_ref and item_id from get_booking instead. Requires a credential owning the booking.","deprecated":true},"provider":{"type":"string"},"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 the supplier’s own reason in `error.message`."},"HotelCancelPreviewResponse":{"type":"object","properties":{"quote":{"type":"string","description":"This quote (\"svq_…\"). Pass it to POST /v1/hotel_cancel_commit — it binds the commit to the figures below. Opaque; the format may evolve.","example":"svq_01J7ZR3M8FKX2P9C"},"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 the supplier’s terms where it has any. Present with `support_level: MANUAL_REQUIRED`. `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"},"item":{"type":"string","description":"The booked item this quote would cancel (\"itm_…\"). A booking holding several items is quoted and cancelled one item at a time.","example":"itm_7f2c9a4e8b1d"},"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` — the refund moved since the quote you acknowledged; `requote` is a fresh quote handle and `current.refund` the figure now. `quote_expired` — the quote stopped binding; take a fresh one. `active_operation_exists` — one cancellation is already running on this booking; poll `active_operation` 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. `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); quote again with `requote`. 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.","example":"already_cancelled"},"requote":{"type":"string","description":"A fresh quote handle, on the refusals that give you one (`quote_drift`, `superseded`). Show the customer the figure it reports, then commit against it.","example":"svq_01J8AB4N7GLY3Q0D"},"current":{"type":"object","properties":{"refund":{"$ref":"#/components/schemas/Money"}},"description":"The customer refund as it stands now, on `quote_drift`. This is what the customer would get if they accepted the new quote."},"active_operation":{"type":"string","description":"The operation already running on this booking, on `active_operation_exists`. 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 item_id for servicing.","example":"JNK-A0AUR2"},"item_id":{"type":"integer","minimum":0,"exclusiveMinimum":true,"maximum":9007199254740991,"description":"Stable booked item ID from get_booking items[].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":42},"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 provider handle retained for existing API callers. Use booking_ref and item_id from get_booking instead. Requires a credential owning the booking.","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 item_id for servicing.","example":"JNK-A0AUR2"},"item_id":{"type":"integer","minimum":0,"exclusiveMinimum":true,"maximum":9007199254740991,"description":"Stable booked item ID from get_booking items[].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":42},"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 provider handle retained for existing API callers. Use booking_ref and item_id from get_booking instead. Requires a credential owning the booking.","deprecated":true},"intent":{"$ref":"#/components/schemas/IntentInput"},"quote":{"type":"string","description":"The \"svq_…\" handle 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 the current refund is answered 409 `quote_drift`, carrying a fresh `requote` handle and the current figure, and nothing is cancelled."}},"required":["quote","acknowledged"],"example":{"booking_ref":"JNK-H1ZK90","last_name":"Doe","quote":"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":"Why the operation is stalled. Present with `state: attention_required`; the value names what Jinko has to resolve, and needs nothing from the caller.","example":"settlement_review"},"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"},"provider_status":{"type":"string","description":"The supplier's own raw status word, verbatim and unmapped. Diagnostic: read `state` to branch on, this to explain what the supplier actually said.","example":"CANCELLED_WITH_CHARGES"},"references":{"type":"object","properties":{"cancellation_reference":{"type":"string","description":"The reference the supplier issued for the cancellation or refund itself, once it has issued one.","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":"The payment provider's own refund id, once one exists.","example":"re_3UBIxK2eZvKYlo2C"}},"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 item_id for servicing.","example":"JNK-A0AUR2"},"item_id":{"type":"integer","minimum":0,"exclusiveMinimum":true,"maximum":9007199254740991,"description":"Stable booked item ID from get_booking items[].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":42},"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 provider handle retained for existing API callers. Use booking_ref and item_id from get_booking instead. Requires a credential owning the booking.","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":{"type":"string","description":"This quote (\"svq_…\"). Pass it to POST /v1/flight_refund_commit — it binds the commit to the figures below. Opaque; the format may evolve.","example":"svq_01J7ZR3M8FKX2P9C"},"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. Present with `support_level: MANUAL_REQUIRED`.","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"},"item":{"type":"string","description":"The booked item this quote covers (\"itm_…\"). 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"},"provider":{"type":"string","description":"Which supplier the operation would be sent to. Informational.","example":"provider_a"},"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}},"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 item_id for servicing.","example":"JNK-A0AUR2"},"item_id":{"type":"integer","minimum":0,"exclusiveMinimum":true,"maximum":9007199254740991,"description":"Stable booked item ID from get_booking items[].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":42},"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 provider handle retained for existing API callers. Use booking_ref and item_id from get_booking instead. Requires a credential owning the booking.","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 item_id for servicing.","example":"JNK-A0AUR2"},"item_id":{"type":"integer","minimum":0,"exclusiveMinimum":true,"maximum":9007199254740991,"description":"Stable booked item ID from get_booking items[].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":42},"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 provider handle retained for existing API callers. Use booking_ref and item_id from get_booking instead. Requires a credential owning the booking.","deprecated":true},"intent":{"$ref":"#/components/schemas/IntentInput"},"quote":{"type":"string","description":"The \"svq_…\" handle 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 the current refund is answered 409 `quote_drift`, carrying a fresh `requote` handle and the current figure, and 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"],"example":{"booking_ref":"JNK-A0AUR2","last_name":"Doe","quote":"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":"Why the operation is stalled. Present with `state: attention_required`; the value names what Jinko has to resolve, and needs nothing from the caller.","example":"settlement_review"},"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"},"provider_status":{"type":"string","description":"The supplier's own raw status word, verbatim and unmapped. Diagnostic: read `state` to branch on, this to explain what the supplier actually said.","example":"CANCELLED_WITH_CHARGES"},"references":{"type":"object","properties":{"cancellation_reference":{"type":"string","description":"The reference the supplier issued for the cancellation or refund itself, once it has issued one.","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":"The payment provider's own refund id, once one exists.","example":"re_3UBIxK2eZvKYlo2C"}},"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 item_id for servicing.","example":"JNK-A0AUR2"},"item_id":{"type":"integer","minimum":0,"exclusiveMinimum":true,"maximum":9007199254740991,"description":"Stable booked item ID from get_booking items[].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":42},"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 provider handle retained for existing API callers. Use booking_ref and item_id from get_booking instead. Requires a credential owning the booking.","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":"This endpoint is in preview. Contact support to request access. It accepts a flight with its current price and returns schema 3.2 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, one-way, nonstop, USD; 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.","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","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Rate limit or quota exceeded.","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 upstream service rejected the request.","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 upstream service is temporarily unavailable. Please retry later.","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 upstream service did not respond in time.","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","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Rate limit or quota exceeded.","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 upstream service rejected the request.","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 upstream service is temporarily unavailable. Please retry later.","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 upstream service did not respond in time.","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","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Rate limit or quota exceeded.","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 upstream service rejected the request.","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 upstream service is temporarily unavailable. Please retry later.","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 upstream service did not respond in time.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}}}}},"/v1/find_dates":{"post":{"tags":["Discovery"],"summary":"Best date options for a route — up to ten cheapest itineraries, one per date-pair, spread across the month","description":"Takes the same request as flight_calendar and returns up to ten cheapest itineraries — one per (departure, return) date-pair — spread across the requested departure window, so you see a diverse set of date options rather than ten 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","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Rate limit or quota exceeded.","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 upstream service rejected the request.","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 upstream service is temporarily unavailable. Please retry later.","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 upstream service did not respond in time.","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. 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","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Rate limit or quota exceeded.","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 upstream service rejected the request.","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 upstream service is temporarily unavailable. Please retry later.","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 upstream service did not respond in time.","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. A search that matches nothing is also a 200: `offers` is empty and `result` is `no_matches` when every provider answered but nothing matched, or `provider_error` when at least one provider failed and no offers were returned. The body carries a recovery `instruction`; retry provider errors rather than changing filters.","headers":{"X-Jinko-Result":{"deprecated":true,"required":false,"description":"Deprecated: read `result` in the body. Sent for empty searches with result no_matches or provider_error.","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 or provider_error.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlightSearchResponse"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlightSearchErrorResponse"},"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/FlightSearchErrorResponse"},"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/FlightSearchErrorResponse"},"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/FlightSearchErrorResponse"},"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/FlightSearchErrorResponse"},"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/FlightSearchErrorResponse"},"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/FlightSearchErrorResponse"},"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","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlightSearchErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Rate limit or quota exceeded.","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/FlightSearchErrorResponse"},"example":{"error":{"code":"UPSTREAM_REJECTED","message":"The upstream service rejected the request.","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/FlightSearchErrorResponse"},"example":{"error":{"code":"UPSTREAM_UNAVAILABLE","message":"The upstream service is temporarily unavailable. Please retry later.","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/FlightSearchErrorResponse"},"example":{"error":{"code":"UPSTREAM_TIMEOUT","message":"The upstream service did not respond in time.","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","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Rate limit or quota exceeded.","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 upstream service rejected the request.","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 upstream service is temporarily unavailable. Please retry later.","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 upstream service did not respond in time.","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","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Rate limit or quota exceeded.","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 upstream service rejected the request.","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 upstream service is temporarily unavailable. Please retry later.","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 upstream service did not respond in time.","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 (or its /v1/book alias) 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 — already being quoted, already paid, cancelled or failed. Read `status` and act on it: wait out a quote in flight, or read the trip to find the booking that already exists. 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","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Rate limit or quota exceeded.","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 upstream service rejected the request.","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 upstream service is temporarily unavailable. Please retry later.","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 upstream service did not respond in time.","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","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Rate limit or quota exceeded.","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 upstream service rejected the request.","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 upstream service is temporarily unavailable. Please retry later.","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 upstream service did not respond in time.","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 (200) or quote still pricing (202)","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","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Rate limit or quota exceeded.","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 upstream service rejected the request.","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 upstream service is temporarily unavailable. Please retry later.","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 upstream service did not respond in time.","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","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Rate limit or quota exceeded.","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 upstream service rejected the request.","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 upstream service is temporarily unavailable. Please retry later.","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 upstream service did not respond in time.","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 success or failure. 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 session","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 cart 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 — already being quoted, already paid, cancelled or failed. Read `status` and act on it: wait out a quote in flight, or read the trip to find the booking that already exists. 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. Search again for the occupancy you intend to book and pick a rate that covers it; 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":"This trip is already being checked out; no second checkout was started.","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 locked 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). 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. 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. **`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 has no travelers, or no contact with both an email and a phone. Send `upsert_travelers` with the travelers and a contact, then check out again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Rate limit or quota exceeded.","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 upstream service rejected the request.","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 upstream service is temporarily unavailable. Please retry later.","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 upstream service did not respond in time.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}}}}},"/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: Thu, 31 Dec 2026 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 session","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 cart 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 — already being quoted, already paid, cancelled or failed. Read `status` and act on it: wait out a quote in flight, or read the trip to find the booking that already exists. 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. Search again for the occupancy you intend to book and pick a rate that covers it; 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":"This trip is already being checked out; no second checkout was started.","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 locked 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). 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. 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. **`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 has no travelers, or no contact with both an email and a phone. Send `upsert_travelers` with the travelers and a contact, then check out again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Rate limit or quota exceeded.","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 upstream service rejected the request.","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 upstream service is temporarily unavailable. Please retry later.","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 upstream service did not respond in time.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}}}}},"/v1/agent_payment/submit":{"post":{"tags":["Pricing & booking"],"summary":"Submit a typed card or 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, quoted_cart_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 when payment is present: 1–255 printable ASCII characters. Reuse only for the same payment submission."},"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":"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":"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 locked 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). 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. 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. **`quote_expired`** — The quoted cart can no longer be paid: it expired, or the trip's travelers changed after it was priced (`reason: travelers_changed`); the typed payment was refused. Call checkout again and submit against the new quoted_cart_id.","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","quoted_cart_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","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Rate limit or quota exceeded.","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 upstream service rejected the request.","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 upstream service did not respond in time.","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","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Rate limit or quota exceeded.","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 upstream service rejected the request.","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 upstream service is temporarily unavailable. Please retry later.","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 upstream service did not respond in time.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}}}}},"/v1/hotel_search":{"post":{"tags":["Hotels"],"summary":"Search live hotel inventory and rates","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","enum":["true","false"],"description":"Override the deployment default for the Nuitee supplier on this first search. Omit to use the platform default."},"required":false,"name":"enable_nuitee","in":"query"},{"schema":{"type":"string","enum":["true","false"],"description":"Override the deployment default for the HotelBeds supplier on this first search. Production policy may refuse enabling it. Omit to use the platform default."},"required":false,"name":"enable_hotelbeds","in":"query"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HotelSearchRequest"}}}},"responses":{"200":{"description":"Matching hotels","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HotelSearchResponse"}}}},"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 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","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Rate limit or quota exceeded.","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 upstream service rejected the request.","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 upstream service is temporarily unavailable. Please retry later.","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 upstream service did not respond in time.","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":"Provider-qualified hotel_ref from hotel_search (preferred), or a legacy native hotel_id. Provider-qualified values are URL-encoded as one path segment.","example":"nuitee: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","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Rate limit or quota exceeded.","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 upstream service rejected the request.","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 upstream service is temporarily unavailable. Please retry later.","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 upstream service did not respond in time.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}}}}},"/v1/ground_search":{"post":{"tags":["Ground"],"summary":"Search live rail, coach and ferry inventory","description":"Search ground-transport connections (rail / coach / ferry). Station and city codes are ISO-country + city — `GBLON` for London, not the IATA `LON`. Each `connections[].id` is a trip item token: pass it verbatim to `POST /v1/trip` as `trip_item_token` to add the journey to a cart, then check out as normal.","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GroundSearchRequest"}}}},"responses":{"200":{"description":"Matching ground connections","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GroundSearchResponse"}}}},"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","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Rate limit or quota exceeded.","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 upstream service rejected the request.","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 upstream service is temporarily unavailable. Please retry later.","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 upstream service did not respond in time.","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`. Each offer's `offer_id` is a trip item token: pass it verbatim to `POST /v1/trip` as `trip_item_token` to add the rental to a cart, 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":"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","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Rate limit or quota exceeded.","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 upstream service rejected the request.","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 upstream service is temporarily unavailable. Please retry later.","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 upstream service did not respond in time.","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 `cancellation_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","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Rate limit or quota exceeded.","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 upstream service rejected the request.","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 upstream service is temporarily unavailable. Please retry later.","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 upstream service did not respond in time.","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 `requote` 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","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Rate limit or quota exceeded.","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 upstream service rejected the request.","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 upstream service is temporarily unavailable. Please retry later.","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 upstream service did not respond in time.","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 an item_id from items and repeat with booking_ref and 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","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Rate limit or quota exceeded.","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 upstream service rejected the request.","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 upstream service is temporarily unavailable. Please retry later.","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 upstream service did not respond in time.","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.","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":["One-Way","Round-Trip"],"example":"Round-Trip"},"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; the cube retains 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","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Rate limit or quota exceeded.","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 upstream service rejected the request.","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 upstream service is temporarily unavailable. Please retry later.","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 upstream service did not respond in time.","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":["One-Way","Round-Trip"],"example":"Round-Trip"},"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; the cube retains 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","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Rate limit or quota exceeded.","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 upstream service rejected the request.","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 upstream service is temporarily unavailable. Please retry later.","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 upstream service did not respond in time.","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.","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":["One-Way","Round-Trip"],"example":"Round-Trip"},"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; the cube retains 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","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Rate limit or quota exceeded.","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 upstream service rejected the request.","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 upstream service is temporarily unavailable. Please retry later.","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 upstream service did not respond in time.","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":["One-Way","Round-Trip"],"example":"Round-Trip"},"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","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Rate limit or quota exceeded.","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 upstream service rejected the request.","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 upstream service is temporarily unavailable. Please retry later.","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 upstream service did not respond in time.","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.","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":["One-Way","Round-Trip"],"example":"Round-Trip"},"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","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Rate limit or quota exceeded.","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 upstream service rejected the request.","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 upstream service is temporarily unavailable. Please retry later.","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 upstream service did not respond in time.","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":["One-Way","Round-Trip"],"example":"Round-Trip"},"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","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Rate limit or quota exceeded.","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 upstream service rejected the request.","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 upstream service is temporarily unavailable. Please retry later.","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 upstream service did not respond in time.","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.","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":["One-Way","Round-Trip"],"example":"Round-Trip"},"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","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Rate limit or quota exceeded.","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 upstream service rejected the request.","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 upstream service is temporarily unavailable. Please retry later.","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 upstream service did not respond in time.","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 item_id for every servicing action. last_name is required for guest lookup; an owning credential may omit it. Legacy provider handles remain deprecated compatibility inputs. 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 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","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Rate limit or quota exceeded.","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 upstream service rejected the request.","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 upstream service is temporarily unavailable. Please retry later.","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 upstream service did not respond in time.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}}}}},"/v1/refund_check":{"post":{"tags":["Post-booking"],"summary":"Check refund eligibility only; use flight_refund_preview for customer-facing amounts","description":"Call get_booking first. Use booking_ref and the selected item_id for every servicing action. last_name is required for guest lookup; an owning credential may omit it. Legacy provider handles remain deprecated compatibility inputs. 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.","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","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Rate limit or quota exceeded.","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 upstream service rejected the request.","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 upstream service is temporarily unavailable. Please retry later.","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 upstream service did not respond in time.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}}}}},"/v1/refund_commit":{"post":{"tags":["Post-booking"],"summary":"Initiate a refund for a booked flight","description":"Call get_booking first. Use booking_ref and the selected item_id for every servicing action. last_name is required for guest lookup; an owning credential may omit it. Legacy provider handles remain deprecated compatibility inputs. 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.","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","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Rate limit or quota exceeded.","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 upstream service rejected the request.","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 upstream service is temporarily unavailable. Please retry later.","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 upstream service did not respond in time.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}}}}},"/v1/refund_status":{"post":{"tags":["Post-booking"],"summary":"Check the status of a refund","description":"Call get_booking first. Use booking_ref and the selected item_id for every servicing action. last_name is required for guest lookup; an owning credential may omit it. Legacy provider handles remain deprecated compatibility inputs. 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 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","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Rate limit or quota exceeded.","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 upstream service rejected the request.","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 upstream service is temporarily unavailable. Please retry later.","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 upstream service did not respond in time.","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 item_id for every servicing action. last_name is required for guest lookup; an owning credential may omit it. Legacy provider handles remain deprecated compatibility inputs. 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":"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 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","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Rate limit or quota exceeded.","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 upstream service rejected the request.","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 upstream service is temporarily unavailable. Please retry later.","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 upstream service did not respond in time.","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 item_id for every servicing action. last_name is required for guest lookup; an owning credential may omit it. Legacy provider handles remain deprecated compatibility inputs. 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":"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 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. Committing the same offer sends the request to Jinko support, who will complete it and refund the customer. 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","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Rate limit or quota exceeded.","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 upstream service rejected the request.","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 upstream service is temporarily unavailable. Please retry later.","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 upstream service did not respond in time.","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 item_id for every servicing action. last_name is required for guest lookup; an owning credential may omit it. Legacy provider handles remain deprecated compatibility inputs. 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":"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":"Nothing was exchanged or charged by this call. The exchange can't be refunded automatically, so the request has been sent to Jinko support, who will complete it and refund the customer. `code` says why — see `ExchangePriceConflictCode`. Follow it on `get_booking` at `items[].servicing.exchange`. 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","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Rate limit or quota exceeded.","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 upstream service rejected the request.","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 upstream service is temporarily unavailable. Please retry later.","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 upstream service did not respond in time.","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 item_id for every servicing action. last_name is required for guest lookup; an owning credential may omit it. Legacy provider handles remain deprecated compatibility inputs. 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 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","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Rate limit or quota exceeded.","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 upstream service rejected the request.","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 upstream service is temporarily unavailable. Please retry later.","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 upstream service did not respond in time.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}}}}},"/v1/hotel_cancel":{"post":{"tags":["Post-booking"],"summary":"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 item_id. Guests must send last_name; an owning credential may omit it. provider_booking_id is deprecated.","description":"Call get_booking first. Use booking_ref and the selected item_id for every servicing action. last_name is required for guest lookup; an owning credential may omit it. Legacy provider handles remain deprecated compatibility inputs. 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 item_id. Guests must send last_name; an owning credential may omit it. provider_booking_id is deprecated.","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 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","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Rate limit or quota exceeded.","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 upstream service rejected the request.","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 upstream service is temporarily unavailable. Please retry later.","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 upstream service did not respond in time.","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 item_id. An owning credential identifies the booking without last_name; guests must supply it. provider_reference is deprecated.","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. `code` is `not_cancellable`, as a sibling of `error`, and `error.message` is the supplier’s own reason; there is no quote to show and nothing to commit. 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":"the supplier reports this booking as cancelled"},"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","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Rate limit or quota exceeded.","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 upstream service rejected the request.","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 upstream service is temporarily unavailable. Please retry later.","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 upstream service did not respond in time.","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 auth mode the preview took — booking_ref + last_name, or provider_reference: 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 the refund has moved since the preview, the call is refused with 409 `quote_drift`, carrying a fresh `requote` handle and the current figure, and nothing is cancelled — so nobody is cancelled into a number they never saw. The other refusals are 409 `quote_expired` (run hotel_cancel_preview again), `active_operation_exists` (poll the `active_operation` it names 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 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":"The refund changed since the quote you acknowledged; nothing was cancelled. Quote again with `requote` and commit against the new 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","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Rate limit or quota exceeded.","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 upstream service rejected the request.","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 upstream service is temporarily unavailable. Please retry later.","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 upstream service did not respond in time.","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 an item_id from items and repeat with booking_ref and 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","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Rate limit or quota exceeded.","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 upstream service rejected the request.","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 upstream service is temporarily unavailable. Please retry later.","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 upstream service did not respond in time.","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 item_id. Guests must send last_name; owning credentials may omit it. provider_reference is deprecated.","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. `code` is `not_cancellable`, as a sibling of `error`, and `error.message` is the airline’s own reason; there is no quote to show, nothing to commit, and `manual_ok` does not apply. 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":"the supplier reports this fare as non-refundable"},"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","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Rate limit or quota exceeded.","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 upstream service rejected the request.","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 upstream service is temporarily unavailable. Please retry later.","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 upstream service did not respond in time.","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 the refund has moved since, the call is refused with 409 `quote_drift`, carrying a fresh `requote` handle and the current figure, and nothing is sent to the airline — so nobody is refunded a number they never saw. 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` it names 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 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","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Rate limit or quota exceeded.","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 upstream service rejected the request.","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 upstream service is temporarily unavailable. Please retry later.","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 upstream service did not respond in time.","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 an item_id from items and repeat with booking_ref and 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","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Rate limit or quota exceeded.","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 upstream service rejected the request.","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 upstream service is temporarily unavailable. Please retry later.","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 upstream service did not respond in time.","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","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Rate limit or quota exceeded.","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 upstream service rejected the request.","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 upstream service is temporarily unavailable. Please retry later.","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 upstream service did not respond in time.","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","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Rate limit or quota exceeded.","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 upstream service rejected the request.","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 upstream service is temporarily unavailable. Please retry later.","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 upstream service did not respond in time.","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","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Rate limit or quota exceeded.","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 upstream service rejected the request.","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 upstream service is temporarily unavailable. Please retry later.","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 upstream service did not respond in time.","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","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Rate limit or quota exceeded.","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 upstream service rejected the request.","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 upstream service is temporarily unavailable. Please retry later.","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 upstream service did not respond in time.","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","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Rate limit or quota exceeded.","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 upstream service rejected the request.","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 upstream service is temporarily unavailable. Please retry later.","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 upstream service did not respond in time.","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","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Rate limit or quota exceeded.","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 upstream service rejected the request.","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 upstream service is temporarily unavailable. Please retry later.","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 upstream service did not respond in time.","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","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMITED","message":"Rate limit or quota exceeded.","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 upstream service rejected the request.","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 upstream service is temporarily unavailable. Please retry later.","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 upstream service did not respond in time.","doc_url":"https://docs.gojinko.com/concepts/errors"}}}}}}}}}}