Tenancy model — two supported shapes
Audact supports both public keyless and org-owned tenant provisioning. Choose the shape that fits your integration.
Audact supports both of the tenant-provisioning shapes an integrator might be choosing between — this is a design decision on the caller's side, not a gap in the API.
Shape 1 — Public, keyless tenant creation
POST /v1/tenants (tenant-management, unauthenticated)
- No API key or org account required to call this endpoint.
- IP rate-limited to 5 creations per IP per hour since it is otherwise open.
- Tenant lifecycle from here on:
GET /v1/tenants/{tenant_id}(viewer+),PUT /v1/tenants/{tenant_id}(owner), API keys viaPOST/GET/DELETE /v1/tenants/{tenant_id}/keys[/{key_id}](owner to mint/revoke, developer+ to list).
Shape 2 — Org-owned tenants with full lifecycle
POST /v1/org/tenants (org-management, requires an authenticated, verified organization) —
provisions a tenant owned by the caller's organization, deliberately without an API key or
default configuration; those are deferred to a separate initialize step so tenant creation and
tenant activation are two auditable actions.
Full lifecycle under the org-owned model:
| Endpoint | Effect |
|---|---|
POST /v1/org/tenants | Create the tenant row, owned by the org. No key, no configuration yet. |
POST /v1/org/tenants/{id}/initialize | Seeds the tenant's first sealed default configuration and reveals its first owner API key (idempotent — replays 409 already_initialized on a second call rather than duplicating). |
POST /v1/org/tenants/{id}/keys | Mints an additional owner API key without touching configuration. |
POST /v1/org/tenants/{id}/enable | Sets status=active, clears the org:tenant-disabled Redis marker. |
POST /v1/org/tenants/{id}/disable | This is PAUSE — see below. |
DELETE /v1/org/tenants/{id} | Logical delete: sets deleted_at, sets the org:tenant-disabled marker. The tenant's KMS HMAC key is retained, never deprovisioned. |
POST /v1/org/tenants/{id}/restore | Clears deleted_at on a soft-deleted tenant; reuses the original hmac_key_arn as-is (no re-provisioning, no new configuration/key). |
POST /v1/org/tenants/{id}/impersonate | Mints a short-lived (~20 min) tenant-scoped JWT so the org admin can act as the tenant's owner against tenant-facing services; every call is audit-logged. |
disable is a real PAUSE
POST /v1/org/tenants/{id}/disable transitions the tenant to status=disabled and sets the
org:tenant-disabled:{tenant_id} Redis marker. From that point:
- The API key authentication middleware checks this marker on the hot path and rejects every
request authenticated with that tenant's API keys with
403, error codetenant_disabled— including impersonation-minted tokens. - The check fails open (allows the request through, logs a warning) if Redis itself is unreachable, so a Redis outage cannot itself become an availability incident for tenants that are not disabled.
enableclears the marker and restoresstatus=active; the reversal is immediate.
This is the tenant-granularity equivalent of a PAUSE. DID-level pause — holding a phone number without routing it — is a separate concept.
Per-tenant isolation in both shapes
- API keys and the webhook HMAC signing secret are per-tenant regardless of which shape created the tenant — there is no shared/global signing secret.
- Every query in tenant-management/org-management scopes by
tenant_idtaken from the authenticated context, never from a request body field.
Recommendation
One Audact tenant per integrator-side tenant gives you per-tenant API key/secret isolation, the
disable → PAUSE mapping at exactly the granularity you likely want, and Audact's existing
cross-tenant isolation boundary — for free, with no extra plumbing. The API does not force this
mapping, but it is the simplest one to reason about.
