Keys
There are three kinds of credential, and a fourth route group that takes none
at all. The differences are worth ten minutes now, because picking the wrong
one produces a 403 whose fix is not obvious from the message.
Every key travels the same way — as a bearer token:
Authorization: Bearer sk_4bT1nQ7xLd9sVzK0pR2eYw8hC6jM3gF5uA1oS7iN0dEThe two scopes
Section titled “The two scopes”A workspace key belongs to one workspace. It is created in the dashboard under Developer → API keys, or minted through the account API. Everything it can see is that one business: its services, its calendars, its bookings.
An account key belongs to the account, which is the billing entity that owns one or more workspaces. The two scopes are separated at the credential itself: an account key can never satisfy a workspace route, and a workspace key can never satisfy an account route.
The account key exists for one job: creating a workspace by API, and then minting the first key for it.
The three key types
Section titled “The three key types”| Prefix | Scope | Where it runs | What it reaches |
|---|---|---|---|
pk_ |
Workspace | A browser | Three endpoints. See below. |
sk_ |
Workspace | Your server | Everything under /v1, except /v1/account. |
ak_ |
Account | Your server | /v1/account only. |
An account key does not unlock workspace routes. If you hold ak_ and want
to read a workspace’s bookings, mint an sk_ for that workspace and use it.
That is deliberate: the account key’s surface is four routes and no more.
The publishable key is public on purpose
Section titled “The publishable key is public on purpose”A publishable key is not a secret, and treating it as one will confuse you.
GET /v1/public/workspaces/{slug} hands the workspace’s pk_ to anybody who
knows the slug. That is by design — it is how a booking page bootstraps itself
with no server of its own.
What makes that safe is not secrecy. It is that a pk_ reaches exactly three
endpoints:
GET /v1/availabilityPOST /v1/bookingsPOST /v1/bookings/{id}/verify
Everywhere else it gets a 403 with secret_key_required. It cannot list your
bookings, read a customer, or change a price.
What the origin check does and does not do
Section titled “What the origin check does and does not do”A publishable key carries a list of allowed origins. When a request arrives
with an Origin header, that header must be the workspace’s own booking page
or an entry on the key’s list. If it is not, the request is refused with
origin_not_allowed.
Two things follow, and the second one surprises people:
It is an anti-abuse control, not a security boundary. It stops a third party
from lifting your pk_ and embedding it in their site, because a browser
stamps their real origin on the request and browsers do not let page JavaScript
forge that header.
A request that sends no Origin header at all is not origin-checked. The
guarantee is the browser’s, and it ends at the browser. curl sets Origin
with one flag, or omits it entirely. So a publishable key does work from a
server, in the narrow sense that the three endpoints above will answer it — it
is simply not what the key is for, and it buys you nothing over a secret key
except a smaller surface and a rate limit.
The controls that do not depend on client honesty are the three-endpoint surface, the rate limits, and revocation.
The secret key
Section titled “The secret key”sk_ is the ordinary server credential. It reaches every workspace route. It
is not origin-checked, and it is not rate-limited.
The full value is shown once, on the response that creates it. No later read can return it. A publishable key can be read back later and a secret key cannot — the publishable one is designed to be public, and the secret one is not.
Keep sk_ on a server. A secret key in browser JavaScript is the whole
workspace, handed to anybody who opens developer tools.
The routes with no key
Section titled “The routes with no key”/v1/public/* takes no Authorization header. The token in the URL is the
only credential:
GET /v1/public/workspaces/{slug}— the slug is the credential, and the response is deliberately public.GET|POST /v1/public/bookings/{token}and itscancelandrescheduleroutes — this is the manage link the customer receives by email. The token comes fromaccess_tokenon the response that created the booking, and that is the only response that ever carries it.
There is no login on this surface, and there never will be. A customer of a Keptslot business is not a Keptslot user.
Losing a key
Section titled “Losing a key”There is no recovery path for a secret key or a webhook signing secret. Both are shown once and cannot be read back. Revoke and re-create, or — for a webhook — call the rotate endpoint.
This is worth saying plainly because the failure is silent and late: everything keeps working until the day you redeploy from a machine that does not have the value.