Appearance
IAM — Tenants
Every account, role and user group belongs to exactly one tenant and carries its id as the first segment of its CRN. A device joins only when it is claimed, assigned or attached to a contract; until then it sits in the register's unassigned pool, under a CRN with no tenant in it.
The entry holds what applies to all of them: rootAccess bounds every rule inside — rules sit on roles and on accounts, never on a user group — and enforce2FA reaches every account, though a role or an account may demand a second factor the tenant does not. Writes raise IAM_TENANT_CREATE, IAM_TENANT_UPDATE, IAM_TENANT_REPLACE and IAM_TENANT_DELETE.
| POST | Create a tenant/api/v1/tenant |
| GET | List tenants/api/v1/tenant |
| GET | Read a tenant/api/v1/tenant/:tenantId |
| GET | Read tenant info/api/v1/tenant/:tenantId/info |
| DELETE | Delete a tenant/api/v1/tenant/:tenantId |
| PATCH | Update a tenant/api/v1/tenant/:tenantId |
| PUT | Replace a tenant/api/v1/tenant/:tenantId |
Create a tenant
Mints the tenant and returns its resource identifier. rootAccess is the ceiling every role and user rule inside the tenant is later held to; omitted it defaults to full rights on the new tenant, and anything wider is refused.
POST
/api/v1/tenantBody
| Name | Description |
|---|---|
enforce2FAbooleanoptional | Whether all users in this tenant are required to enrol in two-factor authentication Default: true |
allowedOriginsarrayoptional | List of origins permitted to make cross-origin requests to this tenant Default: [] |
passwordSecretoptional | Custom secret used to pepper password hashes for this tenant |
rootAccessarrayoptional | Access policies that apply at the root scope of this tenant |
| identifier | The resource this policy applies to |
| Alternative 1 | Resource identifier pattern (CRN), may include wildcards |
| Alternative 2 | Resource identifier pattern (CRN), may include wildcards |
| Alternative 3 | Resource identifier pattern (CRN), may include wildcards |
| Alternative 4 | Resource identifier pattern (CRN), may include wildcards |
| Alternative 5 | Resource identifier pattern (CRN), may include wildcards |
| Alternative 6 | Resource identifier pattern (CRN), may include wildcards |
effectenum | Access policy effect — either allow or deny Possible values: deny, allow |
actionsarray | List of actions permitted by this policy |
policyobjectoptional | Additional constraints for this access policy |
enforce2FAbooleanoptional | Requires the subject to have 2FA active before this access is granted |
targetoptional | Where a device claimed under this rule lands: the kind of owner — user, role or group — or a CRN naming one outright. Only meaningful on a .pair rule. |
| Alternative 1 | Where a device claimed under this rule lands: the kind of owner — user, role or group — or a CRN naming one outright. Only meaningful on a .pair rule.Possible values: user, role, group |
| Alternative 2 | Where a device claimed under this rule lands: the kind of owner — user, role or group — or a CRN naming one outright. Only meaningful on a .pair rule. |
isOwnerbooleanoptional | Marks an owner-relative rule, written as crn#tenant:<id>.user:* and rebound to the holder's own account when the access list is assembled — how a shared role says "your own account" without naming anyone. |
namestring | Short alphanumeric tenant identifier (slug) Pattern: ^\w{4,16}$ |
Response
| Name | Description |
|---|---|
resourceIdentifierstring | Unique Coldwave resource name (CRN) that identifies a resource |
Errors
| Status | Description | Body |
|---|---|---|
403 | You do not have permission to perform this action | |
400 | The requested access permissions exceed what is allowed for this tenant | code: TENANT_NO_SUBSET_ERROR |
401 | No valid access token was presented, or the DPoP proof accompanying it was missing, expired or bound to a different key. | error: string |
500 | The request failed for a reason that is not the caller's to fix. Safe to retry. | error: string, message: string, statusCode: 50 |
List tenants
Every tenant the caller may read, filtered one by one rather than gated as a whole — a root administrator sees all of them, an ordinary tenant administrator only their own.
GET
/api/v1/tenantResponse
| Name | Description |
|---|---|
enforce2FAbooleanoptional | Whether all users in this tenant are required to enrol in two-factor authentication Default: true |
rootAccessarray | Access policies that apply at the root scope of this tenant |
| identifier | The resource this policy applies to |
| Alternative 1 | Resource identifier pattern (CRN), may include wildcards |
| Alternative 2 | Resource identifier pattern (CRN), may include wildcards |
| Alternative 3 | Resource identifier pattern (CRN), may include wildcards |
| Alternative 4 | Resource identifier pattern (CRN), may include wildcards |
| Alternative 5 | Resource identifier pattern (CRN), may include wildcards |
| Alternative 6 | Resource identifier pattern (CRN), may include wildcards |
effectenum | Access policy effect — either allow or deny Possible values: deny, allow |
actionsarray | List of actions permitted by this policy |
policyobjectoptional | Additional constraints for this access policy |
enforce2FAbooleanoptional | Requires the subject to have 2FA active before this access is granted |
targetoptional | Where a device claimed under this rule lands: the kind of owner — user, role or group — or a CRN naming one outright. Only meaningful on a .pair rule. |
| Alternative 1 | Where a device claimed under this rule lands: the kind of owner — user, role or group — or a CRN naming one outright. Only meaningful on a .pair rule.Possible values: user, role, group |
| Alternative 2 | Where a device claimed under this rule lands: the kind of owner — user, role or group — or a CRN naming one outright. Only meaningful on a .pair rule. |
isOwnerbooleanoptional | Marks an owner-relative rule, written as crn#tenant:<id>.user:* and rebound to the holder's own account when the access list is assembled — how a shared role says "your own account" without naming anyone. |
namestring | Short alphanumeric tenant identifier (slug) Pattern: ^\w{4,16}$ |
allowedOriginsarrayoptional | List of origins permitted to make cross-origin requests to this tenant Default: [] |
resourceIdentifierstring | Unique Coldwave resource name (CRN) that identifies a resource |
passwordSecretoptional | Custom secret used to pepper password hashes for this tenant |
createdBystring | Resource identifier of the entity that created this resource |
Errors
| Status | Description | Body |
|---|---|---|
401 | No valid access token was presented, or the DPoP proof accompanying it was missing, expired or bound to a different key. | error: string |
500 | The request failed for a reason that is not the caller's to fix. Safe to retry. | error: string, message: string, statusCode: 50 |
Read a tenant
The full entry, rootAccess included, which takes read on the tenant CRN itself and is deliberately rare. The part everyone inside the tenant may see is /tenant/{tenantId}/info; the pepper reference never appears in a response.
GET
/api/v1/tenant/Yk3pL7rWq2Path Parameters
| Name | Description |
|---|---|
tenantIdstring | Resource id: Base58 (9–10 characters) or a 16-character Crockford snowflake. Not an RFC 4122 UUID. Pattern: ^[0-9A-HJ-NP-Za-km-z]{9,16}$ |
Response
| Name | Description |
|---|---|
enforce2FAbooleanoptional | Whether all users in this tenant are required to enrol in two-factor authentication Default: true |
rootAccessarray | Access policies that apply at the root scope of this tenant |
| identifier | The resource this policy applies to |
| Alternative 1 | Resource identifier pattern (CRN), may include wildcards |
| Alternative 2 | Resource identifier pattern (CRN), may include wildcards |
| Alternative 3 | Resource identifier pattern (CRN), may include wildcards |
| Alternative 4 | Resource identifier pattern (CRN), may include wildcards |
| Alternative 5 | Resource identifier pattern (CRN), may include wildcards |
| Alternative 6 | Resource identifier pattern (CRN), may include wildcards |
effectenum | Access policy effect — either allow or deny Possible values: deny, allow |
actionsarray | List of actions permitted by this policy |
policyobjectoptional | Additional constraints for this access policy |
enforce2FAbooleanoptional | Requires the subject to have 2FA active before this access is granted |
targetoptional | Where a device claimed under this rule lands: the kind of owner — user, role or group — or a CRN naming one outright. Only meaningful on a .pair rule. |
| Alternative 1 | Where a device claimed under this rule lands: the kind of owner — user, role or group — or a CRN naming one outright. Only meaningful on a .pair rule.Possible values: user, role, group |
| Alternative 2 | Where a device claimed under this rule lands: the kind of owner — user, role or group — or a CRN naming one outright. Only meaningful on a .pair rule. |
isOwnerbooleanoptional | Marks an owner-relative rule, written as crn#tenant:<id>.user:* and rebound to the holder's own account when the access list is assembled — how a shared role says "your own account" without naming anyone. |
namestring | Short alphanumeric tenant identifier (slug) Pattern: ^\w{4,16}$ |
allowedOriginsarrayoptional | List of origins permitted to make cross-origin requests to this tenant Default: [] |
resourceIdentifierstring | Unique Coldwave resource name (CRN) that identifies a resource |
passwordSecretoptional | Custom secret used to pepper password hashes for this tenant |
createdBystring | Resource identifier of the entity that created this resource |
Errors
| Status | Description | Body |
|---|---|---|
403 | You do not have permission to perform this action | |
404 | The requested tenant could not be found | |
400 | The request did not match the schema for this endpoint. The details field carries the specific failures. | error: Validation Error |
401 | No valid access token was presented, or the DPoP proof accompanying it was missing, expired or bound to a different key. | error: string |
500 | The request failed for a reason that is not the caller's to fix. Safe to retry. | error: string, message: string, statusCode: 50 |
Read tenant info
The readable face of a tenant, without rootAccess and without the pepper reference. Each field is granted separately as crn#tenant:<id>.info$<field> and one the caller may not read is left out of the response rather than nulled; every user may read the name of their own tenant.
GET
/api/v1/tenant/Yk3pL7rWq2/infoPath Parameters
| Name | Description |
|---|---|
tenantIdstring | Resource id: Base58 (9–10 characters) or a 16-character Crockford snowflake. Not an RFC 4122 UUID. Pattern: ^[0-9A-HJ-NP-Za-km-z]{9,16}$ |
Response
| Name | Description |
|---|---|
resourceIdentifierstring | Unique Coldwave resource name (CRN) that identifies a resource |
namestringoptional | Short name of the tenant. Every user may read this for their own tenant; for any other it takes read on crn#tenant:<id>.info$name.Pattern: ^\w{4,16}$ |
enforce2FAbooleanoptional | Whether the tenant makes every user present a second factor; present only for a caller holding read on crn#tenant:<id>.info$enforce2FA. |
allowedOriginsarrayoptional | The hosts — port included where it is not the default — whose browser origins may call this tenant; present only for a caller holding read on crn#tenant:<id>.info$allowedOrigins. |
Errors
| Status | Description | Body |
|---|---|---|
403 | You do not have permission to perform this action | |
404 | The requested tenant could not be found | |
400 | The request did not match the schema for this endpoint. The details field carries the specific failures. | error: Validation Error |
401 | No valid access token was presented, or the DPoP proof accompanying it was missing, expired or bound to a different key. | error: string |
500 | The request failed for a reason that is not the caller's to fix. Safe to retry. | error: string, message: string, statusCode: 50 |
Delete a tenant
Removes the tenant entry and releases the origins it had registered for browser requests. The resources beneath it — users, roles, groups — are not deleted with it.
DELETE
/api/v1/tenant/Yk3pL7rWq2Path Parameters
| Name | Description |
|---|---|
tenantIdstring | Resource id: Base58 (9–10 characters) or a 16-character Crockford snowflake. Not an RFC 4122 UUID. Pattern: ^[0-9A-HJ-NP-Za-km-z]{9,16}$ |
Errors
| Status | Description | Body |
|---|---|---|
403 | You do not have permission to perform this action | |
404 | The requested tenant could not be found | |
400 | The request did not match the schema for this endpoint. The details field carries the specific failures. | error: Validation Error |
401 | No valid access token was presented, or the DPoP proof accompanying it was missing, expired or bound to a different key. | error: string |
500 | The request failed for a reason that is not the caller's to fix. Safe to retry. | error: string, message: string, statusCode: 50 |
Update a tenant
Writes only the fields present in the body. allowedOrigins: [] really does clear the list and shuts every browser front-end out of the tenant, and rootAccess may not reach beyond crn#tenant:<id>.*.
PATCH
/api/v1/tenant/Yk3pL7rWq2Path Parameters
| Name | Description |
|---|---|
tenantIdstring | Resource id: Base58 (9–10 characters) or a 16-character Crockford snowflake. Not an RFC 4122 UUID. Pattern: ^[0-9A-HJ-NP-Za-km-z]{9,16}$ |
Body
| Name | Description |
|---|---|
namestringoptional | New short identifier for the tenant Pattern: ^\w{4,16}$ |
passwordSecretoptional | Custom secret used to pepper password hashes for this tenant |
rootAccessarrayoptional | Access policies that apply at the root scope of this tenant |
| identifier | The resource this policy applies to |
| Alternative 1 | Resource identifier pattern (CRN), may include wildcards |
| Alternative 2 | Resource identifier pattern (CRN), may include wildcards |
| Alternative 3 | Resource identifier pattern (CRN), may include wildcards |
| Alternative 4 | Resource identifier pattern (CRN), may include wildcards |
| Alternative 5 | Resource identifier pattern (CRN), may include wildcards |
| Alternative 6 | Resource identifier pattern (CRN), may include wildcards |
effectenum | Access policy effect — either allow or deny Possible values: deny, allow |
actionsarray | List of actions permitted by this policy |
policyobjectoptional | Additional constraints for this access policy |
enforce2FAbooleanoptional | Requires the subject to have 2FA active before this access is granted |
targetoptional | Where a device claimed under this rule lands: the kind of owner — user, role or group — or a CRN naming one outright. Only meaningful on a .pair rule. |
| Alternative 1 | Where a device claimed under this rule lands: the kind of owner — user, role or group — or a CRN naming one outright. Only meaningful on a .pair rule.Possible values: user, role, group |
| Alternative 2 | Where a device claimed under this rule lands: the kind of owner — user, role or group — or a CRN naming one outright. Only meaningful on a .pair rule. |
isOwnerbooleanoptional | Marks an owner-relative rule, written as crn#tenant:<id>.user:* and rebound to the holder's own account when the access list is assembled — how a shared role says "your own account" without naming anyone. |
enforce2FAbooleanoptional | Whether all users in this tenant are required to enrol in two-factor authentication |
allowedOriginsarrayoptional | List of origins permitted to make cross-origin requests to this tenant |
Errors
| Status | Description | Body |
|---|---|---|
403 | You do not have permission to perform this action | |
404 | The requested tenant could not be found | |
400 | The requested access permissions exceed what is allowed for this tenant | code: TENANT_NO_SUBSET_ERROR |
401 | No valid access token was presented, or the DPoP proof accompanying it was missing, expired or bound to a different key. | error: string |
500 | The request failed for a reason that is not the caller's to fix. Safe to retry. | error: string, message: string, statusCode: 50 |
Replace a tenant
Overwrites the entry as a whole: an omitted enforce2FA or allowedOrigins falls back to its default rather than to what was stored. Only passwordSecret and createdBy survive the write, so the tenant does not lose its pepper.
PUT
/api/v1/tenant/Yk3pL7rWq2Path Parameters
| Name | Description |
|---|---|
tenantIdstring | Resource id: Base58 (9–10 characters) or a 16-character Crockford snowflake. Not an RFC 4122 UUID. Pattern: ^[0-9A-HJ-NP-Za-km-z]{9,16}$ |
Body
| Name | Description |
|---|---|
enforce2FAbooleanoptional | Whether all users in this tenant are required to enrol in two-factor authentication Default: true |
allowedOriginsarrayoptional | List of origins permitted to make cross-origin requests to this tenant Default: [] |
passwordSecretoptional | Custom secret used to pepper password hashes for this tenant |
rootAccessarrayoptional | Access policies that apply at the root scope of this tenant |
| identifier | The resource this policy applies to |
| Alternative 1 | Resource identifier pattern (CRN), may include wildcards |
| Alternative 2 | Resource identifier pattern (CRN), may include wildcards |
| Alternative 3 | Resource identifier pattern (CRN), may include wildcards |
| Alternative 4 | Resource identifier pattern (CRN), may include wildcards |
| Alternative 5 | Resource identifier pattern (CRN), may include wildcards |
| Alternative 6 | Resource identifier pattern (CRN), may include wildcards |
effectenum | Access policy effect — either allow or deny Possible values: deny, allow |
actionsarray | List of actions permitted by this policy |
policyobjectoptional | Additional constraints for this access policy |
enforce2FAbooleanoptional | Requires the subject to have 2FA active before this access is granted |
targetoptional | Where a device claimed under this rule lands: the kind of owner — user, role or group — or a CRN naming one outright. Only meaningful on a .pair rule. |
| Alternative 1 | Where a device claimed under this rule lands: the kind of owner — user, role or group — or a CRN naming one outright. Only meaningful on a .pair rule.Possible values: user, role, group |
| Alternative 2 | Where a device claimed under this rule lands: the kind of owner — user, role or group — or a CRN naming one outright. Only meaningful on a .pair rule. |
isOwnerbooleanoptional | Marks an owner-relative rule, written as crn#tenant:<id>.user:* and rebound to the holder's own account when the access list is assembled — how a shared role says "your own account" without naming anyone. |
namestring | Short alphanumeric tenant identifier (slug) Pattern: ^\w{4,16}$ |
Errors
| Status | Description | Body |
|---|---|---|
403 | You do not have permission to perform this action | |
404 | The requested tenant could not be found | |
400 | The requested access permissions exceed what is allowed for this tenant | code: TENANT_NO_SUBSET_ERROR |
401 | No valid access token was presented, or the DPoP proof accompanying it was missing, expired or bound to a different key. | error: string |
500 | The request failed for a reason that is not the caller's to fix. Safe to retry. | error: string, message: string, statusCode: 50 |