API Overview
Clawperator exposes a Node-based interface for agent-driven device automation. The agent (brain) calls this API to dispatch actions to an Android device (hand). The API handles device resolution, payload validation, broadcast dispatch, and result collection.
Interaction Model
- Agent constructs an
Executionpayload. - Agent calls
execute(CLI) orPOST /execute(HTTP). - Clawperator validates the payload, resolves the device, dispatches via ADB broadcast, and waits for a
[Clawperator-Result]envelope from logcat. - The result envelope is returned to the agent.
Single-flight enforcement: only one execution per device runs at a time. Concurrent calls return EXECUTION_CONFLICT_IN_FLIGHT.
Clawperator can also provision and manage Android emulators through the Node layer. That gives agents a deterministic alternative runtime to a physical Android device.
Execution Payload
The core unit dispatched to the device.
{
"commandId": "my-cmd-001",
"taskId": "my-task-001",
"source": "my-agent",
"expectedFormat": "android-ui-automator",
"timeoutMs": 30000,
"actions": [
{
"id": "step1",
"type": "click",
"params": {
"matcher": { "resourceId": "com.example:id/submit" }
}
}
]
}
Fields
| Field | Type | Required | Description |
|---|---|---|---|
commandId |
string | yes | Unique command identifier (max 128 chars) |
taskId |
string | yes | Task correlation ID (max 128 chars) |
source |
string | yes | Caller identifier (max 64 chars) |
expectedFormat |
string | yes | Must be "android-ui-automator" |
timeoutMs |
number | yes | Timeout in ms (1000-120000) |
actions |
array | yes | 1-50 actions |
mode |
string | no | "artifact_compiled" or "direct" |
Limits
| Limit | Value |
|---|---|
| Max actions per execution | 50 |
| Min timeout | 1,000 ms |
| Max timeout | 120,000 ms |
| Max payload size | 64,000 bytes |
| Max ID length | 128 chars |
| Max source length | 64 chars |
| Max matcher value length | 512 chars |
Action Types
Canonical Types
| Type | Required params | Description |
|---|---|---|
open_uri |
uri |
Open a URI via the system default handler |
open_app |
applicationId |
Launch an app |
close_app |
applicationId |
Force-stop an app |
click |
matcher |
Tap a UI node |
scroll_and_click |
target |
Scroll to and tap a node |
scroll |
- | Single scroll gesture with outcome reporting |
scroll_until |
- | Bounded scroll loop with machine-readable termination reason |
read_text |
matcher |
Read text from a UI node |
enter_text |
matcher, text |
Type text into a UI node |
wait_for_node |
matcher |
Wait for a node to appear |
snapshot_ui |
- | Capture the canonical hierarchy_xml UI tree |
take_screenshot |
- | Capture screen as PNG |
sleep |
durationMs |
Pause execution |
press_key |
key |
Issue a system navigation key via accessibility |
Aliases (normalized at input)
| Alias | Canonical type |
|---|---|
tap, press |
click |
wait_for, find, find_node |
wait_for_node |
read |
read_text |
snapshot |
snapshot_ui |
screenshot, capture_screenshot |
take_screenshot |
type_text, text_entry, input_text |
enter_text |
open_url |
open_uri |
key_press |
press_key |
NodeMatcher (Selector)
Used to identify a UI node. At least one field is required.
{
"resourceId": "com.example.app:id/button_ok",
"role": "android.widget.Button",
"textEquals": "OK",
"textContains": "Submit",
"contentDescEquals": "Submit button",
"contentDescContains": "submit"
}
All fields are optional but at least one must be non-empty. Values are ORed internally by the device runtime.
Action Params Reference
| Param | Type | Used by |
|---|---|---|
uri |
string | open_uri |
applicationId |
string | open_app, close_app |
matcher |
NodeMatcher | click, read_text, enter_text, wait_for_node |
text |
string | enter_text |
submit |
boolean | enter_text - press enter after typing |
clear |
boolean | enter_text - clear field before typing |
clickType |
string | click - default, long_click, or focus |
target |
NodeMatcher | scroll_and_click |
container |
NodeMatcher | scroll_and_click, scroll, scroll_until |
direction |
string | scroll_and_click, scroll, scroll_until |
maxSwipes |
number | scroll_and_click |
clickAfter |
boolean | scroll_and_click - when false, scroll to target without clicking |
maxScrolls |
number | scroll_until - maximum scroll iterations (default: 20) |
maxDurationMs |
number | scroll_until - wall-clock cap in ms (default: 10000) |
noPositionChangeThreshold |
number | scroll_until - consecutive no-movement scrolls before stopping (default: 3) |
durationMs |
number | sleep |
key |
"back"\|"home"\|"recents" |
press_key |
path |
string | take_screenshot - output file path |
distanceRatio |
number | scroll_and_click, scroll, scroll_until |
settleDelayMs |
number | scroll_and_click, scroll, scroll_until |
findFirstScrollableChild |
boolean | scroll_and_click, scroll, scroll_until - auto-use first scrollable descendant (default: true) |
retry |
object | per-step retry config |
For scroll and scroll_until, omitting container uses the first visible scrollable="true" node. That is convenient on simple screens, but on nested-scroll layouts agents should prefer an explicit container.resourceId taken from snapshot_ui.
Result Envelope
All executions return a ResultEnvelope via the [Clawperator-Result] terminal signal.
{
"commandId": "my-cmd-001",
"taskId": "my-task-001",
"status": "success",
"stepResults": [
{
"id": "step1",
"actionType": "click",
"success": true,
"data": {}
}
],
"error": null
}
ResultEnvelope Fields
| Field | Type | Description |
|---|---|---|
commandId |
string | Correlates to the dispatched command |
taskId |
string | Correlates to the task |
status |
"success"\|"failed" |
Overall execution status |
stepResults |
array | Per-action results |
error |
string or null | Top-level error message if failed |
StepResult Fields
| Field | Type | Description |
|---|---|---|
id |
string | Action ID from the execution |
actionType |
string | Canonical action type |
success |
boolean | Whether the step succeeded |
data |
object | Action-specific output data |
error |
string | Step-level error message |
For snapshot_ui, data.text contains the UI tree string.
For take_screenshot, data.path contains the local file path of the PNG.
For press_key, data.key contains the normalized lowercase key name. If Android rejects the global action, the step returns success: false with data.error: "GLOBAL_ACTION_FAILED".
HTTP API (serve mode)
Start with clawperator serve [--port 3000] [--host 127.0.0.1].
GET /android/emulators
Return configured Android Virtual Devices with normalized compatibility metadata.
Response:
{
"avds": [
{
"name": "clawperator-pixel",
"exists": true,
"running": false,
"supported": true,
"apiLevel": 35,
"abi": "arm64-v8a",
"playStore": true,
"deviceProfile": "pixel_7",
"systemImage": "system-images;android-35;google_apis_playstore;arm64-v8a",
"unsupportedReasons": []
}
]
}
GET /android/emulators/:name
Return the normalized view of one AVD. This is the emulator diagnosis endpoint.
GET /android/emulators/running
Return running emulator devices and boot state.
Response:
{
"devices": [
{
"type": "emulator",
"avdName": "clawperator-pixel",
"serial": "emulator-5554",
"booted": true
}
]
}
POST /android/emulators/create
Create a new supported AVD. The request body may include:
nameapiLeveldeviceProfileabiplayStore
POST /android/emulators/:name/start
Start an existing AVD and return a booted emulator device:
{
"type": "emulator",
"avdName": "clawperator-pixel",
"serial": "emulator-5554",
"booted": true
}
POST /android/emulators/:name/stop
Stop a running emulator by AVD name.
DELETE /android/emulators/:name
Delete an AVD by name.
POST /android/provision/emulator
Provision a supported emulator using deterministic reuse-first orchestration:
- reuse a running supported emulator
- start a stopped supported AVD
- create a new supported AVD
The default profile is Android API 35, Google Play, ABI arm64-v8a, device profile pixel_7, and AVD name clawperator-pixel.
GET /devices
Returns connected devices.
Response:
{ "ok": true, "devices": [{ "serial": "<device_serial>", "state": "device" }] }
POST /execute
Run an execution payload.
Request body:
{
"execution": { /* Execution object */ },
"deviceId": "<device_serial>",
"receiverPackage": "com.clawperator.operator"
}
Response (success):
{ "ok": true, "envelope": { /* ResultEnvelope */ }, "deviceId": "<device_serial>" }
Response (error):
{ "ok": false, "error": { "code": "ERROR_CODE", "message": "..." } }
HTTP status codes:
| Code | Condition |
|---|---|
| 200 | Success |
| 400 | Validation error, missing fields, ambiguous device |
| 404 | Device not found or no devices |
| 413 | Payload too large |
| 423 | Execution conflict (in-flight) |
| 504 | Result envelope timeout |
POST /observe/snapshot
Capture UI snapshot. Body: { "deviceId"?, "receiverPackage"? }.
Same response shape as /execute.
POST /observe/screenshot
Capture screenshot. Body: { "deviceId"?, "receiverPackage"? }.
Same response shape as /execute. The PNG path is in envelope.stepResults[0].data.path.
GET /skills
List or search skills. Use query parameters to filter.
Query parameters: ?app=<package_id>&intent=<intent>&keyword=<text> (all optional).
Response:
{ "ok": true, "skills": [{ "id": "...", "applicationId": "...", "intent": "...", "summary": "..." }], "count": 2 }
GET /skills/:skillId
Get metadata for a specific skill.
Response (success):
{ "ok": true, "skill": { "id": "...", "applicationId": "...", "intent": "...", "summary": "...", "scripts": [...], "artifacts": [...] } }
Response (not found): HTTP 404
{ "ok": false, "error": { "code": "SKILL_NOT_FOUND", "message": "..." } }
POST /skills/:skillId/run
Run a skill script (convenience wrapper).
Request body:
{
"deviceId": "<device_serial>",
"args": ["extra", "args"]
}
Both fields are optional.
Response (success):
{ "ok": true, "skillId": "...", "output": "...", "exitCode": 0, "durationMs": 8500 }
Response (error):
{ "ok": false, "error": { "code": "SKILL_EXECUTION_FAILED", "message": "...", "skillId": "...", "exitCode": 1, "stderr": "..." } }
GET /events (SSE)
Server-Sent Events stream. Emits two event types:
clawperator:result- fired when an execution completes:{ deviceId, envelope }clawperator:execution- fired for every execution attempt:{ deviceId, input, result }heartbeat- initial connection confirmation
Environment Variables
| Variable | Description |
|---|---|
ADB_PATH |
Override path to adb binary |
CLAWPERATOR_RECEIVER_PACKAGE |
Default receiver package (fallback if not passed as option) |
CLAWPERATOR_SKILLS_REGISTRY |
Path to skills-registry.json. If unset, defaults to ./skills/skills-registry.json relative to the working directory. After skills install, set to ~/.clawperator/skills/skills/skills-registry.json. |
Receiver Packages
| Variant | Package ID |
|---|---|
| Release | com.clawperator.operator |
| Debug / Local | com.clawperator.operator.dev |