⚠️ Porta is in beta — APIs and features may change before v1.0
Skip to content

Users API

Manage users within an organization. Users are the end-user accounts that authenticate through Porta's OIDC endpoints.

Base path: /api/admin/organizations/:orgId/users

Create User

http
POST /api/admin/organizations/:orgId/users
FieldTypeRequiredDescription
emailstringEmail address (must be unique within org)
given_namestringFirst name
family_namestringLast name
nicknamestringNickname
passwordstringPassword (NIST SP 800-63B compliant)
phone_numberstringPhone number
localestringUser locale (e.g., en)
picturestringProfile picture URL
json
{
  "email": "alice@example.com",
  "given_name": "Alice",
  "family_name": "Smith",
  "password": "a-secure-password-here"
}

Response: 201 Created

Invite User

http
POST /api/admin/organizations/:orgId/users/invite

Permission: user:invite

Creates a user (if they don't exist) and sends an enhanced invitation email. Supports optional personal message, role/claim pre-assignment, and inviter tracking. Pre-assigned roles and claims are stored in the invitation token and automatically applied when the user accepts the invitation.

FieldTypeRequiredDescription
emailstringEmail address
displayNamestringDisplay name for the invitation email
personalMessagestringPersonal message from the admin (max 500 chars, included in email)
rolesarrayRoles to pre-assign on acceptance
roles[].applicationIduuidApplication the role belongs to
roles[].roleIduuidRole ID to assign
claimsarrayCustom claim values to pre-assign on acceptance
claims[].applicationIduuidApplication the claim belongs to
claims[].claimDefinitionIduuidClaim definition ID
claims[].valueanyClaim value
localestringLocale for the invitation email (default: org default)

Response: 201 Created (new user) or 200 OK (existing user re-invited).

json
{
  "data": {
    "userId": "uuid",
    "email": "user@example.com",
    "created": true,
    "invitationSent": true,
    "expiresAt": "2026-01-08T00:00:00.000Z"
  }
}

Pre-assignment behavior:

  • Referenced applications, roles, and claim definitions are validated at invite time
  • Pre-assignments are applied automatically when the invitation is accepted
  • If a role or claim is deleted between invitation and acceptance, that assignment is skipped (best-effort)
  • Previous pending invitation tokens for the same user are automatically invalidated

Preview Invitation Email

http
POST /api/admin/organizations/:orgId/users/invite/preview

Permission: user:invite

Renders the invitation email without sending it. Returns the HTML, plain text, and subject line for admin review before sending.

FieldTypeRequiredDescription
emailstringRecipient email (for template personalization)
displayNamestringDisplay name
personalMessagestringPersonal message to include
localestringLocale for rendering

Response: 200 OK

json
{
  "data": {
    "html": "<html>...</html>",
    "text": "Plain text version...",
    "subject": "John Doe has invited you to Acme Corp"
  }
}

List Users

http
GET /api/admin/organizations/:orgId/users

Supports page, pageSize, search, status, sort, order parameters. Search queries match against email, given name, family name, and nickname.

Response: 200 OK — Paginated user list.

Get User

http
GET /api/admin/organizations/:orgId/users/:userId

Response: 200 OK — Full user profile.

Update User

http
PUT /api/admin/organizations/:orgId/users/:userId

Updatable fields: given_name, family_name, nickname, phone_number, locale, picture.

INFO

Email changes are not supported through this endpoint to prevent authentication issues.

Response: 200 OK

Status Lifecycle

Users have six possible statuses with controlled transitions:

Status Transition Endpoints

http
POST /api/admin/organizations/:orgId/users/:userId/suspend
POST /api/admin/organizations/:orgId/users/:userId/activate
POST /api/admin/organizations/:orgId/users/:userId/lock
POST /api/admin/organizations/:orgId/users/:userId/unlock
POST /api/admin/organizations/:orgId/users/:userId/archive

Each returns 200 OK with the updated user object.

Set Password

http
POST /api/admin/organizations/:orgId/users/:userId/set-password
FieldTypeRequiredDescription
passwordstringNew password (NIST SP 800-63B compliant)

Passwords are hashed with Argon2id before storage.

Response: 200 OK

User Roles

See Roles & Permissions API for user-role assignment endpoints at:

/api/admin/organizations/:orgId/users/:userId/roles

User Claims

See Custom Claims API for user claim value endpoints.

User 2FA

http
GET  /api/admin/organizations/:orgId/users/:userId/2fa/status
POST /api/admin/organizations/:orgId/users/:userId/2fa/disable
POST /api/admin/organizations/:orgId/users/:userId/2fa/reset

These endpoints allow administrators to view 2FA status, force-disable 2FA, or reset 2FA for a user (forcing re-enrollment).

Account Lockout

Porta automatically locks accounts after repeated failed login attempts (default: 5 attempts). Locked accounts auto-unlock after a cooldown period (default: 15 minutes).

The POST .../lock and POST .../unlock endpoints (see Status Transitions above) allow administrators to manually lock or unlock a user at any time. The auto-lockout system uses the same underlying status transitions.

Lockout thresholds are configurable via the System Configuration API:

  • account_lockout_threshold — Number of failed attempts before auto-lock (default: 5)
  • account_lockout_cooldown_minutes — Minutes before auto-unlock (default: 15)

See the Deployment Guide for full details.

GDPR Data Export

http
GET /api/admin/organizations/:orgId/users/:userId/export

Exports all personal data for a user as a JSON document (GDPR Article 20 — data portability). The export includes:

  • User profile (email, name, phone, locale, etc.)
  • Organization membership
  • Role assignments (with role names and application context)
  • Custom claim values (with claim definitions)
  • Audit log entries related to the user
  • Two-factor authentication enrollment status
  • Active OIDC sessions and grants

Response: 200 OK — JSON document containing all user data.

GDPR Data Purge

http
POST /api/admin/organizations/:orgId/users/:userId/purge

Permanently anonymizes and deletes a user's personal data (GDPR Article 17 — right to erasure). This operation:

  1. Anonymizes the user record (replaces email, names, etc. with anonymized placeholders)
  2. Deletes all associated data: role assignments, custom claim values, tokens, 2FA enrollment, and audit metadata
  3. Executes everything in a single database transaction

Response: 200 OK — Confirmation of purge completion.

Irreversible

Data purge cannot be undone. Super-admin users cannot be purged as a safety measure.

Standalone User Routes

In addition to the org-scoped routes above, a set of standalone user routes is available at /api/admin/users/:userId. These provide direct access to user detail and mutation operations by user ID, without requiring the organization ID in the URL path.

Base path: /api/admin/users/:userId

These routes are primarily used by the Admin GUI SPA, where the user detail page navigates by user ID only. They delegate to the same service functions and require the same admin authentication and RBAC permissions as the org-scoped routes.

Available Standalone Endpoints

MethodPathDescriptionPermission
GET/:userIdGet user by IDuser:read
PUT/:userIdUpdate user profileuser:update
POST/:userId/deactivateDeactivate useruser:suspend
POST/:userId/reactivateReactivate useruser:suspend
POST/:userId/activateAlias for reactivateuser:suspend
POST/:userId/suspendSuspend useruser:suspend
POST/:userId/unsuspendUnsuspend useruser:suspend
POST/:userId/lockLock useruser:suspend
POST/:userId/unlockUnlock useruser:suspend
POST/:userId/passwordSet passworduser:update
DELETE/:userId/passwordClear passworduser:update
POST/:userId/verify-emailMark email verifieduser:update
GET/:userId/historyChange historyuser:read

TIP

The activate endpoint is an alias for reactivate, provided for client compatibility. Both perform the same inactive → active status transition.

Released under the MIT License.