Reference
Bookings
For hotels that run the ZimmerStack PMS and their own booking engine: hand us the reservation your website sold and it lands in the PMS exactly as a front-desk booking — guest, folio, inventory, channels.
What happens to one request
- 1
The key, its scope and its address
A key withoutbookings:writeis403 SCOPE_MISSING; a request from outside the key's allowlist is403 IP_NOT_ALLOWED. Nothing is read yet. - 2
The body, strictly
Every field is validated against the schema the contract publishes. An unknown field is a 400, never dropped silently; names, notes and references are length-capped; amounts are bounded. - 3
Your reservation id
IfexternalReferencewas seen on this property before, you get that booking back with200andreplay: true. Nothing is charged, nothing is written. - 4
The plan and the budgets
The property's plan must carry the PMS (402 MODULE_NOT_ENTITLEDotherwise). Then the three booking budgets: 20 a minute per key, 40 a minute per property, 500 per property per rolling day (the owner can raise it). - 5
One transaction, the front desk's shape
Under the property's inventory lock: the availability gate (409 NO_AVAILABILITYnames the night and rolls everything back), guest find-or-create by phone, the booking, one room row per entry, the payment if any, meal-plan charges, the activity log, the inventory event, and the domain event. All or nothing. - 6
After commit
The drain recomputes the touched nights and pushes them to every connected channel (seconds); the board shows the booking live; the owner is notified. The guest hears from your site, not from us, unless the owner switched our confirmation on.
The rules
- who. Hotels that run the ZimmerStack PMS and their OWN booking engine: hand us the reservation your website sold and it lands in the PMS exactly as a front-desk booking — guest, folio, inventory, channels.
- pricing. You price the stay. Send the nightly rate you charged per room; we store it and never re-quote. currency must equal the property's.
- inventory. Availability is checked inside the booking transaction under the property's inventory lock (a sold-out night is 409 NO_AVAILABILITY, nothing written). The inventory event commits with the booking; the drain pushes the new counts to every connected channel within seconds.
- idempotency. externalReference is YOUR reservation id and is unique per property: the same value again returns the same booking with HTTP 200 and replay: true. Use it on every retry.
- payments. A payment is recorded only when the key holds bookings:payments and never above the stay total. Nothing collected = omit payment; the desk collects at check-in.
- guest. By default the guest hears from YOUR site only; the owner can switch on our confirmation per property (admin → Developers → Settings).
- cancel. POST /bookings/{bookingId}/cancel cancels the whole booking and re-opens its nights on the channels; a booking already checked in is 409 BOOKING_NOT_CANCELLABLE. Date or room changes: cancel and re-create (a modify endpoint is planned).
- scope. A key reads and cancels only bookings the API created on its property; front-desk and OTA bookings are never visible (BOOKING_NOT_FOUND).
Routes
| Route | Scope | What it does |
|---|---|---|
| GET /bookings | bookings:read | Bookings the API created on this property, newest first |
| GET /bookings/{bookingId} | bookings:read | One booking the API created |
| POST /bookings | bookings:write | Create a booking in the PMS from your own booking engine |
| POST /bookings/{bookingId}/cancel | bookings:write | Cancel a booking the API created; its nights re-open on the channels |
Base URL https://api.zimmerstack.com/api/v1/connect-api/v1
GET/bookingsbookings:read
Bookings the API created on this property, newest first
Scope bookings:read. Only bookings a key of this property created; front-desk and OTA bookings are never listed.
- since (query)
- Created on or after this instant; default the last 7 days
- externalReference (query)
- Only the booking with your reservation id
- limit (query)
- Default 50
200OK400Bad query (data.code VALIDATION)401Missing, malformed, unknown or revoked key (data.code API_KEY_INVALID | API_KEY_REVOKED)403The key lacks the scope this route needs (SCOPE_MISSING, details.needed / details.held), or the request came from outside the key's address allowlist (IP_NOT_ALLOWED)409The property is deleted or demo (PROPERTY_UNAVAILABLE); on an ARI route its channel manager is off (CHANNEL_MANAGER_DISABLED); on a booking route it has no PMS (PMS_NOT_AVAILABLE)429A budget was exhausted — data.scope names it (ip · ip-auth · requests · writes · property · property-writes · cells · global); honour Retry-After and pace on the RateLimit header (x-zimmerstack.rateLimits)
GET/bookings/{bookingId}bookings:read
One booking the API created
- bookingId (path, required)
{
"bookingId": 48213,
"reference": "SB-7KQ2MX9D",
"externalReference": "WEB-2026-000123",
"status": "UPCOMING",
"checkIn": "2026-12-20",
"checkOut": "2026-12-23",
"nights": 3,
"currency": "INR",
"totalAmount": "27000.00",
"totalPaid": "9000.00",
"balanceDue": "18000.00",
"guest": {
"name": "Asha Verma",
"phone": "+919876543210",
"email": "asha@example.com"
},
"rooms": [
{
"bookingRoomId": 90001,
"roomTypeId": 412,
"ratePlanId": null,
"mealPlan": "CP",
"adults": 2,
"children": 1,
"nightlyRate": "4500.00",
"status": "UPCOMING"
},
{
"bookingRoomId": 90002,
"roomTypeId": 412,
"ratePlanId": null,
"mealPlan": "CP",
"adults": 2,
"children": 0,
"nightlyRate": "4500.00",
"status": "UPCOMING"
}
],
"inventory": {
"eventRecorded": true,
"channelManager": true,
"pushedBy": "inventory-drain"
},
"replay": false,
"createdAt": "2026-10-20T09:14:03.201Z",
"cancelledAt": null
}200OK401Missing, malformed, unknown or revoked key (data.code API_KEY_INVALID | API_KEY_REVOKED)403The key lacks the scope this route needs (SCOPE_MISSING, details.needed / details.held), or the request came from outside the key's address allowlist (IP_NOT_ALLOWED)404No such booking on this property, or not one the API created409The property is deleted or demo (PROPERTY_UNAVAILABLE); on an ARI route its channel manager is off (CHANNEL_MANAGER_DISABLED); on a booking route it has no PMS (PMS_NOT_AVAILABLE)429A budget was exhausted — data.scope names it (ip · ip-auth · requests · writes · property · property-writes · cells · global); honour Retry-After and pace on the RateLimit header (x-zimmerstack.rateLimits)
POST/bookingsbookings:write
Create a booking in the PMS from your own booking engine
Scope bookings:write (+ bookings:payments to record a payment). One entry in `rooms` per room. The stay is checked for availability under the property's inventory lock and written with its inventory event in one transaction; the channels update within seconds. A repeated externalReference answers 200 with the same booking (replay: true). 1–90 nights, arrival from today to today + horizonDays, in the property's timezone.
- x-idempotency-key (header)
- Optional on top of externalReference: same key + same body within 7 days replays the stored answer.
{
"externalReference": "WEB-2026-000123",
"checkIn": "2026-12-20",
"checkOut": "2026-12-23",
"currency": "INR",
"guest": {
"name": "Asha Verma",
"phone": "+919876543210",
"email": "asha@example.com",
"notes": "Late arrival, after 22:00"
},
"rooms": [
{
"roomTypeId": 412,
"ratePlanId": 901,
"adults": 2,
"children": 1,
"nightlyRate": "4500.00"
},
{
"roomTypeId": 412,
"ratePlanId": 901,
"adults": 2,
"nightlyRate": "4500.00"
}
],
"payment": {
"amount": "9000.00",
"method": "ONLINE",
"reference": "pay_QwErTy123"
},
"notes": "Promo WINTER10 applied on the website"
}{
"bookingId": 48213,
"reference": "SB-7KQ2MX9D",
"externalReference": "WEB-2026-000123",
"status": "UPCOMING",
"checkIn": "2026-12-20",
"checkOut": "2026-12-23",
"nights": 3,
"currency": "INR",
"totalAmount": "27000.00",
"totalPaid": "9000.00",
"balanceDue": "18000.00",
"guest": {
"name": "Asha Verma",
"phone": "+919876543210",
"email": "asha@example.com"
},
"rooms": [
{
"bookingRoomId": 90001,
"roomTypeId": 412,
"ratePlanId": null,
"mealPlan": "CP",
"adults": 2,
"children": 1,
"nightlyRate": "4500.00",
"status": "UPCOMING"
},
{
"bookingRoomId": 90002,
"roomTypeId": 412,
"ratePlanId": null,
"mealPlan": "CP",
"adults": 2,
"children": 0,
"nightlyRate": "4500.00",
"status": "UPCOMING"
}
],
"inventory": {
"eventRecorded": true,
"channelManager": true,
"pushedBy": "inventory-drain"
},
"replay": false,
"createdAt": "2026-10-20T09:14:03.201Z",
"cancelledAt": null
}200Already created by an earlier call with this externalReference (replay: true)201Created. The booking is in the PMS; inventory.pushedBy says how the channels learn.400data.code is one of VALIDATION | CURRENCY_MISMATCH | STAY_INVALID | UNKNOWN_ROOM_TYPE | UNKNOWN_RATE_PLAN | UNKNOWN_ROOM_TYPE_FOR_PLAN | OCCUPANCY_EXCEEDED | PAYMENT_EXCEEDS_TOTAL401Missing, malformed, unknown or revoked key402The property's plan does not carry the PMS403SCOPE_MISSING (no bookings:write), IP_NOT_ALLOWED, or PAYMENT_NOT_ALLOWED (a payment without bookings:payments)409NO_AVAILABILITY (the night and the count are in details; nothing was written), PROPERTY_UNAVAILABLE, PMS_NOT_AVAILABLE413Body larger than 1048576 bytes (x-zimmerstack.limits.maxBodyBytes); nothing was read429data.scope is bookings · property-bookings · property-bookings-day (a rolling day) or one of the request budgets; honour Retry-After
POST/bookings/{bookingId}/cancelbookings:write
Cancel a booking the API created; its nights re-open on the channels
Scope bookings:write. Idempotent: an already-cancelled booking answers 200 with replay: true. A booking the desk has checked in is 409 BOOKING_NOT_CANCELLABLE.
- bookingId (path, required)
{
"reason": "Guest cancelled on the website"
}200Cancelled401Missing, malformed, unknown or revoked key403The key lacks bookings:write, or the address is outside the allowlist404No such booking on this property, or not one the API created409Checked in or otherwise not cancellable from the API; PROPERTY_UNAVAILABLE; PMS_NOT_AVAILABLE429A request budget was exhausted; honour Retry-After
A full example
curl -X POST "https://api.zimmerstack.com/api/v1/connect-api/v1/bookings" \
-H "Authorization: Bearer $ZIMMERSTACK_CONNECT_KEY" \
-H "Content-Type: application/json" \
-d @booking.json{
"externalReference": "WEB-2026-000123",
"checkIn": "2026-12-20",
"checkOut": "2026-12-23",
"currency": "INR",
"guest": {
"name": "Asha Verma",
"phone": "+919876543210",
"email": "asha@example.com",
"notes": "Late arrival, after 22:00"
},
"rooms": [
{
"roomTypeId": 412,
"ratePlanId": 901,
"adults": 2,
"children": 1,
"nightlyRate": "4500.00"
},
{
"roomTypeId": 412,
"ratePlanId": 901,
"adults": 2,
"nightlyRate": "4500.00"
}
],
"payment": {
"amount": "9000.00",
"method": "ONLINE",
"reference": "pay_QwErTy123"
},
"notes": "Promo WINTER10 applied on the website"
}{
"bookingId": 48213,
"reference": "SB-7KQ2MX9D",
"externalReference": "WEB-2026-000123",
"status": "UPCOMING",
"checkIn": "2026-12-20",
"checkOut": "2026-12-23",
"nights": 3,
"currency": "INR",
"totalAmount": "27000.00",
"totalPaid": "9000.00",
"balanceDue": "18000.00",
"guest": {
"name": "Asha Verma",
"phone": "+919876543210",
"email": "asha@example.com"
},
"rooms": [
{
"bookingRoomId": 90001,
"roomTypeId": 412,
"ratePlanId": null,
"mealPlan": "CP",
"adults": 2,
"children": 1,
"nightlyRate": "4500.00",
"status": "UPCOMING"
},
{
"bookingRoomId": 90002,
"roomTypeId": 412,
"ratePlanId": null,
"mealPlan": "CP",
"adults": 2,
"children": 0,
"nightlyRate": "4500.00",
"status": "UPCOMING"
}
],
"inventory": {
"eventRecorded": true,
"channelManager": true,
"pushedBy": "inventory-drain"
},
"replay": false,
"createdAt": "2026-10-20T09:14:03.201Z",
"cancelledAt": null
}Cancel, modify, read
POST /bookings/{bookingId}/cancel cancels the whole booking and re-opens its nights on the channels; a booking the desk has already checked in is 409 BOOKING_NOT_CANCELLABLE. Date or room changes: cancel and create again (a modify endpoint is planned).GET /bookings lists what the API created on the property, newest first; GET /bookings/{bookingId} reads one. Front-desk and OTA bookings are never visible to a key: they answer 404 BOOKING_NOT_FOUND.
Testing before you go live
Use a staging key the owner mints for you (up to five keys per property) and dates a few weeks out. A booking you create is real in the PMS — cancel it when you are done. The Postman collection's folder 5 walks through create → read → list → cancel.
Facts on this page (routes, samples, limits, codes) come from the API's public contract read live, Tue, 20 Oct 2026 13:10:40 GMT.