Reservations
You create a reservation in two steps: hold the inventory while the guest checks out, then confirm the hold. Splitting it this way reserves the room the moment the guest commits, and finalizes the reservation only when you confirm it.
The reservation lifecycle
POST /holds creates a tentative reservation that holds a room for 15 minutes. POST /holds/{id}/confirm turns it into a confirmed reservation and, when you send a payment reference, records the payment. A hold you do not confirm expires and releases its inventory.
Hold inventory
POST /holdsScope: reservations:write · supports Idempotency-Key
Creates a tentative reservation that holds a room type on a rate plan for the date range. The stay is priced (room nights, add-ons, and applicable taxes) onto a new folio, at the same price /rates quoted, including any revenue-optimization daily prices. The hold counts against availability until you confirm it or its 15-minute window passes.
| Body field | Required | Notes |
|---|---|---|
room_type_id | yes | Must belong to this property. |
rate_plan_id | yes | An active plan for the room type. Its visibility and offer rules are checked again. |
check_in | yes | YYYY-MM-DD. |
check_out | yes | YYYY-MM-DD, after check_in; at most 30 nights. |
guest_id | yes | The guest the hold is for. |
adults | yes | Integer from 1 to 20. The party must fit the room type's max_occupancy. |
children | no | Integer from 0 to 20. Default 0. |
child_ages | no | Array of child ages (integers, 0 or more; up to 20). When present it is the child count and drives age-based extra-guest pricing. |
add_ons | no | Array of up to 10 guest-selected add-ons, each priced onto the folio at hold time so it is inside the total. See the fields below. |
promo_code | no | Needed when the chosen rate plan is gated behind a promo code. |
Each entry in add_ons names a guest-bookable service:
| Add-on field | Required | Notes |
|---|---|---|
service_id | yes | The service being added. Each service may appear once. |
modifier_values | no | Object of { modifier_key: value } for the service's fields. Defaults to {}. |
quantity | no | Units of the add-on, a whole number from 1 to 99. Defaults to 1. |
curl -X POST "$VRDN_BASE/holds" \
-H "Authorization: Bearer $VRDN_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 6f1d2c9a-8b3e-4a17-9c2f-1e5b7d0a4c83" \
-d '{
"room_type_id": "9b2f6c3e-8d1a-4f5b-a7c9-4e0d2b8f1a63",
"rate_plan_id": "5c1e8f4a-2b7d-4a9c-8e6f-1d3b9a5c7e02",
"check_in": "2026-08-01",
"check_out": "2026-08-04",
"guest_id": "3f8a2c1b-9d4e-4c6a-8b7f-5e2d1a9c0b34",
"adults": 2
}'Returns 201:
{
"reservation_id": "7d5dc2ea-1f4e-4bfa-9a52-6c3f08b41e27",
"folio_id": "1a6c3e9f-4d2b-4e8a-b5c7-9f0e6d4a2c81",
"hold_expires_at": "2026-06-19T08:45:00.000Z",
"total": "1260.00",
"currency": "USD",
"deposit_amount": "378.00",
"balance_due_date": "2026-07-18",
"status": "tentative"
}| Field | Type | Notes |
|---|---|---|
hold_expires_at | string | When the hold lapses if you have not confirmed it. |
total | string | The full price of the stay. |
deposit_amount | string | What the property's payment terms ask for when reserved. Frozen with the hold. |
balance_due_date | string | null | When the rest is due, under the same terms. |
| Error | When |
|---|---|
409 no_availability | The room type is sold out for the range. |
404 rate_plan_not_found | The plan is inactive, its visibility rules exclude this guest, the stay falls outside its sale or stay window or its length-of-stay or day-of-week rules, or it needs a promo code you did not send. These all read the same, so a hidden plan is never revealed. |
404 room_type_not_found / 404 guest_not_found | The room type or guest does not exist in this property. |
409 guest_merged | The guest was merged into another profile. Hold for that guest instead. |
400 party_too_large | The party is larger than the room type's max_occupancy. |
400 bed_config_removed | The body still sends bed_config. Bed configuration was removed from Veridien, so leave the field out. |
400 exchange_rate_not_configured | The plan's currency has no exchange rate set up. |
404 service_not_found / 409 addon_unavailable | An add-on does not exist or can no longer be added. |
400 duplicate_addon, missing_modifier, invalid_modifier_option, invalid_modifier_value, addon_currency_mismatch | An add-on was listed twice, a required field is missing, a value is not allowed, or its currency differs from the reservation's. |
Confirm a hold
POST /holds/{id}/confirmScope: reservations:write · supports Idempotency-Key
Confirms the hold. Veridien checks availability again (leaving out the hold itself), assigns a physical room, marks the reservation and its stays confirmed, and records the payment you report. It then awards loyalty points on the room revenue, valued in the property's base currency, and sends the guest's confirmation email when the source is set to send it.
You may confirm without a payment. Veridien takes no payments online, so a reservation can be confirmed unpaid; the frozen deposit_amount and balance_due_date then say what is owed, and staff record the money when it arrives.
A payment reference is a claim, not proof
A payment_reference is your assertion that you captured the money. Veridien does not check that any money moved: your hotel is the merchant of record, and you own the funds, disputes, and refunds. Because of that:
- Send a real, unique reference for each reservation. Never reuse or change a payment id across reservations.
- Every confirm with a reference is audit-logged as a payment assertion (which key asserted it, the reference, the amount).
- The checks below reject obviously wrong input, but they cannot prove that money changed hands.
| Body field | Required | Notes |
|---|---|---|
payment_reference | no | Your payment id, up to 200 characters. Leave it out to confirm unpaid. Confirmation is idempotent on this value. |
payment_currency | no | The 3-letter currency you charged in. Rejected if it differs from the reservation currency. |
payment_amount | no | The amount you captured, as a number. It must be at least the deposit and at most the folio total. When you leave it out, the deposit amount is recorded. |
curl -X POST "$VRDN_BASE/holds/7d5dc2ea-1f4e-4bfa-9a52-6c3f08b41e27/confirm" \
-H "Authorization: Bearer $VRDN_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 9a2e7c10-4b3f-4d28-8c1f-2e6b8d0a5d94" \
-d '{ "payment_reference": "example-payment-1001", "payment_currency": "USD", "payment_amount": 1260.00 }'{
"reservation_id": "7d5dc2ea-1f4e-4bfa-9a52-6c3f08b41e27",
"status": "confirmed",
"check_in_date": "2026-08-01",
"check_out_date": "2026-08-04",
"currency": "USD",
"folio_balance": "0.00",
"deposit_amount": "378.00",
"balance_due_date": "2026-07-18",
"already_confirmed": false
}| Error | When |
|---|---|
404 reservation_not_found | No reservation with this id in the property. |
409 hold_expired | The hold's 15-minute window passed before you confirmed. Create a new hold. |
409 no_availability | The hold lost its inventory between hold and confirm. |
409 no_room_to_assign | No physical room is free to assign for the stay. |
409 not_confirmable | The reservation is not a tentative hold (for example, it was cancelled). |
400 currency_mismatch | payment_currency differs from the reservation currency. |
400 amount_below_deposit | payment_amount is less than the deposit. |
400 amount_above_total | payment_amount is more than the folio total. |
400 promo_code_exhausted / promo_code_guest_limit | The reservation's promo code ran out of uses, or this guest has used it as often as it allows. |
Idempotent two ways
Confirming a reservation that is already confirmed, or replaying the same payment_reference, returns the existing result with "already_confirmed": true: no second payment and no second reservation. A unique index on the folio and reference keeps this safe under concurrent requests. Add an Idempotency-Key and confirmation is safe to retry.
List reservations
GET /reservationsScope: reservations:read
Returns the property's reservations, earliest check-in first, each with its folio balance.
| Query parameter | Notes |
|---|---|
guest_id | Only this guest's reservations. |
status | Only this status: tentative, confirmed, checked_in, checked_out, cancelled, or no_show. Any other value is ignored. |
limit | Page size. Default 25, maximum 100. |
offset | Records to skip. |
curl "$VRDN_BASE/reservations?guest_id=3f8a2c1b-9d4e-4c6a-8b7f-5e2d1a9c0b34&limit=10" -H "Authorization: Bearer $VRDN_KEY"{
"has_more": false,
"data": [
{
"id": "7d5dc2ea-1f4e-4bfa-9a52-6c3f08b41e27",
"confirmation_code": "B7XKQ4M2",
"status": "confirmed",
"guest_id": "3f8a2c1b-9d4e-4c6a-8b7f-5e2d1a9c0b34",
"check_in_date": "2026-08-01",
"check_out_date": "2026-08-04",
"room_type_id": "9b2f6c3e-8d1a-4f5b-a7c9-4e0d2b8f1a63",
"room_type_name": "Deluxe Ocean Villa",
"nightly_rate": "420.00",
"currency": "USD",
"folio_balance": "0.00",
"balances": [{ "currency": "USD", "charges": "1260.00", "payments": "1260.00", "balance": "0.00" }]
}
]
}confirmation_code is the code to show guests and staff. Address the reservation in requests by its id. See Conventions for paging through results.
Get a reservation
GET /reservations/{id}Scope: reservations:read
Returns one reservation with its stays (one per room) and folio balance. accommodations is a deprecated copy of stays, kept until 1.0.0; read stays. An unknown id returns 404 reservation_not_found.
curl "$VRDN_BASE/reservations/7d5dc2ea-1f4e-4bfa-9a52-6c3f08b41e27" -H "Authorization: Bearer $VRDN_KEY"{
"id": "7d5dc2ea-1f4e-4bfa-9a52-6c3f08b41e27",
"confirmation_code": "B7XKQ4M2",
"status": "confirmed",
"guest_id": "3f8a2c1b-9d4e-4c6a-8b7f-5e2d1a9c0b34",
"check_in_date": "2026-08-01",
"check_out_date": "2026-08-04",
"room_type_id": "9b2f6c3e-8d1a-4f5b-a7c9-4e0d2b8f1a63",
"room_type_name": "Deluxe Ocean Villa",
"nightly_rate": "420.00",
"currency": "USD",
"adults": 2,
"children": 0,
"number_of_guests": 2,
"booking_source": "direct_web",
"special_notes": null,
"folio_id": "1a6c3e9f-4d2b-4e8a-b5c7-9f0e6d4a2c81",
"folio_balance": "0.00",
"balances": [{ "currency": "USD", "charges": "1260.00", "payments": "1260.00", "balance": "0.00" }],
"stays": [
{
"id": "2b9e6f4d-8a3c-4e1b-9d5f-7a0c4e8b2d63",
"room_id": "c3a7e1f9-5b2d-4e8c-a6f0-9d4b2e7c1a35",
"room_type_id": "9b2f6c3e-8d1a-4f5b-a7c9-4e0d2b8f1a63",
"check_in_date": "2026-08-01",
"check_out_date": "2026-08-04",
"status": "confirmed",
"nightly_rate": "420.00"
}
]
}Cancel a reservation
POST /reservations/{id}/cancelScope: reservations:write · supports Idempotency-Key
Cancels a tentative or confirmed reservation and frees its inventory. A reservation in any other status cannot be cancelled through the API (409 not_cancellable).
Nothing is refunded automatically. If the reservation was paid, the cancellation policy's fee is posted to the folio, which stays open, and refund_due_minor reports what is owed back. When no fee is kept, the folio and any invoice are voided and kept for audit. A promo-code use the reservation claimed is released.
| Body field | Required | Notes |
|---|---|---|
reason | no | Up to 500 characters. Stored as the cancellation reason. Defaults to "Cancelled via API". |
curl -X POST "$VRDN_BASE/reservations/7d5dc2ea-1f4e-4bfa-9a52-6c3f08b41e27/cancel" \
-H "Authorization: Bearer $VRDN_KEY" \
-H "Content-Type: application/json" \
-d '{ "reason": "Guest changed plans" }'{ "reservation_id": "7d5dc2ea-1f4e-4bfa-9a52-6c3f08b41e27", "status": "cancelled", "refund_due_minor": 0 }| Field | Type | Notes |
|---|---|---|
refund_due_minor | integer | What the cancellation policy says is owed back to the guest, in minor units (cents). 0 when the reservation was unpaid or the policy refunds nothing. This is an obligation, not a completed refund: Veridien does not move money, so your property returns it. |
Related pages
- Folios: read the bill and post charges.
- Guests and loyalty: the guest a reservation belongs to.