{"openapi":"3.1.0","info":{"title":"Bitvoya B2B API","version":"1.0.0","description":"Hotel content, prices and bookings for Bitvoya business partners (tenants).\n\nAuthenticate with the API key Bitvoya issued to you: `Authorization: Bearer bvk_live_...`.\nEvery price is one CNY amount: the tax-inclusive total including your service fee.\nErrors are RFC 9457 problem details with a stable `code`. Lists use cursor pagination."},"servers":[{"url":"https://app.bitvoya.com"}],"security":[{"apiKey":[]}],"tags":[{"name":"Account","description":"Check your key."},{"name":"Content","description":"Static content. Needs `catalog:read` (酒店、城市、品牌等静态资料)."},{"name":"Rates","description":"Prices and availability. Needs `rates:read`. Only rates you can prepay in full are listed."},{"name":"Bookings","description":"One room per booking, prepaid in full through a Stripe payment page. Creating and cancelling needs `bookings:write`; reading needs `bookings:read`. After payment Bitvoya confirms the booking with the hotel, which can take hours: wait for status `confirmed` before telling the guest."}],"paths":{"/api/b2b/v1/me":{"get":{"tags":["Account"],"operationId":"getMe","summary":"The tenant and key making the request","responses":{"200":{"description":"The tenant and key.","headers":{"RateLimit-Policy":{"description":"Tenant quota, e.g. `\"tenant\";q=120;w=60`.","schema":{"type":"string"}},"RateLimit":{"description":"Requests left and seconds until the window resets, e.g. `\"tenant\";r=119;t=42`.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Me"}}}}}},"401":{"description":"No key, or the key is invalid, revoked or expired.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The tenant is suspended, or the key lacks a scope.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Over the tenant limit. Wait for Retry-After seconds.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/api/b2b/v1/destinations":{"get":{"tags":["Content"],"operationId":"listDestinations","summary":"Search destinations, or list every destination with hotels","parameters":[{"name":"query","in":"query","required":false,"description":"Search by name in any language or by city code. Search results are one page.","schema":{"type":"string","minLength":1,"maxLength":100}},{"name":"cursor","in":"query","required":false,"description":"From `next_cursor`.","schema":{"type":"string","minLength":1,"maxLength":200}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":50}},{"name":"Accept-Language","in":"header","required":false,"description":"en (default), zh-CN, zh-TW, ja or ko. q-values are honoured. The response says which in Content-Language.","schema":{"type":"string","examples":["en","zh-CN","ja;q=0.9, en;q=0.8"]}}],"responses":{"200":{"description":"One page of destinations.","headers":{"RateLimit-Policy":{"description":"Tenant quota, e.g. `\"tenant\";q=120;w=60`.","schema":{"type":"string"}},"RateLimit":{"description":"Requests left and seconds until the window resets, e.g. `\"tenant\";r=119;t=42`.","schema":{"type":"string"}},"Content-Language":{"schema":{"type":"string"}},"ETag":{"description":"Send it back in If-None-Match to get 304 Not Modified.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","required":["data","next_cursor"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Destination"}},"next_cursor":{"type":["null","string"],"description":"Pass as `cursor` for the next page; null on the last page."}}}}}},"400":{"description":"The request is not valid (`invalid_request`, `invalid_cursor`).","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"No key, or the key is invalid, revoked or expired.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The tenant is suspended, or the key lacks a scope.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Over the tenant limit. Wait for Retry-After seconds.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/api/b2b/v1/hotels":{"get":{"tags":["Content"],"operationId":"listHotels","summary":"List hotels, optionally in one destination or of one brand","description":"Ordered by id. Without filters this walks the whole catalogue, for a full content sync.","parameters":[{"name":"destination_id","in":"query","required":false,"description":"From GET /destinations.","schema":{"type":"string","pattern":"^[0-9]{1,15}$"}},{"name":"brand_id","in":"query","required":false,"description":"From GET /brands.","schema":{"type":"string","pattern":"^[0-9]{1,15}$"}},{"name":"cursor","in":"query","required":false,"description":"From `next_cursor`.","schema":{"type":"string","minLength":1,"maxLength":200}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":50}},{"name":"Accept-Language","in":"header","required":false,"description":"en (default), zh-CN, zh-TW, ja or ko. q-values are honoured. The response says which in Content-Language.","schema":{"type":"string","examples":["en","zh-CN","ja;q=0.9, en;q=0.8"]}}],"responses":{"200":{"description":"One page of hotels.","headers":{"RateLimit-Policy":{"description":"Tenant quota, e.g. `\"tenant\";q=120;w=60`.","schema":{"type":"string"}},"RateLimit":{"description":"Requests left and seconds until the window resets, e.g. `\"tenant\";r=119;t=42`.","schema":{"type":"string"}},"Content-Language":{"schema":{"type":"string"}},"ETag":{"description":"Send it back in If-None-Match to get 304 Not Modified.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","required":["data","next_cursor"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/HotelSummary"}},"next_cursor":{"type":["null","string"],"description":"Pass as `cursor` for the next page; null on the last page."}}}}}},"400":{"description":"The request is not valid (`invalid_request`, `invalid_cursor`).","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"No key, or the key is invalid, revoked or expired.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The tenant is suspended, or the key lacks a scope.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Over the tenant limit. Wait for Retry-After seconds.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/api/b2b/v1/hotels/{hotel_id}":{"get":{"tags":["Content"],"operationId":"getHotel","summary":"One hotel","parameters":[{"name":"hotel_id","in":"path","required":true,"schema":{"type":"string","pattern":"^[0-9]{1,15}$"}},{"name":"Accept-Language","in":"header","required":false,"description":"en (default), zh-CN, zh-TW, ja or ko. q-values are honoured. The response says which in Content-Language.","schema":{"type":"string","examples":["en","zh-CN","ja;q=0.9, en;q=0.8"]}}],"responses":{"200":{"description":"The hotel.","headers":{"RateLimit-Policy":{"description":"Tenant quota, e.g. `\"tenant\";q=120;w=60`.","schema":{"type":"string"}},"RateLimit":{"description":"Requests left and seconds until the window resets, e.g. `\"tenant\";r=119;t=42`.","schema":{"type":"string"}},"Content-Language":{"schema":{"type":"string"}},"ETag":{"description":"Send it back in If-None-Match to get 304 Not Modified.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Hotel"}}}}}},"400":{"description":"The request is not valid (`invalid_request`, `invalid_cursor`).","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"No key, or the key is invalid, revoked or expired.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The tenant is suspended, or the key lacks a scope.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No available hotel with this id (`hotel_not_found`).","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Over the tenant limit. Wait for Retry-After seconds.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/api/b2b/v1/brands":{"get":{"tags":["Content"],"operationId":"listBrands","summary":"Hotel brands with their number of hotels","parameters":[{"name":"Accept-Language","in":"header","required":false,"description":"en (default), zh-CN, zh-TW, ja or ko. q-values are honoured. The response says which in Content-Language.","schema":{"type":"string","examples":["en","zh-CN","ja;q=0.9, en;q=0.8"]}}],"responses":{"200":{"description":"Every brand.","headers":{"RateLimit-Policy":{"description":"Tenant quota, e.g. `\"tenant\";q=120;w=60`.","schema":{"type":"string"}},"RateLimit":{"description":"Requests left and seconds until the window resets, e.g. `\"tenant\";r=119;t=42`.","schema":{"type":"string"}},"Content-Language":{"schema":{"type":"string"}},"ETag":{"description":"Send it back in If-None-Match to get 304 Not Modified.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Brand"}}}}}}},"400":{"description":"The request is not valid (`invalid_request`, `invalid_cursor`).","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"No key, or the key is invalid, revoked or expired.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The tenant is suspended, or the key lacks a scope.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Over the tenant limit. Wait for Retry-After seconds.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/api/b2b/v1/hotels/{hotel_id}/rates":{"get":{"tags":["Rates"],"operationId":"getHotelRates","summary":"Rooms and rates for one stay","description":"From a cache up to 30 minutes old. POST /quotes checks a rate with the supplier.","parameters":[{"name":"hotel_id","in":"path","required":true,"schema":{"type":"string","pattern":"^[0-9]{1,15}$"}},{"name":"checkin","in":"query","required":true,"schema":{"type":"string","pattern":"^[0-9]{4}-[0-9]{2}-[0-9]{2}$","examples":["2026-12-05"]}},{"name":"checkout","in":"query","required":true,"schema":{"type":"string","pattern":"^[0-9]{4}-[0-9]{2}-[0-9]{2}$","examples":["2026-12-05"]}},{"name":"adults","in":"query","required":false,"description":"Adults in the one room.","schema":{"type":"integer","minimum":1,"maximum":4,"default":2}},{"name":"Accept-Language","in":"header","required":false,"description":"en (default), zh-CN, zh-TW, ja or ko. q-values are honoured. The response says which in Content-Language.","schema":{"type":"string","examples":["en","zh-CN","ja;q=0.9, en;q=0.8"]}}],"responses":{"200":{"description":"Rooms with bookable rates; `rooms` is empty when nothing is available.","headers":{"RateLimit-Policy":{"description":"Tenant quota, e.g. `\"tenant\";q=120;w=60`.","schema":{"type":"string"}},"RateLimit":{"description":"Requests left and seconds until the window resets, e.g. `\"tenant\";r=119;t=42`.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/HotelRates"}}}}}},"400":{"description":"The request is not valid (`invalid_request`, `invalid_cursor`).","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"No key, or the key is invalid, revoked or expired.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The tenant is suspended, or the key lacks a scope.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No available hotel with this id (`hotel_not_found`).","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Over the tenant limit. Wait for Retry-After seconds.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"502":{"description":"The supplier returned an error (`supplier_error`).","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"The supplier did not answer (`supplier_unavailable`). Retry later.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/api/b2b/v1/quotes":{"post":{"tags":["Rates"],"operationId":"createQuote","summary":"Check a rate with the supplier and hold its terms for 15 minutes","parameters":[{"name":"Accept-Language","in":"header","required":false,"description":"en (default), zh-CN, zh-TW, ja or ko. q-values are honoured. The response says which in Content-Language.","schema":{"type":"string","examples":["en","zh-CN","ja;q=0.9, en;q=0.8"]}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["rate_key"],"properties":{"rate_key":{"type":"string","minLength":10,"maxLength":400}}}}}},"responses":{"201":{"description":"The quote.","headers":{"RateLimit-Policy":{"description":"Tenant quota, e.g. `\"tenant\";q=120;w=60`.","schema":{"type":"string"}},"RateLimit":{"description":"Requests left and seconds until the window resets, e.g. `\"tenant\";r=119;t=42`.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Quote"}}}}}},"400":{"description":"The request is not valid (`invalid_request`, `invalid_cursor`).","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"No key, or the key is invalid, revoked or expired.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The tenant is suspended, or the key lacks a scope.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"`hotel_not_found`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"The rate can no longer be booked (`rate_unavailable`).","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Over the tenant limit. Wait for Retry-After seconds.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"502":{"description":"`supplier_error`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"`supplier_unavailable`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/api/b2b/v1/bookings":{"post":{"tags":["Bookings"],"operationId":"createBooking","summary":"Book a quote and get its payment page","description":"Checks the rate with the supplier again. If the price or cancellation terms changed you get 409 `quote_changed` with `current_quote`. Send a unique Idempotency-Key per booking: repeating a request with the same key and body returns the same booking (200) instead of a new one.","parameters":[{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique per booking, e.g. a UUID. Kept for good.","schema":{"type":"string","minLength":1,"maxLength":255}},{"name":"Accept-Language","in":"header","required":false,"description":"en (default), zh-CN, zh-TW, ja or ko. q-values are honoured. The response says which in Content-Language.","schema":{"type":"string","examples":["en","zh-CN","ja;q=0.9, en;q=0.8"]}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingRequest"}}}},"responses":{"200":{"description":"Already created with this Idempotency-Key.","headers":{"RateLimit-Policy":{"description":"Tenant quota, e.g. `\"tenant\";q=120;w=60`.","schema":{"type":"string"}},"RateLimit":{"description":"Requests left and seconds until the window resets, e.g. `\"tenant\";r=119;t=42`.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Booking"}}}}}},"201":{"description":"Created. Pay at `payment.url` before `payment.expires_at`.","headers":{"RateLimit-Policy":{"description":"Tenant quota, e.g. `\"tenant\";q=120;w=60`.","schema":{"type":"string"}},"RateLimit":{"description":"Requests left and seconds until the window resets, e.g. `\"tenant\";r=119;t=42`.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Booking"}}}}}},"400":{"description":"The request is not valid (`invalid_request`, `invalid_cursor`).","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"No key, or the key is invalid, revoked or expired.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The tenant is suspended, or the key lacks a scope.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"`quote_not_found` or `hotel_not_found`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"`quote_expired`, `quote_changed` (see `current_quote`) or `rate_unavailable`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"`idempotency_key_reused`: the key was used with a different body.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Over the tenant limit. Wait for Retry-After seconds.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"502":{"description":"`payment_unavailable`: the booking exists (`booking_id`) but the payment page failed; retry with the same key. Or `supplier_error`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"`supplier_unavailable`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"get":{"tags":["Bookings"],"operationId":"listBookings","summary":"Your bookings, newest first","parameters":[{"name":"client_reference","in":"query","required":false,"schema":{"type":"string","minLength":1,"maxLength":64}},{"name":"cursor","in":"query","required":false,"description":"From `next_cursor`.","schema":{"type":"string","minLength":1,"maxLength":200}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":50}}],"responses":{"200":{"description":"One page of bookings.","headers":{"RateLimit-Policy":{"description":"Tenant quota, e.g. `\"tenant\";q=120;w=60`.","schema":{"type":"string"}},"RateLimit":{"description":"Requests left and seconds until the window resets, e.g. `\"tenant\";r=119;t=42`.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","required":["data","next_cursor"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Booking"}},"next_cursor":{"type":["null","string"],"description":"Pass as `cursor` for the next page; null on the last page."}}}}}},"400":{"description":"The request is not valid (`invalid_request`, `invalid_cursor`).","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"No key, or the key is invalid, revoked or expired.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The tenant is suspended, or the key lacks a scope.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Over the tenant limit. Wait for Retry-After seconds.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/api/b2b/v1/bookings/{booking_id}":{"get":{"tags":["Bookings"],"operationId":"getBooking","summary":"One booking","parameters":[{"name":"booking_id","in":"path","required":true,"schema":{"type":"string","pattern":"^BV[0-9]{14}$"}}],"responses":{"200":{"description":"The booking.","headers":{"RateLimit-Policy":{"description":"Tenant quota, e.g. `\"tenant\";q=120;w=60`.","schema":{"type":"string"}},"RateLimit":{"description":"Requests left and seconds until the window resets, e.g. `\"tenant\";r=119;t=42`.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Booking"}}}}}},"400":{"description":"The request is not valid (`invalid_request`, `invalid_cursor`).","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"No key, or the key is invalid, revoked or expired.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The tenant is suspended, or the key lacks a scope.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"`booking_not_found`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Over the tenant limit. Wait for Retry-After seconds.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/api/b2b/v1/bookings/{booking_id}/cancel":{"post":{"tags":["Bookings"],"operationId":"cancelBooking","summary":"Cancel a booking","description":"Unpaid: the payment page is closed. Paid: allowed until the free-cancellation deadline, and the full price is refunded to the card. Send `{}` or `{\"reason\": \"...\"}`.","parameters":[{"name":"booking_id","in":"path","required":true,"schema":{"type":"string","pattern":"^BV[0-9]{14}$"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"reason":{"type":"string","maxLength":200}}}}}},"responses":{"200":{"description":"The cancelled booking.","headers":{"RateLimit-Policy":{"description":"Tenant quota, e.g. `\"tenant\";q=120;w=60`.","schema":{"type":"string"}},"RateLimit":{"description":"Requests left and seconds until the window resets, e.g. `\"tenant\";r=119;t=42`.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Booking"}}}}}},"400":{"description":"The request is not valid (`invalid_request`, `invalid_cursor`).","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"No key, or the key is invalid, revoked or expired.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The tenant is suspended, or the key lacks a scope.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"`booking_not_found`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"`already_cancelled`, `not_cancellable` or `conflict`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Over the tenant limit. Wait for Retry-After seconds.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"502":{"description":"`payment_unavailable`: the payment page could not be closed; retry.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}}},"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"The key Bitvoya issued to you. `X-API-Key: <key>` is accepted too."}},"schemas":{"Problem":{"type":"object","description":"RFC 9457 problem details. `code` is stable and meant for programs; `detail` is for people.","required":["type","title","status","code","detail"],"properties":{"type":{"type":"string","examples":["about:blank"]},"title":{"type":"string","examples":["Unauthorized"]},"status":{"type":"integer","examples":[401]},"code":{"type":"string","examples":["invalid_api_key"],"description":"missing_api_key, invalid_api_key, api_key_revoked, api_key_expired, tenant_suspended, insufficient_scope, rate_limited, invalid_request, invalid_cursor, invalid_rate_key, idempotency_key_required, idempotency_key_reused, hotel_not_found, rate_unavailable, quote_not_found, quote_expired, quote_changed, booking_not_found, already_cancelled, not_cancellable, conflict, payment_unavailable, supplier_unavailable, supplier_error, internal_error"},"detail":{"type":"string"},"request_id":{"type":"string","description":"Quote it when you contact Bitvoya."},"required_scopes":{"type":"array","items":{"type":"string"}},"booking_id":{"type":"string","description":"With payment_unavailable: the booking exists; retry with the same Idempotency-Key."},"current_quote":{"type":"object","description":"With quote_changed: a new quote (see Quote) to review and book."},"errors":{"type":"array","items":{"type":"object","required":["location","message"],"properties":{"location":{"type":"string"},"field":{"type":["null","string"]},"message":{"type":"string"}}}}}},"Me":{"type":"object","required":["tenant","api_key","pricing","rate_limit"],"properties":{"tenant":{"type":"object","required":["code","name"],"properties":{"code":{"type":"string"},"name":{"type":"string"}}},"api_key":{"type":"object","required":["name","prefix","scopes","expires_at"],"properties":{"name":{"type":"string"},"prefix":{"type":"string","examples":["bvk_live_AbCd1234"]},"scopes":{"type":"array","items":{"type":"string","enum":["catalog:read","rates:read","bookings:read","bookings:write"]}},"expires_at":{"type":["null","string"],"format":"date-time"}}},"pricing":{"type":"object","required":["currency","price_basis"],"properties":{"currency":{"type":"string","enum":["CNY"]},"price_basis":{"type":"string","enum":["tax_inclusive_total_with_service_fee"]}}},"rate_limit":{"type":"object","required":["requests_per_minute"],"properties":{"requests_per_minute":{"type":"integer"}}}}},"Destination":{"type":"object","required":["id","name","region","code"],"properties":{"id":{"type":"string","examples":["5736"],"description":"Destination (city) id. Use it as destination_id."},"name":{"type":"string","examples":["Hangzhou"]},"region":{"type":["null","string"],"examples":["Zhejiang, China"],"description":"Province or state and country."},"code":{"type":["null","string"],"examples":["HGH"]}}},"HotelSummary":{"type":"object","required":["id","name","address","destination","brand","location","image_url"],"properties":{"id":{"type":"string","examples":["644"]},"name":{"type":"string","examples":["Sofitel Hangzhou Westlake"]},"address":{"type":["null","string"]},"destination":{"type":["null","object"],"required":["id","name"],"properties":{"id":{"type":"string"},"name":{"type":"string"}}},"brand":{"type":["null","object"],"required":["id","name"],"properties":{"id":{"type":"string"},"name":{"type":"string"}}},"location":{"type":["null","object"],"required":["latitude","longitude"],"properties":{"latitude":{"type":"number"},"longitude":{"type":"number"}}},"image_url":{"type":["null","string"],"description":"Cover image."}}},"Hotel":{"type":"object","required":["id","name","address","destination","brand","location","image_url","group","star_rating","phone","check_in_time","check_out_time","utc_offset","description","amenities","images"],"properties":{"id":{"type":"string","examples":["644"]},"name":{"type":"string","examples":["Sofitel Hangzhou Westlake"]},"address":{"type":["null","string"]},"destination":{"type":["null","object"],"required":["id","name"],"properties":{"id":{"type":"string"},"name":{"type":"string"}}},"brand":{"type":["null","object"],"required":["id","name"],"properties":{"id":{"type":"string"},"name":{"type":"string"}}},"location":{"type":["null","object"],"required":["latitude","longitude"],"properties":{"latitude":{"type":"number"},"longitude":{"type":"number"}}},"image_url":{"type":["null","string"],"description":"Cover image."},"group":{"type":["null","object"],"required":["id","name"],"properties":{"id":{"type":"string"},"name":{"type":"string"}},"description":"Hotel group, e.g. Accor."},"star_rating":{"type":["null","number"],"minimum":1,"maximum":5},"phone":{"type":["null","string"]},"check_in_time":{"type":["null","string"],"examples":["14:00"]},"check_out_time":{"type":["null","string"],"examples":["12:00"]},"utc_offset":{"type":["null","string"],"examples":["+08:00"]},"description":{"type":["null","object"],"description":"In the requested language when we have it, else English, else the original Chinese. `language` says which.","required":["language","text"],"properties":{"language":{"type":"string","enum":["en","zh-CN","zh-TW","ja","ko"]},"text":{"type":"string"}}},"amenities":{"type":"array","items":{"type":"string","enum":["pool","fitness_center","kids_club","lounge","pets_allowed"]}},"images":{"type":"array","items":{"type":"object","required":["url","category"],"properties":{"url":{"type":"string"},"category":{"type":["null","string"],"description":"The supplier's image label, often Chinese."}}}}}},"Brand":{"type":"object","required":["id","name","logo_url","hotel_count"],"properties":{"id":{"type":"string","examples":["1862"]},"name":{"type":"string","examples":["Sofitel Hotels"]},"logo_url":{"type":["null","string"]},"hotel_count":{"type":"integer"}}},"HotelRates":{"type":"object","required":["hotel_id","checkin","checkout","nights","adults","rooms"],"properties":{"hotel_id":{"type":"string"},"checkin":{"type":"string"},"checkout":{"type":"string"},"nights":{"type":"integer"},"adults":{"type":"integer"},"rooms":{"type":"array","description":"Room types with at least one rate you can book, cheapest first. Empty when nothing is available.","items":{"type":"object","required":["room_id","name","image_url","rates"],"properties":{"room_id":{"type":"string"},"name":{"type":"string"},"image_url":{"type":["null","string"]},"rates":{"type":"array","items":{"type":"object","required":["rate_key","name","meal_plan","executive_benefit","price","cancellation","pay_at_hotel"],"properties":{"rate_key":{"type":"string","description":"Pass to POST /quotes."},"name":{"type":"string"},"meal_plan":{"type":["null","string"],"enum":["room_only","breakfast","half_board","full_board",null]},"executive_benefit":{"type":"boolean","description":"Club or executive lounge access."},"price":{"type":"object","description":"Tax-inclusive total for the stay, including your service fee. What you prepay.","required":["amount","currency"],"properties":{"amount":{"type":"string","examples":["1150.00"]},"currency":{"type":"string","examples":["CNY"]}}},"cancellation":{"type":"object","required":["type","deadline","confirmed"],"properties":{"type":{"type":"string","enum":["free_cancellation","non_refundable","unknown"]},"deadline":{"type":["null","string"],"examples":["2026-12-04T16:00:00+08:00"],"description":"Free cancellation until then, hotel local time. After it the booking cannot be cancelled through the API."},"confirmed":{"type":"boolean","description":"false in rate lists for some suppliers: the quote has the confirmed terms."}}},"pay_at_hotel":{"type":["null","object"],"description":"Paid by the guest at the hotel, in the hotel currency. Not included in price.","required":["amount","currency","amount_cny","items"],"properties":{"amount":{"type":"string","examples":["18.48"]},"currency":{"type":"string","examples":["EUR"]},"amount_cny":{"type":["null","string"],"description":"Approximate."},"items":{"type":"array","items":{"type":"object","required":["type","description","amount"],"properties":{"type":{"type":"string","examples":["city_tax"]},"description":{"type":"string"},"amount":{"type":"string"}}}}}}}}}}}}}},"Quote":{"type":"object","required":["quote_id","expires_at","hotel_id","room","rate","checkin","checkout","nights","adults","price","cancellation","pay_at_hotel"],"properties":{"quote_id":{"type":"string","examples":["qt_7Hk2Qx9P..."]},"expires_at":{"type":"string","format":"date-time","description":"Book before this; the booking checks the rate with the supplier again."},"hotel_id":{"type":"string"},"room":{"type":"object","required":["room_id","name"],"properties":{"room_id":{"type":"string"},"name":{"type":"string"}}},"rate":{"type":"object","required":["name","meal_plan","executive_benefit"],"properties":{"name":{"type":"string"},"meal_plan":{"type":["null","string"],"enum":["room_only","breakfast","half_board","full_board",null]},"executive_benefit":{"type":"boolean"}}},"checkin":{"type":"string"},"checkout":{"type":"string"},"nights":{"type":"integer"},"adults":{"type":"integer"},"price":{"type":"object","description":"A decimal amount as a string, to avoid floating-point rounding.","required":["amount","currency"],"properties":{"amount":{"type":"string","examples":["1150.00"]},"currency":{"type":"string","examples":["CNY"]}}},"cancellation":{"type":"object","required":["type","deadline","confirmed"],"properties":{"type":{"type":"string","enum":["free_cancellation","non_refundable","unknown"]},"deadline":{"type":["null","string"],"examples":["2026-12-04T16:00:00+08:00"],"description":"Free cancellation until then, hotel local time. After it the booking cannot be cancelled through the API."},"confirmed":{"type":"boolean","description":"false in rate lists for some suppliers: the quote has the confirmed terms."}}},"pay_at_hotel":{"type":["null","object"],"description":"Paid by the guest at the hotel, in the hotel currency. Not included in price.","required":["amount","currency","amount_cny","items"],"properties":{"amount":{"type":"string","examples":["18.48"]},"currency":{"type":"string","examples":["EUR"]},"amount_cny":{"type":["null","string"],"description":"Approximate."},"items":{"type":"array","items":{"type":"object","required":["type","description","amount"],"properties":{"type":{"type":"string","examples":["city_tax"]},"description":{"type":"string"},"amount":{"type":"string"}}}}}}}},"BookingRequest":{"type":"object","required":["quote_id","guests","contact"],"properties":{"quote_id":{"type":"string","pattern":"^qt_[0-9A-Za-z]{20,40}$"},"client_reference":{"type":"string","minLength":1,"maxLength":64,"description":"Your own booking number."},"guests":{"type":"array","minItems":1,"maxItems":2,"description":"The lead guest first, then optionally a second adult. Names as on the guest's passport or ID.","items":{"type":"object","required":["first_name","last_name"],"properties":{"first_name":{"type":"string","minLength":1,"maxLength":50,"pattern":"^[\\p{L}][\\p{L}\\p{M} .'-]*$"},"last_name":{"type":"string","minLength":1,"maxLength":50,"pattern":"^[\\p{L}][\\p{L}\\p{M} .'-]*$"}}}},"contact":{"type":"object","description":"Your contact for this booking (not sent to the hotel).","required":["email"],"properties":{"email":{"type":"string","format":"email","maxLength":100},"phone":{"type":"string","pattern":"^\\+?[0-9][0-9 ()-]{4,29}$"}}},"arrival_time":{"type":"string","pattern":"^([01][0-9]|2[0-3]):[0-5][0-9]$","examples":["18:00"]},"special_requests":{"type":"array","maxItems":5,"uniqueItems":true,"items":{"type":"string","enum":["king_bed","twin_beds","baby_cot","extra_bed","birthday"]},"description":"Passed to the hotel as requests, not guarantees."},"remarks":{"type":"string","maxLength":200,"description":"Free-text request for the hotel."},"success_url":{"type":"string","maxLength":500,"pattern":"^https://[^\\s]+$","description":"Where the payment page returns after payment. Default: a Bitvoya page."},"cancel_url":{"type":"string","maxLength":500,"pattern":"^https://[^\\s]+$","description":"Where the payment page returns if the payer gives up."}}},"Booking":{"type":"object","required":["booking_id","client_reference","status","hotel","room","rate","checkin","checkout","nights","adults","guests","price","cancellation","pay_at_hotel","payment","hotel_confirmation_number","created_at","updated_at"],"properties":{"booking_id":{"type":"string","examples":["BV20261103123456"]},"client_reference":{"type":["null","string"]},"status":{"type":"string","enum":["awaiting_payment","payment_failed","pending_confirmation","confirmed","cancelled","under_review"],"description":"awaiting_payment: pay at payment.url. payment_failed: the payment page expired or failed; book again. pending_confirmation: paid, we are confirming with the hotel (can take hours). confirmed: the hotel confirmed. cancelled: see payment.status for the refund. under_review: a payment problem Bitvoya is checking."},"hotel":{"type":"object","required":["hotel_id","name"],"properties":{"hotel_id":{"type":"string"},"name":{"type":"string"}}},"room":{"type":"object","required":["room_id","name"],"properties":{"room_id":{"type":"string"},"name":{"type":"string"}}},"rate":{"type":"object","required":["name","meal_plan","executive_benefit"],"properties":{"name":{"type":"string"},"meal_plan":{"type":["null","string"],"enum":["room_only","breakfast","half_board","full_board",null]},"executive_benefit":{"type":"boolean"}}},"checkin":{"type":"string"},"checkout":{"type":"string"},"nights":{"type":"integer"},"adults":{"type":"integer"},"guests":{"type":"array","minItems":1,"maxItems":2,"description":"The lead guest first, then optionally a second adult. Names as on the guest's passport or ID.","items":{"type":"object","required":["first_name","last_name"],"properties":{"first_name":{"type":"string","minLength":1,"maxLength":50,"pattern":"^[\\p{L}][\\p{L}\\p{M} .'-]*$"},"last_name":{"type":"string","minLength":1,"maxLength":50,"pattern":"^[\\p{L}][\\p{L}\\p{M} .'-]*$"}}}},"price":{"type":"object","description":"A decimal amount as a string, to avoid floating-point rounding.","required":["amount","currency"],"properties":{"amount":{"type":"string","examples":["1150.00"]},"currency":{"type":"string","examples":["CNY"]}}},"cancellation":{"type":"object","required":["type","deadline","confirmed"],"properties":{"type":{"type":"string","enum":["free_cancellation","non_refundable","unknown"]},"deadline":{"type":["null","string"],"examples":["2026-12-04T16:00:00+08:00"],"description":"Free cancellation until then, hotel local time. After it the booking cannot be cancelled through the API."},"confirmed":{"type":"boolean","description":"false in rate lists for some suppliers: the quote has the confirmed terms."}}},"pay_at_hotel":{"type":["null","object"],"description":"Paid by the guest at the hotel, in the hotel currency. Not included in price.","required":["amount","currency","amount_cny","items"],"properties":{"amount":{"type":"string","examples":["18.48"]},"currency":{"type":"string","examples":["EUR"]},"amount_cny":{"type":["null","string"],"description":"Approximate."},"items":{"type":"array","items":{"type":"object","required":["type","description","amount"],"properties":{"type":{"type":"string","examples":["city_tax"]},"description":{"type":"string"},"amount":{"type":"string"}}}}}},"payment":{"type":"object","required":["status","url","expires_at","paid_at"],"properties":{"status":{"type":"string","enum":["pending","paid","failed","not_paid","refund_pending","refunded","under_review"]},"url":{"type":["null","string"],"description":"Stripe payment page for the full price, while awaiting_payment."},"expires_at":{"type":["null","string"],"format":"date-time"},"paid_at":{"type":["null","string"],"format":"date-time"}}},"hotel_confirmation_number":{"type":["null","string"],"description":"Once confirmed."},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}}}}}