/v1 merchant endpoint authenticates with a store API key sent as a bearer
token:
Key format
A live key is alwaystbrn_live_ followed by 48 hexadecimal characters. That is the
only accepted shape — anything else fails validation before it reaches the store
lookup.
The tbrn_live_ prefix exists so that a key leaked into a log, a screenshot or a
public repository is recognisable both to a human and to automated secret scanners.
A key is bound to one store. Taberna derives the store from the key, which is
why no
/v1 endpoint takes a store id — POST /v1/orders creates an order for the
key’s store and nothing else.Scopes
Scopes are chosen when the key is minted and are fixed for its lifetime. To change them, create a new key and revoke the old one.
Notes worth internalising:
- Invoice templates read under
invoices:read, not a scope of their own — they are invoice authoring material. webhooks:writeis replay only. It lets a key push a failed delivery back into the queue. It does not permit changing where a store’s events are sent; that stays a dashboard action.- Choosing scopes is mandatory. There is no implicit full-access key. An empty scope array, or an unknown scope name, is rejected at key creation.
orders:write and
orders:read — nothing else. Every scope you add is blast radius if the key leaks.
The one endpoint that needs no scope
GET /v1/store — “whoami” — is callable by any valid key, whatever it was granted.
Its response includes the calling key’s own scopes array:
storefrontUrl is the canonical public origin of the store’s
hosted storefront — an active
custom domain wins over the slug subdomain — and is null
while the storefront is unclaimed or switched off.
401 versus 403
The two are not interchangeable and the distinction matters for how your client should react.- 401 — bad credential
- 403 — insufficient scope
Returned for every authentication failure: missing header, malformed header,
unknown key, revoked key, expired key.The body is always exactly
{ "error": "Unauthorized" }. The reason is
deliberately not disclosed — telling a caller “revoked” rather than “unknown”
would confirm to whoever is guessing that a key exists. Never branch on the body.The RFC 6750 challenge in the WWW-Authenticate header is the only signal that
distinguishes cases:Rotation and revocation
Keys are minted, listed and revoked from the dashboard by a store owner, admin or manager — see Team.1
The raw key is shown once
At creation, and only at creation. Taberna keeps a SHA-256 hash and cannot
recover the original. Put it straight into your secret manager.
2
Rotate by overlap, not by swap
Mint the new key first, deploy it, confirm traffic is flowing on it, then revoke
the old one. Because scopes are immutable, a scope change is always a rotation.
3
Revocation is immediate
A revoked key answers
401 on the next request. So does a key past its optional
expiresAt. Keys created without an expiry do not expire.A key’s
lastUsedAt is tracked, so the key list in the dashboard will tell you
whether a key you are about to revoke is still receiving traffic.