Skip to content

Authentication: secret keys vs publishable keys

Every request to /v1 carries one API key as a bearer token:

Authorization: Bearer sk_live_...

or

Authorization: Bearer pk_live_...

There are two kinds. Using the wrong one for the job is the single most common integration mistake — read this page before you write any code.

  • Live only on your server. Never send one to a browser, a mobile app, or a log you don’t control.
  • Can do anything your operator granted: read guest details, create and cancel bookings, manage rates.
  • Default rate limit: 300 requests/minute, burst 50.

Publishable keys (pk_live_… / pk_test_…)

Section titled “Publishable keys (pk_live_… / pk_test_…)”
  • Safe to embed in a public page — that’s the point. It’s what the widget uses.
  • Hard-capped, on our server, to four things: check availability (including the per-night calendar, which is the same scope), get a quote, create a hold, and start a Stripe-hosted checkout for that hold. On the calendar endpoint specifically, a pk_ key never receives a night’s occupants (guest id, label, state) — that field is sk_/operator-session only.
  • Must be sent from an allowlisted origin. We check the browser’s Origin header against the origins registered for that key; a request from any other origin is refused.
  • Tighter rate limit: 60 requests/minute per key+IP, plus a separate 10/minute/IP cap on hold creation specifically (this stops one visitor from squatting all your inventory with holds).

Why a hold from a pk_ key isn’t a booking

Section titled “Why a hold from a pk_ key isn’t a booking”

A hold made with a publishable key is unconfirmed inventory only. Turning it into a confirmed booking needs a succeeded card payment — either through the sk_-only PaymentIntent route (your own server-side checkout) or through checkout:create (a pk_-reachable hosted-checkout handoff — what the widget uses). Either way, the actual charge and the confirmation check both happen on our server and Stripe’s, never in the browser. That server-side gate is what makes it safe to put a pk_ key in a page anyone can view-source.

Card payment (Stripe) is live: confirming a hold requires a succeeded payment, checked server-side. See Holds and confirmation for the full mechanics.

sk_test_… and pk_test_… hit the exact same API, against the operator’s sandbox. Reservations made with a test key are flagged test, and payments run in Stripe’s test mode. Build and test against _test_ keys; switch to _live_ keys only when you go to production — the request shape doesn’t change, only the prefix.

There’s no sign-up form for you as an integrator. Your operator contact — an admin on the tenant’s dashboard — issues the key and hands you the plaintext once, at creation.

If a key is compromised, tell your operator contact immediately — they can revoke it from their dashboard, and revocation takes effect on the very next request.

  • Quickstart — your first request with a real key.
  • Errors — how a wrong-key-kind request comes back (spoiler: 404, not 403).
  • Rate limits — the limits above, in full.