Book on the hotel's own engine.
Book an offer on the hotel's own engine. The guest pays the property; booking is free.
Step through it: click a status, drag along the line, or use the arrow keys.
- Taken, with its Idempotency-Key, so a retry never books twice.
- The offer is checked again to the cent: revalidation.price_changed says if it moved.
requires_user_actionhandoff: the guest finishes on a hosted page
- The guest pays the property through a hosted checkout link. The payee is always the hotel.
expiredthe link ran outcanceledyou canceled before submission
- Paid, and back on the hotel’s engine to finish the reservation.
confirmation_pendingsubmitted, no confirmation seen yetfailedit couldn’t be completed
- The engine’s own confirmation code comes back.
RX48213
booking.updatedstatus queuedbooking.updatedstatus processingbooking.updatedstatus requires_paymentbooking.updatedstatus processingbooking.updatedstatus confirmed
Checkout payment is in preview; handoff and external modes cover the engines it can’t reach.
Book any offer_id from hotel detail or rates. We re-check the price first, so revalidation.price_changed tells you if it moved.
Booking is free, and VistaLink never holds the guest's money: with checkout the guest pays the property directly. On commissionable stays the commission is split with you; the split is on the Billing page.
Pick a payment mode: checkout, handoff or external. checkout (preview) returns a Stripe Checkout link for your user. handoff gives the guest a hosted page to finish on. external prefills the engine's own page and doesn't track payment.
Bookings need Build or above; on Free a create answers 403 forbidden with details.reason: "tier_required" and details.required_tier: "build".
Send an Idempotency-Key with every create; it's required, so a retry can never book twice. offer, timeline and the payment amount and currency stay empty until we know them. A booking you cancel ends canceled; you can cancel until we've submitted it to the hotel.
/v2/bookings/v2/bookings/{booking_id}/v2/bookings/{booking_id}/cancelCreate a booking
/v2/bookingsAnswers 202 with the Booking, which then moves on its own: poll GET /v2/bookings/{booking_id} or subscribe to booking.updated. Needs the booking scope, the Build plan or higher and an Idempotency-Key; bookings are free and billing is always null. Payment mode is checkout (a hosted checkout link in payment.checkout_url, where the guest pays the property directly), handoff (a hosted session for the guest) or external (the booking engine's own prefilled page).
Free. On commissionable hotels the commission is split with you; see Billing.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
Idempotency-KeyREQUIRED | header | string | Required. Any unique string. Same key and same body returns the original response; same key and a different body is a 409. |
Request body
| Field | Type | Description |
|---|---|---|
hotel_idREQUIRED | string (uuid) | The hotel being booked; must be the hotel the offer belongs to. |
offer_idREQUIRED | string | An offer_id this key received from search, hotel detail, rates or chat in the last 30 minutes. |
stayREQUIRED | Stay | Dates and rooms of a stay, up to 30 nights and 9 rooms. |
guestsREQUIRED | BookingGuests[] | One entry per room in stay.rooms, each with its lead guest. At most 9. |
guests[].roomREQUIRED | integer | Zero-based index into stay.rooms; every room needs exactly one entry. 0–8. |
guests[].leadREQUIRED | object | The room's lead guest, whose name, email and phone go on the reservation. |
guests[].lead.first_nameREQUIRED | string | Lead guest's first name as it should appear on the reservation. |
guests[].lead.last_nameREQUIRED | string | Lead guest's last name as it should appear on the reservation. |
guests[].lead.emailREQUIRED | string (email) | Lead guest's email address; the hotel's engine needs it for every reservation. |
guests[].lead.phoneREQUIRED | string | Lead guest's phone number, international format recommended; the hotel's engine needs it for every reservation. |
guests[].occupants | Occupant[] | Other guests in the room, up to 16; kept with the booking but not sent to the engine. |
guests[].occupants[].first_name | string | null | Guest's first name. |
guests[].occupants[].last_name | string | null | Guest's last name. |
guests[].occupants[].age | integer | null | Guest's age in years, 0 to 120. |
paymentREQUIRED | object | How the guest will pay. |
payment.modeREQUIRED | string | checkout returns a hosted checkout link for pay-now offers, handoff returns a hosted browser session in which the guest completes the booking, external returns the booking engine's own prefilled page. One of checkout, handoff, external. |
payment.return_url | string (uri) | null | checkout only: the https page the guest lands on after paying (with booking_id appended) or abandoning; a VistaLink page when null. |
payment.locale | string | null | checkout only: language of the hosted checkout page as a language tag; pt-BR reads as pt, and unsupported languages fall back to the guest's browser. |
special_requests | string | null | Free-text requests from the guest, up to 2,000 characters; stored with the booking but not sent to the engine today. |
webhook_url | string (uri) | null | An https URL stored with the booking; booking.updated events are delivered only to webhooks created with POST /v2/webhooks, not here. |
Example
curl -X POST 'https://api.vistalink.com/v2/bookings' \
-H "Authorization: Bearer $VISTALINK_API_KEY" \
-H 'Idempotency-Key: 4f1c2b8e-booking-ada-rossi' \
-H "Content-Type: application/json" \
-d '{
"hotel_id": "43d0be71-5f3c-4c8e-9a52-1d7e2b6f0a19",
"offer_id": "off_t3c8e1a9b4f2d7e6a5c0b",
"stay": {
"check_in": "2026-11-14",
"check_out": "2026-11-16",
"rooms": [
{
"adults": 2,
"children_ages": []
}
]
},
"guests": [
{
"room": 0,
"lead": {
"first_name": "Ada",
"last_name": "Rossi",
"email": "[email protected]",
"phone": "+390600000000"
},
"occupants": []
}
],
"payment": {
"mode": "handoff",
"locale": "en"
},
"special_requests": "Late arrival"
}'{
"booking_id": "bk_P2w9Xc4Rt1Lm8Qz3",
"handoff": {
"expires_at": "2026-10-07T10:00:00Z",
"url": "https://bookings.vistalink.com/s/Hn4q8Zr2Wc7Lp1Xt"
},
"hotel_id": "43d0be71-5f3c-4c8e-9a52-1d7e2b6f0a19",
"offer": {
"availability": "available",
"board": "breakfast",
"channel": "direct",
"currency": "EUR",
"engine": "SYNXIS",
"excluded_charges": [],
"free_cancellation": false,
"includes_taxes": true,
"listing_key": "engine_room:1234567",
"nightly_avg": "481.00",
"observed_at": "2026-10-07T09:30:00Z",
"occupancy": {
"adults": 2,
"children_ages": []
},
"offer_id": "off_t3c8e1a9b4f2d7e6a5c0b",
"rate_name": "Non-refundable, breakfast included",
"room_id": "5b1e2f3a-0c4d-4e5f-8a9b-1c2d3e4f5a6b",
"room_name": "Superior Room",
"total": "962.00",
"url": "https://umilta36.com/book?checkin=2026-11-14&checkout=2026-11-16"
},
"revalidation": {
"checked_at": "2026-10-07T09:30:00Z",
"currency": "EUR",
"price_changed": false,
"total": "962.00"
},
"status": "requires_user_action",
"timeline": [
{
"at": "2026-10-07T09:30:00Z",
"state": "queued"
},
{
"at": "2026-10-07T09:30:02Z",
"state": "processing"
},
{
"at": "2026-10-07T09:30:41Z",
"state": "requires_user_action"
}
]
}Response 202
Successful Response
| Field | Type | Description |
|---|---|---|
request_id | string | null | Id of the request that returned this booking; set on create, null on get, cancel and webhook payloads. |
booking_idREQUIRED | string | Booking id, bk_ followed by 16 letters and digits. |
statusREQUIRED | string | Where the booking is: queued, processing, requires_payment (show payment.checkout_url), requires_user_action (send the guest to handoff.url), then confirmed, confirmation_pending (submitted, confirmation not yet seen), failed, expired or canceled. One of queued, processing, requires_payment, requires_user_action, confirmed, confirmation_pending, failed, expired, canceled. |
hotel_idREQUIRED | string (uuid) | The hotel being booked. |
offer | Offer | null | One bookable price for one room and rate, as observed on a channel. |
revalidation | object | null | The result of re-checking the offer on the engine; null until that check has an outcome. |
revalidation.price_changedREQUIRED | boolean | True when the engine's total or currency differs from the offer by any amount, in which case the booking fails with price_changed; false otherwise, including when the room sold out. |
revalidation.total | string | null | Total the engine quoted for the same room and rate, a decimal string in currency; null when the room or rate is no longer offered. |
revalidation.currency | string | null | ISO 4217 currency of total. |
revalidation.checked_at | string (date-time) | null | When the engine was checked, RFC 3339 UTC. |
handoff | object | null | Where the guest finishes the booking in handoff and external modes; null in checkout mode and before the engine step. |
handoff.urlREQUIRED | string (uri) | In handoff mode, the hosted browser session for the guest; in external mode, the booking engine's own prefilled page. |
handoff.expires_at | string (date-time) | null | When the hosted session stops accepting the guest (20 minutes after it is issued); null in external mode. |
confirmation | object | null | The hotel's confirmation; null until status is confirmed. |
confirmation.code | string | null | The hotel's confirmation number; null when the engine confirmed without showing one. |
confirmation.evidenceREQUIRED | string | How the confirmation was established; engine_confirmation_signal (the engine's own confirmation page or code) is the only value produced today. One of engine_confirmation_signal, email, manual. |
confirmation.at | string (date-time) | null | When the booking was confirmed, RFC 3339 UTC. |
payment | BookingPayment | null | The guest's payment. Null for external mode, and for checkout until the link is issued. |
failure | object | null | Why the booking failed or expired; null otherwise. |
failure.reasonREQUIRED | string | Stable failure code: sold_out, price_changed (revalidation), price_changed_at_payment (the engine charged a different total after checkout), engine_blocked, handoff_expired (the guest left the session), payment_expired, payment_failed, unsupported_engine (the engine or offer cannot be booked in this mode), payment_unavailable (hosted checkout is not available; use handoff). One of sold_out, price_changed, price_changed_at_payment, engine_blocked, handoff_expired, payment_expired, payment_failed, unsupported_engine, payment_unavailable. |
failure.message | string | null | One English sentence explaining the failure. |
commission | object | null | Commission terms once the booking is confirmed; null before that. |
commission.commissionableREQUIRED | boolean | True when the hotel has an active commission contract covering today. |
commission.rate | string | null | Commission as a decimal fraction string of the booking total, for example "0.10" for 10%; null when not commissionable or the contract has no rate. |
commission.partner_share | string | null | Your share of the commission as a decimal fraction string, for example "0.50"; null when not commissionable. |
timeline | TimelineEntry[] | Every status the booking has been in, oldest first, starting with queued. |
timeline[].atREQUIRED | string (date-time) | When the booking entered state, RFC 3339 UTC. |
timeline[].stateREQUIRED | string | The status the booking moved to. One of queued, processing, requires_payment, requires_user_action, confirmed, confirmation_pending, failed, expired, canceled. |
billing | null | Always null: bookings are free. |
Errors
| Status | Code | When |
|---|---|---|
| 400 | bad_request | Validation errors carry details.fields[]. |
| 401 | unauthorized | No key, or the key is unknown or revoked. |
| 403 | forbidden | Missing scope, or details.reason tier_required: this needs the Build plan or higher. |
| 404 | not_found | Offer unknown or expired. |
| 409 | conflict | The Idempotency-Key was reused with a different body, or the resource is in a state that forbids the change. |
| 429 | rate_limited | Over the per-minute limit for the tier. Wait Retry-After seconds. |
| 503 | unavailable | We're temporarily unable to serve the call. Retry with backoff. |
Get a booking
/v2/bookings/{booking_id}Synchronous and free; needs the booking scope. Returns the booking's current status, payment, confirmation and timeline.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
booking_idREQUIRED | path | string | The booking_id returned by POST /v2/bookings. |
Example
curl 'https://api.vistalink.com/v2/bookings/bk_P2w9Xc4Rt1Lm8Qz3' \
-H "Authorization: Bearer $VISTALINK_API_KEY"{
"booking_id": "bk_P2w9Xc4Rt1Lm8Qz3",
"handoff": {
"expires_at": "2026-10-07T10:00:00Z",
"url": "https://bookings.vistalink.com/s/Hn4q8Zr2Wc7Lp1Xt"
},
"hotel_id": "43d0be71-5f3c-4c8e-9a52-1d7e2b6f0a19",
"offer": {
"availability": "available",
"board": "breakfast",
"channel": "direct",
"currency": "EUR",
"engine": "SYNXIS",
"excluded_charges": [],
"free_cancellation": false,
"includes_taxes": true,
"listing_key": "engine_room:1234567",
"nightly_avg": "481.00",
"observed_at": "2026-10-07T09:30:00Z",
"occupancy": {
"adults": 2,
"children_ages": []
},
"offer_id": "off_t3c8e1a9b4f2d7e6a5c0b",
"rate_name": "Non-refundable, breakfast included",
"room_id": "5b1e2f3a-0c4d-4e5f-8a9b-1c2d3e4f5a6b",
"room_name": "Superior Room",
"total": "962.00",
"url": "https://umilta36.com/book?checkin=2026-11-14&checkout=2026-11-16"
},
"revalidation": {
"checked_at": "2026-10-07T09:30:00Z",
"currency": "EUR",
"price_changed": false,
"total": "962.00"
},
"status": "requires_user_action",
"timeline": [
{
"at": "2026-10-07T09:30:00Z",
"state": "queued"
},
{
"at": "2026-10-07T09:30:02Z",
"state": "processing"
},
{
"at": "2026-10-07T09:30:41Z",
"state": "requires_user_action"
}
]
}Response 200
Successful Response
| Field | Type | Description |
|---|---|---|
request_id | string | null | Id of the request that returned this booking; set on create, null on get, cancel and webhook payloads. |
booking_idREQUIRED | string | Booking id, bk_ followed by 16 letters and digits. |
statusREQUIRED | string | Where the booking is: queued, processing, requires_payment (show payment.checkout_url), requires_user_action (send the guest to handoff.url), then confirmed, confirmation_pending (submitted, confirmation not yet seen), failed, expired or canceled. One of queued, processing, requires_payment, requires_user_action, confirmed, confirmation_pending, failed, expired, canceled. |
hotel_idREQUIRED | string (uuid) | The hotel being booked. |
offer | Offer | null | One bookable price for one room and rate, as observed on a channel. |
revalidation | object | null | The result of re-checking the offer on the engine; null until that check has an outcome. |
revalidation.price_changedREQUIRED | boolean | True when the engine's total or currency differs from the offer by any amount, in which case the booking fails with price_changed; false otherwise, including when the room sold out. |
revalidation.total | string | null | Total the engine quoted for the same room and rate, a decimal string in currency; null when the room or rate is no longer offered. |
revalidation.currency | string | null | ISO 4217 currency of total. |
revalidation.checked_at | string (date-time) | null | When the engine was checked, RFC 3339 UTC. |
handoff | object | null | Where the guest finishes the booking in handoff and external modes; null in checkout mode and before the engine step. |
handoff.urlREQUIRED | string (uri) | In handoff mode, the hosted browser session for the guest; in external mode, the booking engine's own prefilled page. |
handoff.expires_at | string (date-time) | null | When the hosted session stops accepting the guest (20 minutes after it is issued); null in external mode. |
confirmation | object | null | The hotel's confirmation; null until status is confirmed. |
confirmation.code | string | null | The hotel's confirmation number; null when the engine confirmed without showing one. |
confirmation.evidenceREQUIRED | string | How the confirmation was established; engine_confirmation_signal (the engine's own confirmation page or code) is the only value produced today. One of engine_confirmation_signal, email, manual. |
confirmation.at | string (date-time) | null | When the booking was confirmed, RFC 3339 UTC. |
payment | BookingPayment | null | The guest's payment. Null for external mode, and for checkout until the link is issued. |
failure | object | null | Why the booking failed or expired; null otherwise. |
failure.reasonREQUIRED | string | Stable failure code: sold_out, price_changed (revalidation), price_changed_at_payment (the engine charged a different total after checkout), engine_blocked, handoff_expired (the guest left the session), payment_expired, payment_failed, unsupported_engine (the engine or offer cannot be booked in this mode), payment_unavailable (hosted checkout is not available; use handoff). One of sold_out, price_changed, price_changed_at_payment, engine_blocked, handoff_expired, payment_expired, payment_failed, unsupported_engine, payment_unavailable. |
failure.message | string | null | One English sentence explaining the failure. |
commission | object | null | Commission terms once the booking is confirmed; null before that. |
commission.commissionableREQUIRED | boolean | True when the hotel has an active commission contract covering today. |
commission.rate | string | null | Commission as a decimal fraction string of the booking total, for example "0.10" for 10%; null when not commissionable or the contract has no rate. |
commission.partner_share | string | null | Your share of the commission as a decimal fraction string, for example "0.50"; null when not commissionable. |
timeline | TimelineEntry[] | Every status the booking has been in, oldest first, starting with queued. |
timeline[].atREQUIRED | string (date-time) | When the booking entered state, RFC 3339 UTC. |
timeline[].stateREQUIRED | string | The status the booking moved to. One of queued, processing, requires_payment, requires_user_action, confirmed, confirmation_pending, failed, expired, canceled. |
billing | null | Always null: bookings are free. |
Errors
| Status | Code | When |
|---|---|---|
| 400 | bad_request | Validation errors carry details.fields[]. |
| 401 | unauthorized | No key, or the key is unknown or revoked. |
| 403 | forbidden | Missing scope. |
| 404 | not_found | The hotel, offer, booking, job or webhook doesn't exist for this key. |
| 429 | rate_limited | Over the per-minute limit for the tier. Wait Retry-After seconds. |
| 503 | unavailable | We're temporarily unable to serve the call. Retry with backoff. |
Cancel a booking
/v2/bookings/{booking_id}/cancelSynchronous and free; needs the booking scope. Allowed while the booking is queued, requires_payment or requires_user_action, and while processing only before the engine submit step; after that the cancel is refused with 409 conflict. Canceling after a completed checkout payment sets payment.refund_required: true.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
booking_idREQUIRED | path | string | The booking_id returned by POST /v2/bookings. |
Example
curl -X POST 'https://api.vistalink.com/v2/bookings/bk_P2w9Xc4Rt1Lm8Qz3/cancel' \
-H "Authorization: Bearer $VISTALINK_API_KEY"{
"booking_id": "bk_P2w9Xc4Rt1Lm8Qz3",
"handoff": {
"expires_at": "2026-10-07T10:00:00Z",
"url": "https://bookings.vistalink.com/s/Hn4q8Zr2Wc7Lp1Xt"
},
"hotel_id": "43d0be71-5f3c-4c8e-9a52-1d7e2b6f0a19",
"offer": {
"availability": "available",
"board": "breakfast",
"channel": "direct",
"currency": "EUR",
"engine": "SYNXIS",
"excluded_charges": [],
"free_cancellation": false,
"includes_taxes": true,
"listing_key": "engine_room:1234567",
"nightly_avg": "481.00",
"observed_at": "2026-10-07T09:30:00Z",
"occupancy": {
"adults": 2,
"children_ages": []
},
"offer_id": "off_t3c8e1a9b4f2d7e6a5c0b",
"rate_name": "Non-refundable, breakfast included",
"room_id": "5b1e2f3a-0c4d-4e5f-8a9b-1c2d3e4f5a6b",
"room_name": "Superior Room",
"total": "962.00",
"url": "https://umilta36.com/book?checkin=2026-11-14&checkout=2026-11-16"
},
"revalidation": {
"checked_at": "2026-10-07T09:30:00Z",
"currency": "EUR",
"price_changed": false,
"total": "962.00"
},
"status": "requires_user_action",
"timeline": [
{
"at": "2026-10-07T09:30:00Z",
"state": "queued"
},
{
"at": "2026-10-07T09:30:02Z",
"state": "processing"
},
{
"at": "2026-10-07T09:30:41Z",
"state": "requires_user_action"
}
]
}Response 200
Successful Response
| Field | Type | Description |
|---|---|---|
request_id | string | null | Id of the request that returned this booking; set on create, null on get, cancel and webhook payloads. |
booking_idREQUIRED | string | Booking id, bk_ followed by 16 letters and digits. |
statusREQUIRED | string | Where the booking is: queued, processing, requires_payment (show payment.checkout_url), requires_user_action (send the guest to handoff.url), then confirmed, confirmation_pending (submitted, confirmation not yet seen), failed, expired or canceled. One of queued, processing, requires_payment, requires_user_action, confirmed, confirmation_pending, failed, expired, canceled. |
hotel_idREQUIRED | string (uuid) | The hotel being booked. |
offer | Offer | null | One bookable price for one room and rate, as observed on a channel. |
revalidation | object | null | The result of re-checking the offer on the engine; null until that check has an outcome. |
revalidation.price_changedREQUIRED | boolean | True when the engine's total or currency differs from the offer by any amount, in which case the booking fails with price_changed; false otherwise, including when the room sold out. |
revalidation.total | string | null | Total the engine quoted for the same room and rate, a decimal string in currency; null when the room or rate is no longer offered. |
revalidation.currency | string | null | ISO 4217 currency of total. |
revalidation.checked_at | string (date-time) | null | When the engine was checked, RFC 3339 UTC. |
handoff | object | null | Where the guest finishes the booking in handoff and external modes; null in checkout mode and before the engine step. |
handoff.urlREQUIRED | string (uri) | In handoff mode, the hosted browser session for the guest; in external mode, the booking engine's own prefilled page. |
handoff.expires_at | string (date-time) | null | When the hosted session stops accepting the guest (20 minutes after it is issued); null in external mode. |
confirmation | object | null | The hotel's confirmation; null until status is confirmed. |
confirmation.code | string | null | The hotel's confirmation number; null when the engine confirmed without showing one. |
confirmation.evidenceREQUIRED | string | How the confirmation was established; engine_confirmation_signal (the engine's own confirmation page or code) is the only value produced today. One of engine_confirmation_signal, email, manual. |
confirmation.at | string (date-time) | null | When the booking was confirmed, RFC 3339 UTC. |
payment | BookingPayment | null | The guest's payment. Null for external mode, and for checkout until the link is issued. |
failure | object | null | Why the booking failed or expired; null otherwise. |
failure.reasonREQUIRED | string | Stable failure code: sold_out, price_changed (revalidation), price_changed_at_payment (the engine charged a different total after checkout), engine_blocked, handoff_expired (the guest left the session), payment_expired, payment_failed, unsupported_engine (the engine or offer cannot be booked in this mode), payment_unavailable (hosted checkout is not available; use handoff). One of sold_out, price_changed, price_changed_at_payment, engine_blocked, handoff_expired, payment_expired, payment_failed, unsupported_engine, payment_unavailable. |
failure.message | string | null | One English sentence explaining the failure. |
commission | object | null | Commission terms once the booking is confirmed; null before that. |
commission.commissionableREQUIRED | boolean | True when the hotel has an active commission contract covering today. |
commission.rate | string | null | Commission as a decimal fraction string of the booking total, for example "0.10" for 10%; null when not commissionable or the contract has no rate. |
commission.partner_share | string | null | Your share of the commission as a decimal fraction string, for example "0.50"; null when not commissionable. |
timeline | TimelineEntry[] | Every status the booking has been in, oldest first, starting with queued. |
timeline[].atREQUIRED | string (date-time) | When the booking entered state, RFC 3339 UTC. |
timeline[].stateREQUIRED | string | The status the booking moved to. One of queued, processing, requires_payment, requires_user_action, confirmed, confirmation_pending, failed, expired, canceled. |
billing | null | Always null: bookings are free. |
Errors
| Status | Code | When |
|---|---|---|
| 400 | bad_request | Validation errors carry details.fields[]. |
| 401 | unauthorized | No key, or the key is unknown or revoked. |
| 403 | forbidden | Missing scope. |
| 404 | not_found | The hotel, offer, booking, job or webhook doesn't exist for this key. |
| 409 | conflict | Too late to cancel. |
| 429 | rate_limited | Over the per-minute limit for the tier. Wait Retry-After seconds. |
| 503 | unavailable | We're temporarily unable to serve the call. Retry with backoff. |
How checkout payment works (preview)
- 01Create the booking with
payment.mode: "checkout"and areturn_url. It answers202with the bookingqueued. - 02We re-check the price to the cent. The booking moves to
requires_paymentwithpayment.checkout_url, a Stripe Checkout link that expires after 30 minutes. - 03The guest pays the property. Show them the link; the money goes to the hotel, and
payment.payeeis alwaysproperty. When it completes,payment.statusiscompletedand the booking isprocessing. - 04We finish the reservation on the hotel's own booking engine. If the engine total no longer matches the quote, the booking fails with
price_changed_at_paymentand the guest's payment is reversed. - 05The hotel confirms and the booking is
confirmed, with the hotel's code inconfirmation. Every step firesbooking.updated.
VistaLink never holds the guest's money at any step: Checkout collects it for the property.
Eligibility: pay-now offers on engines where we can reach the hotel's payment form. Anything else fails straight away with unsupported_engine; use handoff for those. While checkout is in preview it may be switched off for your account; then a checkout booking fails at once with payment_unavailable, and handoff still works.
Shared objects.
Objects that appear in more than one place. Billing is on the Billing page and ErrorBody under Errors & limits.
Stay
Dates and rooms of a stay, up to 30 nights and 9 rooms.
| Field | Type | Description |
|---|---|---|
check_inREQUIRED | string (date) | Arrival date, YYYY-MM-DD. |
check_outREQUIRED | string (date) | Departure date, YYYY-MM-DD, after check_in and at most 30 nights later. |
rooms | RoomOccupancy[] | One entry per room, 1 to 9; defaults to one room for two adults. At most 9. |
rooms[].adults | integer | Adults in this room, 1 to 16. Default 2. |
rooms[].children_ages | integer[] | Age of each child in this room, 0 to 17, one entry per child; empty for adults only. At most 8. |
Offer
One bookable price for one room and rate, as observed on a channel.
| Field | Type | Description |
|---|---|---|
offer_idREQUIRED | string | Offer id, off_t plus 20 hex characters, specific to your account; book it with POST /v2/bookings within 30 minutes of receiving it. |
channelREQUIRED | string | Where the price was observed: direct for the hotel's own booking engine, third_party for the Enterprise third-party channel. One of direct, third_party, expedia. |
engine | string | null | Code of the hotel's booking engine for direct offers, for example SYNXIS; null on the third-party channel. |
room_id | string (uuid) | null | Catalogue room this offer is for, matching rooms[].room_id on hotel detail; null when the source names the room only by its own code or name. |
room_name | string | null | Room name as the source shows it; null when the source does not name the room. |
listing_key | string | null | The source's own key for the room, such as engine_room:SUP or room_type:1234567; null when the source has none. |
rate_name | string | null | Rate plan name as the source shows it, or one built from the cancellation terms and board, such as Free cancellation, breakfast included. |
totalREQUIRED | string | Price for the whole stay and the whole party, a decimal string in currency; taxes the source quoted separately are added in when their amount is known. "0" when sold out. |
currencyREQUIRED | string | ISO 4217 currency the price was observed in; never converted. |
nightly_avg | string | null | total divided by the number of nights, for the whole party rather than per room; null when sold out. |
includes_taxes | boolean | null | True when total includes taxes and fees, false when some are listed in excluded_charges, null when the source does not say. |
excluded_charges | ExcludedCharge[] | Charges payable on top of total; usually empty. |
excluded_charges[].kindREQUIRED | string | What the charge is; taxes_and_fees (taxes and fees the source quoted separately) is the only kind produced today. |
excluded_charges[].amountREQUIRED | string | Amount of the charge, a decimal string in currency. |
excluded_charges[].currencyREQUIRED | string | ISO 4217 currency of amount. |
excluded_charges[].per | string | null | What amount is charged per: stay (once for the whole stay, the only basis produced today), night, person or person_night. One of night, stay, person, person_night. |
excluded_charges[].note | string | null | Short note on how the charge is applied, for example quoted on top of the total. |
board | string | null | Meals included: none, breakfast, half (breakfast and dinner), full (all meals) or all_inclusive; null when the source does not say. One of none, breakfast, half, full, all_inclusive. |
free_cancellation | boolean | null | True when the rate can be canceled free of charge, false when it is non-refundable, null when unknown. |
cancellation_deadline | string (date-time) | null | Last moment to cancel free of charge, RFC 3339; set only on offers read live, null otherwise. |
payment | string | null | When the guest pays: prepay at booking or pay_at_hotel at the property; null when the source does not say. One of prepay, pay_at_hotel. |
occupancy | object | null | The party this price is for, totalled across all rooms. |
occupancy.adultsREQUIRED | integer | Adults in the room. |
occupancy.children_ages | integer[] | Age of each child in the room; empty for adults only. |
availability | string | available when the room can be booked at this price, sold_out when the source showed it as unavailable, unknown when it did not say. One of available, sold_out, unknown. Default unknown. |
observed_at | string (date-time) | null | When this price was observed, RFC 3339 UTC. |
url | string (uri) | null | Page on the source where this price was observed; null when the source has no linkable page. |
BookingPayment
The guest's payment. Null for external mode, and for checkout until the link is issued.
| Field | Type | Description |
|---|---|---|
modeREQUIRED | string | The payment mode the booking was created with. One of checkout, handoff, external. |
statusREQUIRED | string | pending until the guest pays, then completed, expired (link or session timed out, or the booking was canceled first) or failed; it never changes after leaving pending. One of pending, completed, expired, failed. |
checkout_url | string (uri) | null | checkout only: the hosted checkout link to show the guest; null in handoff mode. |
expires_at | string (date-time) | null | checkout only: when the checkout link stops working, about 30 minutes after it is issued; null in handoff mode. |
amount | string | null | What the guest pays, the offer's total as a decimal string in currency. |
currency | string | null | ISO 4217 currency of amount. |
payee | string | Who receives the money; always property. Always property. |
completed_at | string (date-time) | null | When the payment completed, RFC 3339 UTC; null until then. |
refund_required | boolean | null | True when the guest's checkout payment completed and the booking will not be made, so the payment must be reversed by the checkout provider: canceled before the engine step, failed with price_changed_at_payment, or the amount paid did not match the booking (payment_failed); null otherwise. |