/api/v1/auth/login
auth: noneValidate credentials and issue a JWT access token plus an httpOnly refresh-token cookie. Returns the user and businessId (null if onboarding incomplete). Constant-time dummy hash verify on unknown emails to prevent timing-based enumeration.
handlers: AuthController.login
| name | required | default | description |
|---|---|---|---|
email bodystring (email) | yes | — | Account email address. |
password bodystring | yes | — | Plaintext password (min 1 char per Zod; argon2-verified against stored hash). |
APIResponseDataObject<LoginResponseDTO>APIError/api/v1/auth/register
auth: noneCreate the first user account. Hashes the password with argon2, creates a Users row with roles=[] and BusinessId=NULL, and fires AUTH_REGISTER + fire-and-forget welcome email. No tokens are issued — the client is expected to redirect to login.
handlers: AuthController.register
| name | required | default | description |
|---|---|---|---|
firstName bodystring | yes | — | Given name (min 1 char). |
lastName bodystring | yes | — | Family name (min 1 char). |
username bodystring | yes | — | Display username (min 1 char). |
email bodystring (email) | yes | — | Email address — must be globally unique. |
password bodystring | yes | — | Plaintext password (min 8 chars). |
confirmPassword bodystring | yes | — | Must equal password (Zod refine). |
APIResponseDataObject<RegisterResponseDTO>APIErrorAPIError/api/v1/auth/refresh
auth: noneRotate the refresh token. Reads the httpOnly refreshToken cookie (or body fallback for native clients), verifies RS256 signature, checks typ='refresh', re-fetches the user to detect deactivation and post-iat password changes, and issues a fresh access+refresh pair.
handlers: AuthController.refresh
| name | required | default | description |
|---|---|---|---|
refreshToken bodystring | no | — | Fallback for native clients whose OS HTTP stack does not reliably persist cookies. Cookie is the primary source. |
refreshToken headercookie | no | — | httpOnly refreshToken cookie set on login. |
APIResponseDataObject<RefreshResponseDTO>APIError/api/v1/auth/logout
auth: noneClear the refresh-token cookie. Idempotent — returns 200 even when the cookie is absent or malformed. Decodes (does not verify) the cookie to write an AUTH_LOGOUT audit event with the userId/businessId when present.
handlers: AuthController.logout
No parameters.
APIResponseDataObject<{ message: string }>/api/v1/auth/setup-status
auth: noneReturns whether any user has been registered yet — used by the frontend to gate the registration form vs. invite-only flow.
handlers: AuthController.setupStatus
No parameters.
APIResponseDataObject<SetupStatusResponse>/api/v1/auth/register-invited
auth: noneComplete registration for a user who received an invite link. Validates the invite token in Redis (invite:{token}), creates the user with the invite's roles and organizationId, deletes the token (single-use), and fires AUTH_REGISTER + welcome email.
handlers: AuthController.registerInvited
| name | required | default | description |
|---|---|---|---|
inviteToken bodyuuid | yes | — | Invite token from the invite email link. |
firstName bodystring | yes | — | Given name (min 1 char). |
lastName bodystring | yes | — | Family name (min 1 char). |
password bodystring | yes | — | Plaintext password (min 8 chars). |
confirmPassword bodystring | yes | — | Must equal password (Zod refine). |
APIResponseDataObject<RegisterResponseDTO>APIErrorAPIErrorAPIError/api/v1/auth/change-password
auth: jwtVerify the caller's current password, hash the new one, and issue a fresh access+refresh token pair. Sets Users.PasswordChangedAt to invalidate all existing refresh tokens. Rate-limited via 'default' bucket.
handlers: AuthController.changePassword
| name | required | default | description |
|---|---|---|---|
currentPassword bodystring | yes | — | Existing password (argon2-verified). |
newPassword bodystring | yes | — | New password (min 8 chars; must differ from currentPassword). |
confirmPassword bodystring | yes | — | Must equal newPassword (Zod refine). |
APIResponseDataObject<ChangePasswordResponseDTO>APIErrorAPIErrorAPIError/api/v1/auth/forgot-password
auth: noneRequest a 6-digit OTP reset code. Always returns 200 with a generic message — silently no-ops for unknown emails (anti-enumeration). Generates a code, SHA-256-hashes it into PasswordResetTokens, and XADDs the raw code to the auth:password-reset Redis Stream for the email worker. Rate-limited via 'default' bucket.
handlers: AuthController.forgotPassword
| name | required | default | description |
|---|---|---|---|
email bodystring (email) | yes | — | Email address. Normalised to lowercase trim before lookup. |
APIResponseDataObject<ForgotPasswordResponseDTO>APIError/api/v1/auth/reset-password
auth: noneConfirm a password reset. Validates the OTP against the stored SHA-256 hash, checks expiry, deletes the token (single-use), hashes the new password with argon2, and sets PasswordChangedAt to invalidate every existing refresh token. All failure modes return the same RESET_TOKEN_INVALID code. Rate-limited via 'default' bucket.
handlers: AuthController.resetPassword
| name | required | default | description |
|---|---|---|---|
email bodystring (email) | yes | — | Email address. |
resetCode bodystring | yes | — | 6-digit OTP from the email. |
newPassword bodystring | yes | — | New password (min 8 chars). |
confirmPassword bodystring | yes | — | Must equal newPassword (Zod refine). |
APIResponseDataObject<ResetPasswordResponseDTO>APIErrorAPIError