Skip to content
Updated Sep 24, 2026 by Barča Dvořáková · Owner: analysisactiveentity Edit on GitHub

Entity: tenantUser ​

Entity Type: Database table

Description: Records that a user belongs to a given tenant — the list a tenant switcher reads from. This is a derived record: it is created and maintained automatically whenever a user gains a tenant-scoped grant through either permissionSetUserAssignment (a direct grant with tenantId set) or groupMembership (joining a group, which always belongs to exactly one tenant) — and it is never written directly by any feature. It holds no role or permission information itself — what the user may do in the tenant is always determined by their direct assignments and group memberships, not by this table.

A user remains a tenant member as long as at least one of these sources is live; only when the last live direct assignment and the last live group membership for that tenant are both gone does this record's status move to removed.

Data Attributes Table ​

Attribute NameDescriptionData TypeDefault ValueRequired (= Nullable)UniqueFormatValidationsIndexExample
idPrimary key of the entity.UUIDGenerated in code (app layer)YesYesUUID v7-Primary Key018ed0b3-c298-7c7a-96d5-8b36f5a7f8d2
userIdThe user who belongs to the tenant.UUID-YesNoUUID v7Foreign Key → user; must existname: idx_tenant_users_user_id, type: btree (active records)018fa51f-fda1-79f4-8461-2cb8f1cabc10
tenantIdThe tenant the user belongs to.UUID-YesNoUUID v7Foreign Key → tenant; must existname: idx_tenant_users_tenant_id, type: btree (active records)018fa51f-fda1-79f4-8461-2cb8f1cabc10
statusMembership status of this user within this tenant.Enum (TenantUserStatus)invitedYesNo-One of: invited, active, removedname: idx_tenant_users_status, type: btree (active records)active
createdAtTimestamp of when the record was created. Immutable after insert.Timestamp with time zonenow() — set in codeYesNoISO 8601Cannot be null; cannot be modified after creation.-2026-06-08T00:00:00Z
updatedAtTimestamp of the last update. Set on insert (equal to createdAt) and updated on every change.Timestamp with time zonenow() — set in codeYesNoISO 8601Cannot be null.-2026-06-08T00:00:00Z
deletedAtTimestamp of soft deletion. Null means the record is active. Once set, immutable.Timestamp with time zone-NoNoISO 8601Immutable once set. Active records: WHERE deletedAt IS NULLIndexed (active records)-
createdByIdentifier of the actor who created the record.String-YesNotype:actorNon-empty.-system:trigger
updatedByIdentifier of the actor who last updated the record.String-YesNotype:actorNon-empty.-system:trigger

Uniqueness: (userId, tenantId) is unique among active (non-deleted) records, enforced by unique index uidx_tenant_users_active, type: btree.

Audited fields ​

Recorded (created/deleted: full set; updated: changed fields only): status.

Filed under subject: userId is not duplicated in this set — every entry sets the audit row's own subjectEntityType = "user" / subjectEntityId = userId, so one person's full tenant-membership history is a single indexed read, the same pattern as groupMembership.

Excluded:

  • id, createdAt, updatedAt, deletedAt, createdBy, updatedBy — redundant with the audit entry's own entityId/createdAt/createdBy, which already identify the record and the write.
  • userId — filed as the audit row's subject reference instead (see above).
  • tenantId — always present (never null, unlike a platform-level permissionSetUserAssignment), so it carries no information beyond the audit entry's own tenantId (RLS scope column).