Skip to content

Reference

Channel manager

For a PMS that uses ZimmerStack purely as its channel manager: push absolute room counts, nightly prices and stay rules; we send them to every connected channel. OTA bookings come back through the webhooks.

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

The model

  • Writes are absolute. absolute values, last write wins within a request, unchanged cells are dropped before anything is written
  • Dates. YYYY-MM-DD, calendar dates in the property's timezone (GET /property); a stay occupies [checkIn, checkOut) — the checkout day is not a night
  • Money. rates are SENT in major units with the property's currency and RETURNED in minor units (rateMinor); stored as whole major units
  • No reservation endpoints for a channel-manager-only tenant. A walk-in booked in your PMS becomes a lower room count here. Mirroring your bookings would create a shadow PMS; the industry pattern is “the PMS pushes counts, the channel manager pushes bookings”.

Routes

RouteScopeWhat it does
GET /availabilityari:readAvailability as our record holds it
GET /batches/{batchId}ari:readOne accepted write and whether it has landed
GET /cataloganyRoom types and rate plans with the numeric ids every write uses
GET /propertyanyThe property this key is for
GET /ratesari:readRates as our record holds them (minor units)
GET /restrictionsari:readRestrictions as our record holds them
GET /syncari:readHas what you sent reached the channels?
PUT /availabilityari:writeSet absolute availability
PUT /ratesari:writeSet nightly sell prices
PUT /restrictionsari:writeSet stay restrictions

Base URL https://api.zimmerstack.com/api/v1/connect-api/v1

GET/availabilityari:read

Availability as our record holds it

Served from our record, never from the channel manager.

from (query)
Default: today in the property's timezone
to (query)
Default: from + 30 days; at most 500 days after from
  • 200OK
  • 400Bad range (data.code RANGE_INVALID)
  • 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/batches/{batchId}ari:read

One accepted write and whether it has landed

batchId (path, required)
Answer (data)
{
  "batchId": "7f1d2c4e-5a6b-4c8d-9e0f-112233445566",
  "resource": "rates",
  "createdAt": "2026-10-12T09:14:03.201Z",
  "requestedCells": 151,
  "acceptedCells": 43,
  "noopCells": 108,
  "rejected": [],
  "status": "pending",
  "pendingCells": 12,
  "lastError": 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 batch on this property
  • 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/catalog

Room types and rate plans with the numeric ids every write uses

  • 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)
  • 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/property

The property this key is for

  • 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)
  • 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/ratesari:read

Rates as our record holds them (minor units)

Served from our record, never from the channel manager.

from (query)
Default: today in the property's timezone
to (query)
Default: from + 30 days; at most 500 days after from
  • 200OK
  • 400Bad range (data.code RANGE_INVALID)
  • 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/restrictionsari:read

Restrictions as our record holds them

null = we hold no opinion for that field on that night.

from (query)
Default: today in the property's timezone
to (query)
Default: from + 30 days; at most 500 days after from
  • 200OK
  • 400Bad range (data.code RANGE_INVALID)
  • 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/syncari:read

Has what you sent reached the channels?

Poll this instead of asking the channel manager: inventory pipe state, rate cells still queued, the projector's last outcome and the newest batch still pending.

  • 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)
  • 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)

PUT/availabilityari:write

Set absolute availability

Each update sets the ABSOLUTE number of rooms for sale for one room type over a date range. Lands in our record and rides the inventory drain to the channels.

x-idempotency-key (header)
Optional. Same key + same body within 7 days replays the stored answer (X-Idempotent-Replay: true); same key + different body is refused (409 IDEMPOTENCY_KEY_REUSED).
Request body
{
  "updates": [
    {
      "roomTypeId": 412,
      "dateFrom": "2026-11-01",
      "dateTo": "2026-11-30",
      "available": 5
    },
    {
      "roomTypeId": 412,
      "dateFrom": "2026-11-14",
      "dateTo": "2026-11-14",
      "available": 2
    }
  ]
}
Answer (data)
{
  "batchId": "7f1d2c4e-5a6b-4c8d-9e0f-112233445566",
  "resource": "rates",
  "requestedCells": 151,
  "acceptedCells": 43,
  "noopCells": 108,
  "rejected": [],
  "pushedBy": "ari-projector",
  "poll": "/api/v1/connect-api/v1/batches/7f1d2c4e-5a6b-4c8d-9e0f-112233445566"
}
  • 202Accepted. The cells that differed from our record are written and queued; the push to the channels is asynchronous (typically seconds, minutes under load, bounded by the channel manager's budget, never lost). Poll `poll` or GET /sync.
  • 400data.code is one of VALIDATION | RANGE_INVALID | HORIZON | TOO_MANY_UPDATES | TOO_MANY_CELLS | UNKNOWN_ROOM_TYPE
  • 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)
  • 413Body larger than 1048576 bytes (x-zimmerstack.limits.maxBodyBytes); nothing was read
  • 422Every group was refused by the property's rate policy; data.details.rejected says why
  • 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)

PUT/ratesari:write

Set nightly sell prices

Each update sets the price of one rate plan over a date range, optionally masked to weekdays (`days`). `currency` must equal the property's. Lands in our record and rides the ARI projector to the channels.

x-idempotency-key (header)
Optional. Same key + same body within 7 days replays the stored answer (X-Idempotent-Replay: true); same key + different body is refused (409 IDEMPOTENCY_KEY_REUSED).
Request body
{
  "updates": [
    {
      "ratePlanId": 901,
      "dateFrom": "2026-11-01",
      "dateTo": "2027-03-31",
      "rate": "4500.00",
      "currency": "INR"
    },
    {
      "ratePlanId": 901,
      "dateFrom": "2026-11-01",
      "dateTo": "2027-03-31",
      "days": [
        "fr",
        "sa"
      ],
      "rate": 5200,
      "currency": "INR"
    }
  ]
}
Answer (data)
{
  "batchId": "7f1d2c4e-5a6b-4c8d-9e0f-112233445566",
  "resource": "rates",
  "requestedCells": 151,
  "acceptedCells": 43,
  "noopCells": 108,
  "rejected": [],
  "pushedBy": "ari-projector",
  "poll": "/api/v1/connect-api/v1/batches/7f1d2c4e-5a6b-4c8d-9e0f-112233445566"
}
  • 202Accepted. The cells that differed from our record are written and queued; the push to the channels is asynchronous (typically seconds, minutes under load, bounded by the channel manager's budget, never lost). Poll `poll` or GET /sync.
  • 400data.code is one of VALIDATION | RANGE_INVALID | HORIZON | TOO_MANY_UPDATES | TOO_MANY_CELLS | UNKNOWN_RATE_PLAN | CURRENCY_MISMATCH
  • 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)
  • 413Body larger than 1048576 bytes (x-zimmerstack.limits.maxBodyBytes); nothing was read
  • 422Every group was refused by the property's rate policy; data.details.rejected says why
  • 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)

PUT/restrictionsari:write

Set stay restrictions

Each update sets ANY subset of stopSell, closedToArrival, closedToDeparture, minStayArrival, minStayThrough, maxStay for one rate plan over a date range; fields you omit are left as they are.

x-idempotency-key (header)
Optional. Same key + same body within 7 days replays the stored answer (X-Idempotent-Replay: true); same key + different body is refused (409 IDEMPOTENCY_KEY_REUSED).
Request body
{
  "updates": [
    {
      "ratePlanId": 901,
      "dateFrom": "2026-12-24",
      "dateTo": "2026-12-26",
      "minStayArrival": 2,
      "closedToArrival": false
    },
    {
      "ratePlanId": 901,
      "dateFrom": "2026-12-31",
      "dateTo": "2026-12-31",
      "stopSell": true
    }
  ]
}
Answer (data)
{
  "batchId": "7f1d2c4e-5a6b-4c8d-9e0f-112233445566",
  "resource": "rates",
  "requestedCells": 151,
  "acceptedCells": 43,
  "noopCells": 108,
  "rejected": [],
  "pushedBy": "ari-projector",
  "poll": "/api/v1/connect-api/v1/batches/7f1d2c4e-5a6b-4c8d-9e0f-112233445566"
}
  • 202Accepted. The cells that differed from our record are written and queued; the push to the channels is asynchronous (typically seconds, minutes under load, bounded by the channel manager's budget, never lost). Poll `poll` or GET /sync.
  • 400data.code is one of VALIDATION | RANGE_INVALID | HORIZON | TOO_MANY_UPDATES | TOO_MANY_CELLS | UNKNOWN_RATE_PLAN
  • 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)
  • 413Body larger than 1048576 bytes (x-zimmerstack.limits.maxBodyBytes); nothing was read
  • 422Every group was refused by the property's rate policy; data.details.rejected says why
  • 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)

What a write answers

202 Accepted — data
{
  "batchId": "7f1d2c4e-5a6b-4c8d-9e0f-112233445566",
  "resource": "rates",
  "requestedCells": 151,
  "acceptedCells": 43,
  "noopCells": 108,
  "rejected": [],
  "pushedBy": "ari-projector",
  "poll": "/api/v1/connect-api/v1/batches/7f1d2c4e-5a6b-4c8d-9e0f-112233445566"
}

acceptedCells differed from our record and were written; noopCells already held the value and cost nothing; rejected names groups our record refused (a plan whose policy forbids exceptions). Poll poll or GET /sync.

GET /batches/{batchId} — data
{
  "batchId": "7f1d2c4e-5a6b-4c8d-9e0f-112233445566",
  "resource": "rates",
  "createdAt": "2026-10-12T09:14:03.201Z",
  "requestedCells": 151,
  "acceptedCells": 43,
  "noopCells": 108,
  "rejected": [],
  "status": "pending",
  "pendingCells": 12,
  "lastError": null
}

Pacing

  • Batch: one PUT per resource carrying ranges (dateFrom..dateTo, optional days) — a 365-night calendar for three plans is a handful of updates, not a thousand
  • Send on change, not on a timer: unchanged cells are dropped and cost nothing, but they still spend your request budget
  • Read RateLimit on every answer and slow down when r approaches 0, instead of waiting for a 429
  • Reuse x-idempotency-key on a retry so a timed-out request can never land twice
  • Poll GET /batches/{batchId} and GET /sync no more than every 15 s; the push to the channels is asynchronous and bounded by the channel manager's own budget

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.