Invitations
POST /api/auth/admin/create-user makes an admin choose the new user's password and sends them nothing — workable for a script, unusable for onboarding a customer. The invitation flow closes that gap: the admin supplies { email, name, role } with no password, Sovrium emails a single-use link, and the invitee sets their own password and lands in an authenticated session.
auth:
strategies:
- type: emailAndPassword
invitationTokenExpiry: '72h' # default; accepts '30s' / '15m' / '72h' / '7d' / ms
emailTemplates:
invitation:
subject: 'You are invited to join, $name'
text: |
Hi $name,
$inviterName invited you to join.
Set your password: $url
This invitation expires in 72 hours.
The Two Endpoints
| Endpoint | Behavior |
|---|---|
POST /api/auth/admin/invite-user |
Accepts { email, name, role } (no password). Returns 200 with { user, invitationSent: true }. 401/403 for unauth / non-admin; 400 for invalid input; 422 when the email already maps to a fully-onboarded user. |
POST /api/auth/admin/accept-invitation |
Backs the public /accept-invitation?token=... page. The invitee sets a password and lands authenticated. 400 for an invalid token, 410 for an expired one. |
Tokens reuse the auth.verification table (the same shape Better Auth uses for password reset), expire after invitationTokenExpiry — 72h by default — and are single-use, consumed on the first successful accept. A replay is rejected rather than silently re-onboarding.
The email body is rendered from the invitation template and substitutes $name, $url, $email and $inviterName.
allowSignUp: false does not block invitations. Closing public self-registration is precisely when you need this flow. Admin-driven user creation stays available whenever auth is configured — see Registration Control.
Role Assignment
Sovrium ships three default roles — admin, member, viewer — with hierarchy levels 80, 40 and 10, and accepts custom roles declared in the auth block.
An invitation carries a role explicitly. When one is not supplied at creation, a new user receives auth.defaultRole, which itself falls back to member:
auth:
strategies:
- type: emailAndPassword
defaultRole: viewer
roles:
- name: editor
description: Can edit content
level: 30
defaultRole is validated against the built-in roles plus your declared roles[], so a typo fails sovrium validate rather than quietly assigning nothing. The first bootstrap admin is always created with the admin role and a verified email, regardless of defaultRole.
Roles drive every authorization decision in the platform: table permissions, per-field access, page access, and the admin API itself. A role can be changed after the fact with POST /api/auth/admin/set-role.
Invitation vs. Create-User
| You want | Use |
|---|---|
| A customer to choose their own password | invite-user — they receive a link and never see an admin-set secret. |
| A service or seed account, no mailbox involved | create-user — you set the password, nothing is sent. |
| To onboard when SMTP is not configured | create-user — an invitation email would never be delivered. |
Onboarding is decoupled from access assignment. Inviting a user creates the account; it grants no tenant scope. Wiring a user to the data they may reach is a separate step through the records API, so an admin can invite first and assign later, or the reverse. See Assignments & Landing Guards.
Related Pages
- User Management — admin bootstrap and the create-user API.
- Registration Control —
allowSignUpandinvitationTokenExpiry. - Roles & RBAC — the full role model and field-level permissions.
- Email Templates — the
invitationsubject and body. - Assignments & Landing Guards — granting a user their tenant scope.
Last updated July 27, 2026
This documentation was written with AI, so errors or outdated content are possible. Sovrium is in beta — contributions and corrections are welcome.