Skip to main content
The surface is 27 tools across 8 scopes. 10 of them are the frozen baseline: they were there from the first release and only leave through a deprecation programme. The rest are additions, marked with the version that introduced them. Additions never change an existing tool, so a client written against the baseline keeps working. Every tool rejects unknown parameters instead of dropping them, so a misspelled field fails loudly rather than acting without it. Billing, purchasing, returning an order, revoking a key and leaving an organisation are not here, and there is no scope for them. They are on the REST API, and the ones that spend or unwind money also require a signed-in administrator rather than an API key. A tool you cannot find in the table below is not missing from your credential’s scopes; it does not exist on this surface.

Surface at a glance

Scopes

Your API key carries scopes, and a tool outside them is never registered on your session. That means tools/list shows exactly what you can call. Calling a tool that exists but needs a scope your key lacks returns an error that names the scope, such as permission_denied with “This key needs devices:lease to call acquire_device”. A name that is not a tool is a protocol-level method not found. runs:start also needs devices:act. Handing a device to a run is permission to let a loop act for you, not permission to act without saying so, so a key without devices:act gets no run tools at all.

What the annotations mean

Every hint is set explicitly on every tool. Read them literally:
  • title is the name a client shows a person. The tool name is the protocol handle; this is the product’s own word for it, and the table above prints both.
  • destructiveHint marks the tools that can destroy state you did not create: the run tools hand the device to an autonomous loop. A tap, a swipe, a hold, typing and a key press are NOT marked, because they do what a finger does and a client that prompts on the hint would prompt on every one of them. What keeps a person in the loop is _meta.requiresUserInteraction, which no “do not ask again” turns off.
  • idempotentHint on an action tool reflects the idempotencyKey contract: a retry that reuses the key replays the recorded outcome instead of executing twice.
  • openWorldHint is false everywhere. Tools act on your own configured fleet, nothing beyond it.

Tools

list_app_device_capabilities

Apps library list app device capabilities. Uses the same permissions and durable request key as REST. A pending or unconfirmed result is not success; query the original key. Never send APK bytes. Required scope: apps:read + devices:read · Added in: 0.8.0 Annotations: title “list app device capabilities”, readOnlyHint true, destructiveHint false, idempotentHint true, openWorldHint false Parameters Result fields (the structuredContent of a successful call)

get_app_inventory

Apps library get app inventory. Uses the same permissions and durable request key as REST. A pending or unconfirmed result is not success; query the original key. Never send APK bytes. Required scope: apps:read + devices:read · Added in: 0.8.0 Annotations: title “get app inventory”, readOnlyHint true, destructiveHint false, idempotentHint true, openWorldHint false Parameters Result fields (the structuredContent of a successful call)

refresh_app_inventory

Apps library refresh app inventory. Uses the same permissions and durable request key as REST. A pending or unconfirmed result is not success; query the original key. Never send APK bytes. Required scope: apps:read + devices:read · Added in: 0.8.0 Annotations: title “refresh app inventory”, readOnlyHint false, destructiveHint false, idempotentHint true, openWorldHint false Parameters Result fields (the structuredContent of a successful call)

get_app_inventory_refresh

Apps library get app inventory refresh. Uses the same permissions and durable request key as REST. A pending or unconfirmed result is not success; query the original key. Never send APK bytes. Required scope: apps:read + devices:read · Added in: 0.8.0 Annotations: title “get app inventory refresh”, readOnlyHint true, destructiveHint false, idempotentHint true, openWorldHint false Parameters Result fields (the structuredContent of a successful call)

list_app_inventory_refresh_results

Apps library list app inventory refresh results. Uses the same permissions and durable request key as REST. A pending or unconfirmed result is not success; query the original key. Never send APK bytes. Required scope: apps:read + devices:read · Added in: 0.8.0 Annotations: title “list app inventory refresh results”, readOnlyHint true, destructiveHint false, idempotentHint true, openWorldHint false Parameters Result fields (the structuredContent of a successful call)

get_app_request

Apps library get app request. Uses the same permissions and durable request key as REST. A pending or unconfirmed result is not success; query the original key. Never send APK bytes. Required scope: apps:read · Added in: 0.8.0 Annotations: title “get app request”, readOnlyHint true, destructiveHint false, idempotentHint true, openWorldHint false Parameters Result fields (the structuredContent of a successful call)

list_devices

Enumerate every reachable device with its per-device capabilities. Consult capabilities before calling action/observe tools, backends differ (a robot hand has camera frames but no UI tree). Required scope: devices:read Annotations: title “List your phones”, readOnlyHint true, destructiveHint false, idempotentHint true, openWorldHint false Parameters This tool takes no parameters. Result fields (the structuredContent of a successful call)

acquire_device

Take a device for your organization before acting on it. Optional: any gesture tool takes the device for you when your organization does not hold it. Returns the lease (leaseId + expiry); keep leaseId to give the device back with release_device. A lease expires on its own after 10 minutes unless renewed. Required scope: devices:lease Annotations: title “Take a phone”, readOnlyHint false, destructiveHint false, idempotentHint false, openWorldHint false Parameters Result fields (the structuredContent of a successful call)

renew_lease

Extend the lease your organization holds on a device, so work longer than one lease can keep its device. leaseId is optional; if supplied it must match the current lease. The lease may be one a colleague or a run took. Slides the expiry to ten minutes from now (it does not add ten minutes to the old expiry), and leaves leaseId, fencingToken and acquiredAt unchanged. Renew before the lease expires: an expired lease cannot be renewed, because by then the device is available to everyone else, and you have to take it again. Required scope: devices:lease · Added in: 0.4.0 Annotations: title “Keep the phone longer”, readOnlyHint false, destructiveHint false, idempotentHint false, openWorldHint false Parameters Result fields (the structuredContent of a successful call)

release_device

Give a device back, by the lease id acquire_device returned. This tool requires the id; other actions accept an omitted id but reject a stale one. An id that is not the current one is refused with lease_mismatch. Required scope: devices:lease Annotations: title “Give the phone back”, readOnlyHint false, destructiveHint false, idempotentHint true, openWorldHint false Parameters Result fields (the structuredContent of a successful call)

screenshot

Capture the device screen. Returns the image plus its pixelSize and, when your organization holds the device, a frameId; aim taps with the 0-1000 grid, and pass frameId as observedFrame on the next action so a stale frame is refused instead of mis-tapped. others says what anybody else did on the device since the previous frame. On a device your organization does not hold this is a watch: you get the image and no frame, and nothing is taken or billed. Required scope: devices:read Annotations: title “See the screen”, readOnlyHint true, destructiveHint false, idempotentHint true, openWorldHint false Parameters Result fields (the structuredContent of a successful call)

ui_snapshot

Structured element tree of the current screen. All bounds inside the tree are already in the 0-1000 grid, coordinates read here can be tapped directly. Required scope: devices:read Annotations: title “Read the screen’s elements”, readOnlyHint true, destructiveHint false, idempotentHint true, openWorldHint false Parameters Result fields (the structuredContent of a successful call)

tap

Tap at (x, y) in the 0-1000 grid. Returns the ActionOutcome: committed means the device applied it (a robot arm has physically finished and returned to hover). Every action result also carries others, what other principals did on the device since your anchor; a person may drive the same device beside you, and you are informed rather than stopped. Required scope: devices:act Annotations: title “Tap”, readOnlyHint false, destructiveHint false, idempotentHint true, openWorldHint false Parameters Result fields (the structuredContent of a successful call)

swipe

Swipe between two points in the 0-1000 grid over durationMs milliseconds (default 300). Required scope: devices:act Annotations: title “Swipe”, readOnlyHint false, destructiveHint false, idempotentHint true, openWorldHint false Parameters Result fields (the structuredContent of a successful call)

type_text

Type into the focused input (focus one first, e.g. by tapping a search field). Required scope: devices:act Annotations: title “Type”, readOnlyHint false, destructiveHint false, idempotentHint true, openWorldHint false Parameters Result fields (the structuredContent of a successful call)

press_key

Press a hardware or navigation key. The keys this tool accepts are the widest set any platform supports; a device supports a subset, published in its list_devices capabilities. Required scope: devices:act Annotations: title “Press a key”, readOnlyHint false, destructiveHint false, idempotentHint true, openWorldHint false Parameters Result fields (the structuredContent of a successful call)

open_app

Open an app by OS-level id (Android package name / iOS bundle id). Required scope: devices:act Annotations: title “Open an app”, readOnlyHint false, destructiveHint false, idempotentHint true, openWorldHint false Parameters Result fields (the structuredContent of a successful call)

long_press

Press and hold at (x, y) in the 0-1000 grid for durationMs (default 800), returning the ActionOutcome like tap. Only on a device whose capabilities say actions.longPress: a backend that cannot hold (a robot arm reads the duration as travel time) refuses with capability_unsupported rather than landing a brief press under this name. Required scope: devices:act · Added in: 0.2.0 Annotations: title “Press and hold”, readOnlyHint false, destructiveHint false, idempotentHint true, openWorldHint false Parameters Result fields (the structuredContent of a successful call)

wake

Turn the device’s screen on and drop its lock screen. Call it when a screenshot comes back black or a live video session shows nothing: a phone whose screen is off renders nothing until it is woken, and this is the ONLY way to wake one: press_key’s power is a toggle, as likely to put an awake phone to sleep. Works where the device’s capabilities say actions.wake: a cloud phone, an emulator or a USB Android handset; a robot-driven phone refuses with capability_unsupported. Safe to repeat: waking an awake phone does nothing. Best-effort, so accepted means the device was asked, not that the screen is on; take a screenshot to see. Writes no receipt, unlike the gesture tools. Required scope: devices:act · Added in: 0.6.0 Annotations: title “Wake the screen”, readOnlyHint false, destructiveHint false, idempotentHint true, openWorldHint false Parameters Result fields (the structuredContent of a successful call)

get_device

One device’s current status and capabilities without enumerating the whole fleet. Required scope: devices:read · Added in: 0.2.0 Annotations: title “Look at one phone”, readOnlyHint true, destructiveHint false, idempotentHint true, openWorldHint false Parameters Result fields (the structuredContent of a successful call)

get_receipt

Look up the receipt for an action by its idempotencyKey, the recovery path when an action’s response was lost (transport timeout, stateless HTTP has no progress channel). found:false means the action never began and the SAME key is safe to retry. This answers about ONE key you already hold; to ENUMERATE recent receipts use GET /v1/receipts on the REST API. Required scope: devices:read · Added in: 0.2.0 Annotations: title “Look up what an action did”, readOnlyHint true, destructiveHint false, idempotentHint true, openWorldHint false Parameters Result fields (the structuredContent of a successful call)

start_run

Start an agent run for one natural-language goal. executor says who drives it. phonebase runs it for you and returns no url. self_hosted means phonebase executes nothing: nothing happens on the device until your own worker connects to the run-scoped VisualRemote base url returned once here, which is a credential, and which is why workerConnected is false. Returns a runId to poll with get_run. Low-level tools stay available for deterministic control. Required scope: runs:start · Added in: 0.7.0 Annotations: title “Start a run”, readOnlyHint false, destructiveHint true, idempotentHint false, openWorldHint false Parameters Result fields (the structuredContent of a successful call)

get_run

Bounded long-poll for a run’s state. Returns immediately by default; with waitMs + sinceState it waits for a change and answers at the deadline regardless, it never hangs. Never echoes the run url. Required scope: runs:start · Added in: 0.7.0 Annotations: title “Watch a run”, readOnlyHint true, destructiveHint false, idempotentHint true, openWorldHint false Parameters Result fields (the structuredContent of a successful call)

list_runs

List this org’s runs, newest first. Each entry carries the goal it was started with and how it ended, so a lost runId is recoverable. Page with the returned nextBefore cursor. Never echoes a run url. Required scope: runs:start · Added in: 0.7.0 Annotations: title “List your runs”, readOnlyHint true, destructiveHint false, idempotentHint true, openWorldHint false Parameters Result fields (the structuredContent of a successful call)

cancel_run

End a run and destroy its VisualRemote credential. Already-terminal runs are returned unchanged, terminal is terminal. Required scope: runs:start · Added in: 0.7.0 Annotations: title “Stop a run”, readOnlyHint false, destructiveHint false, idempotentHint true, openWorldHint false Parameters Result fields (the structuredContent of a successful call)

resume_run

Resume a run that is on hold: one a person paused with pause_run, or one in needs_user_control waiting for a person. If recovery is present, pass expectedRecoveryId equal to its current id and hold the device’s current lease yourself; a missing lease fails with lease_required and another person’s lease fails with lease_mismatch. Missing or stale recovery ids, blocked recovery, or exhausted recovery budget return the run unchanged without restarting it. Recovery waiting preserves progress and freezes the remaining execution time until this explicit continuation. For ordinary pauses or steps such as a login or challenge, existing resume behavior applies: a paused run needs the lease it acts under and fails with lease_mismatch when the device has moved on. Finished and other ineligible runs are returned unchanged. Required scope: runs:start · Added in: 0.7.0 Annotations: title “Let a run carry on”, readOnlyHint false, destructiveHint true, idempotentHint true, openWorldHint false Parameters Result fields (the structuredContent of a successful call)

pause_run

Put a run on hold so a person can act on the device while the agent waits. The run’s deadline stops advancing and the lease it acts under is slid to a fresh window; the hold ends at manualControlUntil, after which an unresumed run is canceled. Resume it with resume_run. Fails with lease_mismatch when the device has already moved on. Required scope: runs:start · Added in: 0.7.0 Annotations: title “Hold a run”, readOnlyHint false, destructiveHint false, idempotentHint false, openWorldHint false Parameters Result fields (the structuredContent of a successful call)