> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orbit.devotel.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Tenant provisioning lifecycle

> How an organization comes into being on Orbit: from organization row through a schema-ready tenant, the fast-core-path split that shrinks the 503 window, and the recovery paths a stuck tenant takes.

# Tenant provisioning lifecycle

The [tenant isolation page](/concepts/tenant-isolation) explains what
a tenant **is** — a catalog row in `organizations` plus one PostgreSQL
schema named `tenant_<tenant_id>`, resolved per request. This page
explains how that tenant **comes into being**: the sequence that turns
an organization row into a schema-ready tenant, the two-phase split
that determines when you stop seeing 503, and the recovery queue a
stuck tenant enters. The individual failure codes the lifecycle emits
(`MISSING_TENANT`, `TENANT_NOT_FOUND`, `TENANT_PROVISIONING`, and
`TENANT_SCHEMA_INCOMPLETE`) are enumerated on the
[troubleshooting ladder](/troubleshooting/tenant-provisioning-lifecycle);
this is the concept page that ladder references.

## The provisioning sequence

Provisioning is not one step — it is a fixed sequence, and the order is
the whole story. A signup ( Clerk webhook ) or a sub-account create
runs it:

```text theme={null}
1. Mint tenant identity    — a tenant UUID + an org id (org_...)
2. Create the org catalog row in `organizations`  (tenant_schema_ready = FALSE)
3. Build the tenant schema synchronously  (fast core path)
4. Flip organizations.tenant_schema_ready = TRUE
5. Mint the org's first API key and bind the user as owner
6. Defer the migration catch-up pass to the background
7. (Signup only) Create the Stripe customer in the background
```

Step 2 exists before step 3 succeeds so the org row is the anchor every
concurrent attempt resolves against — a concurrent request that finds
the row sees a plain `TENANT_PROVISIONING` 503 rather than
double-creating. Steps 3→5 are the **fast core path**: the schema is
created synchronously and the ready flag flips within seconds, so
account-scoped APIs stop returning 503 within seconds of signup. Step 6
(the \~1,200-body `TENANT_MIGRATIONS` catch-up) is deliberately **off the
request path** because it gates nothing, and step 7 (the billing
customer) is likewise deferred — otherwise a slow provider call holds
signup open and a dropped webhook strands the user. Both are reconciled
by background sweeps, never by retrying the request.

Every statement in the schema bootstrap is `CREATE ... IF NOT EXISTS`,
inside one transaction with a transaction-scoped advisory lock — so a
concurrent second provisioning attempt for the same tenant blocks until
the winner commits, then sees the fully-built schema. A whole retry of
the bootstrap is therefore safe: nothing from a failed attempt survives
to conflict.

<Info>
  The titles above name **what really matters**: the flag
  `tenant_schema_ready` is the one readiness signal every gate, scheduler
  fan-out, and repair sweep keys off. A code like
  `TENANT_PROVISIONING` (503) is emitted exactly when that flag is FALSE —
  the lifecycle states on the
  [troubleshooting ladder](/troubleshooting/tenant-provisioning-lifecycle)
  all derive from the same one flag.
</Info>

## Where each lifecycle state comes from

Provisioning emits four bad responses on the ladder, and all four come
from distinct points in the sequence above:

* **`MISSING_TENANT` (400)** — nothing in the request resolved to an org
  (no API key / tenant header); the sequence never even started.
* **`TENANT_NOT_FOUND` (404)** — the org row failed to land in step 2
  (or the request resolves to a deleted/non-existent organization).
* **`TENANT_PROVISIONING` (503)** — the org row exists but steps 3–4
  have not completed (flag FALSE), or have been reset FALSE by the
  repair loop below.
* **`TENANT_SCHEMA_INCOMPLETE` (503)** — the tenant is ready, but a
  **later platform release** added a table this tenant's ledger has not
  yet applied; the literal 42P01 `undefined_table` SQLSTATE is what a
  schema-guarded route maps to this code.

Steady state — the resolved tenant — is step 4 complete and every per-
tenant migration applied by the periodic catch-up sweep.

## When it goes wrong — the recovery queue

A stuck tenant is not repaired by the user refreshing the page. It
enters one of three idempotent recovery paths, all of which re-run the
same `ensureTenantSchema` bootstrap rather than a special repair:

* **Per-request self-heal.** The first request that finds
  `tenant_schema_ready = FALSE` re-runs the bootstrap in-band, then the
  request resolves as if the sequence had completed. This covers the
  "flag stuck FALSE" case.
* **The 15-minute schema-repair sweep.** A scheduler re-runs the
  bootstrap for an org whose flag is TRUE but whose schema is missing —
  the reverse gap no request will ever trigger. This covers the
  "schema was dropped or never fully landed" case.
* **The deferred catch-up + periodic migration sweep.** The background
  `runSingleTenantMigrations` pass (step 6) reconciles any column an
  older body added; the scheduled fleet-wide
  `runTenantMigrations` sweep re-applies flagged per-tenant migrations
  so a long-lived tenant and a same-day signup converge.

The flag is the pivot for all three: a self-heal fires exactly when it
is FALSE, a sweep re-applies exactly what the per-tenant `_migrations`
ledger says is missing, and one flag read is enough for the platform to
route the repair.

## Relationship to isolation

Provisioning answers **how** the tenant boundary comes into being;
[tenant isolation](/concepts/tenant-isolation) then answers **what**
that boundary is — the shared public catalog vs the per-tenant schema —
and **how the API resolves it per request** — from your API key, never
from a client-supplied `tenant_id`. Read pages in that order; the
lifecycle states above are diagnosed on the
[troubleshooting ladder](/troubleshooting/tenant-provisioning-lifecycle)
and the schema-drift-specific rung has its own runbook at
[TENANT\_SCHEMA\_INCOMPLETE (503)](/troubleshooting/tenant-schema-incomplete-503).

## Cross-references

* [Tenant isolation](/concepts/tenant-isolation) — what the boundary is
  and how resolution works.
* [Troubleshoot tenant provisioning lifecycle](/troubleshooting/tenant-provisioning-lifecycle)
  — the four-code ladder and the fix per code.
* [TENANT\_SCHEMA\_INCOMPLETE](/troubleshooting/tenant-schema-incomplete-503)
  — the dedicated runbook for the drift code.
* [Scheduler fleet model](/concepts/scheduler-fleet-model) — the
  bounded per-tenant tick pattern behind the repair sweep above.
