Test mode: payments go through Stripe test mode and are not real. Use test cards only.

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

FieldRule
Username3 to 32 characters, starts with a letter, then letters, digits and underscores; stored in lower case; reserved names are refused
Password8 to 128 characters with an uppercase letter, a lowercase letter and a digit
EmailOptional. 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 email
  • password
  • totp_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_type
  • expires_in (31536000 seconds, 365 days)
  • the user

The site also sets the cookie wl_session (HttpOnly, SameSite Strict).

Warning
A new sign-in replaces your personal token 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 .
AnswerMeaning
401 Invalid username or passwordWrong 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: trueThe password was right, send totp_code
401 Invalid 2FA codeThe code is wrong or was already used
403 Account is disabledAn administrator blocked the account
409 secret_unreadableThe server cannot read the stored 2FA secret; an administrator must reset your 2FA
429Too 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

Scan the QR code or type the secret.

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.

Note
If you lose the authenticator, an administrator can reset your 2FA from the admin section. Keep the secret or a backup of the authenticator.

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

In the dashboard open Settings → Integrations → Telegram → Link Telegram. After you confirm with your password (and the 2FA code when it is on), the relay issues a one-time code (POST /api/v1/auth/telegram/link-code) that lives 10 minutes. Send /link <code> to the bot.

2 Sign in

Press the Telegram button on the sign-in page. The Android app opens the relay's Telegram login in a Custom Tab and returns through a 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:

PathWhat 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 loginThe 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:

ActionPaths
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:

ActionRequest
Change the emailPUT /api/v1/auth/me
Change the passwordPUT /api/v1/auth/password
Get a Telegram link codePOST /api/v1/auth/telegram/link-code
Unlink TelegramDELETE /api/v1/auth/telegram/link
Delete the accountDELETE /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 (with X-2FA-Required: true when the code is missing).
  • A wrong password counts towards the lockout.
  • Past the code limit the answer is 429.
Careful
DELETE /api/v1/auth/me deletes the account for good; there is no undo.

Forgot the password

WaySteps
EmailPOST /auth/forgot-password with the email; a link valid for 15 minutes arrives; POST /auth/reset-password-email with the token and new_password
TelegramPOST /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:

ActionRequest
List the web sessions (devices)GET /api/v1/auth/sessions
Sign one outDELETE /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

TriggerResult
Five wrong passwordsThe 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 endpoints429 with Retry-After (Limits and errors)

Wrong 2FA codes have their own counter and do not add to the password count.

Continue reading