Skip to content
Fluxer API

Authentication resource

These routes create accounts, sign people in and manage their sessions. Every route is under /v1/auth, and Authentication explains the token formats.

Most routes here need no Authorization header, because the request has its own ticket, code or credential. The routes that need a user session token, except log out, reject a bot token and an OAuth2 bearer token with 403 ACCESS_DENIED.

An instance reports how people sign in as account_identity in its instance features. On a username instance, these routes return 400 EMAIL_UNAVAILABLE_ON_INSTANCE and send no email.

When the instance enforces SSO, the password, passkey, email code, registration, email verification, password reset, recovery, IP authorization and username routes return 403 SSO_REQUIRED. The SSO routes, logout, the session routes, redeem a sign-in link, the desktop and origin handoff routes and the MFA routes for a ticket from complete SSO keep working.

Each sign-in creates one session, and a session lasts until it is revoked. When a session is revoked, its Gateway connection gets Invalid Session with d: false.

A new session token and the account it belongs to. Every route that signs someone in returns these fields.

FieldTypeDescription
tokenstringThe new user session token, returned only here
user_idsnowflakeThe authenticated user ID
userpartial user objectThe authenticated account
device_proof?stringA value to send to identify an account from this device later
entry?1?guild entry objectThe community to open next, from the invite used at sign-up

1 Returned only by registration, and null when no invite applied or the account still has to enter a date of birth

When a route can return this object or a different one, check for token.

The public single sign-on state. The instance discovery document has the same object.

FieldTypeDescription
enabledbooleanWhether SSO is turned on and its provider is fully configured
enforcedbooleanWhether every user has to sign in with SSO
username_from_providerbooleanWhether each SSO sign-in sets the username from the provider, so SSO accounts can't change it
display_name?stringThe provider display name
button_label?stringThe text for the SSO sign-in button, or null to use the app's default
redirect_uristringThe default redirect URI for the provider callback
available?booleanNot sent by the API, so check enabled

Where to send the user to sign in with the identity provider.

FieldTypeDescription
authorization_urlstringThe provider URL to send the user to, unchanged
statestringThe one-use state to pass to complete SSO, valid for 10 minutes
redirect_uristringThe callback URI for this state
client_bindingstringA one-use secret to send back to complete SSO with this state

The result of complete SSO. It extends an authentication token response or an MFA challenge response with the redirect bound to the state. A flow from start connecting SSO returns only linked and redirect_to.

FieldTypeDescription
tokenstringThe new user session token
user_idsnowflakeThe authenticated user ID
userpartial user objectThe authenticated account
redirect_tostringThe redirect_to given to start SSO, or an empty string
linked?booleantrue when the identity was connected to the account that started start connecting SSO. No session is created

When mfa is true, finish the sign-in with the ticket the same way as after password login.

Registration pending approval response object

Section titled “Registration pending approval response object”

Registration returns this object when an admin has to approve the account first.

FieldTypeDescription
registration_pending_approvalbooleanAlways true
user_idsnowflakeThe ID of the account waiting for approval

A request from a blocked network returns 403 GLOBAL_IP_BANNED or GLOBAL_IP_TEMPORARILY_BANNED, and the error response has these extra fields.

FieldDescription
ip_addressThe client IP address
appeal_emailAlways null on a self-hosted instance
appeals_supportedAlways false on a self-hosted instance
ban_kindpermanent, or temporary_24h for any ban with an expiry
expires_atWhen the ban ends as an ISO 8601 timestamp, or null
reasonThe rule behind the block as title and url, or null
product_nameThe instance name used in message

A blocked network can still load GET /.well-known/fluxer, which the app uses to show the block message. Password, passkey, MFA and SSO sign-in still answer from a blocked network so a suspended or closed account sees its enforcement notice. Every other result is the same network block error.

ACCOUNT_SUSPENDED_TEMPORARILY and ACCOUNT_SUSPENDED_PERMANENTLY errors have a notice field with the enforcement notice for the account, in the request locale.

FieldDescription
decision_idThe decision reference, or null
kindsuspension or closure
permanenttrue for a suspension with no end date
issued_atWhen the decision was made, or null
ends_atWhen the suspension ends, or null
deletion_atWhen the account will be deleted, or null
summaryOne sentence saying what happened
effectsWhat the decision means for the account
ruleThe rule broken as title and url, or null
factsThe finding, then where and when it happened
statementA note from the team, or null
how_we_decidedHow the case came to the team and who decided
appealemail, deadline and account_has_email, or null
redressOther ways to challenge the decision

Lines in effects, facts and redress can have Markdown links, [text](url), which the app shows as links.

Sign-in routes return this object when the account has a second factor. Finish with complete login with TOTP or complete login with WebAuthn MFA.

FieldTypeDescription
mfabooleanAlways true
ticketstringThe ticket for the MFA route, valid for 5 minutes or until 5 wrong codes
allowed_methodsarray[string]The methods the account can use, from totp, webauthn and backup_codes (max 10 items)
totpbooleanWhether the account has an authenticator app
webauthnbooleanWhether passkeys are turned on as a second factor
backup_codesbooleanWhether the account has an unused backup code

Backup codes go to complete login with TOTP as code.

The WebAuthn options routes return a WebAuthn authentication options object. Pass it to the browser's credential request.

WebAuthn sign-in routes take the browser credential result as response and the challenge as challenge. The WebAuthn assertion object on the Multi-factor authentication resource has its fields.

One active session of the account.

FieldTypeDescription
id_hashstringThe ID of the session, used by terminate authentication sessions
client_info??client info objectThe device that created the session
masked_ip?stringThe partly hidden IP address of the session
approx_last_used_at??ISO8601 timestampAbout when the session was last used
currentbooleanWhether this session made the current request

The device behind a session or a pending handoff, taken from its request headers.

FieldTypeDescription
platform??stringThe client platform, for display only
os??stringThe operating system
browser??stringThe browser, or null for the desktop and mobile apps
devicestringmobile or desktop
location??client location objectThe approximate location of the IP address

The approximate location of a session or handoff IP address.

FieldTypeDescription
city??stringThe approximate city
region??stringThe approximate region
country??stringThe approximate country
FieldTypeDescription
validbooleanWhether the token is still valid

The result of recover an account, which extends an authentication token response or an MFA challenge response with these fields.

FieldTypeDescription
recovery_keystringThe new recovery key, 8 groups of 4 characters joined by -. Show it to the person, because it is returned only here
recovery_kit_created_atISO8601 timestampWhen the new recovery kit was created

The state of a sign-in that waits for IP authorization.

FieldTypeDescription
completedbooleanWhether the link in the email was used and a session was created
token??stringThe new user session token, when completed is true
user_id??snowflakeThe authenticated user ID, when completed is true
user??partial user objectThe authenticated account, when completed is true
FieldTypeDescription
suggestionsarray[string]Usernames made from the display name, currently at most one (max 20 items)
FieldTypeDescription
availablebooleanWhether the username is free

The next step after identify an account.

FieldTypeDescription
ticketstringThe ticket for the next step, valid for 15 minutes
accountstringexisting when an account uses the identifier, new otherwise
sign_in_method?stringpassword or code for an existing account, null for a new one
registration?identify registration objectWhat signing up needs, or null for an existing account
FieldTypeDescription
modestringHow new accounts are let in, one of open, approval or closed
consent_requiredbooleanWhether signing up needs consent set to true
requires_passwordbooleanWhether signing up needs a password, because the instance can't send email codes
instant_account_availability?stringOne of the instant account availability values
FieldTypeDescription
purposestringsign_in for an existing account, sign_up for a new one
code_expires_atISO8601 timestampWhen the code stops working, 10 minutes after sending
resend_available_atISO8601 timestampWhen you can ask for another code, 30 seconds after sending
FieldTypeDescription
handoff_codestringThe code the web app or a mobile app redeems, 43 URL-safe base64 characters
FieldTypeDescription
captcha_providerstringaltcha when the check is on, none when it is off
altcha_challenge?objectThe ALTCHA challenge, when captcha_provider is altcha

A pending desktop handoff. Show code only to the person signing in, because anyone with it can see the device details.

FieldTypeDescription
codestringThe 12-character code to enter on the signed-in device, shown as 2 groups of 6
expires_atISO8601 timestampWhen the handoff expires, 5 minutes after creation
poll_secret?stringThe secret you need to get the token or cancel the handoff
return_method?stringdeep_link when the request set a return_uri, code otherwise

The device details the signed-in device shows before it approves.

FieldTypeDescription
statusstringpending or expired
client_info??client info objectThe device that started the handoff, or null when expired
return_method?stringdeep_link or code, when the status is pending
FieldTypeDescription
statusstringOne of pending, denied, expired or completed
token??stringThe new user session token, when the status is completed
user_id??snowflakeThe authenticated user ID, when the status is completed
user??partial user objectThe authenticated account, when the status is completed

An unknown code reports expired.

FieldTypeDescription
return_urlstringThe return_uri with the handoff code and a one-time grant in its query string
FieldTypeDescription
handoff_idstringThe one-use ID the other origin redeems, 43 base64url characters
FieldTypeDescription
payloadstringThe string stored by create origin handoff, unchanged
GET/v1/auth/sso/statusUnauthenticated

Returns the SSO status of the instance.

StatusBodyCondition
200SSO status objectThe SSO state was returned

The rate limit is 10 requests per 10 seconds, on the shared auth:sso:start bucket.

POST/v1/auth/sso/startUnauthenticated

Starts single sign-on and returns an SSO start object. Send the user to authorization_url.

FieldTypeDescription
redirect_to?1?stringThe path to open after sign-in
redirect_uri?2?stringThe callback URI for the result

1 Kept only when it is a path on this instance that starts with a single /, is at most 2,048 characters and has no backslash or control character

2 Must be the instance default from get SSO status or fluxer://auth/sso/callback for the mobile apps. Any other value returns the field code INVALID_URL_FORMAT

StatusBodyCondition
200SSO start objectThe state was created
403error responseFEATURE_TEMPORARILY_DISABLED when SSO is off or not fully configured
503error responseSSO_PROVIDER_UNAVAILABLE when SSO is on but the provider's discovery document can't be fetched

The rate limit is 10 requests per 10 seconds, on the auth:sso:start bucket.

POST/v1/auth/sso/completeUnauthenticated

Signs in or creates the account linked to the provider identity and returns an SSO completion response. The state works once, and after any failure you start again with start SSO.

An unlinked identity is linked to the account with the same verified email only when the instance sends email and that account's email is confirmed. Otherwise it returns SSO_ACCOUNT_LINK_REQUIRES_SIGN_IN, and the person connects the account with start connecting SSO. An account with a second factor or a passkey is asked for it on every SSO sign-in, except a sign-in to an already linked account from a state started with the mobile redirect_uri. A new account has no password and accepts no invite, and it joins the instance's single community when the instance has one. On an email instance it gets the provider's verified email, and when SSO is enforced each sign-in sets the account email to it.

FieldTypeDescription
codestringThe authorization code from the provider (1-4096 characters)
statestringThe state from start SSO (1-4096 characters)
client_binding??stringThe client_binding from start SSO. Required unless the state was started with the mobile redirect_uri (1-128 characters)
StatusBodyCondition
200SSO completion responseThe account was signed in, or its second factor is needed
400error responseINVALID_OR_EXPIRED_SSO_STATE, SSO_EMAIL_NOT_VERIFIED or another provider or linking code
400error responseINVALID_OR_EXPIRED_SSO_STATE also when client_binding is missing or does not match the state
403error responseSSO_REQUIRED when the account is unknown and new accounts are off
403error responseREGISTRATION_PENDING_APPROVAL, REGISTRATION_REJECTED or a suspension code
503error responseSSO_PROVIDER_UNAVAILABLE when the provider didn't answer or its token or keys URL returned a 5xx

The rate limit is 15 requests per 10 seconds, on the auth:sso:complete bucket.

POST/v1/users/@me/sso/link/startMFA

Starts connecting the instance's SSO provider to the current account after sudo mode and returns an SSO start object. Send the user to authorization_url, then finish with complete SSO, which returns linked and creates no new session. It works while the instance enforces SSO. The session that started it has to stay signed in until it finishes, and an identity another account holds returns SSO_IDENTITY_LINKED_TO_ANOTHER_ACCOUNT.

The body is the sudo verification object.

StatusBodyCondition
200SSO start objectThe state was created
403error responseSUDO_MODE_REQUIRED without a valid sudo proof
403error responseFEATURE_TEMPORARILY_DISABLED when SSO is off or not fully configured

The rate limit is 10 requests per 10 seconds, on the shared auth:sso:start bucket.

POST/v1/auth/registerUnauthenticated

Creates an account and returns an authentication token response, or a registration pending approval response when an admin has to approve it. The request needs a CAPTCHA, and joining a community sends Guild Member Add.

A body with only global_name creates an instant account with a username made from that name. The person keeps it by claiming it.

If you lose the response, try to sign in before you retry, because registration isn't idempotent.

FieldTypeDescription
X-Captcha-Token?stringThe solved ALTCHA challenge, see CAPTCHA handling
Accept-Language?stringThe language for the account and the verification email
FieldTypeDescription
email?1stringThe account email address
username?2stringThe username (1-32 characters, letters, digits and underscores)
global_name?stringThe display name (1-32 characters)
password?3stringThe password (8-256 characters)
date_of_birth?4stringThe date of birth as YYYY-MM-DD
consent?5booleanWhether the person accepted the terms of service (default false)
invite_code??stringAn invite to accept after registration (0-256 characters)
registration_url_code?6?stringA registration URL code from an admin (1-128 characters)
theme?stringThe starting theme, one of dark, dark_legacy, coal, light or system
setup_token?7?stringThe setup token of a new self-hosted instance (1-256 characters)

1 An address already used by any account returns the field code EMAIL_ALREADY_IN_USE. On a username instance, older apps send the username here as name@ followed by the instance host. Any other address returns the field code INSTANCE_ADDRESS_REQUIRED, and the address is never stored

2 Without it, Fluxer makes one from global_name. On a none tag style instance a taken name returns the field code USERNAME_ALREADY_TAKEN

3 A known breached password returns the field code PASSWORD_IS_TOO_COMMON

4 Used when the instance collects date of birth. An age under the minimum for the region (13 by default) returns the field code MUST_BE_MINIMUM_AGE. Without it, the account joins its invite's community after it sets one

5 Must be true on an instance with terms or a privacy policy, or you get the field code MUST_AGREE_TO_TOS_AND_PRIVACY_POLICY

6 Lets the person sign up while registration is closed. An unknown code returns 400 REGISTRATION_URL_INVALID

7 Needed until the first admin account exists. A missing value returns the field code SETUP_TOKEN_REQUIRED, and the account that uses it gets the full Admin ACL

StatusBodyCondition
200authentication token response | registration pending approval responseThe account was created or is waiting for approval
400error responseCAPTCHA_REQUIRED or INVALID_CAPTCHA
400error responseREGISTRATION_CREDENTIALS_REQUIRED when an account waiting for approval has no password or email
403error responseREGISTRATION_CLOSED, INSTANT_ACCOUNTS_DISABLED or CONTENT_BLOCKED
423error responseGENERAL_ERROR when a new instance is still setting its sign-in method

The rate limit is 10 requests per 10 seconds, on the auth:register bucket.

POST/v1/auth/identifyUnauthenticated

Starts identifier-first sign-in for an existing or new account and returns an identify response object. It works only when identifier_first is on in the instance features, and otherwise returns 403 FEATURE_TEMPORARILY_DISABLED.

FieldTypeDescription
X-Captcha-Token?stringThe solved ALTCHA challenge, asked for only after many lookups
FieldTypeDescription
email?stringThe email address, on an email instance
login?stringThe username, on a username instance
device_proof?stringThe device_proof from an earlier sign-in on this device (1-128 characters)
registration_url_code??stringA registration URL code from an admin, for a sign-up (1-128 characters)
StatusBodyCondition
200identify response objectThe identifier was looked up

The rate limit is 10 requests per 10 seconds, on the auth:identify bucket.

POST/v1/auth/email-code/sendUnauthenticated

Emails a 6-digit code for an identify ticket and returns an email code send response object. A sign-up needs a CAPTCHA and goes through the same checks as register an account.

FieldTypeDescription
X-Captcha-Token?stringThe solved ALTCHA challenge, see CAPTCHA handling
FieldTypeDescription
ticketstringThe ticket from identify an account (1-128 characters)
registration_url_code??stringA registration URL code from an admin, for a sign-up (1-128 characters)
StatusBodyCondition
200email code send response objectThe code was sent
400error responseEMAIL_CODES_UNAVAILABLE when the instance can't send codes
400error responseUNKNOWN_SIGN_IN_TICKET, or PASSWORD_SIGN_IN_REQUIRED when the account has a password
429error responseEMAIL_CODE_RESEND_COOLDOWN before retry_after seconds have passed

The rate limit is 5 requests per minute, on the auth:email_code:send bucket.

POST/v1/auth/email-code/verifyUnauthenticated

Checks the emailed code and signs in to the existing account, or creates the account for a new address the way register an account does, with the email already verified. A code allows 5 tries. When the code confirms an address that was never confirmed, it also ends every other session of the account, forgets its authorized IP addresses and revokes its OAuth2 grants.

FieldTypeDescription
ticketstringThe ticket from identify an account (1-128 characters)
codestringThe 6-digit code from the email
consent?1booleanWhether the person accepted the terms of service, for a sign-up
registration_url_code??stringA registration URL code from an admin, for a sign-up (1-128 characters)
invite_code??stringAn invite to open after sign-up (0-256 characters)
username?stringThe username for a new account (1-32 characters, letters, digits and underscores). Ignored when signing in
global_name?stringThe display name for a new account (1-32 characters). Ignored when signing in

1 Needed when consent_required is true in the identify registration object

StatusBodyCondition
200authentication token response | MFA challenge response | registration pending approval responseThe code was accepted
400error responseINVALID_SIGN_IN_CODE or SIGN_IN_CODE_EXPIRED

The rate limit is 10 requests per minute, on the auth:email_code:verify bucket.

GET/v1/auth/register/handoff/challengeUnauthenticated

Returns the CAPTCHA challenge for start an account for the website. Only a trusted caller can use it, with its key in X-Fluxer-Internal-Key and the visitor IP address in X-Fluxer-Client-IP.

StatusBodyCondition
200CAPTCHA challenge objectThe challenge was returned
403error responseMISSING_ACCESS for an untrusted caller, or INSTANT_ACCOUNTS_DISABLED

The rate limit is 20 requests per minute, on the auth:register:handoff:challenge bucket.

POST/v1/auth/register/handoffUnauthenticated

Checks a display name typed on the website and returns an account handoff code that the app redeems within 15 minutes. No account exists until create a website account runs. Only a trusted caller can use it, as with get the website sign-up challenge.

FieldTypeDescription
X-Captcha-TokenstringThe visitor's solved ALTCHA challenge, see CAPTCHA handling
FieldTypeDescription
global_namestringThe display name for the new account (1-32 characters)
nonce_hashstringThe hex SHA-256 of a random nonce the visitor's browser keeps
locale?stringThe locale of the page the visitor started from (2-16 characters)
registration_url_code??stringA registration URL code from an admin (1-128 characters)
StatusBodyCondition
200account handoff objectThe display name is allowed
403error responseREGISTRATION_CLOSED, INSTANT_ACCOUNTS_DISABLED or CONTENT_BLOCKED

The rate limit is 10 requests per 10 seconds, on the auth:register:handoff bucket.

POST/v1/auth/register/handoff/redeemUnauthenticated

Creates the account started on the website and returns an authentication token response. The account follows the instant account rules of register an account.

FieldTypeDescription
handoff_codestringThe code from the website, 43 URL-safe base64 characters
noncestringThe nonce whose hash was sent with the handoff (16-256 URL-safe base64 characters)
consent?booleanWhether the person accepted the terms of service
global_name?stringThe display name, when the person changed it in the app (1-32 characters)
StatusBodyCondition
200authentication token responseThe account was created
400error responseUNKNOWN_ACCOUNT_HANDOFF, or INVALID_ACCOUNT_HANDOFF_NONCE, which uses up the code

The rate limit is 10 requests per minute, on the auth:register:handoff:redeem bucket.

POST/v1/auth/loginUnauthenticated

Signs in with an email address or username and a password. Returns an authentication token response, or an MFA challenge response when the account has a second factor. The request needs a CAPTCHA.

An unknown account and a wrong password both return the field code INVALID_EMAIL_OR_PASSWORD, or INVALID_LOGIN_OR_PASSWORD on a username instance.

A sign-in from a new IP address on an account without a second factor returns 403 IP_AUTHORIZATION_REQUIRED with a ticket and sends an email. Poll with poll IP authorization until the person uses the link. Instances that send no email or turn off new-IP authorization skip this step.

Signing in turns a disabled account back on and cancels a deletion the person scheduled.

FieldTypeDescription
X-Captcha-Token?stringThe solved ALTCHA challenge, see CAPTCHA handling
X-Fluxer-Client-Properties?stringBase64 JSON with the app's os, for the client info
User-Agent?stringThe client string shown in list authentication sessions
FieldTypeDescription
email?1stringThe email address, or the username from older apps on a username instance
login?1stringThe email address or username (1-320 characters)
passwordstringThe password (8-256 characters)
invite_code??stringAn invite to accept after a sign-in with no second factor (0-256 characters)

1 Send one of the two. On a username instance, alex, alex#0 and alex@ followed by the instance host all mean the same account

StatusBodyCondition
200authentication token response | MFA challenge responseThe credentials were accepted
403error responseIP_AUTHORIZATION_REQUIRED when the IP address is new
403error responseA suspension code with an enforcement notice, after the last factor

The rate limit is 10 requests per 10 seconds, on the auth:login bucket.

POST/v1/auth/login/mfa/totpUnauthenticated

Finishes a sign-in with an authenticator code or a backup code and returns an authentication token response.

FieldTypeDescription
codestringThe authenticator code or an unused backup code
ticketstringThe ticket from password login or complete SSO
StatusBodyCondition
200authentication token responseThe code was accepted
400error responseThe field code INVALID_CODE, or SESSION_TIMEOUT for an expired ticket

The rate limit is 5 requests per minute, on the shared auth:login:mfa bucket.

POST/v1/auth/login/mfa/webauthn/authentication-optionsUnauthenticated

Returns WebAuthn authentication options for the passkeys of the account behind an MFA ticket. The ticket stays valid.

FieldTypeDescription
ticketstringThe ticket from password login or complete SSO (1-256 characters)
StatusBodyCondition
200WebAuthn authentication options objectThe challenge was created
400error responseNO_PASSKEYS_REGISTERED when the account has no passkey

The rate limit is 5 requests per minute, on the shared auth:login:mfa bucket.

POST/v1/auth/login/mfa/webauthnUnauthenticated

Finishes a sign-in with a passkey and returns an authentication token response.

FieldTypeDescription
responseWebAuthn assertion objectThe browser credential result
challengestringThe challenge from get WebAuthn MFA options
ticketstringThe ticket from password login or complete SSO
StatusBodyCondition
200authentication token responseThe passkey was accepted
400error responseTWO_FACTOR_REQUIRED when passkeys aren't the account's second factor
401error responsePASSKEY_AUTHENTICATION_FAILED when the passkey check fails

The rate limit is 5 requests per minute, on the shared auth:login:mfa bucket.

POST/v1/auth/webauthn/authentication-optionsUnauthenticated

Returns WebAuthn authentication options for signing in with a passkey and no password. The request has no body.

StatusBodyCondition
200WebAuthn authentication options objectThe challenge was created

The rate limit is 20 requests per 10 seconds, on the auth:webauthn:options bucket.

POST/v1/auth/webauthn/authenticateUnauthenticated

Signs in with a passkey and returns an authentication token response.

FieldTypeDescription
responseWebAuthn assertion objectThe browser credential result
challengestringThe challenge from get discoverable WebAuthn options
StatusBodyCondition
200authentication token responseThe passkey was accepted
401error responsePASSKEY_AUTHENTICATION_FAILED when the passkey check fails

The rate limit is 10 requests per 10 seconds, on the auth:webauthn:authenticate bucket.

POST/v1/auth/logoutBot

Revokes the session whose token is in the Authorization header and returns 204. Other sessions stay signed in. A bot token is accepted and revokes nothing. When the instance cleans up unclaimed accounts on the last sign-out, ending the last session of an account nobody can sign in to again schedules its deletion 24 hours later.

StatusBodyCondition
204emptyThe session was revoked

The rate limit is 20 requests per 10 seconds, on the auth:logout bucket.

POST/v1/auth/verifyUnauthenticated

Uses the token from a verification email to mark the account's email verified, and returns 204. Fluxer sends User Update.

FieldTypeDescription
tokenstringThe token from the email, 64 characters
StatusBodyCondition
204emptyThe email was verified
400error responseThe field code INVALID_OR_EXPIRED_VERIFICATION_TOKEN

The rate limit is 10 requests per minute, on the auth:verify bucket.

POST/v1/auth/verify/resend

Sends a new verification email, valid for 24 hours, and returns 204. It sends nothing when the email is already verified.

StatusBodyCondition
204emptyThe email was sent or was already verified

The rate limit is 10 requests per minute, on the auth:verify:resend bucket.

POST/v1/auth/forgotUnauthenticated

Emails a password reset link, valid for 1 hour, and returns 204. The request needs a CAPTCHA, and the response is the same whether or not an account has the address.

FieldTypeDescription
X-Captcha-Token?stringThe solved ALTCHA challenge, see CAPTCHA handling
FieldTypeDescription
emailstringThe address to send the reset link to
StatusBodyCondition
204emptyThe request was accepted

The rate limit is 5 requests per minute, on the auth:forgot bucket.

GET/v1/auth/reset/{token}Unauthenticated

Checks a 64-character reset token without using it and returns a password reset validity object.

StatusBodyCondition
200password reset validity objectThe token was checked

The rate limit is 20 requests per minute, on the auth:reset:validate bucket.

POST/v1/auth/resetUnauthenticated

Sets a new password with a reset token. Returns an authentication token response, or an MFA challenge response when the account has a second factor. On a username instance, reset tokens come from an admin's password reset link.

FieldTypeDescription
tokenstringThe reset token, 64 characters
passwordstringThe new password (8-256 characters)
StatusBodyCondition
200authentication token response | MFA challenge responseThe password was changed
400error responseThe field code INVALID_OR_EXPIRED_RESET_TOKEN or PASSWORD_IS_TOO_COMMON

The rate limit is 10 requests per minute, on the auth:reset bucket.

POST/v1/auth/recoverUnauthenticated

Sets a new password with the recovery key from a recovery kit and returns an account recovery response with a new key. Only a username instance has this route, and an email instance returns 400 USERNAME_SIGN_IN_ONLY. The request needs a CAPTCHA.

FieldTypeDescription
X-Captcha-Token?stringThe solved ALTCHA challenge, see CAPTCHA handling
FieldTypeDescription
loginstringThe username (1-320 characters)
recovery_key1stringThe recovery key from the recovery kit (1-128 characters)
passwordstringThe new password (8-256 characters)

1 Spaces, dashes and letter case are ignored

StatusBodyCondition
200account recovery responseThe password and the recovery kit were replaced
400error responseThe field code INVALID_RECOVERY_KEY, also for an unknown username, or PASSWORD_IS_TOO_COMMON

The rate limit is 10 requests per minute, on the auth:recover bucket.

POST/v1/auth/email-revertUnauthenticated

Uses the token sent to the old address after an email change to put that address back and set a new password. Returns an authentication token response and sends User Update. The token is valid for 24 hours.

FieldTypeDescription
tokenstringThe token from the email, 64 characters
passwordstringThe new password (8-256 characters)
StatusBodyCondition
200authentication token responseThe email was put back
400error responseThe field code INVALID_OR_EXPIRED_REVERT_TOKEN or PASSWORD_IS_TOO_COMMON

The rate limit is 10 requests per minute, on the auth:email_revert bucket.

GET/v1/auth/sessions

Returns the account's active authentication sessions, most recently used first.

StatusBodyCondition
200array[authentication session object]The sessions were returned

The rate limit is 40 requests per 10 seconds, on the auth:sessions bucket.

POST/v1/auth/sessions/logoutMFA

Signs out the sessions you name and returns 204. It needs sudo mode, from the X-Fluxer-Sudo-Mode-JWT header or a password or MFA proof in the body. Leave out the id_hash of the current session to stay signed in.

FieldTypeDescription
session_id_hashesarray[string]The id_hash values of the sessions to sign out (max 100 entries)
password?stringThe password, for an account with no authenticator app and no passkey (8-256 characters)
mfa_method?stringThe proof method, one of totp, webauthn or email_code
mfa_code?stringThe authenticator code or a backup code, for totp (1-32 characters)
webauthn_response?WebAuthn assertion objectThe passkey result, for webauthn
webauthn_challenge?stringThe sudo mode challenge, for webauthn
email_code_ticket?stringThe ticket from send a sudo email code (1-128 characters)
email_code?stringThe 6-digit code from that email
StatusBodyCondition
204emptyThe sessions were signed out
403error responseSUDO_MODE_REQUIRED when proof is missing

The rate limit is 20 requests per 10 seconds, on the auth:sessions:logout bucket.

POST/v1/auth/authorize-ipUnauthenticated

Uses the token from the IP authorization email to approve the new IP address and returns 204. The waiting device gets its session from poll IP authorization.

FieldTypeDescription
tokenstringThe token from the email
StatusBodyCondition
204emptyThe IP address was approved
400error responseThe field code INVALID_OR_EXPIRED_AUTHORIZATION_TOKEN

The rate limit is 5 requests per minute, on the auth:authorize_ip bucket.

POST/v1/auth/sign-in-linkUnauthenticated

Signs in with the token from a sign-in link made by create admin sign-in link and returns an authentication token response. The link works once, for 15 minutes.

FieldTypeDescription
tokenstringThe token from the fragment of the sign-in link
StatusBodyCondition
200authentication token responseThe account was signed in
400error responseThe field code INVALID_OR_EXPIRED_AUTHORIZATION_TOKEN

The rate limit is 5 requests per minute, on the shared auth:authorize_ip bucket.

POST/v1/auth/ip-authorization/resendUnauthenticated

Sends the IP authorization email again and returns 204. Each ticket allows one resend, 30 seconds or more after the sign-in.

FieldTypeDescription
ticketstringThe ticket from the IP_AUTHORIZATION_REQUIRED error
StatusBodyCondition
204emptyThe email was sent again
400error responseIP_AUTHORIZATION_RESEND_LIMIT_EXCEEDED when the ticket was already resent
429error responseIP_AUTHORIZATION_RESEND_COOLDOWN with resend_available_in

The rate limit is 5 requests per minute, on the auth:ip_authorization_resend bucket.

GET/v1/auth/ip-authorization/pollUnauthenticated

Returns the IP authorization poll state of a waiting sign-in. After the link is used, the token is returned for 60 seconds.

FieldTypeDescription
ticketstringThe ticket from the IP_AUTHORIZATION_REQUIRED error
StatusBodyCondition
200IP authorization poll objectThe state was returned
400error responseThe field code INVALID_OR_EXPIRED_AUTHORIZATION_TICKET

The rate limit is 60 requests per minute, on the auth:ip_authorization_poll bucket.

POST/v1/auth/username-suggestionsUnauthenticated

Returns username suggestions made from a display name. It doesn't check or reserve the name.

FieldTypeDescription
global_namestringThe display name (1-32 characters)
StatusBodyCondition
200username suggestions objectThe suggestions were returned

The rate limit is 10 requests per 10 seconds, on the shared auth:register bucket.

GET/v1/auth/username-availabilityUnauthenticated

Returns whether a username is free, as a username availability object. Only an instance with the none tag style has this route, and others return 400 USERNAME_SIGN_IN_ONLY. It doesn't reserve the name.

FieldTypeDescription
usernamestringThe username to check (1-32 characters)
StatusBodyCondition
200username availability objectThe username was checked

The rate limit is 30 requests per minute, on the auth:username_availability bucket.

POST/v1/auth/handoff/initiateUnauthenticated

Starts signing in a device from one that is already signed in. Returns a handoff initiation object with a code that the person enters on the signed-in device.

FieldTypeDescription
return_uri?stringfluxer://handoff, fluxer-canary://handoff or fluxer-development://handoff
StatusBodyCondition
200handoff initiation objectThe handoff was created

The rate limit is 10 requests per minute, on the auth:handoff:initiate bucket.

GET/v1/auth/handoff/{code}/infoUnauthenticated

Returns handoff information so the signed-in device can show which device is asking. You have to call this before complete desktop handoff or deny a desktop handoff. Hyphens, spaces and letter case in the code are ignored.

StatusBodyCondition
200handoff information objectThe handoff was found or has expired
400error responseINVALID_HANDOFF_CODE when the code is malformed or was looked up 3 times
400error responseHANDOFF_CODE_ALREADY_USED when the same network already approved this code
429error responseHANDOFF_ATTEMPTS_EXCEEDED when this IP address has 5 failed handoff attempts in 15 minutes, with retry_after

The rate limit is 10 requests per minute, on the auth:handoff:info bucket.

POST/v1/auth/handoff/complete

Approves a handoff and creates a session for the other device. Send the approving device's session token in the Authorization header or as token. Returns a handoff completion object for the deep_link return method, or 204 for code.

FieldTypeDescription
codestringThe handoff code
token?stringThe approving device's session token, when you don't send Authorization
user_idsnowflakeThe ID of the approving account
return_method?stringdeep_link or code (default code)
StatusBodyCondition
200handoff completion objectThe handoff was approved with deep_link
204emptyThe handoff was approved with code
400error responseINVALID_HANDOFF_CODE or HANDOFF_CODE_EXPIRED when the code is unknown or expired
400error responseHANDOFF_CODE_ALREADY_USED when the same network already approved this code
401error responseINVALID_TOKEN when the approving session is not active
429error responseHANDOFF_ATTEMPTS_EXCEEDED when this IP address has 5 failed handoff attempts in 15 minutes, with retry_after

The rate limit is 10 requests per minute, on the auth:handoff:complete bucket.

POST/v1/auth/handoff/{code}/denyUnauthenticated

Declines a handoff and returns 204, and the other device sees denied.

StatusBodyCondition
204emptyThe handoff was denied
400error responseINVALID_HANDOFF_CODE when the code is unknown, expired or not looked up first
429error responseHANDOFF_ATTEMPTS_EXCEEDED when this IP address has 5 failed handoff attempts in 15 minutes, with retry_after

The rate limit is 10 requests per minute, on the auth:handoff:deny bucket.

GET/v1/auth/handoff/{code}/statusUnauthenticated

Returns the handoff status without the token, and reports a completed handoff as pending. To get the token, use get desktop handoff status with the poll secret.

StatusBodyCondition
200handoff status objectThe status was returned
400error responseINVALID_HANDOFF_CODE when the code is malformed
429error responseHANDOFF_ATTEMPTS_EXCEEDED when this IP address has 5 failed handoff attempts in 15 minutes, with retry_after

The rate limit is 60 requests per minute, on the auth:handoff:status bucket.

Get desktop handoff status with the poll secret

Section titled “Get desktop handoff status with the poll secret”
POST/v1/auth/handoff/{code}/statusUnauthenticated

Returns the handoff status and, once, the new session token. Keep the token when you get it, because later polls return expired. A wrong secret returns pending.

FieldTypeDescription
poll_secretstringThe secret from initiate desktop handoff
grant?stringThe grant from the handoff completion link, for deep_link
StatusBodyCondition
200handoff status objectThe status was returned
400error responseINVALID_HANDOFF_CODE when the code is malformed
429error responseHANDOFF_ATTEMPTS_EXCEEDED with retry_after for a wrong poll secret once this IP address has 5 failures in 15 minutes

The rate limit is 60 requests per minute, on the shared auth:handoff:status bucket.

DELETE/v1/auth/handoff/{code}Unauthenticated

Cancels a handoff and returns 204. A session that was already created stays signed in.

FieldTypeDescription
poll_secretstringThe secret from initiate desktop handoff
StatusBodyCondition
204emptyThe handoff was cancelled
400error responseINVALID_HANDOFF_CODE, also for a wrong secret

The rate limit is 10 requests per minute, on the auth:handoff:cancel bucket.

POST/v1/auth/origin-handoff

Stores encrypted app state for 2 minutes so another web origin of the same instance can take it once. Returns an origin handoff object. The sender keeps the encryption key, and Fluxer only stores the payload.

FieldTypeDescription
nonce_hashstringThe SHA-256 of the receiving origin's nonce, as 64 lowercase hex characters
payloadstringThe encrypted state as base64url (1-8,388,608 characters)
StatusBodyCondition
200origin handoff objectThe state was stored

The rate limit is 3 requests per 10 minutes, on the auth:origin_handoff:create bucket.

POST/v1/auth/origin-handoff/redeemUnauthenticated

Returns the state stored by create origin handoff as an origin handoff payload object. Every attempt deletes the state, even one with a wrong nonce.

FieldTypeDescription
handoff_idstringThe ID from create origin handoff, 43 base64url characters
noncestringThe nonce whose SHA-256 was sent, as base64url (16-256 characters)
StatusBodyCondition
200origin handoff payload objectThe state was returned and deleted
400error responseINVALID_ORIGIN_HANDOFF_NONCE when the nonce does not match
404error responseUNKNOWN_ORIGIN_HANDOFF when the ID is unknown, expired or used

The rate limit is 10 requests per minute, on the auth:origin_handoff:redeem bucket.