Skip to content
Fluxer API

Email and password changes

Changing the email address or password takes several calls. Fluxer emails a code, you send it back with the ticket from the first call, and each step returns what the next one needs. Every route needs a user session, and bots can't use them.

On an instance where people sign in with a username (account_identity is username in the instance features), accounts have no email address. There the email change and password change routes return 400 EMAIL_UNAVAILABLE_ON_INSTANCE. Use Set a new password, Get recovery kit status and Create a recovery kit instead, which return 400 USERNAME_SIGN_IN_ONLY on an email instance.

An account linked to an instance that enforces single sign-on gets 403 SSO_REQUIRED from every email change and password change route, because the provider manages its email and sign-in.

Keep these values out of logs and URLs, because anyone who has one can continue the change.

ValueDescription
ticketA UUID from the first call of a flow. It belongs to one account and stops working once the flow completes
codeAn emailed code like ABCD-1234, valid for 10 minutes. Send it exactly as written, in uppercase with the hyphen
original_proofA UUID that proves the original address step, needed to request and verify the new address
verification_proofA UUID that proves the password change code, needed to complete the change
email_tokenA UUID for one verified new address, valid for 30 minutes and usable once

You can resend a code 30 seconds after the previous send, and every email also counts against a per-account limit (see Rate limits). A bad ticket, code, proof or token returns 400 INVALID_FORM_BODY. The validation error entry has the specific code, such as INVALID_VERIFICATION_CODE or VERIFICATION_CODE_EXPIRED, and its path is the field.

An email change goes through these steps in order. When require_original is false, the ticket skips the first verify step and Start email change returns original_proof right away.

StepCallResult
1Start email changeA ticket and a code to the current address
2Verify original emailThe original address proof
3Request new emailA code sent to the new address
4Verify new emailThe email token
5Apply email changeThe updated user

Calling a step out of order returns ORIGINAL_EMAIL_MUST_BE_VERIFIED_FIRST, ORIGINAL_VERIFICATION_NOT_REQUIRED, ORIGINAL_EMAIL_ALREADY_VERIFIED or NO_NEW_EMAIL_REQUESTED.

A new email change ticket.

FieldTypeDescription
ticketstringThe ticket for the later steps
require_originalbooleanWhether you must verify a code sent to the current address first. True when that address is verified
original_email?stringThe current address on the account
original_proof?stringThe proof, set only when require_original is false
original_code_expires_at?ISO8601 timestampWhen the code sent to the current address expires
resend_available_at?ISO8601 timestampWhen you can resend the code to the current address
{
"ticket": "6f1a4c9e-3d52-4a77-9b0e-2c8f5d41ab30",
"require_original": true,
"original_email": "old@example.com",
"original_proof": null,
"original_code_expires_at": "2026-03-04T18:10:00.000Z",
"resend_available_at": "2026-03-04T18:00:30.000Z"
}

A ticket with a new address and a code sent to it.

FieldTypeDescription
ticketstringThe ticket for the later steps
new_emailstringThe new address, trimmed
new_code_expires_atISO8601 timestampWhen the code sent to the new address expires
resend_available_at?ISO8601 timestampWhen you can resend the code
FieldTypeDescription
original_proofstringThe proof for the later email change steps
FieldTypeDescription
email_tokenstringThe token Apply email change uses

A new password change ticket.

FieldTypeDescription
ticketstringThe ticket for the later steps
code_expires_atISO8601 timestampWhen the emailed code expires
resend_available_at?ISO8601 timestampWhen you can resend the code
FieldTypeDescription
verification_proofstringThe proof Complete password change needs

The new session after a password change. Fluxer ends every session on the account, including the one that made the request, and you send later requests with this token. Modify current user adds the same fields when it changes the password.

FieldTypeDescription
tokenstringThe token for the new session
auth_session_id_hashstringThe base64url hash of the new session ID
FieldTypeDescription
has_recovery_kitbooleanWhether the account has a recovery kit
created_at?ISO8601 timestampWhen the current kit was created

A new recovery kit, whose key Recover an account uses to set a new password without signing in.

FieldTypeDescription
recovery_keystringThe key, 8 groups of 4 Crockford base32 characters joined by -
created_atISO8601 timestampWhen the kit was created
POST/v1/users/@me/email-change/start

Creates an email change ticket and, when the current address is verified, emails it a code. A claimed account with no email address gets MUST_HAVE_EMAIL_TO_CHANGE_IT. An instance that can't send email returns 400 EMAIL_CODES_UNAVAILABLE here and from Resend original email code.

The body is empty or an empty object.

StatusBodyCondition
200email change start objectThe ticket was created

The rate limit is 10 requests per minute, on the user:email_change:start bucket.

POST/v1/users/@me/email-change/resend-original

Sends a new code to the current address and makes the previous code invalid.

FieldTypeDescription
ticketstringThe ticket from Start email change (1-256 characters)
StatusBodyCondition
204emptyThe code was sent

The rate limit is 10 requests per minute, on the user:email_change:resend_original bucket.

POST/v1/users/@me/email-change/verify-original

Checks the code sent to the current address and returns a proof, and calling it again on the same ticket returns the same proof.

FieldTypeDescription
ticketstringThe ticket from Start email change (1-256 characters)
codestringThe code sent to the current address (1-256 characters)
StatusBodyCondition
200original email verification objectThe current address is verified

The rate limit is 20 requests per minute, on the user:email_change:verify_original bucket.

POST/v1/users/@me/email-change/request-new

Sets the new address on the ticket and emails it a code. Calling it again with another address replaces the address on the ticket. The new address must differ from the current one, use a domain the instance allows and not belong to another account.

FieldTypeDescription
ticketstringThe ticket from Start email change (1-256 characters)
new_emailstringThe new address (1-254 characters)
original_proofstringThe proof from Start email change or Verify original email (1-256 characters)
new_password?stringA password to check against known breached passwords. Not saved (8-256 characters)
StatusBodyCondition
200new email request objectThe code was sent to the new address

The rate limit is 10 requests per minute, on the user:email_change:request_new bucket.

POST/v1/users/@me/email-change/resend-new

Sends a new code to the new address and makes the previous code invalid.

FieldTypeDescription
ticketstringThe ticket from Start email change (1-256 characters)
StatusBodyCondition
204emptyThe code was sent

The rate limit is 10 requests per minute, on the user:email_change:resend_new bucket.

POST/v1/users/@me/email-change/verify-new

Checks the code sent to the new address, completes the ticket and returns an email token. The account email doesn't change until you call Apply email change.

FieldTypeDescription
ticketstringThe ticket from Start email change (1-256 characters)
codestringThe code sent to the new address (1-256 characters)
original_proofstringThe proof from Start email change or Verify original email (1-256 characters)
StatusBodyCondition
200new email verification objectThe new address is verified

The rate limit is 20 requests per minute, on the user:email_change:verify_new bucket.

POST/v1/users/@me/email-change/applyMFA

Sets the verified address on the account and returns the updated user. A claimed account needs sudo mode, and an unclaimed account doesn't.

The token fails with EMAIL_TOKEN_EXPIRED after 30 minutes and INVALID_EMAIL_TOKEN once used.

The body extends the sudo verification object with email_token.

FieldTypeDescription
email_tokenstringThe token from Verify new email (1-256 characters)
StatusBodyCondition
200user objectThe new address was set

Fluxer sends User Update and emails the previous address, if there was one, a link to revert the change. Sessions and the password don't change.

The rate limit is 20 requests per minute, on the user:email_change:apply bucket.

When mail to an account's address bounces, the user object has email_bounced set to true. The three routes below set a new address without the original address step or sudo mode. Other accounts use the normal email change flow.

Request replacement email for bounced address

Section titled “Request replacement email for bounced address”
POST/v1/users/@me/email-change/bounced/request-new

Creates a ticket, sets the new address on it and emails it a code. The address is checked the same way as in Request new email.

FieldTypeDescription
new_emailstringThe new address (1-254 characters)
StatusBodyCondition
200new email request objectThe code was sent to the new address

The rate limit is 10 requests per minute, on the user:email_change:bounced:request_new bucket.

POST/v1/users/@me/email-change/bounced/resend-new

Sends a new code to the new address and makes the previous code invalid.

FieldTypeDescription
ticketstringThe ticket from Request replacement email for bounced address (1-256 characters)
StatusBodyCondition
204emptyThe code was sent

The rate limit is 10 requests per minute, on the user:email_change:bounced:resend_new bucket.

Verify replacement email for bounced address

Section titled “Verify replacement email for bounced address”
POST/v1/users/@me/email-change/bounced/verify-new

Checks the code and sets the new address on the account in the same call. Returns the updated user with email_bounced set to false, and Fluxer sends User Update.

FieldTypeDescription
ticketstringThe ticket from Request replacement email for bounced address (1-256 characters)
codestringThe code sent to the new address (1-256 characters)
StatusBodyCondition
200user objectThe new address was set

The rate limit is 20 requests per minute, on the user:email_change:bounced:verify_new bucket.

POST/v1/users/@me/password-change/start

Creates a password change ticket and emails a code to the account address. An account with no email address gets MUST_HAVE_EMAIL_TO_CHANGE_IT.

The body is empty or an empty object.

StatusBodyCondition
200password change start objectThe code was sent

The rate limit is 10 requests per minute, on the user:password_change:start bucket.

POST/v1/users/@me/password-change/resend

Sends a new code and makes the previous code invalid.

FieldTypeDescription
ticketstringThe ticket from Start password change (1-256 characters)
StatusBodyCondition
204emptyThe code was sent

The rate limit is 10 requests per minute, on the user:password_change:resend bucket.

POST/v1/users/@me/password-change/verify

Checks the emailed code and returns a proof, and calling it again on the same ticket returns the same proof.

FieldTypeDescription
ticketstringThe ticket from Start password change (1-256 characters)
codestringThe emailed code (1-256 characters)
StatusBodyCondition
200password verification objectThe code is correct

The rate limit is 20 requests per minute, on the user:password_change:verify bucket.

POST/v1/users/@me/password-change/complete

Sets the new password and returns a new session. An account with TOTP or a WebAuthn credential also needs sudo mode with that factor. A password found in known breaches fails with PASSWORD_IS_TOO_COMMON.

The body extends the sudo verification object with these fields.

FieldTypeDescription
ticketstringThe ticket from Start password change (1-256 characters)
verification_proofstringThe proof from Verify password change code (1-256 characters)
new_passwordstringThe new password (8-256 characters)
StatusBodyCondition
200password change completion objectThe password was changed

Fluxer sends Auth Session Change with the old and new session ID hashes.

The rate limit is 10 requests per minute, on the user:password_change:complete bucket.

POST/v1/users/@me/passwordMFA

Changes the password on a username instance after sudo mode and returns a new session. It also deletes the account's recovery kit. A password found in known breaches fails with PASSWORD_IS_TOO_COMMON, and an SSO account with no password gets PASSWORD_NOT_SET. An instance that enforces single sign-on returns 403 SSO_REQUIRED.

The body extends the sudo verification object with new_password.

FieldTypeDescription
new_passwordstringThe new password (8-256 characters)
StatusBodyCondition
200password change completion objectThe password was changed

Fluxer sends Auth Session Change with the old and new session ID hashes.

The rate limit is 10 requests per minute, on the user:password:update bucket.

GET/v1/users/@me/recovery-kit

Returns whether the account has a recovery kit.

StatusBodyCondition
200recovery kit status objectThe status was returned

The rate limit is 20 requests per minute, on the user:recovery_kit:get bucket.

POST/v1/users/@me/recovery-kitMFA

Creates a recovery kit after sudo mode and makes the previous key stop working. Show the key to the person before you discard the response, because Fluxer never returns it again. An SSO account with no password gets PASSWORD_NOT_SET, and an instance that enforces single sign-on returns 403 SSO_REQUIRED.

The body is the sudo verification object.

StatusBodyCondition
200recovery kit objectThe kit was created

The rate limit is 6 requests per minute, on the user:recovery_kit:create bucket.