Members and invitations

Invite people into an organization, accept or decline, create accounts directly, and remove or leave members.

An organization's members are the users with an accepted membership in it. People join by invitation, or an administrator can create an account for them directly. Joining grants almost nothing: what a member may do is decided by the roles they are given afterwards.

All of these routes act on the caller's active organization, taken from the session. None of them takes an organization id or uid, so a caller cannot act on an organization they are not in.

Who can do what, out of the box

Org owner (signed up and created it)Administrator (admin role)Member (joined by invitation)
See their own organizations, switch, leave (not their home org)YesYesYes
See, accept and decline invitations addressed to themYesYesYes
Their own sign-in methods, 2FA and sessionsYesYesYes
List members, invite, create accounts, revoke invitations, remove members, rename, set the 2FA requirementYesYesNo
Assign roles to members, manage the organization's roles and policiesYesNoNo

The owner holds the user and superuser roles in their own organization. Someone who accepts an invitation gets no roles, only the per-person Org Member Baseline in the table's first three rows. You grant more by giving them a role; see Assigning roles. Nothing above reaches your app's structure (tables, handlers, endpoints, agents, jobs): that stays yours unless you grant it on purpose.

Inviting someone

An administrator invites by email

POST /{appUid}/autocrud/org_invitations/actions/org-invites with the person's email. The invitation lasts expiresInDays (1 to 90, default 7). Inviting the same address again re-sends the pending invitation rather than creating a second one.

They get an email on your app's behalf

It names your app, the organization and who invited them, and tells them to accept inside the app. It carries no link: acceptance happens through your own UI. The response's emailSent says whether it went out.

They sign in and accept

The invitee signs up (if they have no account yet) or signs in, lists the invitations addressed to them, and accepts one by its uid. An invitation sent to an address matches the account with that email.

# An administrator, signed in to the organization
curl -X POST "$AUTHORIC_URL/$APP_UID/autocrud/org_invitations/actions/org-invites" \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "inviteeEmail": "sam@example.com", "expiresInDays": 14 }'

# The invitee, signed in to their own account
curl "$AUTHORIC_URL/$APP_UID/autocrud/org_invitations/actions/org-invites/received?status=pending" \
  -H "Authorization: Bearer $SAM_TOKEN"

curl -X POST "$AUTHORIC_URL/$APP_UID/autocrud/org_invitations/actions/org-invites/accept" \
  -H "Authorization: Bearer $SAM_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "invitationUid": "<invitation uid>" }'

The invite answers with the invitation's uid and status:

{
  "invitationUid": "0199a6b4-7d21-7e05-8f3a-4b9c1d2e6f70",
  "status": "pending",
  "emailSent": true,
  "message": "Invitation sent to sam@example.com — they accept it in the app",
  "invitee": { "subjectType": "user", "email": "sam@example.com" }
}

An accepted invitation answers with orgUid, the organization joined. The session stays in its current organization: refresh with activeOrgUid to switch into the new one.

The rest of an invitation's life

WhoRoute (under /{appUid}/autocrud/org_invitations/actions/)Does
InviteePOST org-invites/deny { invitationUid }Declines it
AdministratorPOST org-invites/cancel { invitationUid }Cancels a pending invitation addressed to the caller (not in the member baseline by default)
AdministratorGET org-invites/sent?status=Lists the organization's invitations
AdministratorPOST org-invites/revoke { invitationUid }Revokes one the organization sent

Creating an account directly

For back-office tools and imports, an administrator can create the account and its membership in one call, without an email: POST /{appUid}/autocrud/org_invitations/actions/org-invites/internal-create with inviteeEmail and optionally fname, lname and password. If an account with that address already exists it is added to the organization instead. A newly created account gets its own home organization, like any other, and joins yours at once. The response names the account by invitee.uid and says whether it was created.

Listing, removing and leaving

RouteDoes
GET /{appUid}/autocrud/org_memberships/actions/org-membershipsLists the active organization's members, each with membershipUid, status, org and subject (uid, email, name) (administrators)
POST /{appUid}/autocrud/org_memberships/actions/org-memberships/remove { membershipUid }Removes a member (administrators)
POST /{appUid}/autocrud/org_memberships/actions/org-memberships/leaveThe caller leaves the active organization

Removing a member ends their authority there in the same step: their roles in that organization are deleted and their pending invitations to it are revoked. Re-admitting them later starts from nothing, never from whatever they held before.

Three removals are refused, so an organization can never be left without someone in charge:

  • nobody can leave, or be removed from, their home organization;
  • the organization's owner cannot be removed;
  • the last member holding an administrator or owner role cannot be removed.

Errors to branch on

These come from the organization handlers and are returned in error.code.

CodeStatusMeaning
CANNOT_INVITE_SELF400The caller invited their own address.
EMAIL_NOT_VERIFIED403The caller has not confirmed their own email address yet, so they cannot invite anyone. See Confirming the email address.
INVITEE_ALREADY_MEMBER409That person is already a member.
INVITEE_EMAIL_ALREADY_EXISTS409Creating an account directly collided with an existing address. Invite it instead.
INVITATION_NOT_FOUND404No invitation with that uid.
INVITATION_TARGET_MISMATCH403The invitation is addressed to someone else.
INVITATION_NOT_PENDING409Already accepted, declined or revoked.
INVITATION_EXPIRED410Past its expiry. Ask for a new one.
MEMBERSHIP_NOT_FOUND404No such member in the active organization.
OWN_ORG_LEAVE_FORBIDDEN, OWN_ORG_SELF_REMOVAL_FORBIDDEN403That is the caller's home organization.
ORG_OWNER_REMOVAL_FORBIDDEN403The target owns the organization.
LAST_PRIVILEGED_MEMBER_FORBIDDEN409The target is the last administrator or owner.

A caller without the grant for an action gets a 403 whose message names the action that was refused (for example org_invitations:orgInvitesCreate).

What's next

On this page