Docs/Tenants
Organisations

Tenants

Create, list, update, disable and delete Tenants under /v1/tenants with an Organisation Key.

A Tenant is one of your customers. It has its own machines, files and sessions, kept apart from every other Tenant's. Your backend manages Tenants with an Organisation Key. The Organisation comes from the key, never from the request. Any other credential gets 403:

403 Forbidden · organisation_key_requiredJSON
{ "error": "organisation_key_required", "message": "Tenant routes take an Organisation Key." }

A Tenant token gets 403 not_available_for_tenant instead. A Tenant of another Organisation is 404 not_found. The console's Tenants page in the portal does the same things for a signed-in owner or admin.

The Tenant object

A TenantJSON
{
  "tenantId": "Xk2p9QzR4tLm8vNa3bCd",
  "name": "Riverside Clinic",
  "externalId": "clinic-4821",
  "metadata": { "plan": "pro" },
  "status": "active",
  "createdAt": 1790000000000,
  "disabledAt": null,
  "machineCount": 2
}
FieldNotes
tenantIdSai's id for the Tenant. Pass it in the paths below and to the token exchange.
name1 to 100 characters.
externalIdYour id for the customer, or null. 1 to 128 characters of A-Z a-z 0-9 . _ : @ -. Unique in the Organisation. Set on create only.
metadataYour own string pairs. Up to 20 keys of 1 to 40 characters, values up to 500 characters.
statusactive or disabled.
createdAtUnix time in milliseconds.
disabledAtUnix time in milliseconds when it was disabled, or null.
machineCountEnrolled machines.

Create a Tenant

Create a TenantcURL
curl -s "$SAI_API_URL/v1/tenants" \
  -H "Authorization: Bearer $SAI_ORG_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Riverside Clinic",
    "externalId": "clinic-4821",
    "metadata": { "plan": "pro" }
  }'
FieldTypeRequiredNotes
namestringyes1 to 100 characters.
externalIdstringnoYour id for the customer. Cannot be changed later.
metadataobjectnoString pairs. See The Tenant object.
201 Created · TenantJSON
{
  "tenantId": "Xk2p9QzR4tLm8vNa3bCd",
  "name": "Riverside Clinic",
  "externalId": "clinic-4821",
  "metadata": { "plan": "pro" },
  "status": "active",
  "createdAt": 1790000000000,
  "disabledAt": null,
  "machineCount": 0
}
StatuserrorWhen
400invalid_bodyA field is missing or out of range. details says which.
409external_id_takenAnother Tenant in the Organisation has this externalId.
422tenant_limit_reachedThe Organisation has 100 Tenants and has never added credit.
429rate_limitedMore than 1,000 Tenants created in an hour.

List Tenants

List active TenantscURL
curl -s "$SAI_API_URL/v1/tenants?status=active&limit=50" \
  -H "Authorization: Bearer $SAI_ORG_KEY"
QueryNotes
statusactive or disabled. Omit for both.
limit1 to 100. Default 50.
cursorThe nextCursor of the previous page.
externalIdFind the Tenant with this externalId. Cannot be combined with status.
200 OK · TenantsJSON
{
  "tenants": [{ "tenantId": "Xk2p9QzR4tLm8vNa3bCd", "name": "Riverside Clinic", "...": "..." }],
  "nextCursor": null
}

Newest first. nextCursor is null on the last page. With externalId, the list has zero or one Tenant, so you can map your own customer to a Tenant without storing tenantId:

Find a Tenant by your own idcURL
curl -s "$SAI_API_URL/v1/tenants?externalId=clinic-4821" \
  -H "Authorization: Bearer $SAI_ORG_KEY"
StatuserrorWhen
400invalid_requestA bad query value, or externalId combined with status.

Get a Tenant

Get a TenantcURL
curl -s "$SAI_API_URL/v1/tenants/$TENANT_ID" \
  -H "Authorization: Bearer $SAI_ORG_KEY"

200 with the Tenant, or 404 not_found.

Update a Tenant

Rename a Tenant and replace its metadatacURL
curl -s -X PATCH "$SAI_API_URL/v1/tenants/$TENANT_ID" \
  -H "Authorization: Bearer $SAI_ORG_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Riverside Clinic East",
    "metadata": { "plan": "enterprise" }
  }'
FieldTypeNotes
namestring1 to 100 characters.
metadataobjectReplaces the whole map.
statusstringactive or disabled. See Disable a Tenant.

Send at least one field. 200 with the updated Tenant.

StatuserrorWhen
400invalid_bodyThe body is empty or has a bad value.
404not_foundNo such Tenant.

Disable a Tenant

Disable a TenantcURL
curl -s -X PATCH "$SAI_API_URL/v1/tenants/$TENANT_ID" \
  -H "Authorization: Bearer $SAI_ORG_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "status": "disabled" }'

200 with the Tenant, status now disabled. A disabled Tenant cannot run anything:

  • Its Tenant tokens are refused, and the exchange returns 403 tenant_disabled.
  • Its machines stay enrolled but cannot connect.
  • No enrollment code can be created for it.

{ "status": "active" } brings it back with its machines and data.

Delete a Tenant

Delete a TenantcURL
curl -s -X DELETE "$SAI_API_URL/v1/tenants/$TENANT_ID" \
  -H "Authorization: Bearer $SAI_ORG_KEY"

204 No Content. The Tenant is disabled and every machine is revoked. The machines must be enrolled again if the Tenant is enabled later.

To erase a Tenant's data, delete it from the console's Tenants page. Erasure removes the Tenant's machines, sessions, files, memory and search history, and frees its externalId. It cannot be undone. The Tenant must be disabled for an hour first.

StatuserrorWhen
404not_foundNo such Tenant.