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 three things: check availability, get a quote, create a hold. It cannot read guest details or move money, no matter what a compromised or misconfigured setting says — the cap is enforced in code, not just by convention.
  • 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 booking needs the guest to verify their email with a one-time code and — once payment support ships — to pay. Both of those steps run on our server, 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.

Payment itself (Stripe) hasn’t shipped yet: confirming a verified hold today has no payment check. See Holds and confirmation for what that means for your integration in the meantime.

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 once payments ship. 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. Your operator contact requests a key from us, and we hand you the plaintext once, at creation — it isn’t shown again, so store it somewhere safe (a secrets manager, not a spreadsheet or a chat message). Key issuance currently goes through our internal tooling, not a self-serve dashboard, so a new or rotated key comes from your operator contact.

If a key is compromised, tell your operator contact immediately — revocation takes effect on the very next request.