Skip to content

Operate

Security

What protects the property on our side, in plain words — and the handful of things you must do on yours.

The layers a request passes

address budget

key (hash, constant-time)

allowlist

scope

strict schema

standing & plan

budgets

tenancy (one property)

row

Every layer refuses with a stable code and writes nothing. Read left to right: the cheap checks come first, so a flood costs us 429s, not database reads.

What we do

  • Keys are never stored. We keep a keyed hash under a derived secret and compare it in constant time across every candidate. Nobody at ZimmerStack can read a key back; HQ can only revoke.
  • One key, one property. A key acts as the owner for exactly one property. Room-type, rate-plan and booking ids outside it are refused by code, never hinted at.
  • Scopes. Every route names the one scope it needs. A key without it is 403 SCOPE_MISSING. Recording a payment is a separate grant from creating a booking.
  • Address allowlist. An optional per-key CIDR list, checked against the address our edge saw (your forwarded-for headers are ignored). Refusals are counted and shown to the owner.
  • Strict schemas. Every body is validated against the published contract: unknown fields refused, strings capped, numbers bounded. Nothing you did not design for reaches a row.
  • Budgets before writes. Per address, per key, per property, per day — and a global ceiling. Checked in order from cheapest to dearest, so a flood costs us 429s, not database reads.
  • Inventory is gated, every time. A booking is written under the same lock and availability gate the front desk uses; a sold-out night rolls the whole transaction back.
  • Guests hear from you. By default we send API bookings no email or WhatsApp — your site already did — so a leaked key can never turn our mailer into a spam cannon.
  • Zero direct channel calls. A key never reaches the channel manager. Writes land in our record; our own paced senders carry them, inside the channel manager's budget.
  • Uniform refusals. A bad key is 401 and says no more; a foreign id is 404, never 403; a revoked key is told so. There is no enumeration oracle.
  • Audit. Every key event (created, edited, rotated, revoked, refused by allowlist) and every booking the API created is traceable to the key, on the property's activity log and in the owner's screens.

What you must do

  • Keep the key on a server. Never in a browser, a mobile app, a repository or a chat. Load it from your secret store; one key per environment.
  • Set the allowlist. Give the owner your servers' public egress addresses. If they change, the owner edits the key; no rotation needed.
  • Grant the least. A booking engine needs bookings:write (and bookings:read to reconcile); add bookings:payments only if your site collects money. A PMS needs the ari pair.
  • Rotate on any doubt. A rotated key keeps the old one answering for 24 hours; swapping costs nothing. Revoke on a confirmed leak.
  • Use externalReference. Your own reservation id makes every retry safe: the same value returns the same booking. Send it always.
  • Verify webhooks. Check the signature with a constant-time compare, refuse stale timestamps, and treat deliveries as at-least-once.
  • Watch the owner's screens. Admin → Developers shows every key's last use, refused addresses and every API booking. Ask the owner to look if anything seems off.