Skip to main content

Tenant — How it works

Overview

The tenant engine defines the platform's top-level multi-tenancy boundary. A tenant is an isolated customer/organization; every business row across the platform carries a tenant_id, and tenant-scoped uniqueness and access decisions hang off it. The engine also holds a tenant's branding, configuration, and subscription.

Data model & ownership

TablePurpose
tenantThe isolation boundary; unique tenant code/name.
tenant_brandingPer-tenant look & feel.
tenant_configurationPer-tenant settings.
tenant_subscriptionSubscription/plan state.

Key rules & invariants

  • Tenant code is unique; a tenant is the scoping key other engines validate against (e.g. demographic search requires a valid tenant).
  • Cross-tenant data never mixes — tenant-scoped rows filter by tenant_id.
  • tenant.status is LifecycleStatus (TERM-008D). It was free VARCHAR(30) with DEFAULT_STATUS = "ACTIVE" in the write service. The wire type is the shared enum (ACTIVE / SUSPENDED). Jackson rejects an unrecognised value with 400; migration tenant/004 normalises then CHECK-constrains and HALTs on dirty data. Null on create still defaults to ACTIVE. ⚠ Not the same column: tenant_subscription.subscription_status remains a separate residual string (out of this slice's six-entity scope).
  • tenant.tenant_type is TenantType (TERM-008K). It was free VARCHAR(60): seeds used PLATFORM and GOVERNMENT, operating tenants were often NULL, and the frontend typed HOSPITAL_GROUP. Jackson rejects an unrecognised value with 400; migration tenant/009 backfills blank rows to OPERATING, normalises, CHECK-constrains, sets NOT NULL, and HALTs on dirty data. Null on create defaults to OPERATING.

API

See the API Reference. Endpoint groups under /api/v1/tenant: tenants, branding, configuration, subscription.

Configuration & feature flags

None.