Workspaces

The partitions of the account a key can be confined to, and the people who reach them.

Base URL https://app.mandala.computer/api/v1. Every request carries an API key, and failures take the shape under Errors.

GET/workspaces

List workspacesviewer+

The account’s workspaces, oldest first. A workspace partitions the account’s computers: an API key issued against one reaches that workspace’s computers only, and a computer’s workspace_id names the workspace it is in.

An API key confined to a workspace lists that one workspace and no other. An account-wide key sees them all, whatever its holder’s role.

A member or owner can move a computer with an account-wide credential using PUT /computers/{id}/workspace, or in the dashboard’s Settings → General → Workspace, to another workspace or to none. The computer must be stopped with no saved session, and have no snapshots, no secret bindings and no proxy credentials. Once it has moved, API keys confined to its old workspace get 404 for it and keys confined to the new one can reach it; its events, webhooks included, follow it to the new workspace; and its desktop and share credentials are replaced, so links from before stop working.

Set MANDALA_API_KEY to your bearer key. MANDALA_BASE_URL, when set, is the full API base including /api/v1. Replace the illustrative resource IDs with yours. These pinned 0.16.0 SDK and direct HTTP examples do not read saved device-login credentials.

curl

base_url="${MANDALA_BASE_URL:-}"
if [ -z "$base_url" ]; then base_url='https://app.mandala.computer/api/v1'; fi
curl --fail-with-body "${base_url%/}"'/workspaces' \
  -H "Authorization: Bearer $MANDALA_API_KEY"

Python — Mandala SDK 0.16.0

# Python 3.10+: pip install mandala-computer==0.16.0
import os
from mandala_computer import Client

with Client(api_key=os.environ["MANDALA_API_KEY"], base_url=os.environ.get("MANDALA_BASE_URL") or "https://app.mandala.computer/api/v1") as client:
    result = client.workspaces.list()
    print(result)

TypeScript — Mandala SDK 0.16.0

// Node.js 22+: npm install mandala-computer@0.16.0
import { Client } from "mandala-computer";

const apiKey = process.env.MANDALA_API_KEY;
if (!apiKey) throw new Error("Set MANDALA_API_KEY");
const client = new Client({apiKey, baseUrl: process.env.MANDALA_BASE_URL || "https://app.mandala.computer/api/v1"});
const result = await client.workspaces.list();
console.log(JSON.stringify(result, null, 2));

200 Illustrative successful response.

HTTP 200
Content-Type: application/json
X-Request-ID: 3f2c9a1e-7b4d-4e8a-9c61-0d5f2b8e4a17

[
  {
    "id": "wsp-a1b2c3d4e5f6",
    "name": "ci",
    "created_at": "2026-09-16T12:00:00.000Z"
  }
]

401 Platform bearer credential missing.

HTTP 401
Content-Type: application/json
X-Request-ID: 3f2c9a1e-7b4d-4e8a-9c61-0d5f2b8e4a17
WWW-Authenticate: Bearer

{
  "error": "Authentication required",
  "reason": "missing",
  "request_id": "3f2c9a1e-7b4d-4e8a-9c61-0d5f2b8e4a17"
}

401 Platform bearer credential invalid.

HTTP 401
Content-Type: application/json
X-Request-ID: 3f2c9a1e-7b4d-4e8a-9c61-0d5f2b8e4a17
WWW-Authenticate: Bearer error="invalid_token"

{
  "error": "invalid API key",
  "reason": "invalid",
  "request_id": "3f2c9a1e-7b4d-4e8a-9c61-0d5f2b8e4a17"
}

401 Platform bearer credential revoked.

HTTP 401
Content-Type: application/json
X-Request-ID: 3f2c9a1e-7b4d-4e8a-9c61-0d5f2b8e4a17
WWW-Authenticate: Bearer error="invalid_token"

{
  "error": "API key revoked",
  "reason": "revoked",
  "request_id": "3f2c9a1e-7b4d-4e8a-9c61-0d5f2b8e4a17"
}

405 Unsupported method on this public path; HEAD returns these headers without a body.

HTTP 405
Content-Type: application/json
X-Request-ID: 3f2c9a1e-7b4d-4e8a-9c61-0d5f2b8e4a17
Allow: GET, HEAD, POST, OPTIONS

{
  "error": "method not allowed",
  "request_id": "3f2c9a1e-7b4d-4e8a-9c61-0d5f2b8e4a17"
}

Response

The workspaces, oldest first.

Response fields

idalwaysstring

wsp- and twelve hex characters. What a computer’s workspace_id names.

namealwaysstring

Its name, unique within the account. Up to 40 characters.

created_atalwaysstring

RFC 3339 timestamp.

POST/workspaces

Create a workspaceowner+

Makes a workspace on the account. Owners only, with an account-wide API key: a key confined to a workspace is refused (403), because it could otherwise make itself somewhere new to stand.

The name is trimmed, must be 1 to 40 characters with no ASCII control characters (U+0000 to U+001F and U+007F), and must not be one the account already uses; an account holds at most 500 workspaces. Each of those is a 400 with a sentence, as is any field but name.

Set MANDALA_API_KEY to your bearer key. MANDALA_BASE_URL, when set, is the full API base including /api/v1. Replace the illustrative resource IDs with yours. These pinned 0.16.0 SDK and direct HTTP examples do not read saved device-login credentials.

curl

base_url="${MANDALA_BASE_URL:-}"
if [ -z "$base_url" ]; then base_url='https://app.mandala.computer/api/v1'; fi
curl --fail-with-body -X POST "${base_url%/}"'/workspaces' \
  -H "Authorization: Bearer $MANDALA_API_KEY" \
  -H "Content-Type: application/json" \
  --data-raw '{"name":"customer-acme"}'

Python — Mandala SDK 0.16.0

# Python 3.10+: pip install mandala-computer==0.16.0
import json, os
from mandala_computer import Client

with Client(api_key=os.environ["MANDALA_API_KEY"], base_url=os.environ.get("MANDALA_BASE_URL") or "https://app.mandala.computer/api/v1") as client:
    body = json.loads("{\"name\":\"customer-acme\"}")
    result = client.workspaces.create(body["name"])
    print(result)

TypeScript — Mandala SDK 0.16.0

// Node.js 22+: npm install mandala-computer@0.16.0
import { Client } from "mandala-computer";

const apiKey = process.env.MANDALA_API_KEY;
if (!apiKey) throw new Error("Set MANDALA_API_KEY");
const client = new Client({apiKey, baseUrl: process.env.MANDALA_BASE_URL || "https://app.mandala.computer/api/v1"});
const body = {"name":"customer-acme"};
const result = await client.workspaces.create({name: body.name});
console.log(JSON.stringify(result, null, 2));

201 Illustrative successful response.

HTTP 201
Content-Type: application/json
X-Request-ID: 3f2c9a1e-7b4d-4e8a-9c61-0d5f2b8e4a17

{
  "id": "wsp-a1b2c3d4e5f6",
  "name": "customer-acme",
  "created_at": "2026-09-16T12:00:00.000Z"
}

401 Platform bearer credential missing.

HTTP 401
Content-Type: application/json
X-Request-ID: 3f2c9a1e-7b4d-4e8a-9c61-0d5f2b8e4a17
WWW-Authenticate: Bearer

{
  "error": "Authentication required",
  "reason": "missing",
  "request_id": "3f2c9a1e-7b4d-4e8a-9c61-0d5f2b8e4a17"
}

401 Platform bearer credential invalid.

HTTP 401
Content-Type: application/json
X-Request-ID: 3f2c9a1e-7b4d-4e8a-9c61-0d5f2b8e4a17
WWW-Authenticate: Bearer error="invalid_token"

{
  "error": "invalid API key",
  "reason": "invalid",
  "request_id": "3f2c9a1e-7b4d-4e8a-9c61-0d5f2b8e4a17"
}

401 Platform bearer credential revoked.

HTTP 401
Content-Type: application/json
X-Request-ID: 3f2c9a1e-7b4d-4e8a-9c61-0d5f2b8e4a17
WWW-Authenticate: Bearer error="invalid_token"

{
  "error": "API key revoked",
  "reason": "revoked",
  "request_id": "3f2c9a1e-7b4d-4e8a-9c61-0d5f2b8e4a17"
}

405 Unsupported method on this public path; HEAD returns these headers without a body.

HTTP 405
Content-Type: application/json
X-Request-ID: 3f2c9a1e-7b4d-4e8a-9c61-0d5f2b8e4a17
Allow: GET, HEAD, POST, OPTIONS

{
  "error": "method not allowed",
  "request_id": "3f2c9a1e-7b4d-4e8a-9c61-0d5f2b8e4a17"
}

Request body

namerequiredstring

Its name: 1 to 40 characters after trimming, unique within the account.

Response

The new workspace.

Response fields

idalwaysstring

wsp- and twelve hex characters. What a computer’s workspace_id names.

namealwaysstring

Its name, unique within the account. Up to 40 characters.

created_atalwaysstring

RFC 3339 timestamp.

Failures

403

Also the answer, with no reason, to any API key confined to a workspace.

413

This route reads at most 4 KiB (4096 bytes) of request body, less than the general cap.

GET/workspaces/{id}

Get a workspaceviewer+

One workspace. An id this credential cannot see is a 404, the same as one that does not exist: another account’s, and — for an API key confined to a workspace — any workspace but its own.

A computer can carry the workspace_id of a workspace that has since been deleted, if the workspace was deleted while it still held computers, before that was refused; if a create or a snapshot clone into it was under way when it was deleted; or if it is a clone (POST /computers/{id}/clone) of such a computer, which keeps its source’s workspace_id. That id answers 404 here. One thing about such a computer is handled: its egress credentials resolve account-wide only, so an egress_proxy naming one of that workspace’s own secrets is held closed (egress_proxy_pending true) until it is saved again naming an account-wide secret. Such a computer can be moved to a live workspace, or to none, with PUT /computers/{id}/workspace or from the dashboard once its secret bindings and proxy credentials are removed and it meets the other conditions under GET /workspaces.

Set MANDALA_API_KEY to your bearer key. MANDALA_BASE_URL, when set, is the full API base including /api/v1. Replace the illustrative resource IDs with yours. These pinned 0.16.0 SDK and direct HTTP examples do not read saved device-login credentials.

curl

base_url="${MANDALA_BASE_URL:-}"
if [ -z "$base_url" ]; then base_url='https://app.mandala.computer/api/v1'; fi
curl --fail-with-body "${base_url%/}"'/workspaces/wsp-a1b2c3d4e5f6' \
  -H "Authorization: Bearer $MANDALA_API_KEY"

Python — Mandala SDK 0.16.0

# Python 3.10+: pip install mandala-computer==0.16.0
import os
from mandala_computer import Client

with Client(api_key=os.environ["MANDALA_API_KEY"], base_url=os.environ.get("MANDALA_BASE_URL") or "https://app.mandala.computer/api/v1") as client:
    id = "wsp-a1b2c3d4e5f6"
    result = client.workspaces.get(id)
    print(result)

TypeScript — Mandala SDK 0.16.0

// Node.js 22+: npm install mandala-computer@0.16.0
import { Client } from "mandala-computer";

const apiKey = process.env.MANDALA_API_KEY;
if (!apiKey) throw new Error("Set MANDALA_API_KEY");
const client = new Client({apiKey, baseUrl: process.env.MANDALA_BASE_URL || "https://app.mandala.computer/api/v1"});
const id = "wsp-a1b2c3d4e5f6";
const result = await client.workspaces.get(id);
console.log(JSON.stringify(result, null, 2));

200 Illustrative successful response.

HTTP 200
Content-Type: application/json
X-Request-ID: 3f2c9a1e-7b4d-4e8a-9c61-0d5f2b8e4a17

{
  "id": "wsp-a1b2c3d4e5f6",
  "name": "ci",
  "created_at": "2026-09-16T12:00:00.000Z"
}

401 Platform bearer credential missing.

HTTP 401
Content-Type: application/json
X-Request-ID: 3f2c9a1e-7b4d-4e8a-9c61-0d5f2b8e4a17
WWW-Authenticate: Bearer

{
  "error": "Authentication required",
  "reason": "missing",
  "request_id": "3f2c9a1e-7b4d-4e8a-9c61-0d5f2b8e4a17"
}

401 Platform bearer credential invalid.

HTTP 401
Content-Type: application/json
X-Request-ID: 3f2c9a1e-7b4d-4e8a-9c61-0d5f2b8e4a17
WWW-Authenticate: Bearer error="invalid_token"

{
  "error": "invalid API key",
  "reason": "invalid",
  "request_id": "3f2c9a1e-7b4d-4e8a-9c61-0d5f2b8e4a17"
}

401 Platform bearer credential revoked.

HTTP 401
Content-Type: application/json
X-Request-ID: 3f2c9a1e-7b4d-4e8a-9c61-0d5f2b8e4a17
WWW-Authenticate: Bearer error="invalid_token"

{
  "error": "API key revoked",
  "reason": "revoked",
  "request_id": "3f2c9a1e-7b4d-4e8a-9c61-0d5f2b8e4a17"
}

405 Unsupported method on this public path; HEAD returns these headers without a body.

HTTP 405
Content-Type: application/json
X-Request-ID: 3f2c9a1e-7b4d-4e8a-9c61-0d5f2b8e4a17
Allow: GET, HEAD, PATCH, DELETE, OPTIONS

{
  "error": "method not allowed",
  "request_id": "3f2c9a1e-7b4d-4e8a-9c61-0d5f2b8e4a17"
}

Path parameters

idrequiredstring

The workspace id.

Response

The workspace.

Response fields

idalwaysstring

wsp- and twelve hex characters. What a computer’s workspace_id names.

namealwaysstring

Its name, unique within the account. Up to 40 characters.

created_atalwaysstring

RFC 3339 timestamp.

PATCH/workspaces/{id}

Rename a workspaceowner+

Renames a workspace. Its id does not change, so the API keys confined to it and the computers in it are untouched. Owners only, with an account-wide API key: a key confined to a workspace is refused (403), its own workspace included.

The name follows the rules a create does, and a name the account already uses is a 400, as is any field but name. An id this credential cannot see is a 404, the same as one that does not exist.

Set MANDALA_API_KEY to your bearer key. MANDALA_BASE_URL, when set, is the full API base including /api/v1. Replace the illustrative resource IDs with yours. These pinned 0.16.0 SDK and direct HTTP examples do not read saved device-login credentials.

curl

base_url="${MANDALA_BASE_URL:-}"
if [ -z "$base_url" ]; then base_url='https://app.mandala.computer/api/v1'; fi
curl --fail-with-body -X PATCH "${base_url%/}"'/workspaces/wsp-a1b2c3d4e5f6' \
  -H "Authorization: Bearer $MANDALA_API_KEY" \
  -H "Content-Type: application/json" \
  --data-raw '{"name":"customer-acme-prod"}'

Python — Mandala SDK 0.16.0

# Python 3.10+: pip install mandala-computer==0.16.0
import json, os
from mandala_computer import Client

with Client(api_key=os.environ["MANDALA_API_KEY"], base_url=os.environ.get("MANDALA_BASE_URL") or "https://app.mandala.computer/api/v1") as client:
    id = "wsp-a1b2c3d4e5f6"
    body = json.loads("{\"name\":\"customer-acme-prod\"}")
    result = client.workspaces.rename(id, body["name"])
    print(result)

TypeScript — Mandala SDK 0.16.0

// Node.js 22+: npm install mandala-computer@0.16.0
import { Client } from "mandala-computer";

const apiKey = process.env.MANDALA_API_KEY;
if (!apiKey) throw new Error("Set MANDALA_API_KEY");
const client = new Client({apiKey, baseUrl: process.env.MANDALA_BASE_URL || "https://app.mandala.computer/api/v1"});
const id = "wsp-a1b2c3d4e5f6";
const body = {"name":"customer-acme-prod"};
const result = await client.workspaces.rename(id, body.name);
console.log(JSON.stringify(result, null, 2));

200 Illustrative successful response.

HTTP 200
Content-Type: application/json
X-Request-ID: 3f2c9a1e-7b4d-4e8a-9c61-0d5f2b8e4a17

{
  "id": "wsp-a1b2c3d4e5f6",
  "name": "customer-acme-prod",
  "created_at": "2026-09-16T12:00:00.000Z"
}

401 Platform bearer credential missing.

HTTP 401
Content-Type: application/json
X-Request-ID: 3f2c9a1e-7b4d-4e8a-9c61-0d5f2b8e4a17
WWW-Authenticate: Bearer

{
  "error": "Authentication required",
  "reason": "missing",
  "request_id": "3f2c9a1e-7b4d-4e8a-9c61-0d5f2b8e4a17"
}

401 Platform bearer credential invalid.

HTTP 401
Content-Type: application/json
X-Request-ID: 3f2c9a1e-7b4d-4e8a-9c61-0d5f2b8e4a17
WWW-Authenticate: Bearer error="invalid_token"

{
  "error": "invalid API key",
  "reason": "invalid",
  "request_id": "3f2c9a1e-7b4d-4e8a-9c61-0d5f2b8e4a17"
}

401 Platform bearer credential revoked.

HTTP 401
Content-Type: application/json
X-Request-ID: 3f2c9a1e-7b4d-4e8a-9c61-0d5f2b8e4a17
WWW-Authenticate: Bearer error="invalid_token"

{
  "error": "API key revoked",
  "reason": "revoked",
  "request_id": "3f2c9a1e-7b4d-4e8a-9c61-0d5f2b8e4a17"
}

405 Unsupported method on this public path; HEAD returns these headers without a body.

HTTP 405
Content-Type: application/json
X-Request-ID: 3f2c9a1e-7b4d-4e8a-9c61-0d5f2b8e4a17
Allow: GET, HEAD, PATCH, DELETE, OPTIONS

{
  "error": "method not allowed",
  "request_id": "3f2c9a1e-7b4d-4e8a-9c61-0d5f2b8e4a17"
}

Path parameters

idrequiredstring

The workspace id.

Request body

namerequiredstring

The new name: 1 to 40 characters after trimming, unique within the account.

Response

The workspace, renamed.

Response fields

idalwaysstring

wsp- and twelve hex characters. What a computer’s workspace_id names.

namealwaysstring

Its name, unique within the account. Up to 40 characters.

created_atalwaysstring

RFC 3339 timestamp.

Failures

403

Also the answer, with no reason, to any API key confined to a workspace.

413

This route reads at most 4 KiB (4096 bytes) of request body, less than the general cap.

DELETE/workspaces/{id}

Delete a workspaceowner+

Deletes an empty workspace, and in the same step REVOKES every API key confined to it, whoever holds them: a key has nothing left to reach once its workspace is gone, and it is never widened to the account instead. The answer says how many were revoked. Owners only, with an account-wide API key: a key confined to a workspace is refused (403), its own workspace included.

Webhook subscriptions confined to the workspace (those created with a key scoped to it) are deleted with it in the same step, with every delivery record they hold, pending ones included, as DELETE /webhooks/{id} does; the answer does not count them. Account-wide subscriptions are not affected.

A workspace that still holds computers cannot be deleted: the answer is 409 with reason: "unsupported", saying how many, and nothing is deleted or revoked. The count includes a computer whose unfinished workspace change is moving it into this workspace. Move those computers with PUT /computers/{id}/workspace or in the dashboard, or delete them, first. When the platform cannot confirm the workspace is empty because part of it did not answer, the answer is 503, also with nothing deleted; try again shortly.

A workspace holding active secrets also answers 409 with reason: "unsupported", saying how many. Delete those secrets explicitly with DELETE /secrets/{id} first, using their workspace_id and current revision_id, then retry. The refusal preserves the workspace, its keys and all secret data. Deleted secrets do not block workspace deletion. Deleting a secret frees an active slot but does not reduce the lifetime count of 1000 created secrets.

This safeguard prevents new orphan secrets; it does not repair secrets orphaned by older workspace deletions. Those remain unreachable: listing or creating with a deleted workspace_id is a 400, and reading, replacing or deleting one of its secrets is a 404. They still count toward the 100 active secrets per account.

Snapshots taken in it are kept. An account-wide key or the dashboard can still clone them: a clone built from the disk (a disk snapshot, or memory: false) lands in no workspace, and one that would resume a memory snapshot’s session is refused with 409 (see POST /snapshots/{id}/clone).

Set MANDALA_API_KEY to your bearer key. MANDALA_BASE_URL, when set, is the full API base including /api/v1. Replace the illustrative resource IDs with yours. These pinned 0.16.0 SDK and direct HTTP examples do not read saved device-login credentials.

curl

base_url="${MANDALA_BASE_URL:-}"
if [ -z "$base_url" ]; then base_url='https://app.mandala.computer/api/v1'; fi
curl --fail-with-body -X DELETE "${base_url%/}"'/workspaces/wsp-a1b2c3d4e5f6' \
  -H "Authorization: Bearer $MANDALA_API_KEY"

Python — Mandala SDK 0.16.0

# Python 3.10+: pip install mandala-computer==0.16.0
import os
from mandala_computer import Client

with Client(api_key=os.environ["MANDALA_API_KEY"], base_url=os.environ.get("MANDALA_BASE_URL") or "https://app.mandala.computer/api/v1") as client:
    id = "wsp-a1b2c3d4e5f6"
    result = client.workspaces.delete(id)
    print(result)

TypeScript — Mandala SDK 0.16.0

// Node.js 22+: npm install mandala-computer@0.16.0
import { Client } from "mandala-computer";

const apiKey = process.env.MANDALA_API_KEY;
if (!apiKey) throw new Error("Set MANDALA_API_KEY");
const client = new Client({apiKey, baseUrl: process.env.MANDALA_BASE_URL || "https://app.mandala.computer/api/v1"});
const id = "wsp-a1b2c3d4e5f6";
const result = await client.workspaces.delete(id);
console.log(JSON.stringify(result, null, 2));

200 Illustrative successful response.

HTTP 200
Content-Type: application/json
X-Request-ID: 3f2c9a1e-7b4d-4e8a-9c61-0d5f2b8e4a17

{
  "ok": true,
  "revoked_keys": 2
}

401 Platform bearer credential missing.

HTTP 401
Content-Type: application/json
X-Request-ID: 3f2c9a1e-7b4d-4e8a-9c61-0d5f2b8e4a17
WWW-Authenticate: Bearer

{
  "error": "Authentication required",
  "reason": "missing",
  "request_id": "3f2c9a1e-7b4d-4e8a-9c61-0d5f2b8e4a17"
}

401 Platform bearer credential invalid.

HTTP 401
Content-Type: application/json
X-Request-ID: 3f2c9a1e-7b4d-4e8a-9c61-0d5f2b8e4a17
WWW-Authenticate: Bearer error="invalid_token"

{
  "error": "invalid API key",
  "reason": "invalid",
  "request_id": "3f2c9a1e-7b4d-4e8a-9c61-0d5f2b8e4a17"
}

401 Platform bearer credential revoked.

HTTP 401
Content-Type: application/json
X-Request-ID: 3f2c9a1e-7b4d-4e8a-9c61-0d5f2b8e4a17
WWW-Authenticate: Bearer error="invalid_token"

{
  "error": "API key revoked",
  "reason": "revoked",
  "request_id": "3f2c9a1e-7b4d-4e8a-9c61-0d5f2b8e4a17"
}

405 Unsupported method on this public path; HEAD returns these headers without a body.

HTTP 405
Content-Type: application/json
X-Request-ID: 3f2c9a1e-7b4d-4e8a-9c61-0d5f2b8e4a17
Allow: GET, HEAD, PATCH, DELETE, OPTIONS

{
  "error": "method not allowed",
  "request_id": "3f2c9a1e-7b4d-4e8a-9c61-0d5f2b8e4a17"
}

409 Move or delete its computers first; nothing was changed.

HTTP 409
Content-Type: application/json
X-Request-ID: 3f2c9a1e-7b4d-4e8a-9c61-0d5f2b8e4a17

{
  "error": "This workspace still holds 2 computers. Move or delete them first: a workspace that holds computers cannot be deleted.",
  "reason": "unsupported",
  "request_id": "3f2c9a1e-7b4d-4e8a-9c61-0d5f2b8e4a17"
}

409 Delete the active secrets first; the workspace, keys and secret data are preserved.

HTTP 409
Content-Type: application/json
X-Request-ID: 3f2c9a1e-7b4d-4e8a-9c61-0d5f2b8e4a17

{
  "error": "This workspace still holds 1 secret. Delete it first: a workspace that holds secrets cannot be deleted.",
  "reason": "unsupported",
  "request_id": "3f2c9a1e-7b4d-4e8a-9c61-0d5f2b8e4a17"
}

Path parameters

idrequiredstring

The workspace id.

Response

Deleted, with the count of keys it revoked.

Response fields

okalwaysboolean

Always true. A failure is a status code, not ok: false.

revoked_keysalwaysinteger

How many API keys were confined to the workspace and were revoked with it, whoever held them.

Failures

403

Also the answer, with no reason, to any API key confined to a workspace.

409

The workspace still holds computers or active secrets, with reason: "unsupported" and a count. Computers include any that an unfinished workspace change is moving into it. Nothing was deleted: no key was revoked and no webhook subscription removed. Move or delete its computers and explicitly delete its secrets before retrying.

503

Whether the workspace holds computers could not be confirmed. Nothing was deleted.

GET/workspaces/{id}/members

List a workspace’s membersviewer+

The account’s accepted members, oldest member first. Workspaces do not divide people: members share every workspace at their account role, subject to seat and sign-in suspension. Outstanding invitations not yet accepted are left out. A member waiting for a seat, because the plan has fewer seats than the account has people, is listed with seat_suspended: true and cannot act on the account until their seat is restored. suspended reports global sign-in suspension independently; either or both flags can be true.

An API key confined to a workspace cannot list members (403), because the list is the whole account’s. Use an account-wide key.

Set MANDALA_API_KEY to your bearer key. MANDALA_BASE_URL, when set, is the full API base including /api/v1. Replace the illustrative resource IDs with yours. These pinned 0.16.0 SDK and direct HTTP examples do not read saved device-login credentials.

curl

base_url="${MANDALA_BASE_URL:-}"
if [ -z "$base_url" ]; then base_url='https://app.mandala.computer/api/v1'; fi
curl --fail-with-body "${base_url%/}"'/workspaces/wsp-a1b2c3d4e5f6/members' \
  -H "Authorization: Bearer $MANDALA_API_KEY"

Python — Mandala SDK 0.16.0

# Python 3.10+: pip install mandala-computer==0.16.0
import os
from mandala_computer import Client

with Client(api_key=os.environ["MANDALA_API_KEY"], base_url=os.environ.get("MANDALA_BASE_URL") or "https://app.mandala.computer/api/v1") as client:
    id = "wsp-a1b2c3d4e5f6"
    result = client.workspaces.members(id)
    print(result)

TypeScript — Mandala SDK 0.16.0

// Node.js 22+: npm install mandala-computer@0.16.0
import { Client } from "mandala-computer";

const apiKey = process.env.MANDALA_API_KEY;
if (!apiKey) throw new Error("Set MANDALA_API_KEY");
const client = new Client({apiKey, baseUrl: process.env.MANDALA_BASE_URL || "https://app.mandala.computer/api/v1"});
const id = "wsp-a1b2c3d4e5f6";
const result = await client.workspaces.members(id);
console.log(JSON.stringify(result, null, 2));

200 Illustrative successful response.

HTTP 200
Content-Type: application/json
X-Request-ID: 3f2c9a1e-7b4d-4e8a-9c61-0d5f2b8e4a17

[
  {
    "user_id": "usr-0123456789abcdef",
    "email": "dana@example.com",
    "name": "Dana Whitfield",
    "role": "owner",
    "accepted_at": "2026-08-01T09:00:00.000Z",
    "suspended": false,
    "seat_suspended": false
  },
  {
    "user_id": "usr-89abcdef01234567",
    "email": "sam@example.com",
    "name": null,
    "role": "viewer",
    "accepted_at": "2026-09-02T15:30:00.000Z",
    "suspended": false,
    "seat_suspended": true
  }
]

401 Platform bearer credential missing.

HTTP 401
Content-Type: application/json
X-Request-ID: 3f2c9a1e-7b4d-4e8a-9c61-0d5f2b8e4a17
WWW-Authenticate: Bearer

{
  "error": "Authentication required",
  "reason": "missing",
  "request_id": "3f2c9a1e-7b4d-4e8a-9c61-0d5f2b8e4a17"
}

401 Platform bearer credential invalid.

HTTP 401
Content-Type: application/json
X-Request-ID: 3f2c9a1e-7b4d-4e8a-9c61-0d5f2b8e4a17
WWW-Authenticate: Bearer error="invalid_token"

{
  "error": "invalid API key",
  "reason": "invalid",
  "request_id": "3f2c9a1e-7b4d-4e8a-9c61-0d5f2b8e4a17"
}

401 Platform bearer credential revoked.

HTTP 401
Content-Type: application/json
X-Request-ID: 3f2c9a1e-7b4d-4e8a-9c61-0d5f2b8e4a17
WWW-Authenticate: Bearer error="invalid_token"

{
  "error": "API key revoked",
  "reason": "revoked",
  "request_id": "3f2c9a1e-7b4d-4e8a-9c61-0d5f2b8e4a17"
}

405 Unsupported method on this public path; HEAD returns these headers without a body.

HTTP 405
Content-Type: application/json
X-Request-ID: 3f2c9a1e-7b4d-4e8a-9c61-0d5f2b8e4a17
Allow: GET, HEAD, OPTIONS

{
  "error": "method not allowed",
  "request_id": "3f2c9a1e-7b4d-4e8a-9c61-0d5f2b8e4a17"
}

Path parameters

idrequiredstring

The workspace id.

Response

The members, oldest first.

Response fields

user_idalwaysstring

usr- and sixteen hex characters.

emailalwaysstring

The address they sign in with.

namealwaysstring or null

Their display name, or null when they have not set one.

rolealwaysstring

owner, member or viewer — their role on the account, which is their role in every workspace.

accepted_atalwaysstring

RFC 3339 timestamp of when they joined the account.

suspendedalwaysboolean

true for global sign-in suspension. They are still on the account. This is independent of seat_suspended; either or both flags can be true.

seat_suspendedalwaysboolean

true when this member is waiting for a seat on this account and cannot act on it. This is independent of sign-in suspension (suspended) and returns to false when their seat is restored.

Failures

403

Also the answer to any API key confined to a workspace, with no reason: the members are the whole account’s.