Awwwth Docs

Awwwth / API reference

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.

MethodPathPurpose
GET/api/auth/configPublic backend selection and secure account console URL
POST/api/auth/keycloak/startSame-origin/CSRF-protected browser sign-in; returnTo, optional signup, forceLogin, reauthenticate; returns {redirect} and an HttpOnly flow cookie
GET/auth/keycloak/callbackOne-use, browser-bound state/nonce/PKCE validation and local session creation
POST/auth/keycloak/backchannel-logoutKeycloak 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.

Back to README

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 and Content-Type: application/json. Send {} for actions without fields.
  • Mutating /api/ requests require Origin matching AUTH_ORIGIN and X-Awwwth-Request: 1.
  • Authenticated mutations also require X-Awwwth-Account set to the current user's UUID, returned by /api/session. A mismatch produces account_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 awwwth cookie in development or __Host-awwwth in production. Company cookie names append _<company-uuid>. Cookies use Path=/, SameSite=Lax, and a 30-day maximum age; production adds Secure.
  • 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

MethodPathJSON fields / result
GET/api/tenantCompany {id, name}, or {id: null, name: "Awwwth"}
POST/api/registername, email, password; signs in and queues verification
POST/api/loginemail, password; session or MFA challenge
POST/api/mfa/loginchallenge, code; accepts authenticator or recovery code
POST/api/forgotemail; returns success even when no active account matches
POST/api/resettoken, password, optional code (needed when MFA is enabled); clears cookie
POST/api/verifytoken
POST/api/passkeys/login/optionsemail; returns challengeToken and publicKey options
POST/api/passkeys/loginchallengeToken, 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

MethodPathFields / behaviorFresh
GET/api/sessionReturns id, role, verifiedNo
GET/api/accountReturns user, sessions, passkeys, connections, activity, recoveryCount, addressesNo
POST/api/account/linkCompany 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/logoutRevoke current session and clear cookieNo
POST/api/reauthpassword, optional code for MFANo
POST/api/passkeys/reauth/optionsReturn passkey assertion optionsNo
POST/api/passkeys/reauthchallengeToken, credentialNo
PATCH/api/profilename, optional phone; empty phone clears itNo
POST/api/addresseslabel, address, optional id for update, optional boolean isDefaultNo
DELETE/api/addresses/:idDelete a saved addressNo
POST/api/passwordNew password; revoke sessions and clear cookieYes
DELETE/api/accountDelete current account and clear cookieYes
POST/api/sessions/revoke-allRevoke all sessions and clear cookieYes
DELETE/api/sessions/:idRevoke selected sessionYes
DELETE/api/connections/:clientIdDisconnect applicationYes
POST/api/verificationQueue another verification emailNo
POST/api/mfa/setupVerified email required; returns secret, challenge, uriYes
POST/api/mfa/enablechallenge, code; returns ten recoveryCodesYes
POST/api/mfa/disableDisable authenticatorYes
POST/api/passkeys/register/optionsVerified email required; returns challengeToken, publicKeyYes
POST/api/passkeys/registerchallengeToken, name, credentialYes
DELETE/api/passkeys/:idRemove credentialYes

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.

MethodPathFields / behavior
GET/api/companiesReturn accessible companies
POST/api/companiesname; verified email and fresh authentication; returns company with status 201
GET/api/companies/:idReturn company, users, clients, and pagination
POST/api/companies/:id/clientsname, redirectUri, optional logoutUri; fresh authentication; returns {id, secret} with status 201
POST/api/companies/:id/users/:userId/statusstatus: active or disabled; fresh authentication
POST/api/companies/:id/users/:userId/reset-passwordQueue reset email for an active company user; fresh authentication

Platform administration

All routes require a platform administrator. Mutations also require fresh authentication.

MethodPathFields / behavior
GET/api/adminReturn users, clients, and pagination
POST/api/admin/clientsname, redirectUri, optional logoutUri; returns {id, secret}
POST/api/admin/users/:id/statusstatus: active or disabled
DELETE/api/admin/users/:idDelete 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

MethodPathAuthentication / purpose
GET/.well-known/openid-configurationPublic provider metadata
GET/oauth/jwksPublic signing keys
GET/oauth/authorizeReuse a verified directory session automatically; otherwise redirect to account UI
POST/api/oauth/authorizeFirst-party consent submission; authorization fields plus decision; approval requires session and request_token
POST/oauth/tokenHTTP Basic client credentials; authorization-code or refresh-token grant
GET/oauth/userinfoBearer access token
POST/oauth/introspectHTTP Basic client credentials and form field token
POST/oauth/revokeHTTP Basic client credentials and form field token; returns {}
GET/healthDatabase readiness check; returns {status: "ok"}
GET, HEAD/healthzProcess 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.