Skip to content

Provisioning

Provisioning spans two planes of the Consystence topology:

  1. Org provisioning — the account control plane creates a tenant organisation inside a cell by calling POST /api/provision/org on the cell. This mints the org, adopts its identity, and seeds the first owner.
  2. 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.

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.

{
"tenantId": "org_ab12cd34",
"slug": "acme",
"orgType": "Enterprise",
"licenceTier": "Enterprise",
"firstAdmin": { "email": "[email protected]", "oid": "4b57..." },
"domainBindings": ["acme.example"]
}
FieldMeaning
tenantIdThe 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.
slugRouting label only — drives the {slug}.consystence.com subdomain. Unique per canonical domain, not the membership key.
firstAdminThe first owner, account-authoritative (keyed on Entra oid; email is a verified attribute / invitation channel, never the key).
orgType, licenceTier, domainBindingsAccepted 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.

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.

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.

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.

ConditionStatus
Bad / empty request400
Bad or absent X-Api-Key provisioning credential401 / 403 — fail-closed, no fallback
Slug already bound to a different tenant409
Seeding could not complete500 — 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.

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:

PurposeEndpointAuth
Read server stateGET /api/setup/statusAnonymous
Activate with a one-time codePOST /api/setup/site-activationAnonymous — a fresh server holds no token
Factory reset to fresh-install statePOST /api/setup/resetPlatform 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": "…" }.

ConditionStatus
Not a site-mode server; missing code; Server:CloudUrl not configured; the relay failed400
Server already configured409

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.

ConditionStatus
Missing code / installId, or a malformed siteCode400
Cell not yet enrolled with the account503 — fail-closed; onboarding never silently succeeds without the account
Account rejected the code or was unreachable502
Account timed out504

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.

  • The org runtime created here is the tenant boundary for everything in The platformauthentication, 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.