Obexal Docs

Docs/Directory and provisioning/Invitations

Invitations

The invitation model in detail, from the admin API to the activation link, with role re-validation at acceptance, expiry and revocation.

Obexal's directory works on an invitation model: accepting an invitation is what creates and activates the account. This page is the full reference; for the guided walkthrough, start with Invite your team.

Invitation equals activation

Self-signup is off by default (fail-closed: also off if the setting is unreadable), so the directory only contains accounts you explicitly invited or provisioned. An invitation is nominative (bound to one email address), single use, and doubles as email verification: the activated account starts with a verified address, because the link itself proves control of the mailbox.

If your use case needs open registration, an admin can re-enable it per organization: the console setting maps to branding.allowSelfSignup on PATCH /v1/admin/tenant.

Create an invitation

POST /v1/admin/invitations (member management permission, session or admin API token):

curl -sS -X POST https://accounts.obexal.com/v1/admin/invitations \
  -H "Authorization: Bearer $OBEXAL_API_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"email":"alice@example.eu","role":"member","givenName":"Alice","familyName":"Martin"}'
# 202 -> {"status":"ok"}   (audit event: admin.invitation.created)
FieldRequiredMeaning
emailyesThe invitee's address; only this exact address can activate the invitation
roleyesA system role (owner, admin, member) or a custom role key
givenNamenoPre-fills the profile at activation
familyNamenoPre-fills the profile at activation

At creation, the anti-escalation guard of RBAC applies: you can only grant a role whose permissions are a subset of your own, and only an owner can invite an owner. Only one pending invitation may exist per address: inviting it again returns 409. The invitation token is random (32 bytes); only its SHA-256 hash is stored.

The invitee receives an email linking to https://accounts.obexal.com/accept-invite?token=... (your custom domain replaces the default one). The page calls the public endpoint:

curl -sS -X POST https://accounts.obexal.com/v1/auth/accept-invite \
  -H 'Content-Type: application/json' \
  -d "{\"token\":\"$INVITE_TOKEN\",\"password\":\"$NEW_PASSWORD\",\"givenName\":\"Alice\",\"familyName\":\"Martin\"}"
# 200 -> {"user":{...},"redirectUrl":"/dashboard"}   (session cookie set: auto-login)

The invitee chooses a password (validated against the organization's password policy, 12 characters minimum by default), may set or complete their first and last name (their input takes precedence over the admin pre-fill), and is signed in automatically onto the "My Apps" portal. Error cases: 400 invalid_invite (unknown, revoked or expired link), 409 already_active (the account already has a password: an invitation never overwrites credentials), 400 invalid_request (password rejected by policy), 429 rate_limited.

There is also a compatibility path for a user who is already signed in to the organization: POST /v1/invitations/accept with {"token":"..."} attaches the invitation's role to the existing account, provided the session's email matches the invitation.

Role re-validation at acceptance

The role granted at acceptance is not blindly the role stored at creation. Between the two moments, the inviter may have been demoted or removed, or a custom role deleted. Obexal therefore re-validates the inviter's current authority when the link is used: the role must still exist, and its permissions must still be a subset of the inviter's permissions at that moment (owner remains grantable by owners only). If either check fails, the invitee joins as member (fail-closed).

Note

This closes the classic time-of-check to time-of-use gap: a pending invitation can never confer a privilege that outlives the authority of the person who issued it.

Expiry and revocation

Invitations expire after 7 days by default. GET /v1/admin/invitations lists them with status (pending, accepted, revoked), expiresAt, createdAt and, when relevant, acceptedAt.

curl -sS -X DELETE https://accounts.obexal.com/v1/admin/invitations/<id> \
  -H "Authorization: Bearer $OBEXAL_API_TOKEN"
# 200 -> {"status":"ok"}   (audit event: admin.invitation.revoked)

A revoked or expired link stops working immediately (invalid_invite). There is no separate resend action: revoke the pending invitation and invite the address again to generate a fresh link. Acceptance is audited as admin.invitation.accepted, with the role actually granted.