Every protected endpoint requires two headers:Documentation Index
Fetch the complete documentation index at: https://docs.kataven.ai/llms.txt
Use this file to discover all available pages before exploring further.
| Header | What it carries |
|---|---|
Authorization: Bearer <token> | Today: a Zitadel-issued OIDC ID token. Soon: a long-lived API key. |
X-Account-ID: <slug> | Your account/tenant identifier. Determines which per-account database the request operates on. |
Per-account databases
Kataven is multi-tenant by database isolation, not by row-level filtering. Each account has its own PostgreSQL database. TheX-Account-ID header tells the API which one to connect to —
there is no account_id column anywhere because there’s no possible
cross-tenant leak.
Account names are validated against a regex
(^[a-z0-9][a-z0-9_-]{0,62}$) — anything else is rejected at the
edge.
The reserved account kataven-admin is rejected with 403. It exists
for system administration only and isn’t usable as a tenant.
Public endpoints
A small set of endpoints intentionally requires no auth:GET /api/accountsandGET /api/accounts/validate/{account}— used by the login page to discover accounts before any token exists.POST /api/login— legacy REST login (mock; for dev only).POST /api/v1/widget/auth/*andGET /api/v1/widget/config— called cross-origin from customer-page widgets, before any end-user has a Kataven session. These have their own per-key auth (pk_live_*/sk_live_*) — see Widget concepts.GET /api/user/org— handler validates the bearer itself against Zitadel.
Errors
| Status | Meaning |
|---|---|
400 | Missing X-Account-ID header, or invalid account name. |
401 | Missing or invalid bearer token. |
403 | kataven-admin rejected, or admin-only endpoint without X-Admin: true. |
404 | Resource not found in this account’s database. |
429 | Cost-cap exceeded (concurrent calls / per-minute / daily). |
Roadmap: long-lived API keys
The current “Bearer + Zitadel JWT” flow works for interactive users but is awkward for headless integrations (cron jobs, CI, AI agents). A long-lived API-key flow is on the roadmap — sameAuthorization
header, different token format. SDKs already accept whatever string
you pass as api_key, so when the flow ships your code won’t change.
If you’re integrating from a server today, the cleanest workaround is
to obtain a JWT from Zitadel via service-account credentials and
refresh it before expiry. Reach out if you need help with that — once
API keys ship the workaround disappears.