See Computers for the Linux sandbox and Browser sessions for the managed browser. A mobile device is a sibling of both: its own resource, with its own lifecycle. It needs no computer.
idle_timeout_minutes and max_duration_minutes, both set at create; the platform checks them every minute.
The mobile device object
string
Unique id (e.g.
mob_...).string
Always
mobile_device.string
Human-readable name. The one field you can change after create.
string
android.string
13, 14 or 15.string
phone. tablet and watch are reserved and refused today.string
us, eu or as — where the device is allowed to run. It is the one placement control; a region is never a parameter.integer
Minutes without an action after which the platform ends the device (1–180, default 10). Checked once a minute.
integer
Hard ceiling on the device’s life (1–1440, default 120).
string[]
Package names (
com.example.app) of apps in the platform’s curated app catalogue, installed when the device starts. There is no upload route. There is no install route for a running device.string | null
null while alive; then requested, idle_timeout, max_duration, unfunded (stopped by the platform because the balance or plan no longer covered it) or unknown.string | null
One sentence when provisioning failed, else
null.string
Creation timestamp.
string | null
When it ended, or
null while alive.string | null
The agent session whose
mobile tool opened it, the session it was created for (session_id at create), or null.Create a mobile device
POST /v1/mobile_devices → 202 Accepted — scope computers:write. The row is recorded before the provider is asked, so a create whose answer is lost is still found and ended, never a device nobody can find; a provision that failed leaves the list. The request then waits up to ~45 seconds for the device to become ready and answers with whatever is true at that moment. A phone usually takes about a minute, so expect creating and poll GET /v1/mobile_devices/{id}. There is no limit on how many devices an organization runs, only on what it can pay for: a create is admitted when the prepaid balance covers 10 minutes of runway ($0.39) for every live device and the new one, plus what the live ones have run so far — one device needs $0.39, a fifth $1.95 plus the minutes the four running have accrued. Short of that, the create is 402 insufficient_credits before anything is provisioned, and the message names the amount needed. When every phone slot on the platform is in use, the create is 429 rate_limited; try again shortly. A device bills $0.039 per minute from create to end, booked when it ends (Pricing).
string
Defaults to
device.string
android, the default and today the only value; ios is 400 validation_failed.string
13 | 14 | 15, defaults to 15.string
phone, the default and today the only value; tablet and watch are 400 validation_failed.string
us | eu | as. Defaults to us.integer
1–180. Defaults to
10.integer
1–1440. Defaults to
120.string[]
Up to 10 package names (
com.example.app) of apps in the platform’s curated app catalogue, installed when the device starts.string
An agent session of yours. The device is filed under it, and that session’s
mobile tool uses it instead of opening its own phone. When the session ends, the device ends with it. A session that has already ended is refused with 409 session_terminal.Retrieve & list
Truth: who says what the status is
The platform ends a device on its idle and maximum timers, checking once a minute; the provider can also end one on its own (an error, a stopped account) without telling us.status on a row is what we last wrote; GET /v1/mobile_devices/{id} asks the provider and rewrites the row when they disagree, filling terminated_at and termination_reason. The list reads rows only — a provider call per row is not a read — so a list can lag until a detail is opened. Neither read wakes or bills anything: a device bills while it is alive whether or not you look at it.
Rename
PATCH /v1/mobile_devices/{id} → 200 OK — scope computers:write. The body is strict: any field other than name is 400 validation_failed naming it. Recorded in the audit log as mobile_device.updated.
string
required
1–200 characters.
Act on the device
POST /v1/mobile_devices/{id}/act → 200 OK — scope computers:write. One action per call; the device must be ready, otherwise 409 computer_unavailable. Not written to the audit log: an action is work, as exec on a computer is.
The selector words match the screen’s elements:
image is the screen, filled only by screenshot. Every other action answers with image: null and a one-line account of what ran in output, so take a screenshot to see its result. A tap by selector taps the centre of the first element that matches, an exact text beating a partial one; a selector nothing matches is 409 computer_unavailable.
Open a live view
POST /v1/mobile_devices/{id}/live_view → 200 OK — scope computers:write. Takes no body. Opening it counts as activity for the idle timer, as an action does; taps through the stream do not reach the API, so a client that keeps a phone open only through the stream should take a screenshot now and then.
url is the device’s page on the dashboard, where the stream plays for a signed-in member of your organization. It is a link, not a credential. stream is what the dashboard’s realtime player takes: it is returned only to a signed-in member, and is null for an API key (open url instead) or when no stream is offered.
409 computer_unavailable while the device is still starting, 404 not_found once it has ended. Audited as mobile_device.live_view_opened — who and when, never the credential.
Delete a mobile device
DELETE /v1/mobile_devices/{id} → 200 OK — scope computers:write. Ends the device at the provider and marks the row terminated with termination_reason: "requested". Idempotent. Audited as mobile_device.deleted.
Response