Base URL https://app.mandala.computer/api/v1. Every request carries an API key, and failures take the shape under Errors.
GET/computers/{id}/activities
Read API activity historymember+
Selected requests sent through the computer API. This does not identify an agent or show all guest work. A hosted agent run (POST /computers/{id}/agent, POST /chat/completions or the dashboard agent) adds one summary row with channel managed-agent and action agent, carrying funded and cost_usd. It stays pending while the run lasts, for at most about two hours. Each input or bash command the run sends to the desktop is also recorded as its own row with channel managed-agent, so one run can add many rows; its screenshots are not recorded. Requests from an API key carry channel api, and the dashboard’s own carry session-api. History retains up to 7 days, 10000 rows per computer and 100000 per account, including pending rows. Each safe metadata row is at most 1024 bytes; pages contain at most 50 rows. Expired rows are excluded immediately; bounded periodic physical cleanup can lag with backlog or downtime. Pages are newest first with a fixed insertion watermark. Cursor tokens are opaque and bound to account, computer, current workspace and credential scope. Use changes=1 with changes_cursor to receive inserts and final updates, including old rows finishing. Follow next_cursor to drain a bounded change page. Send next_cursor back with the same changes value it came with: a history cursor without changes, a change cursor with changes=1. A history cursor that is invalid, expired, minted for another computer or credential scope, or a change cursor sent without changes=1, answers 400; it never restarts at the first page. In changes mode, any cursor that cannot continue (expired, invalid, or a history cursor) answers gap:true instead. A gap requires refreshing history; reads never replay an action. Member or owner access and fresh computer scope are required for every read. Reads work while stopped or suspended without waking the guest. Unavailable scope or storage answers 503 with incomplete:true, never an empty success. Capture is best effort. Unknown outcomes are never resolved by reading output; an accepted background request is not completion. No command, output, input text or arbitrary error prose is retained. Any other query parameter, or either one sent twice, is a 400.
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. SDK setup first GETs the computer. Python start/stop/suspend/restart also refresh it with another GET; TypeScript refreshes only if the action returns an acknowledgement.
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%/}"'/computers/vm-a1b2c3d4e5f6/activities' \
-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 = "vm-a1b2c3d4e5f6"
c = client.computers.get(id)
result = c.activities()
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 = "vm-a1b2c3d4e5f6";
const c = await client.computers.get(id);
const result = await c.activities();
console.log(JSON.stringify(result, null, 2));
Opaque next_cursor for older pages, or changes_cursor when changes=1. At most 2048 characters.
changes
string
Read the bounded change journal. Requires a cursor (a missing one is a 400). A cursor that cannot continue, expired, invalid or a history cursor, returns gap:true.
Response
Safe retained rows or change projections with retention and capture health. Responses are no-store.
Fresh scope or the activity store is unavailable. The body includes incomplete:true; this is not evidence of no activity.
GET/computers/{id}/activities/{activity}
Read one API activitymember+
One retained safe summary. Current computer access and the row’s recorded account/workspace scope are checked again. Missing, expired, deleted and outside-scope rows answer the same 404. No output or guest files are read. No query parameters are accepted.
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. SDK setup first GETs the computer. Python start/stop/suspend/restart also refresh it with another GET; TypeScript refreshes only if the action returns an acknowledgement.
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%/}"'/computers/vm-a1b2c3d4e5f6/activities/act_d4e5f6a7d4e5f6a7d4e5f6a7d4e5f6a7' \
-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 = "vm-a1b2c3d4e5f6"
activity = "act_d4e5f6a7d4e5f6a7d4e5f6a7d4e5f6a7"
c = client.computers.get(id)
result = c.activity(activity)
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 = "vm-a1b2c3d4e5f6";
const activity = "act_d4e5f6a7d4e5f6a7d4e5f6a7d4e5f6a7";
const c = await client.computers.get(id);
const result = await c.activity(activity);
console.log(JSON.stringify(result, null, 2));
An activity_id from GET /computers/{id}/activities (act_ followed by 32 lowercase hex characters).
Response
Safe activity metadata only; never raw execution content. Responses are no-store.
Response fields
activity_idalways
string
Immutable activity ID; a request identity, never an execution idempotency key.
account_idalways
string
computer_idalways
string
workspace_idalways
string or null
The actual workspace when the request was admitted.
channelalways
"api" | "session-api" | "managed-agent"
Who sent the request. api: an API key, or a connected app acting through one. session-api: the dashboard, signed in. managed-agent: a hosted agent run — its summary row (action agent), and a row for each input or bash command it sent to the desktop.
Known response status. A dispatched error does not prove the action had no effect.
exit_code
integer
Present only for a known synchronous command exit, including signed failures. Timeout sentinel values are omitted.
execution_id
string
Optional immutable execution ID returned by the original request; never inferred from PID. No output is fetched to populate history.
Failures
400
This detail route accepts no query parameters.
503
Fresh computer scope or the activity store could not be read; the response includes incomplete:true.
GET/computers/{id}/activities/{activity}/results
Read passive activity result linksmember+
Member-only metadata for at most eight newest retained versions with a ninth-row lookahead and more flag. No query/body. One current daemon scope proof covers sequential file-metadata verification; original authority, history revision and result generations are checked again before return. Historical activity visibility does not authorize a result from another recorded scope. No guest output/file/exec calls, wake, content reads or new capture. Replies are at most 8192 bytes; items at most 896 bytes. An existing activity without versions returns an empty list; missing history is 404; unavailable owner/history/current scope is 503. Results use exact execution identity, a trusted synchronous request token, or verified caller-selected artifact association; no PID/time/path inference. Artifacts do not imply creation or execution success. Late links advance activity revision without rewriting observed_at.
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. SDK setup first GETs the computer. Python start/stop/suspend/restart also refresh it with another GET; TypeScript refreshes only if the action returns an acknowledgement.
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%/}"'/computers/vm-a1b2c3d4e5f6/activities/act_d4e5f6a7d4e5f6a7d4e5f6a7d4e5f6a7/results' \
-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 = "vm-a1b2c3d4e5f6"
activity = "act_d4e5f6a7d4e5f6a7d4e5f6a7d4e5f6a7"
c = client.computers.get(id)
result = c.activity_results(activity)
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 = "vm-a1b2c3d4e5f6";
const activity = "act_d4e5f6a7d4e5f6a7d4e5f6a7d4e5f6a7";
const c = await client.computers.get(id);
const result = await c.activityResults(activity);
console.log(JSON.stringify(result, null, 2));
An activity_id from GET /computers/{id}/activities (act_ followed by 32 lowercase hex characters).
Response
Finite references and availability/truncation metadata only; no URLs, hashes, output or paths. No-store.
Response fields
activity_idalways
string
revisionalways
integer
morealways
boolean
itemsalways
object or object or object or object or object or object[]
Failures
429
Also result_capacity with Retry-After: 60 when 4 reads of retained results and artifacts are already in flight for this account, or 32 across the platform; a read is refused rather than queued.
503
The activity history, current scope or local backing storage cannot be verified, the history changed while it was being read, or the read ran out of time (activity_results_unavailable). It can be sent again. A linked result or artifact whose stored file or record fails its check is withdrawn: a 200 lists it with availability: "unavailable", and reading it directly answers 404. This 503 may come before or after such a withdrawal, so it says nothing about any one item: send the read again, or read the item directly.