Skip to content

Provision a new tenant

Provisioning a tenant runs a single workflow that takes a request from the control plane console and ends with a healthy, traffic-ready tenant at its own subdomain. This guide walks the request through so you know what each phase does and which events show up in the audit feed.

Before you begin
  • The Owner or Admin role in your organisation
  • A free tenant slug — the platform derives the tenant's resource names from this, so slugs in use by an active or suspended tenant are rejected

Three things travel with the request: a tenant slug, the organisation that owns the tenant, and a plan tier. Everything else is derived deterministically from the slug, so the same input always produces the same tenant identity.

Each phase is idempotent: if the platform retries after a transient failure, the phases that already completed are skipped. The audit feed shows progress in real time. The high-level phases:

  1. Validate the request. The slug is checked for collisions, the plan exists, the caller's role admits provisioning.
  2. Allocate the tenant's resources. Storage, identity, and the tenant's traffic boundary are stood up in its name.
  3. Deploy the tenant's workloads. The platform launches the tenant's services and waits for their readiness checks to clear.
  4. Mark the tenant active. State flips to active; traffic to https://<slug>-<org>.<domain> starts succeeding.
  5. Send the welcome emails. A best-effort three-email sequence reaches the first admin (welcome, console URL, onboarding).

If the platform gets stuck on a particular phase, the audit feed names the failing step and the next stable state. See Recover a stuck provisioning saga.

The control plane console UI fires this same request. The platform CLI offers an explicit alternative:

Example multi-language
Terminal window
# Using the intelliauth-cli
intelliauth platform tenants create \
--org cymmetri \
--slug banking \
--plan growth \
--first-admin admin@cymmetri.com
// Using the @intelliauth/control-plane-sdk
import { ControlPlaneClient } from '@intelliauth/control-plane-sdk'
const cp = new ControlPlaneClient({ baseUrl: 'https://manage.intelliauth.com' })
const tenant = await cp.tenants.create({
org: 'cymmetri',
slug: 'banking',
plan: 'growth',
firstAdmin: 'admin@cymmetri.com',
})
console.log('Tenant id:', tenant.id)
POST /api/v1/organizations/{org_id}/tenants HTTP/1.1
Host: manage.intelliauth.com
Authorization: Bearer <platform-operator-token>
Content-Type: application/json
{
"slug": "banking",
"plan": "growth",
"first_admin_email": "admin@cymmetri.com"
}

The console flow is a single dialog with five fields. The empty form:

https://manage.intelliauth.local/dashboard/tenants
The Create-a-tenant dialog with empty Name, Slug, Region, Plan, and Admin email fields
Figure 1 — Create a tenant dialog (empty). Name is freeform; slug must be lowercase + hyphens; region defaults to Local; plan defaults to your org's plan; admin email gets the activation message.

Filled in for a "Production" tenant:

https://manage.intelliauth.local/dashboard/tenants
The Create-a-tenant dialog filled in with name Production, slug production, and admin email admin@example.com
Figure 2 — The same dialog filled. Plan tier and the first admin email drive downstream side effects: plan snapshot, activation email.

If a phase fails after retries, the workflow transitions the tenant to failed and rolls back the work it has done so far — releasing every resource it allocated. The tenant stays in failed state with its audit history intact; retry provisioning from the console, or decommission to clear the slot.

After provisioning, the tenant admin signs in at the tenant's subdomain (https://<slug>-<org>.<domain>) and starts configuring applications, users, and MFA. From the control plane you can still observe the tenant's health, suspend/resume it, or decommission it when the customer leaves.