API reference
With AUTH_BACKEND=keycloak, sign-in and credentials are delegated to Keycloak while the account, company, administration and OAuth APIs remain in Awwwth. See Keycloak authentication. Native login/register/password/verification/MFA/passkey endpoints return keycloak_managed (409) in this mode.
| Method | Path | Purpose |
|---|---|---|
| GET | /api/auth/config | Public backend selection and secure account console URL |
| POST | /api/auth/keycloak/start | Same-origin/CSRF-protected browser sign-in; returnTo, optional signup, forceLogin, reauthenticate; returns {redirect} and an HttpOnly flow cookie |
| GET | /auth/keycloak/callback | One-use, browser-bound state/nonce/PKCE validation and local session creation |
| POST | /auth/keycloak/backchannel-logout | Keycloak form-encoded signed logout_token; validated without browser cookies; returns 204 |
Keycloak reauthentication requires the current account/session and never changes its user. Return paths are restricted to the Awwwth account page. In this mode /api/logout returns {redirect} for browser logout after local revocation, and /api/account includes authBackend and freshUntil.
This reference describes the routes in http_adapter.ts. Account APIs support the first-party account interface. External applications should use the OIDC integration.
Request conventions
/api/POST and PATCH requests require a JSON object andContent-Type: application/json. Send{}for actions without fields.- Mutating
/api/requests requireOriginmatchingAUTH_ORIGINandX-Awwwth-Request: 1. - Authenticated mutations also require
X-Awwwth-Accountset to the current user's UUID, returned by/api/session. A mismatch producesaccount_changed(409). - Company requests include
X-Awwwth-Tenant: <company-uuid>. Omit it for platform accounts, including company management by an owner. - Authentication uses the HTTP-only
awwwthcookie in development or__Host-awwwthin production. Company cookie names append_<company-uuid>. Cookies usePath=/,SameSite=Lax, and a 30-day maximum age; production addsSecure. - Request bodies are limited to 65,536 bytes. Responses use
Cache-Control: no-store. - “Fresh” below means authentication within five minutes. Use password or passkey reauthentication if the server returns
reauthentication_required.
Ordinary action responses are { "ok": true }. Registration and completed login return { "user": <profile> } and set a cookie. Password login with MFA enabled instead returns { "mfaRequired": true, "challenge": "..." }.
API faults use this shape:
{"error":{"code":"unauthenticated","message":"Please sign in to continue."}}
OAuth faults normally use { "error": "invalid_grant", "error_description": "..." }. Common statuses include 400 for invalid input, 401 for missing/invalid authentication, 403 for authorization or reauthentication requirements, 404 for missing resources, 409 for state conflicts, 413 for oversized bodies, 415 for content type, 429 for limits, and 503 for unexpected failures.
Public account routes
| Method | Path | JSON fields / result |
|---|---|---|
| GET | /api/tenant | Company {id, name}, or {id: null, name: "Awwwth"} |
| POST | /api/register | name, email, password; signs in and queues verification |
| POST | /api/login | email, password; session or MFA challenge |
| POST | /api/mfa/login | challenge, code; accepts authenticator or recovery code |
| POST | /api/forgot | email; returns success even when no active account matches |
| POST | /api/reset | token, password, optional code (needed when MFA is enabled); clears cookie |
| POST | /api/verify | token |
| POST | /api/passkeys/login/options | email; returns challengeToken and publicKey options |
| POST | /api/passkeys/login | challengeToken, credential |
| GET | /api/oauth/client?client_id=... | Application name and tenantId |
Names are limited to 80 characters, emails to 254, and passwords to 6–128. Verification links last 24 hours; password reset links last 30 minutes. MFA login and passkey challenges last five minutes.
Signed-in account routes
| Method | Path | Fields / behavior | Fresh |
|---|---|---|---|
| GET | /api/session | Returns id, role, verified | No |
| GET | /api/account | Returns user, sessions, passkeys, connections, activity, recoveryCount, addresses | No |
| POST | /api/account/link | Company header/session plus main-account cookie; both verified, same-email accounts must have authenticated within five minutes. Links credentials, preserves company IDs/roles and ends old company sessions. | Yes |
| POST | /api/logout | Revoke current session and clear cookie | No |
| POST | /api/reauth | password, optional code for MFA | No |
| POST | /api/passkeys/reauth/options | Return passkey assertion options | No |
| POST | /api/passkeys/reauth | challengeToken, credential | No |
| PATCH | /api/profile | name, optional phone; empty phone clears it | No |
| POST | /api/addresses | label, address, optional id for update, optional boolean isDefault | No |
| DELETE | /api/addresses/:id | Delete a saved address | No |
| POST | /api/password | New password; revoke sessions and clear cookie | Yes |
| DELETE | /api/account | Delete current account and clear cookie | Yes |
| POST | /api/sessions/revoke-all | Revoke all sessions and clear cookie | Yes |
| DELETE | /api/sessions/:id | Revoke selected session | Yes |
| DELETE | /api/connections/:clientId | Disconnect application | Yes |
| POST | /api/verification | Queue another verification email | No |
| POST | /api/mfa/setup | Verified email required; returns secret, challenge, uri | Yes |
| POST | /api/mfa/enable | challenge, code; returns ten recoveryCodes | Yes |
| POST | /api/mfa/disable | Disable authenticator | Yes |
| POST | /api/passkeys/register/options | Verified email required; returns challengeToken, publicKey | Yes |
| POST | /api/passkeys/register | challengeToken, name, credential | Yes |
| DELETE | /api/passkeys/:id | Remove credential | Yes |
The public user profile contains id, tenantId, email, phone, name, verified, role, mfaEnabled, and createdAt. Phone numbers require an international country code. An address has line1, optional line2, city, state, postalCode, and country. Passkey credentials require WebAuthn binary/JSON conversion; see the implementation in public/app.ts. Accounts support up to 20 passkeys.
Company management
These routes require a platform session. Company-specific actions require ownership; the administrator role does not bypass that check.
| Method | Path | Fields / behavior |
|---|---|---|
| GET | /api/companies | Return accessible companies |
| POST | /api/companies | name; verified email and fresh authentication; returns company with status 201 |
| GET | /api/companies/:id | Return company, users, clients, and pagination |
| POST | /api/companies/:id/clients | name, redirectUri, optional logoutUri; fresh authentication; returns {id, secret} with status 201 |
| POST | /api/companies/:id/users/:userId/status | status: active or disabled; fresh authentication |
| POST | /api/companies/:id/users/:userId/reset-password | Queue reset email for an active company user; fresh authentication |
Platform administration
All routes require a platform administrator. Mutations also require fresh authentication.
| Method | Path | Fields / behavior |
|---|---|---|
| GET | /api/admin | Return users, clients, and pagination |
| POST | /api/admin/clients | name, redirectUri, optional logoutUri; returns {id, secret} |
| POST | /api/admin/users/:id/status | status: active or disabled |
| DELETE | /api/admin/users/:id | Delete selected account |
Company detail and administrator list requests support page (positive integer, default 1), search (up to 254 characters), and status (all, active, disabled). Responses include {page, pageSize, total}; page size is 20 and out-of-range pages are clamped to the available range.
Account deletion anonymizes the user record and removes associated authentication data rather than removing every database record. An account that owns company workspaces cannot be deleted while they remain assigned to it.
OAuth and health routes
| Method | Path | Authentication / purpose |
|---|---|---|
| GET | /.well-known/openid-configuration | Public provider metadata |
| GET | /oauth/jwks | Public signing keys |
| GET | /oauth/authorize | Reuse a verified directory session automatically; otherwise redirect to account UI |
| POST | /api/oauth/authorize | First-party consent submission; authorization fields plus decision; approval requires session and request_token |
| POST | /oauth/token | HTTP Basic client credentials; authorization-code or refresh-token grant |
| GET | /oauth/userinfo | Bearer access token |
| POST | /oauth/introspect | HTTP Basic client credentials and form field token |
| POST | /oauth/revoke | HTTP Basic client credentials and form field token; returns {} |
| GET | /health | Database readiness check; returns {status: "ok"} |
| GET, HEAD | /healthz | Process liveness; returns 204 without a database check |
The three client-authenticated OAuth POST routes require application/x-www-form-urlencoded. See Integration for parameters and token handling.
/oauth/authorize also accepts screen_hint=signup or screen_hint=forgot to open the corresponding hosted account screen. A screen hint requests interaction even when an Awwwth browser session exists; it does not change the requested scopes or authorize an unverified account.
Rate limits
Account mutations normally allow 30 requests per IP and route bucket per minute. Login, forgot-password, and passkey login options also allow 10 requests per account/directory/route per five minutes. MFA challenge completion, reset-token attempts, and reauthentication have five-attempt limits per five minutes. Verification email requests allow three per user per five minutes.
Client-authenticated token and revocation requests allow 6,000 per client/endpoint/minute; introspection allows 60,000. Failed client authentication is limited to 30 per IP/minute. A 429 response supplies Retry-After: 60; some underlying windows are longer, so callers must continue to handle subsequent 429 responses.
Automatic authorization is limited to 60 requests per identity session/client/minute. Registered apps are trusted for automatic sign-in within their account directory; see the CLI and shared-sign-in guide.
Embedded passkey sign-in
POST /oauth/passkey/embed uses confidential client_secret_basic authentication and form-encoded standard authorization-code/S256 PKCE parameters plus email. It returns {url} for a five-minute, encrypted provider panel ticket. The redirect URI must exactly match the registered application; its origin is the only permitted embedding origin. This endpoint does not authorize an unregistered localhost or arbitrary origin.
GET /passkey/embed?ticket=... serves the visible provider-owned panel with an exact frame-ancestors policy. POST /api/passkeys/embed/options and /api/passkeys/embed/finish accept the ticket, enforce provider-origin CSRF and rate limits, and require a one-use challenge bound to that ticket/company/top origin. Finish additionally accepts challenge and credential. The signed WebAuthn data must report provider origin, crossOrigin=true and the exact registered topOrigin. Completion returns {callback,parentOrigin} with only an OIDC authorization code. No provider cookies or session/access tokens are sent to the embedding app. The consuming callback must still verify its browser attempt, state, issuer, PKCE and account permissions.