Skip to content

Set a tenant's write-mode (relocation quiesce / ownership flip)

PUT
/v1/tenant-write-mode
curl --request PUT \
--url 'https://api.aerandil.com/v1/tenant-write-mode?tenant_id=2489E9AD-2EE2-8E00-8EC9-32D5F69181C0' \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{ "mode": "active", "reason": "example" }'

Set the tenant’s write-mode. Setting read_only is the quiesce step of a cross-cell relocation cutover (cloud proposal 0007 §4 step 3): while read_only, the tenant’s writes are refused at the store’s write-gate and surface as 503 + Retry-After on every write endpoint, while reads stay live. Setting relocated is the post-cutover ownership flip (0007 §5): the Hub flips the source cell to not-owner once the tenant has been rebound to its new cell, and a straggler write that still resolves here is refused with 421 Misdirected Request (terminal — re-resolve to the owning cell), while reads stay live. Setting active lifts either state.

Idempotent: PUT replaces a singleton operational state, so it carries no Idempotency-Key, and no If-Match (this is operator state, not a versioned object — last writer wins).

Operator-level scope: tenant_admin:write.

tenant_id
required
string format: uuid

The tenant whose write-mode is read or set.

Media typeapplication/json

Set a tenant’s write-mode, with an optional operator note.

object
mode
required

read_only quiesces the tenant (writes refused with 503 + Retry-After; reads live) — the transient cutover freeze; relocated marks this cell as no longer the owner after a relocation cuts over (writes refused with 421 Misdirected Request; reads live) — the §5 ownership flip, terminal at this cell; active lifts either state.

string
Allowed values: active read_only relocated
reason

Operator note recorded for audit (e.g. the relocation job id).

string | null

The persisted write-mode record.

Media typeapplication/json

A tenant’s write-mode plus the provenance of the last change. A tenant that was never set reads back active with null provenance.

object
mode
required
string
Allowed values: active read_only relocated
reason

Operator note from the last set, if any.

string | null
set_by

The principal that last set the mode (serialised).

object | null
tenant_id
required
string format: uuid
updated_at

When the mode was last set; null for a synthesised default.

string | null format: date-time
Example
{
"mode": "active"
}

Missing the tenant_admin:write scope. Tenant-level tokens MUST NOT carry this scope.

Media typeapplication/json

Structured error wrapper. Every non-2xx response from any Aerandil endpoint serialises to this shape; the HTTP status carries the failure class (4xx/5xx), the body adds a stable code for programmatic dispatch plus a human-readable message.

object
error
required
object
code
required

Stable, programmatic error code (e.g. unauthorized, forbidden, not_found, invalid_body, invalid_url, precondition_failed, no_secondary_key). Clients dispatch on code, not on the message (which may be localised or contextualised in future versions).

string
instance_id

Request-id correlation handle echoed on the request-id response header — support teams correlate without scraping logs.

string
message
required

Human-readable explanation of the failure.

string
parameters

Per-error structured detail. The shape varies by code (e.g. forbidden_action carries {"action": "..."}; precondition_failed carries {"current_version": N}).

Examplegenerated
{
"error": {
"code": "example",
"instance_id": "example",
"message": "example",
"parameters": "example"
}
}

Body invalid — unknown field or unrecognised mode.

Media typeapplication/json

Structured error wrapper. Every non-2xx response from any Aerandil endpoint serialises to this shape; the HTTP status carries the failure class (4xx/5xx), the body adds a stable code for programmatic dispatch plus a human-readable message.

object
error
required
object
code
required

Stable, programmatic error code (e.g. unauthorized, forbidden, not_found, invalid_body, invalid_url, precondition_failed, no_secondary_key). Clients dispatch on code, not on the message (which may be localised or contextualised in future versions).

string
instance_id

Request-id correlation handle echoed on the request-id response header — support teams correlate without scraping logs.

string
message
required

Human-readable explanation of the failure.

string
parameters

Per-error structured detail. The shape varies by code (e.g. forbidden_action carries {"action": "..."}; precondition_failed carries {"current_version": N}).

Examplegenerated
{
"error": {
"code": "example",
"instance_id": "example",
"message": "example",
"parameters": "example"
}
}