Skip to main content
The Hub API accepts two credential types in Authorization: Bearer <token>: Recommended for any code you write: use an sk_live_ API key. Mint one at Hub UI → Settings → API Keys.
That’s it — no X-Account-ID header needed. The backend resolves your account from the sk_live_ prefix.

Hostnames

api.kataven.ai is decoupled from the dashboard — outages on hub.* don’t affect programmatic access, and rate-limit / monitoring policies apply independently.

Mint, list, revoke API keys

API key management is Hub UI / Zitadel-JWT only. A leaked sk_live_ cannot mint another sk_live_, by design — otherwise key rotation wouldn’t be a real defense. The POST/PATCH/DELETE operations on /api/v1/api-keys return 403 Forbidden for sk_live_ callers; the SDK doesn’t even expose them. In the Hub UI:
  1. Settings → API Keys → Create API key
  2. Pick a name (e.g. ci-deploy-bot) and an expiry (Never, 1 year, 90 days, 30 days). Per-tenant policy may restrict the max expiry.
  3. Click Create. The full token shows up once — copy it immediately. We don’t store the plaintext, only an HMAC-SHA256 hash with a server-side pepper.
  4. Use the token in Authorization: Bearer ….
Revocation is instant (one-click in the same UI). A revoked key fails on the next request, propagated within ~10 ms via Redis Pub/Sub.

Two key types, two purposes

After the 2026-05-08 split, credentials live in two separate tables with two separate prefixes: Visitor-JWT signing on your backend uses any sk_live_ API key — no paired pk_live_ required. The widget key is purely an account identifier the browser hands back to /api/v1/widget/config. Both are managed at Settings → API Keys in the Hub UI. Widget key CRUD is also available via the SDK (it’s a public identifier, so minting one carries no security risk).

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 (or, for sk_live_ tokens, the prefix) tells the API which one to connect to — there is no account_id column anywhere. 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 on regular endpoints with 403. It’s used only for the operator console.

Public endpoints

A small set of endpoints intentionally requires no auth:
  • GET /api/accounts and GET /api/accounts/validate/{account} — account discovery before any token exists.
  • 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.

Errors

Programmatic verification

Internally, both token types go through the same path:
The middleware caches verified results for 30 seconds per token, so repeat requests skip the gRPC hop entirely. Revocations propagate via Redis Pub/Sub in ~10ms. For the full architecture, see docs/plan/api-keys.md.