Every route requires the four CPET headers (X-Customer, X-Product, X-Env, X-Tenant). The error envelope is uniform: {"error": "...", "code": "..."}. Auth-flow endpoints are rate-limited per (tenant, caller IP) → 429 + Retry-After. Request bodies are capped at 1 MiB.
| Method + Path | Request → Response |
|---|
POST /auth/login | {identity, password, identity_type?, realm?} → {token, refresh_token, expires_in, user}. MFA-enabled → {mfa_required: true, mfa_token, methods}. Managed tenants → {session_id, expires_at} + kis_session cookie. Errors: 401 invalid_credentials, 400 unknown_realm, 403 provider_not_allowed |
POST /auth/refresh | {refresh_token} → new pair. Replay → 401 token_reuse + family revocation. Roles re-read, realm preserved |
POST /auth/logout | {refresh_token} — revokes the rotation family |
POST /auth/password/reset/request / confirm | Codes via the notify service; always 200 (enumeration resistance) |
POST /auth/apikey | {service?, capabilities?, expires_in_days?} → {id, key, expires_at, capabilities} — key shown once; capabilities may not exceed the issuer’s own (403 scope_exceeds_authority) |
DELETE /auth/apikey/:id | Revoke |
POST /auth/token | Authorization: ApiKey <key>, ?audience= — exchanges a service API key for a short service PASETO (type: "service" + CPET scope claims). 403 not_a_service_key, 401 apikey_invalid |
POST /auth/bootstrap | {token, email, password, firstname, lastname} — one-shot first-superadmin creation gated by bootstrap.token; closes itself once a superadmin exists |
POST /auth/mfa/totp/setup → enable → verify, disable | TOTP lifecycle; codes are single-use (401 code_reused); disable requires a current code |
POST /auth/magic/request / verify | Magic-link login via the configured channel |
POST /auth/webauthn/register/begin|finish, /auth/webauthn/assert/begin|finish | Passkeys; 503 webauthn_disabled when not configured |
GET /auth/oauth/:provider/start / callback | OAuth with PKCE S256; 10-minute state |
GET /.well-known/jwks.json | Per-tenant Ed25519 public keys |
| Method + Path | Behavior |
|---|
POST /superadmin/impersonate | {tenant, user_id, reason, read_write?} → {token, session_id, expires_in, mode, acting_as, tenant}. Enforced in order: plane operator → exact role (impersonate-readonly / impersonate) → grant on the target tenant → target user’s allow_impersonation → mint with act claim → force-managed session → audit in both planes. reason is mandatory |
Enforced by each entity’s access: rules; Bearer token + CPET headers:
| Method + Path | Purpose |
|---|
GET/POST /rest/:entity · POST /rest/:entity/search · GET/PUT/PATCH/DELETE /rest/:entity/id/:id · POST /rest/:entity/id/:id/restore | Generic CRUD over every IAM entity (including product-defined ones) — full detail in the IAM API reference |
/admin/user, /admin/role, /admin/realm, /admin/tenant | Domain aliases, admin-gated |
GET /schema, GET /schema/:entity | Schema introspection |
Separation of duties: admin can look, superadmin can change.
| Role | Routes |
|---|
| admin | GET /admin/data/seeds, /admin/data/applied, /admin/data/plans/:id, /admin/data/locks; POST /admin/data/plans (generate) |
| superadmin | POST /admin/data/plans/:id/apply, seed runs, lock release, retention, maintenance |
| superadmin | /superadmin/tenant/:t/* — cross-tenant onboarding, incl. DELETE /superadmin/tenant/:t/cache |
| Method + Path | Purpose |
|---|
POST /superadmin/registry/tenants | {slug, displayname} — register |
GET /superadmin/registry/tenants | List |
POST /superadmin/registry/tenants/provision | {slug, admin_email, admin_password} — register → bootstrap → seed → activate, idempotent; reports steps applied |
id here is the control-plane row id — never the bearer credential.
| Method + Path | Purpose |
|---|
POST /internal/session/exchange | {session_id, audience?} → {token, format, expires_at, cpet}; 401 invalid_session with no reason oracle |
POST /internal/session/revoke | {id, reason} → 204 — instant revocation |
POST /internal/session/rotate | {id, aal} → new session id (fixation defense, AAL step-up) |
GET /internal/session/list?principal=<id> | Enumerate a principal’s sessions |