5. Tenants
Creating, listing, and configuring the tenants that partition a realm, including each tenant’s StorageSet quota allocations.
Endpoint Summary
| Path | Summary | Method |
|---|---|---|
/api/v1/tenants |
List tenants | GET |
/api/v1/tenants |
Create tenant | POST |
/api/v1/tenants/archived |
List archived tenants | GET |
/api/v1/tenants/roles |
List roles available on the current tenant | GET |
/api/v1/tenants/{tenant_id} |
Get tenant info | GET |
/api/v1/tenants/{tenant_id} |
Update tenant | PATCH |
/api/v1/tenants/{tenant_id} |
Delete tenant | DELETE |
/api/v1/tenants/{tenant_id}/storagesets |
List tenant storageset quota allocations | GET |
/api/v1/tenants/{tenant_id}/storagesets/{storageset_id} |
Get tenant storageset | GET |
/api/v1/tenants/{tenant_id}/storagesets/{storageset_id} |
Assign or resize tenant storageset | PUT |
/api/v1/tenants/{tenant_id}/storagesets/{storageset_id} |
Unassign tenant storageset | DELETE |
GET /api/v1/tenants
List tenants
Returns a list of all tenants, each carrying the network status the realm manager reports for it. Any platform role that grants tenants:list may call it.
Authentication: required (PasswordLogin or BearerJWT).
Responses
| Status | Description | Schema |
|---|---|---|
| 200 | Successful response containing the list of tenants | ListTenantResponse |
| 401 | Unauthorized. Authentication required. | - |
| 403 | Forbidden. Insufficient permissions. | - |
| 500 | Internal server error. | - |
Response Body (200, ListTenantResponse)
| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
tenants |
array of TenantSummarySchema object |
no | n/a | Array of tenant summary objects. Empty array if no tenants found | |
total |
integer | yes | n/a | 0, 5, 50 |
Total count of tenants in this response. |
Example
{
"tenants" : [
{
"created_at" : "2026-03-02T12:00:00Z",
"description" : "FinTech tenant",
"id" : "e97b94d6-9cc1-4325-a267-1064ae538ccd",
"name" : "fintech",
"status" : "active",
"updated_at" : "2026-03-02T12:00:00Z"
}
],
"total" : 1
}POST /api/v1/tenants
Create tenant
Creates a new tenant with the specified configuration and returns its ID. Platform Admin only.
Authentication: required (PasswordLogin or BearerJWT).
Request Body (application/json, required)
Schema: TenantCreateRequest
Request payload for creating a tenant.
| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
auth_settings |
LocalAuthSettings object or PamAuthSettings object |
yes | n/a | {"auth_mode":"local"}, {"auth_mode":"pam","service":"login"} |
Tenant authentication settings. Discriminated on auth_mode. |
description |
string, nullable | no | null |
FinTech department tenant, Engineering team workspace |
Tenant description. |
display_name |
string | yes | n/a | FinTech, Engineering, Data Science |
Human-readable tenant name. Must be 1-255 printable characters. |
email |
string | yes | n/a | admin@fintech.example.com, ops@vdura.example.com |
Tenant contact email address. |
name |
string | yes | n/a | fintech, eng, datasci |
Tenant name, used for volume mounting and as the prefix of a tenant-qualified username. Must be 3-10 alphanumeric characters and unique within the system. Stored in lower case. platform is reserved. Immutable after creation. |
Responses
| Status | Description | Schema |
|---|---|---|
| 201 | Tenant created successfully | TenantCreateResponse |
| 400 | Bad request. Invalid parameters or validation error. | - |
| 401 | Unauthorized. Authentication required. | - |
| 403 | Forbidden. Insufficient permissions. | - |
| 409 | Conflict. This realm serves its clients over InfiniBand, which cannot carry the VLAN or VXLAN encapsulation multi-tenancy needs, so no tenant can be created on it. | - |
| 422 | Validation error. Returned for invalid request body. | - |
| 500 | Internal server error. | - |
| 503 | Service unavailable. A backend dependency the request needs is unreachable. The request was well formed and changed nothing, so the same call is worth retrying once the dependency is back; which one failed is recorded server-side, not in the response. | - |
Response Body (201, TenantCreateResponse)
| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
id |
string (uuid) | yes | n/a | e97b94d6-9cc1-4325-a267-1064ae538ccd |
Unique tenant identifier (UUID) assigned by the system |
Example
{
"id" : "e97b94d6-9cc1-4325-a267-1064ae538ccd"
}GET /api/v1/tenants/archived
List archived tenants
Returns the deleted tenants kept in the platform archive, newest deletion first. Platform Admin only. An archived tenant exists in neither the storage fabric nor the configuration store: the entry is all that is left of it, and it is never removed. Its display name and contact email have no other surviving copy.
Authentication: required (PasswordLogin or BearerJWT).
Responses
| Status | Description | Schema |
|---|---|---|
| 200 | Successful response containing the deleted tenants held in the platform archive. | ListArchivedTenantResponse |
| 401 | Unauthorized. Authentication required. | - |
| 403 | Forbidden. Insufficient permissions. | - |
| 500 | Internal server error. | - |
Response Body (200, ListArchivedTenantResponse)
| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
tenants |
array of ArchivedTenantSchema object |
no | n/a | Array of archived tenants, newest deletion first. Empty array if none were ever deleted. | |
total |
integer | yes | n/a | 0, 5, 50 |
Total count of archived tenants in this response. |
Example
{
"tenants" : [
{
"created_at" : "2026-01-10T08:00:00Z",
"deleted_at" : "2026-04-02T11:20:00Z",
"description" : "Decommissioned engineering tenant",
"display_name" : "Engineering (retired)",
"email" : "eng-owner@vdura.example.com",
"id" : "3f2b1c0d-5e4a-4b8c-9d7e-6a5b4c3d2e1f",
"name" : "oldeng",
"status" : "archived",
"updated_at" : "2026-04-02T11:15:00Z"
}
],
"total" : 1
}GET /api/v1/tenants/roles
List roles available on the current tenant
Returns the roles available on the caller’s current tenant - resolved from the auth context, with no tenant_id path parameter. The response is a roles array with a total; each entry carries the role id and the permissions map of resource to the actions that role may perform on it. Roles are read from the current tenant’s RBAC policy, so Platform and Tenant contexts each return the role set defined and enforced on their own tenant.
Authentication: required (PasswordLogin or BearerJWT).
Responses
| Status | Description | Schema |
|---|---|---|
| 200 | Roles available on the current tenant retrieved successfully. | TenantRolesResponse |
| 401 | Unauthorized. Authentication required. | - |
| 403 | Forbidden. Insufficient permissions. | - |
| 404 | Tenant RBAC policy roles not found. | - |
| 500 | Internal server error. | - |
Response Body (200, TenantRolesResponse)
| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
roles |
array of TenantRole object |
no | n/a | Array of roles the tenant policy defines. Empty array if the policy defines none. | |
total |
integer | yes | n/a | 2, 5 |
Total count of roles in this response. |
Example
{
"roles" : [
{
"id" : "admin",
"permissions" : {
"roles" : [
"view",
"update"
],
"tenants" : [
"view"
],
"users" : [
"list",
"view",
"create",
"update",
"delete"
],
"volumes" : [
"list",
"view",
"create",
"update",
"delete",
"action"
]
},
"reserved" : false
},
{
"id" : "s3-gateway",
"permissions" : {
"fs-users" : [
"read-s3-secret",
"list"
]
},
"reserved" : true
}
],
"total" : 2
}GET /api/v1/tenants/{tenant_id}
Get tenant info
Returns details for a specific tenant by its ID, including the network status the realm manager reports for it. The platform tenant may view any tenant, and a Tenant Admin may view only their own tenant.
Authentication: required (PasswordLogin or BearerJWT).
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
tenant_id |
path | string (uuid) | yes | Tenant identifier (UUID) |
Responses
| Status | Description | Schema |
|---|---|---|
| 200 | Successfully retrieved detailed information about the tenant. | GetTenantResponse |
| 400 | Bad request. Invalid tenant_id parameter. | - |
| 401 | Unauthorized. Authentication required. | - |
| 403 | Forbidden. Insufficient permissions. | - |
| 404 | Tenant not found. | - |
| 422 | Validation Error | HTTPValidationError |
| 500 | Internal server error. | - |
Response Body (200, GetTenantResponse)
| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
tenant |
Tenant object |
yes | n/a | Tenant entity with all available details. |
PATCH /api/v1/tenants/{tenant_id}
Update tenant
Updates a tenant using PATCH semantics. Only the fields provided in the request are modified. Platform Admin only.
Authentication: required (PasswordLogin or BearerJWT).
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
tenant_id |
path | string (uuid) | yes | Tenant identifier (UUID) |
Request Body (application/json, required)
Schema: TenantPatchRequest
Request payload for updating a tenant via PATCH.
Allows partial updates. At least one field must be provided. Patchable fields: display_name, email, description, auth_settings. Tenant Admin can update display_name, email, description and auth_settings. name is the immutable handle and is not patchable.
| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
auth_settings |
LocalAuthSettings object or PamAuthSettings object, nullable |
no | null |
{"auth_mode":"local"}, {"auth_mode":"pam","service":"login"} |
Updated tenant authentication settings. |
description |
string, nullable | no | null |
Updated tenant description, New department focus |
Updated tenant description. |
display_name |
string, nullable | no | null |
FinTech Renamed, Engineering 2.0 |
Updated human-readable tenant name. Must be 1-255 printable characters. |
email |
string, nullable | no | null |
admin@fintech.example.com |
Updated tenant contact email address. |
Responses
| Status | Description | Schema |
|---|---|---|
| 204 | Tenant updated successfully. | - |
| 400 | Bad request. Invalid tenant_id or custom validation error. | - |
| 401 | Unauthorized. Authentication required. | - |
| 403 | Forbidden. Insufficient permissions. | - |
| 404 | Tenant not found. | - |
| 422 | Validation error. Returned for invalid request body (all fields are optional). | - |
| 500 | Internal server error. | - |
DELETE /api/v1/tenants/{tenant_id}
Delete tenant
Deletes the specified tenant by its ID, removing its records from both the storage fabric and the configuration store and leaving an entry in the platform archive. Platform Admin only. Safe to repeat: a delete interrupted part-way is resumed by sending it again.
Authentication: required (PasswordLogin or BearerJWT).
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
tenant_id |
path | string (uuid) | yes | Tenant identifier (UUID) |
Responses
| Status | Description | Schema |
|---|---|---|
| 204 | Tenant deleted successfully. | - |
| 401 | Unauthorized. Authentication required. | - |
| 403 | Forbidden. Insufficient permissions. | - |
| 404 | Tenant not found. | - |
| 409 | Conflict. The tenant cannot be deleted as it currently stands: it still owns volumes or subnets, a delete of it is already running, or it is the immutable PanAPI Platform Tenant. The detail says which; the example shows the shape, not the alternatives. |
- |
| 422 | Validation Error | HTTPValidationError |
| 500 | Internal server error. | - |
GET /api/v1/tenants/{tenant_id}/storagesets
List tenant storageset quota allocations
Lists the storageset quota allocations assigned to a tenant. Callers may only list their own tenant.
Authentication: required (PasswordLogin or BearerJWT).
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
tenant_id |
path | string (uuid) | yes | Tenant identifier (UUID) |
Responses
| Status | Description | Schema |
|---|---|---|
| 200 | Successful response containing the tenant’s storageset quota allocations. | ListTenantStorageSetResponse |
| 401 | Unauthorized. Authentication required. | - |
| 403 | Forbidden. Insufficient permissions. | - |
| 404 | Tenant not found. | - |
| 422 | Validation Error | HTTPValidationError |
| 500 | Internal server error. | - |
Response Body (200, ListTenantStorageSetResponse)
| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
storagesets |
array of StorageSetAssignment object |
no | n/a | Array of storageset assignments (id + quota + name). Empty array if none are assigned. | |
total |
integer | yes | n/a | 0, 2, 5 |
Total count of storageset assignments in this response. |
Example
{
"storagesets" : [
{
"quota_bytes" : 2199023255552,
"storageset_id" : 1,
"storageset_name" : "Set 1"
},
{
"quota_bytes" : 5497558138880,
"storageset_id" : 3,
"storageset_name" : "Set 3"
}
],
"total" : 2
}GET /api/v1/tenants/{tenant_id}/storagesets/{storageset_id}
Get tenant storageset
Returns a single storageset quota allocation for a tenant. Callers may only access their own tenant.
Authentication: required (PasswordLogin or BearerJWT).
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
tenant_id |
path | string (uuid) | yes | Tenant identifier (UUID) |
storageset_id |
path | integer | yes | StorageSet identifier |
Responses
| Status | Description | Schema |
|---|---|---|
| 200 | Successfully retrieved the tenant storageset quota allocation. | GetTenantStorageSetResponse |
| 400 | Bad request. Invalid tenant_id parameter. | - |
| 401 | Unauthorized. Authentication required. | - |
| 403 | Forbidden. Insufficient permissions. | - |
| 404 | Tenant or storageset not found. | - |
| 422 | Validation error. | - |
| 500 | Internal server error. | - |
Response Body (200, GetTenantStorageSetResponse)
| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
storageset |
StorageSetAssignment object |
yes | n/a | Storageset assignment (id + quota + name). |
Example
{
"storageset" : {
"quota_bytes" : 2199023255552,
"storageset_id" : 1,
"storageset_name" : "Set 1"
}
}PUT /api/v1/tenants/{tenant_id}/storagesets/{storageset_id}
Assign or resize tenant storageset
Upserts a storageset quota allocation for a tenant: assigns the storageset with the given quota if it is not yet assigned, or replaces the quota if it already is. The combined cross-tenant quota must fit within the storageset capacity. Only platform-admin and storage-admin may change tenant storageset quotas, for any tenant; a Tenant Admin gets 403.
Authentication: required (PasswordLogin or BearerJWT).
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
tenant_id |
path | string (uuid) | yes | Tenant identifier (UUID) |
storageset_id |
path | integer | yes | StorageSet identifier |
Request Body (application/json, required)
Schema: TenantStorageSetQuotaRequest
Request body for assigning or resizing a tenant storageset quota.
| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
quota_bytes |
integer | yes | n/a | 2199023255552, 5497558138880 |
Maximum storage quota in bytes that the tenant can consume within this storageset. |
Example
{
"quota_bytes" : 2199023255552
}Responses
| Status | Description | Schema |
|---|---|---|
| 200 | Storageset assigned to the tenant, or its quota updated. Returns the resulting allocation. | GetTenantStorageSetResponse |
| 400 | Bad request. The requested quota exceeds the storageset capacity. | - |
| 401 | Unauthorized. Authentication required. | - |
| 403 | Forbidden. Insufficient permissions. | - |
| 404 | Tenant or storageset not found. | - |
| 422 | Validation error. Returned for an invalid request body or path parameter. | - |
| 500 | Internal server error. | - |
Response Body (200, GetTenantStorageSetResponse)
| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
storageset |
StorageSetAssignment object |
yes | n/a | Storageset assignment (id + quota + name). |
Example
{
"storageset" : {
"quota_bytes" : 2199023255552,
"storageset_id" : 1,
"storageset_name" : "Set 1"
}
}DELETE /api/v1/tenants/{tenant_id}/storagesets/{storageset_id}
Unassign tenant storageset
Unassigns a storageset from a tenant. Callers may only modify their own tenant.
Authentication: required (PasswordLogin or BearerJWT).
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
tenant_id |
path | string (uuid) | yes | Tenant identifier (UUID) |
storageset_id |
path | integer | yes | StorageSet identifier |
Responses
| Status | Description | Schema |
|---|---|---|
| 204 | Storageset unassigned from the tenant successfully. No content is returned. | - |
| 401 | Unauthorized. Authentication required. | - |
| 403 | Forbidden. Insufficient permissions. | - |
| 404 | Tenant or storageset not found. | - |
| 422 | Validation Error | HTTPValidationError |
| 500 | Internal server error. | - |