User Data Model
Overview
The User object represents an authenticated user in the Business Platform. It extends TenantEntity and contains identity, permission, and profile information alongside platform-specific metadata.
Core Fields
| Field | Type | Description |
|---|---|---|
id |
string |
Unique identifier for the user |
email |
string |
Canonical email address. Single source of truth — profileFields.email only stores privacy metadata |
emailVerified |
boolean |
Better Auth flag indicating the email has been verified |
image |
string | null |
Profile image URL (from Better Auth / OAuth providers) |
displayName |
string |
Human-readable display name (computed from profileFields, not stored) |
language |
string |
Preferred UI language as an ISO 639-1 code (e.g. "de", "en"). When set, overrides browser detection and is used by server-rendered content such as emails |
permissions |
string[] |
List of permissions in the format {TENANT_ID}::{PERMISSION}, e.g. 67f3b695bb457009cc645282::read |
profileFields |
UserProfileFields |
Map of custom profile field values |
badges |
EarnedBadge[] |
Array of badges earned by the user (see Badges) |
passwordChangeDisabled |
boolean |
If true, the user cannot change their password |
projectCreationDisabled |
boolean |
If true, the user cannot create new projects |
profileFieldChangeDisabled |
boolean |
If true, the user cannot edit their own profile fields |
messagingDisabled |
boolean |
If true, the user cannot create messages or message threads |
isPlatformOwner |
boolean |
If true, the user has platform-wide admin access (e.g. can run migrations) |
recentProjects |
Record<string, number> |
Map of projectId → last-opened timestamp (ms) |
lastSeenReleaseVersion |
string |
Last release version the user has seen (for multi-device release-notes tracking) |
personalAgentPermissions |
Record<string, Record<string, { read, write }>> |
Per-project, per-type AI tool permissions for the personal agent |
optInDecisions |
UserOptInDecision[] |
Record of the user's opt-in decisions |
Badges
Users earn badges as they complete key actions on the platform. The badges field on the User object stores all badges a user has earned.
EarnedBadge
Each entry in the badges array conforms to the EarnedBadge interface:
| Field | Type | Description |
|---|---|---|
name |
string |
The unique identifier of the badge (see Badge Names) |
earnedAt |
number |
Unix timestamp (milliseconds) indicating when the badge was awarded |
Example:
{
"badges": [
{
"name": "projectCreated",
"earnedAt": 1713800000000
},
{
"name": "firstApplicationCreated",
"earnedAt": 1713900000000
}
]
}
Badge Names
The following badge names are defined in BADGE_NAMES:
| Constant | Value | Awarded When |
|---|---|---|
PROJECT_CREATED |
"projectCreated" |
User successfully creates their first project |
FIRST_ACTION_CREATED |
"firstActionCreated" |
User creates their first action within a project |
FIRST_PROJECT_MEMBER_INVITE |
"firstProjectMemberInvite" |
User invites the first member to a project |
FIRST_DATA_TYPE_CREATED |
"firstDataTypeCreated" |
User creates their first data type |
FIRST_APPLICATION_CREATED |
"firstApplicationCreated" |
User creates their first application |
Badge Awarding Behavior
- Badges are awarded at most once per user. If a user already holds a badge, subsequent qualifying actions do not re-award it.
- Badge checks occur server-side via the
checkBadgeutility after the relevant resource is successfully created. - When a badge is newly awarded, the user receives a system notification.
- Badge data is persisted on the
Userdocument in thebadgesarray.
Viewing Badges
Users can view their earned and locked badges through the Badges button in the user profile panel. The modal displays:
- Earned badges — shown with a success border, badge icon, description, and the date the badge was earned.
- Locked badges — shown in greyscale with a lock indicator, listing what needs to be done to earn them.
The overall progress is shown as a count: earned / total available.
LocalAuthUser
LocalAuthUser is a session-scoped subset of User used during request handling. The projection lives in packages/server/src/auth/session-user-freshness.ts and is an explicit allow-list — only the fields listed here are copied from the User document onto the session and become available on the client as page.data.session.user.
| Field | Type | Description |
|---|---|---|
id |
string |
User identifier |
createdAt |
number |
Creation timestamp (ms) |
updatedAt |
number |
Last-update timestamp (ms) |
tenantId |
string |
Tenant the user belongs to |
email |
string |
Canonical email address |
permissions |
string[] |
Effective permissions for the session |
profileFields |
UserProfileFields |
Profile field values |
badges |
EarnedBadge[] |
Earned badges, mirrored from the User document |
optInDecisions |
UserOptInDecision[] |
Opt-in decisions |
revision |
number |
Document revision number used for optimistic concurrency |
passwordChangeDisabled |
boolean |
Mirrored flag |
projectCreationDisabled |
boolean |
Mirrored flag |
profileFieldChangeDisabled |
boolean |
Mirrored flag |
messagingDisabled |
boolean |
Mirrored flag |
isPlatformOwner |
boolean |
Mirrored flag |
recentProjects |
Record<string, number> |
Mirrored map of projectId → timestamp (ms) |
lastSeenReleaseVersion |
string |
Mirrored last-seen release version |
language |
string |
Preferred UI language (ISO 639-1 code); used by the client to pre-set i18n on session hydration |
personalAgentPermissions |
Record<string, Record<string, { read, write }>> |
Personal-agent tool permissions |
Fields on
Userthat are not carried intoLocalAuthUser(e.g.image,emailVerified) must be re-fetched server-side when needed.
Schema Validation
The UserSchema validates the badges field as an array of objects, each requiring both name (string) and earnedAt (number) properties. Documents that do not conform to this shape will fail validation.