Skip to main content

Accounts

Overview

The accounts module manages the full account lifecycle: creation (with automatic JWT re-issue), name editing, account dissolution with confirmation, and leaving accounts as a non-owner member. Lumio supports multi-account switching -- a user can own multiple accounts and be a member of others.

Architecture

Dashboard UI / ID App
|
v
Next.js API Proxy (/api/accounts)
|
+---> REST API (POST/GET/DELETE /v1/accounts, POST /v1/accounts/{id}/leave)
| |
| +--> JWT re-issue on create/dissolve
|
+---> GraphQL (AccountQuery / AccountMutation)
|
v
db::overlays, db::auth, db::members, db::roles (PostgreSQL)

Account Creation Flow

  1. User calls POST /v1/accounts with an optional plan (defaults to free).
  2. The account is created with the user's display name and the specified plan.
  3. Default roles (Owner, Administrator, Moderator, Viewer) are created via db::roles::create_default_roles.
  4. The user is added as a member with the Owner role.
  5. All active sessions for the user are switched to the new account (active_account_id updated).
  6. A fresh JWT is issued containing the new account_id and returned alongside the account data.

Account Dissolution Flow

  1. User (must be owner) calls DELETE /v1/accounts/{id} or dissolveAccount mutation with confirm_name matching the account name exactly.
  2. All sessions referencing this account have active_account_id set to NULL.
  3. The account is deleted (CASCADE handles related tables: memberships, roles, overlays, etc.).
  4. A fresh JWT without account_id is returned so the user stays authenticated but can select another account.

Leave Account Flow

  1. Non-owner members can call POST /v1/accounts/{id}/leave or leaveAccount mutation.
  2. Ownership check prevents the owner from leaving (they must dissolve instead).
  3. The user's membership row is deleted.

API

REST Endpoints

MethodPathDescriptionAuth
POST/v1/accountsCreate a new accountAuthenticated
GET/v1/accounts/{id}Get account detailsActive account must match
DELETE/v1/accounts/{id}Dissolve account (owner only)Owner + confirm_name
POST/v1/accounts/{id}/leaveLeave an accountAuthenticated member

POST /v1/accounts

{
"plan": "free"
}

Response (201):

{
"data": { "id": "...", "name": "...", "plan": "free", ... },
"token": "lm_eyJ..."
}

DELETE /v1/accounts/{id}

{
"confirm_name": "My Account Name"
}

GraphQL Queries

QueryArgsReturnsPermission
accountid: UUIDAccount?account:read

Users can only query their own active account.

GraphQL Mutations

MutationArgsReturnsPermission
createAccountinput: CreateAccountInput!CreateAccountResult!AuthGuard (any authenticated)
updateAccountid: UUID!, name: String!Account!account:edit
dissolveAccountconfirmName: String!DissolveAccountResult!account:delete
leaveAccount--LeaveAccountResult!AuthGuard (any authenticated)
  • updateAccount validates that the name is not empty and the target account matches the active account.
  • dissolveAccount requires exact name confirmation and owner status.
  • leaveAccount has no permission guard beyond authentication -- any member can leave.

GraphQL Types

type Account {
id: UUID!
ownerId: UUID
name: String!
plan: String!
createdAt: String!
updatedAt: String!
}

type DissolveAccountResult {
success: Boolean!
}

type LeaveAccountResult {
success: Boolean!
}

Permissions

PermissionDescription
account:readView account details
account:editEdit account name
account:deleteDissolve account (also requires owner check)

Included in: Owner role has all three. Administrator has account:read and account:edit. The leaveAccount mutation bypasses permission guards (only requires authentication).

Database

Table: accounts

ColumnTypeDescription
idUUID (PK)Account ID
owner_idUUID (FK)Account owner user ID
nameTEXTAccount display name
planTEXTPlan tier (free, pro, enterprise)
created_atTIMESTAMPTZCreation timestamp
updated_atTIMESTAMPTZLast update timestamp

Related tables (CASCADE delete):

  • account_memberships -- User-account membership with role
  • account_roles -- Custom roles for the account
  • account_role_permissions -- Permissions assigned to roles
  • overlays, channel_rewards, se_tokens, integration_configs, etc.

Key Files

FilePurpose
apps/api/src/graphql/accounts.rsGraphQL queries and mutations
apps/api/src/routes/accounts.rsREST endpoints with JWT re-issue
apps/api/src/db/auth.rsUser lookup, session management
apps/api/src/db/roles.rsDefault role creation
apps/api/src/db/members.rsMembership management
crates/lo-auth/src/rbac.rsPermission constants (account:read/edit/delete)