Multi-tenancy Overview
How HelloJohn's multi-tenancy model works — tenants, organizations, isolated user pools, and per-tenant configuration.
HelloJohn is built multi-tenant from the ground up. Every user, session, and configuration lives inside a tenant — a fully isolated workspace.
The tenancy model
HelloJohn Instance
└── Tenant A (your SaaS customer "Acme Corp")
│ ├── Users
│ ├── Auth config (own OAuth apps, MFA policy, etc.)
│ └── Organizations (optional sub-groups within the tenant)
└── Tenant B (your SaaS customer "Globex Inc")
│ ├── Users
│ ├── Auth config
│ └── Organizations
└── Tenant C (your internal app)
└── UsersWhat is a tenant?
A tenant is an isolated namespace for a group of users. Think of it as a workspace, organization, or account in your SaaS product.
Tenants are fully isolated:
- A user in Tenant A cannot authenticate to Tenant B (unless explicitly invited)
- Tokens issued for Tenant A are rejected by Tenant B
- Auth config (OAuth apps, MFA rules, allowed domains) is independent per tenant
What are organizations?
Organizations are sub-groups within a tenant. Use them when your tenants need their own internal structure:
Tenant: Acme Corp
├── Org: Engineering
├── Org: Sales
└── Org: DesignOrganizations have their own members and roles. A user can belong to multiple orgs within a tenant.
Organizations are optional. If your SaaS product doesn't need sub-groups within a customer account, you don't need to use organizations at all.
Tenant ID in JWTs
Every access token includes the user's active tenant:
{
"sub": "usr_01HX...",
"tenant_id": "ten_01HX...",
"org_id": "org_01HX...", // if in an org
"roles": ["admin"]
}Your backend uses tenant_id to scope data — always filter queries by tenant:
// Good — scoped to tenant
users, err := db.Query("SELECT * FROM users WHERE tenant_id = $1", claims.TenantID)
// Bad — returns data from all tenants
users, err := db.Query("SELECT * FROM users")Common patterns
B2B SaaS (one tenant per customer)
Each customer gets their own tenant. You create the tenant when they sign up:
POST /v2/admin/tenants
{
"name": "Acme Corp",
"slug": "acme-corp",
"owner_email": "admin@acme.com"
}Users at Acme sign up/in under that tenant. Their data is isolated from all other tenants.
Single-tenant app
Create one tenant for all your users. Most apps start here and add multi-tenancy later.
Consumer app with workspaces
Each workspace is a tenant. Users can switch between workspaces (tenants) using different refresh tokens.
Switching tenants
A user can have sessions in multiple tenants. The SDK handles tenant switching:
const { switchTenant } = useAuth()
await switchTenant('ten_01HY...')
// User is now authenticated in the new tenant
// access_token has new tenant_id claim