Provisioning
Provisioning spans two planes of the Consystence topology:
- Org provisioning — the account control plane creates a tenant organisation inside a cell by calling
POST /api/provision/orgon the cell. This mints the org, adopts its identity, and seeds the first owner. - Site activation — a freshly deployed Site server redeems a one-time activation code through its cell, which attaches it to an org, consumes a licence seat when one is available, and selects or creates its site.
The first is a server-to-server (S2S) call between Consystence-operated infrastructure. The second is the bootstrap a deployed server runs against its cell.
Provisioning flow
Section titled “Provisioning flow”sequenceDiagram
participant Op as Operator (account)
participant Acc as Account control plane
participant Cell as Cell (org manager)
participant Srv as Site / edge server
Op->>Acc: Create org (entitlement, slug, first owner)
Acc->>Acc: Mint org UUID (org_*) + slug CNAME
Acc->>Cell: POST /api/provision/org (X-Api-Key csk_…)
Cell->>Cell: Adopt UUID as org.Id, seed first owner
Cell-->>Acc: { tenantSlug, orgId, alreadyExisted }
Note over Srv,Cell: later, on a deployed site server
Srv->>Srv: POST /api/setup/site-activation { code, siteCode? }
Srv->>Cell: POST /api/provision/site-activation { code, installId, siteCode? }
Cell->>Acc: POST /v1/site-activation/redeem (pull credential)
Acc-->>Cell: signed bundle (code burned, seat consumed)
Cell-->>Srv: bundle + site→cloud sync key
In the flow above, POST /v1/site-activation/redeem is an account-service internal endpoint — the cell relays the redemption to the account control plane server-to-server. It is unrelated to the retired public /v1 gateway (see the public API surface).
Org provisioning — POST /api/provision/org
Section titled “Org provisioning — POST /api/provision/org”The account is the provisioning authority: it owns entitlement, rate limits, slug uniqueness, DNS routing, and the first owner. The cell is the executor — it creates the org runtime for exactly the tenant the authenticated account service names. A cell never self-provisions an unknown tenant.
Request
Section titled “Request”{ "tenantId": "org_ab12cd34", "slug": "acme", "orgType": "Enterprise", "licenceTier": "Enterprise", "domainBindings": ["acme.example"]}| Field | Meaning |
|---|---|
tenantId | The account-minted org UUID (org_* format). It is the org’s identity and the idempotency key. The cell adopts it verbatim as its synthetic org.Id and equality-guards adoption — it never mints its own. |
slug | Routing label only — drives the {slug}.consystence.com subdomain. Unique per canonical domain, not the membership key. |
firstAdmin | The first owner, account-authoritative (keyed on Entra oid; email is a verified attribute / invitation channel, never the key). |
orgType, licenceTier, domainBindings | Accepted on the contract but unused by the cell at launch — forward-compatibility only. licenceTier is a flag, not full licence activation; caps and domain-binding gating are deferred. |
Identity and the first owner
Section titled “Identity and the first owner”The cell keys org membership on the Entra oid and recognises the org-membership roles owner, admin, member (see roles). The provisioning call seeds the supplied firstAdmin as the irreducible owner — an org is never created adminless; org creation and the owner landing complete together (or together on an idempotent retry), a property pinned by a test. Subsequent owner/admin changes are pulled from the account; the cell consumes elevated roles rather than originating them.
Idempotency
Section titled “Idempotency”Provisioning is idempotent by tenantId (the UUID) — create-or-return-existing. A repeat POST for an existing UUID returns the existing org and never duplicates or re-seeds it. The UUID is minted once at provisioning intent and reused on every retry.
Response
Section titled “Response”The cell returns:
{ "tenantSlug": "acme", "orgId": "org_ab12cd34", "alreadyExisted": false }orgId echoes the adopted UUID. alreadyExisted: false maps to 201 Created; true is the idempotent hit and maps to 200 OK — the status code is a redundant signal of the same outcome.
| Condition | Status |
|---|---|
| Bad / empty request | 400 |
Bad or absent X-Api-Key provisioning credential | 401 / 403 — fail-closed, no fallback |
| Slug already bound to a different tenant | 409 |
| Seeding could not complete | 500 — safe to retry; provisioning is idempotent and the org is never left adminless |
Entitlement, rate-limit, and global-ceiling checks are enforced account-side and never reach the cell.
Authentication — the provisioning credential
Section titled “Authentication — the provisioning credential”The account→cell call is the most sensitive surface in the platform and uses an X-Api-Key tenant key (a plaintext csk_… value) — not a CIAM bearer, and not the interactive platform-admin policy. The account↔cell credential is established out-of-band when the cell is enrolled, and is not self-mintable.
A not-yet-enrolled cell fail-closes — provisioning returns a clear “cell not enrolled / no provisioning credential” error rather than any silent fallback. See Authentication for the full MultiAuth scheme selector and cell topology for the enrolment model.
Deprovisioning — DELETE /api/provision/org/{slug}
Section titled “Deprovisioning — DELETE /api/provision/org/{slug}”The teardown counterpart removes an org runtime from the cell — including the org’s API keys, command-audit records, and site users. As with creation it is an authority-gated operation; account-side, tenant deletion resolves by the org UUID.
Site activation
Section titled “Site activation”A freshly deployed Site server has no org binding until it is activated. Activation is code-keyed: the account issues a one-time activation code that is site-bound — issued against a site declared in the tenant’s site registry — and the server presents only that code; there is no interactive login at the server. Redeeming the code attaches the server to its org, consumes a SiteServer seat from the org’s licence pools when one is available, and selects or creates a site.
The surviving /api/setup surface is exactly three endpoints:
| Purpose | Endpoint | Auth |
|---|---|---|
| Read server state | GET /api/setup/status | Anonymous |
| Activate with a one-time code | POST /api/setup/site-activation | Anonymous — a fresh server holds no token |
| Factory reset to fresh-install state | POST /api/setup/reset | Platform admin; site-mode servers only |
On the site — POST /api/setup/site-activation
Section titled “On the site — POST /api/setup/site-activation”The body is the one-time code plus an optional site choice — an existing siteCode joins that site, a new one creates it (siteName is the optional display name for a new site):
{ "code": "...", "siteCode": "line-4", "siteName": "Line 4" }The server reads its write-once install id locally (never minted during activation), relays { code, installId, siteCode, siteName } to its cell’s POST /api/provision/site-activation, and applies the returned bundle (verify → persist → Configured), answering 200 with { "status": "configured", "orgSlug": "…" }.
| Condition | Status |
|---|---|
Not a site-mode server; missing code; Server:CloudUrl not configured; the relay failed | 400 |
| Server already configured | 409 |
On the cell — POST /api/provision/site-activation
Section titled “On the cell — POST /api/provision/site-activation”The relay is anonymous but rate-limited (it shares the provisioning limiter’s bounded keyless partition; the 256-bit code defeats brute force) and cloud-mode only (404 on a Site server). The cell forwards { code, installId } to the account’s code-keyed redeem (POST /v1/site-activation/redeem) under the enrolment pull credential (established above), then creates-or-joins the requested site in the org’s cloud site registry and — for the site’s first server, the active syncer — mints the site→cloud sync key, bound to the globally unique {orgSlug}/{siteCode} sync identity.
| Condition | Status |
|---|---|
Missing code / installId, or a malformed siteCode | 400 |
| Cell not yet enrolled with the account | 503 — fail-closed; onboarding never silently succeeds without the account |
| Account rejected the code or was unreachable | 502 |
| Account timed out | 504 |
Redemption is single-use but install-idempotent: a code already consumed by a different install is denied (a uniform “activation denied” — no oracle distinguishing unknown, consumed, expired, or wrong-tenant), while the same install may re-redeem to recover a lost response, so a crash between redeem and apply retries safely. Enrolment is licence-independent: with no available SiteServer seat the redeem still enrols the server identity-only (licence fields empty), per the degrade-not-kill posture below.
For offline-first sites (a Site server may run disconnected; a cell never does), the returned bundle carries an account-signed licence artifact verified against the account’s published keys, so the server does not dial the account again after activation. There is no dedicated offline-activation endpoint: the initial redeem requires reachability from the server to its cell.
Where this fits
Section titled “Where this fits”- The org runtime created here is the tenant boundary for everything in The platform — authentication, roles, and the org/site model.
- Site activation binds Site servers; for the edge gateway specifically, see Edge deployment.
- The published API reference is CLI-scoped: of the endpoints on this page it publishes only
GET /api/setup/status(plus the platform-operator org re-bootstrap endpoint). The server-to-server provisioning calls and the activation and reset endpoints sit outside it — the contracts on this page are canonical.