Zudo Works

Authentication endpoints

Method & pathBody / queryNotes
GET /install/status [public]—{ installed, logo_url }
POST /install [public]company_name, currency_code, locale, time_zone, owner { email, password, display_name }One-time setup on a fresh self-hosted install; 409 install_already_completed afterwards
POST /auth/signup [public]company_name, display_name, email, password, country_code, currency_code?, locale?, time_zone?202 { signup_token } and a 6-digit code by e-mail (10-minute token)
POST /auth/signup/verify [public]signup_token, otp201: creates tenant + owner, signs in; 400 after 3 wrong codes (email_already_registered if taken)
POST /auth/login [public]email, password, tenant_slug?{ mfa_required: true, challenge, method, methods, email_hint, attempts_left, resend_in_seconds } or { select_workspace: true, workspaces[] }; throttled. No session until MFA succeeds
POST /auth/mfa/send-code [public]challenge(Re)sends the e-mail code: 1 / 60 s, 5 / hour per user (429 rate_limited)
POST /auth/mfa/verify [public]challenge, codeApp / e-mail / backup code → { user, permissions, tenant, settings, super_admin, mfa_method, new_device, totp_required }; 400 mfa_code_invalid (details: attempts_left, fallback), 423 mfa_locked, 401 mfa_challenge_expired
POST /auth/mfa/fallback [public]challenge, to: email \| backupOnly once the current method is exhausted and the hold elapsed server-side (425 mfa_hold_not_elapsed)
POST /auth/logout—Destroys the session and its device row
GET /me/security—Two-factor status + devices[] (device, ip, location, created, last active, expires, current)
POST /me/security/totp/setup—{ otpauth_url, qr_data_url, manual_key } (pending until confirmed)
POST /me/security/totp/confirmcodeEnables the app; ends the user's other sessions
POST /me/security/totp/disablepasswordRemoves the app (403 mfa_totp_required when the workspace requires it, owners excepted)
POST /me/security/backup-codespassword{ codes[10], generated_at } — shown once, stored hashed; replaces older codes
DELETE /me/security/devices/:id · POST /me/security/devices/sign-out-others—Sign out one device / every other device
POST /users/:id/mfa/reset [owner]—Member back to the e-mail code, signed out everywhere, e-mailed, audited
GET /auth/me—{ user (incl. avatar_url), permissions, settings, tenant, super_admin }
POST /auth/forgot-password [public]emailAlways 202; e-mails a single-use link valid 30 minutes
POST /auth/reset-password [public]token, passwordSets the password, ends all sessions
GET /health [public]—Liveness

Last updated