Docs/Control
API reference

Control

/v1/machines/{machineId}/control — open, run code on, interrupt and close direct control of a machine.

View as Markdown

The endpoints behind direct control (research preview). Control is a lease on one machine, so it lives under the machine:

https://api.simular.ai/v1/machines/{machineId}/control

machineId is one of your cloud computers from GET /v1/agents/machines. You hold at most one lease per machine, so there is no separate id: the machine is the handle.

Every call carries Authorization: Bearer sapi_..., as everywhere else. See Authentication.

Open

POST /v1/machines/{machineId}/control

No body.

json
{ "machineId": "m_7f2a", "expiresAfterIdleSec": 600 }

Answers 201 once the lease holds the computer. If a Sai task holds it, or the computer has to start, that can take up to three minutes. If you already hold an open lease on the machine, it answers 200 at once with the same body.

A lease whose computer stopped answering (for example, the service behind it was restarted abruptly) still looks open to this call. The first exec that gets no answer closes it with 410 control_closed and reason unresponsive, and the next open starts a fresh lease.

Exec

POST /v1/machines/{machineId}/control/exec

json
{
  "code": "var r = await machine.exec({ command: 'hostname' }); r.stdout.trim()",
  "timeoutMs": 60000
}
FieldRequiredNotes
codeyesSimulang (JavaScript). Up to 200,000 characters.
timeoutMsnoHow long the block may run. Default 120,000, min 1,000, max 300,000.

Any other field, or a value out of range, answers 400 invalid_body.

json
{
  "result": "WIN-CLOUD-7F2A",
  "logs": [],
  "observations": {},
  "variables": [
    { "name": "r", "type": "object", "preview": "{ stdout: 'WIN-CLOUD-7F2A\\r\\n', … }" }
  ],
  "durationMs": 412
}
FieldNotes
resultThe block's last expression as JSON. A snapshot comes back as its outline text. Over 256 KB, it is truncated to a string.
logsConsole output, oldest first. Up to 200 lines of 2,000 characters.
errorPresent when the block did not finish: { kind, message }.
observations.screenshot{ base64, mimeType }. Present only when this block captured the screen. If it was too large to return, observations.screenshotOmitted is true instead.
variables[{ name, type, preview }] for every variable the REPL holds.
durationMsHow long the block ran.

A block that throws is still a 200. Read error.kind:

kindMeaning
exceptionThe code threw. Variables set before the throw are kept.
timeoutThe block ran past timeoutMs and was stopped, or it waited past timeoutMs behind an earlier block and did not run.
interruptedYou called interrupt.

A stopped block normally keeps the REPL's variables. A block that does not stop within a few seconds of a timeout or interrupt is ended by restarting the REPL, and the earlier variables are gone.

Blocks run one at a time, in the order they arrive. A second exec sent while one is running waits for it, and that wait counts toward its own timeoutMs.

If the lease has ended, exec answers 410 control_closed with a reason:

reasonMeaning
closedYou closed it.
idle_timeoutIt sat idle and released the computer.
lock_lostA Sai task took the computer while the lease was quiet.
unresponsiveThe computer did not answer within the block's timeoutMs plus 30 seconds, so the lease was closed.
shutdownThe service behind the lease restarted.
json
{
  "error": "control_closed",
  "message": "The computer stopped answering, so this control lease was closed. Open a new one.",
  "reason": "unresponsive"
}

Open control again for a fresh lease. With no lease on the machine at all, exec answers 404 control_not_open.

Interrupt

POST /v1/machines/{machineId}/control/interrupt

Stops the running block at its next step or loop iteration. Its exec answers with error.kind: "interrupted".

json
{ "interrupted": true }

false means nothing was running. Answers 202.

Get

GET /v1/machines/{machineId}/control

Your latest lease on the machine, open or closed.

json
{
  "machineId": "m_7f2a",
  "status": "open",
  "createdAt": 1791273600000,
  "lastExecAt": 1791273712000,
  "expiresAfterIdleSec": 600
}

A closed lease has status: "closed" and a closedReason, one of the exec reason values above. createdAt and lastExecAt are milliseconds since the epoch; lastExecAt is null before the first block. A lease that failed to open never replaces the one shown here. 404 control_not_open if you never opened control on the machine.

Close

DELETE /v1/machines/{machineId}/control

Clears the REPL and releases the computer. Answers 204, also when no lease is open. Always close when you are done: until then, Sai's tasks on that computer wait.

Errors

Error bodies are { "error": "<code>", "message": "..." }.

StatuserrorWhen
400invalid_machineThe machineId in the path is malformed.
400machine_not_supportedThe machine is your own desktop. Control runs on cloud computers.
400invalid_bodyExec: a missing or unknown field, or a value out of range. details lists the problems.
403forbiddenOpen: the computer refused this account.
403phone_verification_required, phone_allowance_in_useFree plan: verify a phone number in the Sai app first.
404machine_not_foundNot yours, or does not exist.
404control_not_openNo lease on this machine. Open one first.
409machine_busyA Sai task or another lease holds the computer. Honor Retry-After when set.
409not_free_tierOpen on the free computer, but the account is no longer on the free plan.
410control_closedThe lease has ended; reason says why (see Exec). Open control again.
429budget_exhaustedFree plan: today's computer time is used up. Resets at midnight UTC.
429rate_limitedMore than 30 leases opened, or 3,000 blocks run, in an hour.
502dispatcher_unavailableOpen: Sai could not reach the computer. Retry shortly.
503machine_unavailable, no_capacityOpen: the computer did not come online in time. Honor Retry-After.

Gotchas

  • var, not let or const, for anything you want in the next block.
  • Screenshots are base64 in the JSON. Decode before saving; do not log them.
  • The free plan's computer is deleted when the lease closes: copy out anything you need first.
  • Every exec counts as activity. A lease you stop using releases the computer after 10 minutes, or 2 when a Sai task is waiting for it.