# REST API

Every operation of the Silan local API, grouped the way the OpenAPI document groups them.

Control iPhones, Android phones, simulators and emulators attached to a Mac running Silan.

Draft contract for the Silan engine's REST API. The MCP server is generated from the same operations
(see `x-silan-mcp` on each operation), so REST and MCP never drift apart.

Conventions:
- Local base URL is `http://127.0.0.1:47800` (port is configurable; the engine writes the real port to
  `~/Library/Application Support/Silan/engine.json`). Only the loopback interface is bound.
- Every request needs `Authorization: Bearer silan_sk_...` (a local key) unless it arrives over the relay,
  where an OAuth 2.1 access token is used instead.
- Requests with an `Origin` header are refused unless the origin is on the engine's allow list
  (protects against DNS rebinding and drive-by browser pages).
- Coordinates default to native screenshot pixels, origin top-left, current orientation.
- Errors use RFC 9457 `application/problem+json` with a stable `code`; a failed action never returns 200.
- Any mutating device call accepts `Prefer: respond-async` and then returns 202 with a job.
- Devices under another holder's lease return 409 `DEVICE_LEASED`; pass the lease token in `Silan-Lease`.

The document has 64 operations in 17 groups.

| Group | Operations | What it covers |
|---|---|---|
| [System](/reference/rest/system/) | 2 | Engine health and versions. |
| [Devices](/reference/rest/devices/) | 6 | Attached devices, their details and apps. |
| [Observation](/reference/rest/observation/) | 6 | Screens, element trees and live views. |
| [Actions](/reference/rest/actions/) | 2 | Taps, typing, gestures, buttons and app control. |
| [Waits](/reference/rest/waits/) | 2 | Engine-side waits and one-off checks. |
| [Leases](/reference/rest/leases/) | 3 | Claims so parallel agents never share a device. |
| [Jobs](/reference/rest/jobs/) | 2 | Long-running operations. |
| [Device extras](/reference/rest/device-extras/) | 6 | Clipboard, location, notifications and test pushes. |
| [Credentials](/reference/rest/credentials/) | 3 | Person-confirms login hand-off and the password-manager bridge. |
| [Scenarios](/reference/rest/scenarios/) | 1 | Multi-device scenario validation. |
| [Runs](/reference/rest/runs/) | 6 | Scenario and routine runs, reports and artifacts. |
| [Recordings](/reference/rest/recordings/) | 3 | Synced multi-device recordings. |
| [Agent](/reference/rest/agent/) | 6 | The built-in agent. |
| [Routines](/reference/rest/routines/) | 3 | Saved and scheduled routines. |
| [Safety](/reference/rest/safety/) | 8 | Approvals, stop-everything, policy and audit. |
| [Admin](/reference/rest/admin/) | 4 | Keys and usage. |
| [Events](/reference/rest/events/) | 1 | Server-sent event stream. |

## All operations

| Operation | Method and path | Summary |
|---|---|---|
| [`health`](/reference/rest/system/#health) | `GET /v1/health` | Engine liveness and component status |
| [`version`](/reference/rest/system/#version) | `GET /v1/version` | Engine, API and MCP protocol versions |
| [`listDevices`](/reference/rest/devices/#listdevices) | `GET /v1/devices` | List devices this key may see |
| [`getDevice`](/reference/rest/devices/#getdevice) | `GET /v1/devices/{deviceId}` | One device with capabilities and health |
| [`updateDevice`](/reference/rest/devices/#updatedevice) | `PATCH /v1/devices/{deviceId}` | Rename or relabel a device |
| [`listApps`](/reference/rest/devices/#listapps) | `GET /v1/devices/{deviceId}/apps` | Installed apps |
| [`installApp`](/reference/rest/devices/#installapp) | `POST /v1/devices/{deviceId}/apps` | Install an app build (.ipa, .app, or .apk) from a path on this Mac |
| [`uninstallApp`](/reference/rest/devices/#uninstallapp) | `DELETE /v1/devices/{deviceId}/apps/{appId}` | Uninstall an app |
| [`observe`](/reference/rest/observation/#observe) | `GET /v1/devices/{deviceId}/observe` | The agent's look at the screen - screenshot reference plus a compact element list |
| [`screenshot`](/reference/rest/observation/#screenshot) | `GET /v1/devices/{deviceId}/screenshot` | Current screen as an image |
| [`getScreenshot`](/reference/rest/observation/#getscreenshot) | `GET /v1/screenshots/{screenshotId}` | Fetch a recent screenshot by id (kept in memory for 10 minutes unless part of a run) |
| [`getElements`](/reference/rest/observation/#getelements) | `GET /v1/devices/{deviceId}/elements` | Full element tree |
| [`findElements`](/reference/rest/observation/#findelements) | `POST /v1/devices/{deviceId}/elements/find` | Find elements matching a query |
| [`streamMjpeg`](/reference/rest/observation/#streammjpeg) | `GET /v1/devices/{deviceId}/stream.mjpeg` | Simple live view for local tools (loopback only; the remote view uses WebRTC) |
| [`act`](/reference/rest/actions/#act) | `POST /v1/devices/{deviceId}/actions` | Perform one action (tap, type, swipe, press a button, launch an app...) |
| [`actBatch`](/reference/rest/actions/#actbatch) | `POST /v1/devices/{deviceId}/actions/batch` | Run several actions in order; stops at the first failure |
| [`waitFor`](/reference/rest/waits/#waitfor) | `POST /v1/devices/{deviceId}/waits` | Wait inside the engine until a condition holds on this device |
| [`check`](/reference/rest/waits/#check) | `POST /v1/devices/{deviceId}/checks` | Evaluate a condition once and report pass or fail with evidence |
| [`createLease`](/reference/rest/leases/#createlease) | `POST /v1/leases` | Claim devices so no other agent drives them |
| [`renewLease`](/reference/rest/leases/#renewlease) | `POST /v1/leases/{leaseId}` | Extend a lease |
| [`releaseLease`](/reference/rest/leases/#releaselease) | `DELETE /v1/leases/{leaseId}` | Release devices |
| [`getJob`](/reference/rest/jobs/#getjob) | `GET /v1/jobs/{jobId}` | Job status and result |
| [`cancelJob`](/reference/rest/jobs/#canceljob) | `DELETE /v1/jobs/{jobId}` | Cancel a job |
| [`getClipboard`](/reference/rest/device-extras/#getclipboard) | `GET /v1/devices/{deviceId}/clipboard` | Read the device clipboard (text only) |
| [`setClipboard`](/reference/rest/device-extras/#setclipboard) | `PUT /v1/devices/{deviceId}/clipboard` | Put text on the device clipboard |
| [`setLocation`](/reference/rest/device-extras/#setlocation) | `PUT /v1/devices/{deviceId}/location` | Simulate a location (simulators, emulators, developer-mode devices) |
| [`clearLocation`](/reference/rest/device-extras/#clearlocation) | `DELETE /v1/devices/{deviceId}/location` | Stop simulating a location |
| [`listNotifications`](/reference/rest/device-extras/#listnotifications) | `GET /v1/devices/{deviceId}/notifications` | Notifications the engine has observed on this device |
| [`sendTestPush`](/reference/rest/device-extras/#sendtestpush) | `POST /v1/devices/{deviceId}/push` | Send a test push to an app build you own |
| [`fillCredential`](/reference/rest/credentials/#fillcredential) | `POST /v1/devices/{deviceId}/credentials/fill` | Type a saved credential into the focused field without revealing it |
| [`requestPersonLogin`](/reference/rest/credentials/#requestpersonlogin) | `POST /v1/devices/{deviceId}/credentials/request-person` | Hand the login to the person (default password mode) |
| [`listCredentialSites`](/reference/rest/credentials/#listcredentialsites) | `GET /v1/credentials/sites` | Sites enabled for the password-manager bridge (names only, never secrets) |
| [`validateScenario`](/reference/rest/scenarios/#validatescenario) | `POST /v1/scenarios/validate` | Parse and statically check a scenario (roles, sync points, deadlocks, capabilities) |
| [`listRuns`](/reference/rest/runs/#listruns) | `GET /v1/runs` | Run history |
| [`startRun`](/reference/rest/runs/#startrun) | `POST /v1/runs` | Start a scenario run on bound devices |
| [`getRun`](/reference/rest/runs/#getrun) | `GET /v1/runs/{runId}` | Run status, per-role progress, failures |
| [`cancelRun`](/reference/rest/runs/#cancelrun) | `DELETE /v1/runs/{runId}` | Cancel a running scenario |
| [`getReport`](/reference/rest/runs/#getreport) | `GET /v1/runs/{runId}/report` | Run report as HTML, JUnit XML, or JSON |
| [`listArtifacts`](/reference/rest/runs/#listartifacts) | `GET /v1/runs/{runId}/artifacts` | Screenshots, videos, logs and trees produced by a run |
| [`startRecording`](/reference/rest/recordings/#startrecording) | `POST /v1/recordings` | Record several devices on one clock |
| [`getRecording`](/reference/rest/recordings/#getrecording) | `GET /v1/recordings/{recordingId}` | Recording status and files |
| [`stopRecording`](/reference/rest/recordings/#stoprecording) | `DELETE /v1/recordings/{recordingId}` | Stop and finalize a recording |
| [`startAgentTask`](/reference/rest/agent/#startagenttask) | `POST /v1/agent/tasks` | Give the built-in agent a task on one or more devices |
| [`getAgentTask`](/reference/rest/agent/#getagenttask) | `GET /v1/agent/tasks/{taskId}` | Task status, steps and cost so far |
| [`cancelAgentTask`](/reference/rest/agent/#cancelagenttask) | `DELETE /v1/agent/tasks/{taskId}` | Stop the task |
| [`steerAgentTask`](/reference/rest/agent/#steeragenttask) | `POST /v1/agent/tasks/{taskId}/messages` | Add guidance to a running task |
| [`takeOver`](/reference/rest/agent/#takeover) | `POST /v1/agent/tasks/{taskId}/takeover` | Pause the agent at the next step boundary and give input to the person |
| [`handBack`](/reference/rest/agent/#handback) | `POST /v1/agent/tasks/{taskId}/handback` | Return control to the agent with an optional note |
| [`listRoutines`](/reference/rest/routines/#listroutines) | `GET /v1/routines` | Saved and scheduled routines |
| [`saveRoutine`](/reference/rest/routines/#saveroutine) | `POST /v1/routines` | Save a routine from a finished agent task or a scenario |
| [`runRoutine`](/reference/rest/routines/#runroutine) | `POST /v1/routines/{routineId}/runs` | Run a routine now |
| [`listApprovals`](/reference/rest/safety/#listapprovals) | `GET /v1/approvals` | Pending and recent approval requests |
| [`getApproval`](/reference/rest/safety/#getapproval) | `GET /v1/approvals/{approvalId}` | One approval request |
| [`decideApproval`](/reference/rest/safety/#decideapproval) | `POST /v1/approvals/{approvalId}` | Approve or deny (person or admin scope only; an actor can never approve its own request) |
| [`stopEverything`](/reference/rest/safety/#stopeverything) | `POST /v1/stop` | Stop every agent, run, job and pressed input on every device, and refuse new actions until resumed |
| [`resume`](/reference/rest/safety/#resume) | `DELETE /v1/stop` | Clear the stop (person or admin scope only) |
| [`getPolicy`](/reference/rest/safety/#getpolicy) | `GET /v1/policy` | Current safety policy profiles |
| [`updatePolicy`](/reference/rest/safety/#updatepolicy) | `PUT /v1/policy` | Replace policy profiles (admin scope) |
| [`listAudit`](/reference/rest/safety/#listaudit) | `GET /v1/audit` | Audit log entries |
| [`listKeys`](/reference/rest/admin/#listkeys) | `GET /v1/keys` | Local API keys (never the secret) |
| [`createKey`](/reference/rest/admin/#createkey) | `POST /v1/keys` | Create a key; the secret is shown once |
| [`revokeKey`](/reference/rest/admin/#revokekey) | `DELETE /v1/keys/{keyId}` | Revoke a key at once |
| [`getUsage`](/reference/rest/admin/#getusage) | `GET /v1/usage` | Model spend, run counts and device time |
| [`streamEvents`](/reference/rest/events/#streamevents) | `GET /v1/events` | Server-sent events for devices, jobs, runs, approvals and agent steps |