tenants
This table stores customer organizations onboarded into DirtView. Each row represents one company or tenant and acts as the root record for tenant-scoped data, branding, routing, and configuration.
Purpose
The purpose of this table is to define the top-level customer organization record for every DirtView tenant. It provides the tenant identity that ties together tenant users, projects, files, forms, drawings, and branding.
What this table does
The tenants table is the anchor of the shared-schema
multi-tenant model.
- Identifies each customer organization uniquely
- Stores subdomain and optional custom-domain information
- Stores branding and tenant preferences
- Supports tenant lifecycle operations such as activation and suspension
- Provides the root key used for tenant isolation across the database
Why this table is defined
DirtView uses a shared database and shared schema, which means the system needs a clear top-level tenant record so every tenant-owned resource can be scoped correctly.
This table also supports sales-led onboarding, tenant branding, subdomain routing, and internal tenant management from the admin dashboard.
Columns
| Column | Type | Required | Description | Example |
|---|---|---|---|---|
id |
uuid | Yes | Primary key for the tenant record | tenant_001 |
name |
text | Yes | Customer organization name | Acme Subcontracting |
subdomain |
text | Yes | Unique company subdomain used in routing | acme |
custom_domain |
text | No | Optional custom domain for post-MVP domain mapping | portal.acmebuild.com |
status |
text | Yes | Tenant lifecycle state | active |
plan_tier |
text | No | Commercial plan or package level | pilot |
website_url |
text | No | Company website used during onboarding and branding setup | https://acmebuild.com |
branding |
jsonb | Yes | Branding payload such as colors, logo, and visual settings | {"primary_color":"#003366"} |
preferences |
jsonb | Yes | Tenant-level settings and configuration values | {"timezone":"America/Chicago"} |
logo_file_id |
uuid | No | Reference to the logo asset in the files table | file_789 |
created_at |
timestamptz | Yes | Creation timestamp | 2026-04-01 08:00:00+00 |
updated_at |
timestamptz | Yes | Last updated timestamp | 2026-04-11 09:15:00+00 |
deleted_at |
timestamptz | No | Soft-delete timestamp if tenant is retired | NULL |
Relationships
- id is referenced by most tenant-scoped tables throughout the system
- logo_file_id may reference files.id
- id is referenced by tenant_memberships
- id is referenced by projects, employees, files, forms, drawings, and audit tables
- id may be referenced by feature_flags for tenant overrides
How it is used
- Resolved from subdomain on web login and request routing
- Used to scope tenant users and memberships
- Used to namespace storage paths and project data
- Used by internal admin dashboard during onboarding and lifecycle management
- Used to load branding into the customer-facing UI and exports
- Used as the root key for RLS-based tenant isolation
Access and security
- This is a high-importance business table
- Only authorized internal flows should create or suspend tenants
- Tenant users should not be able to alter core tenant identity fields directly
- Subdomain values must be unique and validated carefully
- Tenant suspension should integrate with token/session revocation logic
- Changes to sensitive fields should be audited
Example scenarios
Scenario 1: New tenant onboarding
A super admin creates a new tenant row, sets the subdomain, uploads logo and branding metadata, and invites the initial tenant admin.
Scenario 2: Subdomain routing
A request comes in for acme.dirtview.com. The system resolves the
subdomain to the corresponding tenant record.
Scenario 3: Tenant suspension
The tenant status changes from active to
suspended, which should restrict access on future requests.
Notes and assumptions
- This is one of the foundational tables in the platform
- Every tenant-scoped business table should ultimately tie back to this table
subdomainshould be lowercase and validated against naming rulesbrandingandpreferencesare intentionally flexible via JSONB- Future enhancement: split branding into a dedicated table if needed