Sign-in and 2FA
How an account signs in, what a second factor changes, which actions ask for the password again, and what happens after wrong attempts.
Create an account
| Field | Rule |
|---|---|
| Username | 3 to 32 characters, starts with a letter, then letters, digits and underscores; stored in lower case; reserved names are refused |
| Password | 8 to 128 characters with an uppercase letter, a lowercase letter and a digit |
| Optional. The password reset by email and the mailer need it |
Registration is POST /api/v1/auth/register. A taken username or email answers 409.
Sign in with a password
The login request takes these fields:
username: a username or an emailpasswordtotp_code: only when the account has 2FA
POST /api/v1/auth/login
{ "username": "alice", "password": "…", "totp_code": "123456" }The answer holds:
api_token(pat_…)token_typeexpires_in(31536000 seconds, 365 days)- the user
The site also sets the cookie wl_session (HttpOnly, SameSite Strict).
pat_…: the previous one stops working. A second client that must stay signed in independently asks for its own token with POST /api/v1/auth/client-token (a sess_… token that a later sign-in does not replace). See Tokens . | Answer | Meaning |
|---|---|
| 401 Invalid username or password | Wrong name or password, or the account is locked; the answer is the same for all three |
| 401 2FA code required with header X-2FA-Required: true | The password was right, send totp_code |
| 401 Invalid 2FA code | The code is wrong or was already used |
| 403 Account is disabled | An administrator blocked the account |
| 409 secret_unreadable | The server cannot read the stored 2FA secret; an administrator must reset your 2FA |
| 429 | Too many attempts; see Lockouts below |
Two-factor codes (TOTP)
2FA uses a standard authenticator app: six digits, a new code every 30 seconds. The relay accepts the code of the current window and one window either side. A code that was accepted is spent for about 90 seconds.
1 Start the setup
POST /api/v1/auth/2fa/setup returns a random base32 secret, an otpauth:// link and a QR code. Nothing is saved yet. In the dashboard this is Account → Security. 2 Add the secret to your authenticator app
3 Confirm with a code
POST /api/v1/auth/2fa/enable with the secret and a code from the app. A secret is base32 of 16 to 128 characters. When 2FA is already on, replacing the secret also needs current_code from the old one. POST /api/v1/auth/2fa/disable turns it off with a valid code. A code is exactly six ASCII digits.
Sign in with Telegram
Telegram signs in an account that is already linked to it. It never creates an account: an unlinked Telegram user gets 403.
1 Link Telegram
POST /api/v1/auth/telegram/link-code) that lives 10 minutes. Send /link <code> to the bot. 2 Sign in
wakelink://auth link. A login can also be confirmed in the bot with Authorize. 3 Unlink
DELETE /api/v1/auth/telegram/link or Unlink in the settings. This asks for your password (Confirmations). Telegram does not get around 2FA. If the account has it:
| Path | What happens |
|---|---|
| Widget (POST /auth/telegram) | 401 2FA code required; the signed Telegram data is not spent, send it again with totp_code |
| Android redirect (GET /auth/telegram/app-redirect) | No token is issued: the app gets wakelink://auth?error=2FA+code+required, the site /login?telegram_error=2fa_required. Sign in with the password and the code instead |
| Bot login | The poll answers {"status": "needs_2fa"} until the request carries totp_code; a wrong code leaves the login waiting |
Telegram data is accepted for 24 hours (10 minutes for the Android redirect) and signs in once.
Passkeys
A signed-in account can register passkeys, rename or remove them and sign in with one:
| Action | Paths |
|---|---|
| Register | /auth/passkeys/register/options and /register |
| Sign in | /auth/passkeys/login/options and /login |
Passkeys are also a way to confirm an administrator's sensitive actions.
Confirmations for risky actions
These actions ask for the current password, and for a totp_code too when 2FA is on:
| Action | Request |
|---|---|
| Change the email | PUT /api/v1/auth/me |
| Change the password | PUT /api/v1/auth/password |
| Get a Telegram link code | POST /api/v1/auth/telegram/link-code |
| Unlink Telegram | DELETE /api/v1/auth/telegram/link |
| Delete the account | DELETE /api/v1/auth/me |
The password goes as current_password. DELETE and the link code request take both values as a JSON body.
- A missing or wrong password or code answers
400(withX-2FA-Required: truewhen the code is missing). - A wrong password counts towards the lockout.
- Past the code limit the answer is
429.
DELETE /api/v1/auth/me deletes the account for good; there is no undo. Forgot the password
| Way | Steps |
|---|---|
| POST /auth/forgot-password with the email; a link valid for 15 minutes arrives; POST /auth/reset-password-email with the token and new_password | |
| Telegram | POST /auth/request-telegram-reset or /start reset in the bot; a code valid for 15 minutes; POST /auth/reset-password with the code and new_password |
The requests answer the same for every input, so they do not reveal whether an account exists. An account is sent a new code or link once per 5 minutes, and a new one cancels the previous. The reset ends a lockout, issues a new personal token and signs out every web and WebSocket session; 2FA stays on. A code or link works once.
Devices and sessions
In the dashboard this is Devices & sessions. The API:
| Action | Request |
|---|---|
| List the web sessions (devices) | GET /api/v1/auth/sessions |
| Sign one out | DELETE /api/v1/auth/sessions/{session_id} |
A web session lasts 24 hours. Changing the password, a reset and the admin action sign out everywhere end all of them.
Lockouts
| Trigger | Result |
|---|---|
| Five wrong passwords | The account is locked for 30 minutes; each next lockout is 30 minutes longer, up to 24 hours. After a served lockout the count starts again |
| More than five wrong 2FA codes in 15 minutes (login, Telegram, settings) | 429, and the code is not checked until the window passes |
| Many requests from one IP to login, registration, reset and Telegram endpoints | 429 with Retry-After (Limits and errors) |
Wrong 2FA codes have their own counter and do not add to the password count.
- Tokens — token types, lifetime and rotation
- Limits and errors — rate limits and what the codes mean
- Troubleshooting — cannot sign in