Authentication
How humans and agents authenticate with the Cafébec API.
Cafébec runs two auth tracks side-by-side. They never share token
infrastructure, but they resolve server-side into a unified Actor
object so controllers and the audit log treat both identically.
Human users — Better Auth
Sign-up / sign-in / session endpoints are mounted at /auth/*:
POST /auth/sign-up/email { email, password, name }
POST /auth/sign-in/email { email, password }
POST /auth/sign-out
GET /auth/get-sessionOn success the API sets an HTTP-only cafebec.session_token cookie
scoped to .cafebec.ca in production. The operator console's fetch
wrapper uses credentials: 'include' to carry it through; never send
it in JavaScript.
Agents & services — API keys
Admins mint keys from the console; the plain value is shown once. Subsequent requests send:
Authorization: Bearer vnd_<32-chars>Keys are bcrypt-hashed at rest, with the first 12 chars stored in the
clear for identification. Scopes are attached at creation time and
enforced by the @RequireScopes guard — a key with only
clients:read can't invoke a write endpoint or a mutating MCP tool.
Error envelope
On 401, every endpoint returns:
{
"code": "UNAUTHENTICATED",
"message": "Authentication required",
"next_actions": [
"POST /auth/sign-in",
"Authorization: Bearer vnd_..."
]
}Agents should key off code, not HTTP status. FORBIDDEN means your
credential is valid but lacks the right scope — a different recovery
than UNAUTHENTICATED.