Skip to Content
ArchitectureMulti-Tenant Model

Multi-Tenant Model

How Tenancy Works

Each tenant is an organizations row. The platform resolves organization_id from the incoming hostname on every request using a 4-strategy resolver:

  1. custom_domain match — tenant’s own domain (e.g. app.{theirdomain}.com)
  2. primary_hostname match (e.g. app.gritcert.com)
  3. Slug subdomain match (e.g. tenantslug.gritcert.com)
  4. Dev fallback (local only)

Isolation Guarantees

  • Every DB query on a tenant route is scoped with WHERE organization_id = ?
  • A student enrolled at two tenants has two separate enrollment rows, two certificates, and two compliance records. Data never crosses tenant boundaries.
  • Platform admin routes (/api/platform/*) are completely separate from tenant routes and require a different auth middleware.

Tenant Setup

When a new tenant is provisioned via the admin console:

  1. organizations row created with slug and hostname
  2. Course entitlements granted via organization_courses
  3. First admin invited via Clerk invitation flow
  4. DNS configured (subdomain on gritcert.com or custom domain)

Current Tenants

Tenant names, domains, and course entitlements are not published here. Use the GritCert Admin Console at admin.gritcert.com  to view live tenant status.

Last updated on
© 2026 GritCert. Internal platform documentation.