Skip to main content

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.

Every protected endpoint requires two headers:
HeaderWhat 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.
GET /hub-api/api/agents HTTP/1.1
Host: api.kataven.ai
Authorization: Bearer eyJhbGciOiJSUzI1NiIs...
X-Account-ID: acme

Per-account databases

Kataven is multi-tenant by database isolation, not by row-level filtering. Each account has its own PostgreSQL database. The X-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/accounts and GET /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/* and GET /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

StatusMeaning
400Missing X-Account-ID header, or invalid account name.
401Missing or invalid bearer token.
403kataven-admin rejected, or admin-only endpoint without X-Admin: true.
404Resource not found in this account’s database.
429Cost-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 — same Authorization 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.