Authaz logoAuthaz
DocumentationAPI Reference
  • Get Started

    • Authaz
    • Core Concepts
    • Set up your app
    • Quickstart — cURL
  • Authentication

    • Authentication Settings
    • Signup
    • Invitations
    • Password Authentication
    • Multi-Factor Auth
    • Magic Link
    • OAuth / Social Login
    • Passkey (WebAuthn)
    • SAML SSO
    • Machine-to-Machine (M2M)
    • API Keys
  • Authorization

    • Authorization
    • Resources
    • Policies
    • Roles
    • Access Explorer
  • Tenancy

    • Multi-tenancy
    • Tenancy Customization
  • Brand & Host

    • Branding
    • Custom Domains
    • Communications & Email Templates
  • Operate

    • Users
    • Analytics
    • Audit Logs
    • Application Settings
  • SDK Quickstarts

    • Quickstart — Next.js
    • Quickstart — React SPA
    • Quickstart — Hono
    • Quickstart — .NET (Authaz.Sdk)
  • Recipes

    • Recipes & Cookbook
    • Next.js — first integration
    • Next.js — B2B SaaS (multi-tenant)
    • Hono — first integration
    • Hono — B2B SaaS (multi-tenant)
    • React SPA — first integration
    • React SPA — B2B SaaS (multi-tenant)
    • .NET — first integration
    • .NET — B2B SaaS (multi-tenant)
  • Reference

    • Tokens
    • API Reference
    • Errors & Troubleshooting
  • Documentation

    • How Authaz is Built
  1. Authaz
  2. Docs
  3. Tenancy
  4. Multi-tenancy

Tenancy

Multi-tenancy

3 min read·Updated Jun 19, 2026

Most B2B SaaS is multi-tenant: each customer (a tenant) gets their own workspace, users, and permissions. Authaz handles the isolation — you don't write the filters.

curl -X POST https://your-app.authaz.io/api/v1/applications \
  -H "X-API-Key: $AUTHAZ_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Customer Portal",
    "tenancyType": "multi_tenant",
    "tenancyMode": "shared"
  }'

Pick a model#

Three options. Decide at application-creation time — the model can't be migrated in place.

Single-tenant#

application
└── users   (one pool, all peers)

One pool. No tenants. Roles apply application-wide.

For B2C, internal tools, anything where "customer workspace" doesn't apply.

Shared pool

Previous
Access Explorer
Next
Tenancy Customization
#
application
└── users   (one pool)
    └── memberships → tenant A · tenant B · tenant C

Users live in one pool, belong to one or many tenants. Roles are scoped per tenant — same identity can be Admin in Acme and Viewer in Initech.

For products with cross-workspace identity.

Isolated pools#

application
├── tenant A → users pool A
├── tenant B → users pool B
└── tenant C → users pool C

Each tenant gets its own pool. No cross-tenant identity at all — same email in two tenants = two separate users.

For healthcare, finance, government, anything where tenants must not see the existence of each other's identities.

The user-pool model is one decision. Which features each tenant can customize on its own — branding, email templates, email provider, the auth stack — is a separate set of switches. See Tenancy Customization.

Create a tenant#

curl -X POST https://your-app.authaz.io/api/v1/applications/{appId}/tenants \
  -H "X-API-Key: $AUTHAZ_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "Acme Corp", "metadata": {"plan": "pro"}}'

metadata is free-form JSON. Billing tier, region, contract ID — anything you want to read back later.

Tenant-scoped Management API#

Tenant scope is path-based. Always.

OperationApplication-scopedTenant-scoped
List usersGET /v1/usersGET /v1/applications/{appId}/tenants/{tenantId}/users
Send invitationPOST /v1/invitationsPOST /v1/applications/{appId}/tenants/{tenantId}/invitations
Assign a rolePOST /v1/role-assignmentsPOST /v1/applications/{appId}/tenants/{tenantId}/role-assignments
Provider configPUT /v1/applications/{appId}/auth/...PUT /v1/applications/{appId}/tenants/{tenantId}/auth/...

You'll never see ?tenantId=... in a URL. The path is the contract.

Tenant scope can also come from the caller: a Bearer token with a tenant_id claim auto-scopes the call, even on the application-scoped path. That's what powers your end-customers' self-service admin pages — same endpoints, narrower view.

Tokens carry the tenant#

When a user signs in to a tenant, the access token records it:

{
  "sub": "user_01h...",
  "org_id": "0199...",
  "tenant_id": "tenant_acme",
  "roles": ["admin"],
  "permissions": ["invoices:create", "users:read"],
  "exp": 1735689600
}

Your backend reads tenant_id straight from the JWT — no extra lookup, no header to forward.

To pin the tenant up front (subdomain-per-tenant pattern), pass tenant_id=... on the authorize URL:

https://your-app.authaz.io/oauth2/authorize
  ?client_id=...
  &tenant_id=tenant_acme
  &response_type=code
  &...

To require a tenant on every authorize, set requireTenantHint: true in Authentication Settings.

Tenant-scoped permissions#

Assign a role inside a tenant:

curl -X POST https://your-app.authaz.io/api/v1/applications/{appId}/tenants/{tenantId}/role-assignments \
  -H "X-API-Key: $AUTHAZ_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "userId": "user_01h...", "roleId": "role_admin" }'

Permission checks resolve global + tenant-scoped roles automatically:

curl -X POST https://your-app.authaz.io/api/v1/authorization/check \
  -H "X-API-Key: $AUTHAZ_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "userId": "user_01h...",
    "resource": "invoices",
    "action": "create",
    "tenantId": "tenant_acme"
  }'
{ "allowed": true }

Sub-millisecond, regardless of how many tenants you have.

Cross-tenant access#

Your own staff sometimes needs to read across tenants. Two options:

API key without a tenant claimHits app-scoped paths, sees everything.
JWT without a tenant_id claimSame effect — Authaz won't auto-scope.

Both are power tools. Log every call.

Next#

  • Tenancy Customization — what each tenant can override.
  • Authentication Settings — requireTenantHint and per-tenant token lifetimes.
  • cURL quickstart — see the tenant claim ride through the OAuth flow.