Skip to content

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.

Your PMSanother vendor's deskYour websiteyour own booking engineZimmerStackchannel manageravailability · rates · restrictionsPMSbookings · guests · folio · inventoryChannelsBooking.com · MakeMyTrip · Agoda · …

What happens to one request

one transaction, under the property's inventory lock

key · scope · address

strict body

budgets

availability gate

guest · booking · rooms · payment

inventory event

202/201 to you

drain → channels (seconds)A refusal anywhere before the transaction writes nothing. A sold-out night inside it rolls everything back: 409 NO_AVAILABILITY.The inventory event commits with the booking, so a crash cannot lose the push; the drain finds the dirty nights and recomputes the counts.
  1. 1

    The key, its scope and its address

    A key without bookings:write is 403 SCOPE_MISSING; a request from outside the key's allowlist is 403 IP_NOT_ALLOWED. Nothing is read yet.
  2. 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. 3

    Your reservation id

    If externalReference was seen on this property before, you get that booking back with 200 and replay: true. Nothing is charged, nothing is written.
  4. 4

    The plan and the budgets

    The property's plan must carry the PMS (402 MODULE_NOT_ENTITLED otherwise). 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. 5

    One transaction, the front desk's shape

    Under the property's inventory lock: the availability gate (409 NO_AVAILABILITY names 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. 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

RouteScopeWhat it does
GET /bookingsbookings:readBookings the API created on this property, newest first
GET /bookings/{bookingId}bookings:readOne booking the API created
POST /bookingsbookings:writeCreate a booking in the PMS from your own booking engine
POST /bookings/{bookingId}/cancelbookings:writeCancel 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
  • 200OK
  • 400Bad 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)
Answer (data)
{
  "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
}
  • 200OK
  • 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)
  • 404No such booking on this property, or not one the API created
  • 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)

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.
Request body
{
  "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"
}
Answer (data)
{
  "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_TOTAL
  • 401Missing, malformed, unknown or revoked key
  • 402The property's plan does not carry the PMS
  • 403SCOPE_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_AVAILABLE
  • 413Body larger than 1048576 bytes (x-zimmerstack.limits.maxBodyBytes); nothing was read
  • 429data.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)
Request body
{
  "reason": "Guest cancelled on the website"
}
  • 200Cancelled
  • 401Missing, malformed, unknown or revoked key
  • 403The key lacks bookings:write, or the address is outside the allowlist
  • 404No such booking on this property, or not one the API created
  • 409Checked in or otherwise not cancellable from the API; PROPERTY_UNAVAILABLE; PMS_NOT_AVAILABLE
  • 429A request budget was exhausted; honour Retry-After

A full example

curl
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
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"
}
201 Created — data
{
  "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.