Skip to content
Stackship documentation Svenska

Access controlUsers

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_state instead, 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 expired too. 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 violations array with one sentence per rule the password breaks.
  • Set-password on an invitation — the body has no violations field; the broken rules are listed in error, separated by ; .

Choose a password that meets every rule and retry.