Invitation and password errors
The error codes the invitation, password-reset and password-change routes answer with, when each happens, and what to do about it.
These codes come from three flows: inviting people to a boundary, resetting a forgotten password,
and changing your own password. The body has the platform's usual shape — error, code,
correlationId and traceId, described in The error body —
with module set to rbac. The code is in code; error is a sentence for people and is not
stable.
Refusals that happen before these routes run keep the platform-wide codes: a missing permission is
FORBIDDEN, a request body that cannot be read is MODEL_BINDING_FAILED or BAD_REQUEST, and a
missing or expired sign-in is UNAUTHORIZED. See Errors.
How invitations work in the portal is described in
Invite and manage users.
Inviting and managing invitations
These routes need boundaries/invitations/create on the boundary:
| Route | What it does |
|---|---|
POST /boundaries/{boundaryId}/providers/authorization/invitations |
Creates an invitation |
DELETE /boundaries/{boundaryId}/providers/authorization/invitations/{invitationId} |
Revokes one |
POST /boundaries/{boundaryId}/providers/authorization/invitations/{invitationId}/resend |
Resends one |
A create request is checked in this order: the e-mail address and the role, the boundary's tenant,
the scope and who may grant the role, then existing invitations and assignments. The first check
that fails decides the code. Besides the codes below, these routes can answer
not_found and invalid_state.
unknown_role
400. Create only. roleDefinitionName does not exactly match the name of any role — built-in
or custom. Use the name as it is shown under Access control (IAM) → Roles, for example
Contributor.
forbidden
403. Create only. You may send invitations in this boundary, but the role is one that only exists at the platform root — Platform Owner, Platform Contributor, Platform Reader or LLM Gateway Consumer — and you are not Owner or Platform Owner at the root, assigned directly or through active just-in-time access. Ask a Platform Owner to send the invitation.
This lowercase forbidden is not the platform-wide FORBIDDEN, which the same route answers,
also with 403, when you lack boundaries/invitations/create itself.
tenant_resolution_failed
503. Create only. The platform could not find the tenant the boundary belongs to: the
boundary does not exist, the service that looks it up is not allowed to read it, or the boundary
has no tenant. These causes do not go away by themselves, so retrying rarely helps. Report it
with the correlationId.
duplicate_pending
409. Create only. The boundary already has an open invitation — Pending or Awaiting login — for the same e-mail address, whatever its role or scope. Addresses are compared without regard to case. Resend the existing invitation, or revoke it and invite again with the role and scope you want.
already_member
409. Create only. The address belongs to an existing user who already holds this role at this scope, assigned directly to them. A role held through a group, the same role at another scope, or another role at this scope does not block the invitation. Nothing needs doing; to give the user more access, choose another role or scope.
already_accepted
409. Revoke only. The invitation has been accepted, so there is nothing left to revoke. To take the access away, remove the role assignment — see Role assignments.
already_revoked
409. Revoke only. The invitation was revoked already. Nothing needs doing.
resend_limit
429. Resend only. The invitation has been resent as often as it may be for now. With the
default settings, the first three resends of an invitation always go through; after that, one
more is allowed once 24 hours have passed since the last resend. The limit applies to each
invitation separately, and a platform administrator can change it. The response has no
Retry-After header.
Wait until 24 hours after the last resend, or revoke the invitation and invite the person again — a new invitation has its own limit.
Accepting an invitation
The link in the invitation e-mail carries a token. The portal uses it on these routes:
| Route | Sign-in | What it does |
|---|---|---|
POST /invitations/{token}/set-password |
Not needed | Creates the account for someone who has none. Body: password, firstName, lastName |
POST /invitations/accept |
Needed | Accepts the invitation as the signed-in user. Body: token |
Besides the codes below, these routes can answer not_found,
expired, invalid_state and, on set-password,
password_policy.
GET /invitations/{token}, which the portal calls to show the invitation, does not use these
codes: a link that cannot be used answers 404 with the platform-wide NOT_FOUND.
email_not_verified
400. Accept only. Your account's e-mail address is not verified. This is checked before anything else, so it is the answer even when the token is wrong. Verify the address, then sign in again and accept.
email_mismatch
409. Accept only. You are signed in with another address than the one the invitation was sent to, or your sign-in carries no e-mail address at all. The comparison ignores case. Sign out, sign in with the invited address, and open the link again.
user_already_exists
409. Set-password only. The invitation was made for someone without an account, but an account with that address exists now — for example because the person accepted another invitation first. The invitation switches to the flow for existing users: sign in with the existing account, then accept. Opening the link again shows the sign-in option.
Resetting a forgotten password
| Route | What it does |
|---|---|
POST /identity/password-reset |
Sends a reset link. Body: email |
POST /identity/password-reset/{token} |
Sets the new password. Body: password |
Requesting a reset always answers 202 Accepted, whether or not the address has an account, and
whether or not an e-mail is sent. Too many requests from one IP address answer 429 with the
platform-wide REQUEST_FAILED.
Setting the new password can answer not_found, expired
and password_policy. A reset link is valid for 60 minutes by default,
and can be used once.
GET /identity/password-reset/{token}, which the portal calls to show the reset page, does not
use these codes: a link that cannot be used answers 404 with the platform-wide NOT_FOUND.
Changing your password
POST /identity/change-password, with body password, changes the signed-in user's own password.
Besides the codes below, it can answer password_policy, which is
checked last.
unknown_user
401. Your sign-in does not identify a user the identity provider knows: the token has no
usable subject, or the account no longer exists. This is checked first. Sign in again; if the
error stays, report it with the correlationId.
reauth_required
401. You signed in too long ago. Changing a password needs a sign-in from the last five minutes; a refreshed token keeps its original sign-in time and does not count. Sign in again — entering your password — and retry within five minutes.
not_locally_authenticated
409. The account has no password on the platform to change: it signs in through an external identity provider, or it is a service principal. Change the password at the external provider. A service principal authenticates with its secret instead — see Service accounts.
Codes several flows share
not_found
404.
- Revoke or resend — there is no invitation with that id in this boundary. Reload the list of invitations.
- Set-password or accept — the token is empty, malformed or unknown. The link from an invitation that has since been resent is unknown too: each resend replaces the link. Use the newest invitation e-mail, or ask for a new invitation.
- Setting a new password after a reset — the link is unknown, malformed, has already been used, or has been retired because another reset link for the account was used first. All these cases answer the same on purpose. Request a new reset.
expired
410.
- Set-password or accept — the invitation is past its expiry date. The call that finds this
marks the invitation Expired; later calls with the same link answer
invalid_stateinstead, and so does every call once the platform's periodic clean-up has marked it. An expired invitation cannot be resent. Ask the person who invited you to send a new invitation. - Setting a new password after a reset — the reset link has expired. Every later call with it
answers
expiredtoo. Request a new reset.
invalid_state
400. The request cannot be carried out, most often because of the invitation's status. The
error sentence says which case applies.
- Create — the e-mail address is empty; a platform root role was given a scope other than the platform root; or another role was given a scope that is not valid or lies outside this boundary. Correct the request.
- Resend — the invitation is Accepted, Revoked or Expired. Send a new invitation instead.
- Set-password — the invitation is for someone who already has an account (sign in and accept instead), or it is no longer Pending. An invitation that is Awaiting login is not an error: set-password answers 200 again, with the same sign-in link.
- Accept — the invitation is Accepted, Revoked or Expired, or it was made for someone without an account and the password has not been set yet. Set the password first, or ask for a new invitation.
password_policy
422. The password does not meet the platform's password rules. The rules are published at
GET /identity/password-policy (and at GET /invitations/password-policy): minimum length,
minimum numbers of digits, lower-case, upper-case and special characters, and whether the
password may contain the e-mail address. An empty password answers this code too.
- Setting a new password after a reset, and changing your password — the body has a
violationsarray with one sentence per rule the password breaks. - Set-password on an invitation — the body has no
violationsfield; the broken rules are listed inerror, separated by;.
Choose a password that meets every rule and retry.