Every one of these is contract: the UI must render each, not just the happy path. This is how a reset that mailed into the void, and a policy rejection with no message, get designed out before build.
Submitting · request in flight
Expired or invalid token · reset-confirm 400
This reset link has expired
Links are single-use and expire after 30 minutes. Request a new one to continue.
Password policy rejection · 400
That password is too common. Use at least 12 characters and avoid words like "password".
Something went wrong · retry
We couldn't update your password
Something went wrong on our side. Your password is unchanged — try again.
Set-password when one exists · POST /password 409
You already have a password
Use "Change password" to update it, or reset it if you've forgotten it.
Change · wrong current password · 400 INVALID_CREDENTIALS
That's not your current password. Check it, or reset your password instead.