switching clears the token & responses
No token
36

Decommission a tenant (suspend → soft-delete → hard-delete)

Suspend → soft-delete → hard-delete. Hard delete is two-phase and four-eyes. Requires tenant-lifecycle-operator. Restore is the counterpart of suspend — and the only lifecycle action that needs no step-up.

  1. PATCH /v1/admin/tenants/{tenantId} { action: suspend } → frozen (step-up)
  2. PATCH /v1/admin/tenants/{tenantId} { action: restore } → back to active (NO step-up)
  3. PATCH /v1/admin/tenants/{tenantId} { action: soft-delete } → soft-deleted (step-up)
  4. DELETE /v1/admin/tenants/{tenantId} → hard delete (four-eyes + step-up)
PATCH/v1/admin/tenants/{tenantId}adminSuspend tenant (step-up)

Lifecycle branch: exactly one of { name } or { action } — neither or both is a 400, and stepUp on a rename is refused rather than ignored. suspend → frozen. Step-up required. A frozen tenant refuses new tokens: its members' logins and switch-tenant calls answer 403 tenant_unavailable (scenarios 9 / 20). THE OTHER BRANCH OF THIS ENDPOINT IS A RENAME — send { name } alone, with no stepUp. It has no scenario of its own because it is not a lifecycle act, but it is worth knowing that it EMITS NO PROJECTION EVENT: the tenant's ledger row already exists, so the reconciler re-emits the new name within PROJECTION_RECONCILE_INTERVAL_MS (default 1h) and an app's copy is stale until then BY DESIGN. That is a stale row, not a missing one, and a resync does not shorten it (scenario 42).

⛓ Needs an out-of-band value?
Path parameters
Request body (JSON)
ℹ︎Field guide — what each value means & where it comes from6 fields

Updates a tenant with EXACTLY ONE of a rename ({ name }) or a lifecycle transition ({ action, stepUp? }); the body is .strict() so unknown fields fail. Auth needs the gateway context (handled) plus the tenant-lifecycle-operator role; suspend/soft-delete also require a step-up factor (stepUp), while restore does not and rename must not carry stepUp.

tenantIdpathUUIDrequiredfrom a response
Identifies the tenant to update. The user does not type this; copy the id from a prior tenant list or get response in the tester. Malformed UUID returns 400.
namebodystringconditionalyou choose
Provide ONLY for the rename branch; the user freely types the new tenant name (trimmed, 1-200 chars). Supply exactly one of name OR action, never both; stepUp is forbidden when name is present.
actionbodyenumconditionalyou choose
Provide ONLY for the lifecycle branch; the user picks the transition. Supply exactly one of name OR action. suspend and soft-delete require stepUp; restore does not.
  • suspendTransitions the tenant to status 'frozen'; requires a step-up factor; audits tenant_suspended and emits the matching outbox event.
  • restoreTransitions the tenant back to status 'active'; no step-up required; audits tenant_restored and emits the matching outbox event.
  • soft-deleteMarks the tenant deleted; requires a step-up factor; audits tenant_deleted and emits the tenant.deleted outbox event.
stepUpbodyobjectconditionalyou choose
Step-up re-authentication factor, an object { password?, recoveryCode? } with at least one present. REQUIRED for action suspend or soft-delete; FORBIDDEN on a rename and not needed for restore.
stepUp.passwordbodystringconditionalyou choose
The caller's own account password, freely typed to satisfy step-up. Provide either this or stepUp.recoveryCode (at least one) when stepUp is required.
stepUp.recoveryCodebodystringconditionalauthenticator
A one-time recovery code from the caller's MFA authenticator, used as the step-up factor instead of a password. Provide either this or stepUp.password (at least one) when stepUp is required.
Bearer token
PATCH https://api.kerja.team/v1/admin/tenants/1c2d0000-0000-7000-8000-0000000000ac
↩︎Response guide — what comes back & what each value means7 fields

200 OK returning the updated Tenant wrapped in the { data } envelope (no token/cookie or Cache-Control headers); status reflects the applied transition (suspend -> frozen, restore -> active, soft-delete marks the tenant deleted).

dataobjectalways
The { data } envelope wrapping the updated Tenant object after the rename or lifecycle transition. 200 OK; no Set-Cookie or Cache-Control headers of note.
data.idUUIDalways
The tenant's unique identifier (unchanged by the update). Reuse it to address the tenant in subsequent admin calls.
data.namestringalways
The tenant's display name (1-200 chars). Reflects the new value after a rename, or the existing name after a lifecycle action.
data.slugstringalways
The tenant's URL-safe slug. Stable across updates; identifies the tenant in routing/keys.
data.statusenumalways
The tenant's lifecycle status, reflecting the transition just applied (suspend -> frozen, restore -> active). The tester surfaces this to confirm the action took effect.
  • provisioningTenant is being set up and not yet usable.
  • activeTenant is live and operational; result of a restore.
  • frozenTenant is suspended; result of a suspend action.
  • archivedTenant has been archived/retired.
data.createdAtISO-8601always
Timestamp when the tenant was created (unchanged by the update). Display-only.
data.updatedAtISO-8601always
Timestamp of this most recent update; advanced by the rename or lifecycle transition. Confirms the change was persisted.
PATCH/v1/admin/tenants/{tenantId}adminRestore tenant (no step-up)

Sends a frozen tenant back to active — the counterpart of suspend, and the ONLY lifecycle action that needs NO step-up (undoing a freeze is the safe direction).

⛓ Needs an out-of-band value?
Path parameters
Request body (JSON)
ℹ︎Field guide — what each value means & where it comes from6 fields

Updates a tenant with EXACTLY ONE of a rename ({ name }) or a lifecycle transition ({ action, stepUp? }); the body is .strict() so unknown fields fail. Auth needs the gateway context (handled) plus the tenant-lifecycle-operator role; suspend/soft-delete also require a step-up factor (stepUp), while restore does not and rename must not carry stepUp.

tenantIdpathUUIDrequiredfrom a response
Identifies the tenant to update. The user does not type this; copy the id from a prior tenant list or get response in the tester. Malformed UUID returns 400.
namebodystringconditionalyou choose
Provide ONLY for the rename branch; the user freely types the new tenant name (trimmed, 1-200 chars). Supply exactly one of name OR action, never both; stepUp is forbidden when name is present.
actionbodyenumconditionalyou choose
Provide ONLY for the lifecycle branch; the user picks the transition. Supply exactly one of name OR action. suspend and soft-delete require stepUp; restore does not.
  • suspendTransitions the tenant to status 'frozen'; requires a step-up factor; audits tenant_suspended and emits the matching outbox event.
  • restoreTransitions the tenant back to status 'active'; no step-up required; audits tenant_restored and emits the matching outbox event.
  • soft-deleteMarks the tenant deleted; requires a step-up factor; audits tenant_deleted and emits the tenant.deleted outbox event.
stepUpbodyobjectconditionalyou choose
Step-up re-authentication factor, an object { password?, recoveryCode? } with at least one present. REQUIRED for action suspend or soft-delete; FORBIDDEN on a rename and not needed for restore.
stepUp.passwordbodystringconditionalyou choose
The caller's own account password, freely typed to satisfy step-up. Provide either this or stepUp.recoveryCode (at least one) when stepUp is required.
stepUp.recoveryCodebodystringconditionalauthenticator
A one-time recovery code from the caller's MFA authenticator, used as the step-up factor instead of a password. Provide either this or stepUp.password (at least one) when stepUp is required.
Bearer token
PATCH https://api.kerja.team/v1/admin/tenants/1c2d0000-0000-7000-8000-0000000000ac
↩︎Response guide — what comes back & what each value means7 fields

200 OK returning the updated Tenant wrapped in the { data } envelope (no token/cookie or Cache-Control headers); status reflects the applied transition (suspend -> frozen, restore -> active, soft-delete marks the tenant deleted).

dataobjectalways
The { data } envelope wrapping the updated Tenant object after the rename or lifecycle transition. 200 OK; no Set-Cookie or Cache-Control headers of note.
data.idUUIDalways
The tenant's unique identifier (unchanged by the update). Reuse it to address the tenant in subsequent admin calls.
data.namestringalways
The tenant's display name (1-200 chars). Reflects the new value after a rename, or the existing name after a lifecycle action.
data.slugstringalways
The tenant's URL-safe slug. Stable across updates; identifies the tenant in routing/keys.
data.statusenumalways
The tenant's lifecycle status, reflecting the transition just applied (suspend -> frozen, restore -> active). The tester surfaces this to confirm the action took effect.
  • provisioningTenant is being set up and not yet usable.
  • activeTenant is live and operational; result of a restore.
  • frozenTenant is suspended; result of a suspend action.
  • archivedTenant has been archived/retired.
data.createdAtISO-8601always
Timestamp when the tenant was created (unchanged by the update). Display-only.
data.updatedAtISO-8601always
Timestamp of this most recent update; advanced by the rename or lifecycle transition. Confirms the change was persisted.
PATCH/v1/admin/tenants/{tenantId}adminSoft-delete tenant (step-up)

Marks the tenant soft-deleted — the prerequisite for a hard delete. Step-up required.

⛓ Needs an out-of-band value?
Path parameters
Request body (JSON)
ℹ︎Field guide — what each value means & where it comes from6 fields

Updates a tenant with EXACTLY ONE of a rename ({ name }) or a lifecycle transition ({ action, stepUp? }); the body is .strict() so unknown fields fail. Auth needs the gateway context (handled) plus the tenant-lifecycle-operator role; suspend/soft-delete also require a step-up factor (stepUp), while restore does not and rename must not carry stepUp.

tenantIdpathUUIDrequiredfrom a response
Identifies the tenant to update. The user does not type this; copy the id from a prior tenant list or get response in the tester. Malformed UUID returns 400.
namebodystringconditionalyou choose
Provide ONLY for the rename branch; the user freely types the new tenant name (trimmed, 1-200 chars). Supply exactly one of name OR action, never both; stepUp is forbidden when name is present.
actionbodyenumconditionalyou choose
Provide ONLY for the lifecycle branch; the user picks the transition. Supply exactly one of name OR action. suspend and soft-delete require stepUp; restore does not.
  • suspendTransitions the tenant to status 'frozen'; requires a step-up factor; audits tenant_suspended and emits the matching outbox event.
  • restoreTransitions the tenant back to status 'active'; no step-up required; audits tenant_restored and emits the matching outbox event.
  • soft-deleteMarks the tenant deleted; requires a step-up factor; audits tenant_deleted and emits the tenant.deleted outbox event.
stepUpbodyobjectconditionalyou choose
Step-up re-authentication factor, an object { password?, recoveryCode? } with at least one present. REQUIRED for action suspend or soft-delete; FORBIDDEN on a rename and not needed for restore.
stepUp.passwordbodystringconditionalyou choose
The caller's own account password, freely typed to satisfy step-up. Provide either this or stepUp.recoveryCode (at least one) when stepUp is required.
stepUp.recoveryCodebodystringconditionalauthenticator
A one-time recovery code from the caller's MFA authenticator, used as the step-up factor instead of a password. Provide either this or stepUp.password (at least one) when stepUp is required.
Bearer token
PATCH https://api.kerja.team/v1/admin/tenants/1c2d0000-0000-7000-8000-0000000000ac
↩︎Response guide — what comes back & what each value means7 fields

200 OK returning the updated Tenant wrapped in the { data } envelope (no token/cookie or Cache-Control headers); status reflects the applied transition (suspend -> frozen, restore -> active, soft-delete marks the tenant deleted).

dataobjectalways
The { data } envelope wrapping the updated Tenant object after the rename or lifecycle transition. 200 OK; no Set-Cookie or Cache-Control headers of note.
data.idUUIDalways
The tenant's unique identifier (unchanged by the update). Reuse it to address the tenant in subsequent admin calls.
data.namestringalways
The tenant's display name (1-200 chars). Reflects the new value after a rename, or the existing name after a lifecycle action.
data.slugstringalways
The tenant's URL-safe slug. Stable across updates; identifies the tenant in routing/keys.
data.statusenumalways
The tenant's lifecycle status, reflecting the transition just applied (suspend -> frozen, restore -> active). The tester surfaces this to confirm the action took effect.
  • provisioningTenant is being set up and not yet usable.
  • activeTenant is live and operational; result of a restore.
  • frozenTenant is suspended; result of a suspend action.
  • archivedTenant has been archived/retired.
data.createdAtISO-8601always
Timestamp when the tenant was created (unchanged by the update). Display-only.
data.updatedAtISO-8601always
Timestamp of this most recent update; advanced by the rename or lifecycle transition. Confirms the change was persisted.
DELETE/v1/admin/tenants/{tenantId}adminHard-delete tenant (four-eyes + step-up)

Phase two. The tenant must already be soft-deleted (else 422). approverOperatorId must differ from the caller and hold tenant-lifecycle-operator (else 403, four-eyes). Produces a deletion attestation.

⛓ Needs an out-of-band value?
Path parameters
Request body (JSON)
ℹ︎Field guide — what each value means & where it comes from5 fields

Hard-deletes an already soft-deleted tenant; phase two of the lifecycle. Requires the tenant-lifecycle-operator role plus step-up re-auth and four-eyes (a second operator approver who differs from you). No Idempotency-Key needed. Authorization / x-gateway-context are handled by the tester and gateway. Body is .strict() (unknown fields rejected).

tenantIdpathUUIDrequiredfrom a response
The tenant to hard-delete. Copy it from a prior listing/get of tenants in the tester (e.g. a tenant list or get response). Must be a valid UUID (malformed -> 400) and the tenant must already be soft-deleted (else 422).
stepUp.passwordbodystringconditionalyou choose
Your account password, typed by you to satisfy step-up re-auth. The stepUp object requires at least one of password or recoveryCode; provide this if you are verifying with your password.
stepUp.recoveryCodebodystringconditionalauthenticator
A one-time recovery code from your MFA authenticator setup, used as an alternative step-up factor. The stepUp object requires at least one of password or recoveryCode; provide this instead of password if recovering via a backup code.
approverOperatorIdbodyUUIDrequiredfrom a response
The second operator who approves the deletion (four-eyes). Obtain this UUID from a prior operator list/get response in the tester. Must differ from the caller (equal -> 403) and the approver must hold an active tenant-lifecycle-operator role (else 422).
reasonbodystringrequiredyou choose
A free-text justification you type for the hard delete (e.g. a GDPR erasure ticket id). Trimmed, 1-500 characters. Recorded in the tenant_deletion_attested audit entry.
Bearer token
DELETE https://api.kerja.team/v1/admin/tenants/1c2d0000-0000-7000-8000-0000000000ac
↩︎Response guide — what comes back & what each value means4 fields

200 OK with a { data } envelope wrapping a TenantDeletionAttestation receipt; no special response headers (no Set-Cookie, no Cache-Control: no-store). The body confirms the hard-delete was attested under four-eyes approval.

data.tenantIdUUIDalways
The id of the tenant that was hard-deleted; echoes the path param. Use to confirm the correct tenant was purged.
data.attestedByUUIDalways
Operator id of the caller who initiated and attested the deletion (the step-up actor).
data.approverUUIDalways
Operator id of the second person who approved under four-eyes; differs from attestedBy and holds tenant-lifecycle-operator.
data.attestedAtISO-8601always
Server time of the attestation, recorded in the tenant_deletion_attested audit event. Use as the deletion timestamp.