API Overview
Purpose
Define the smallest end-to-end contract an agent needs to use Clawperator correctly: the execution payload, the CLI success wrapper, the [Clawperator-Result] envelope, and the exact fields to branch on.
What Clawperator Is
Clawperator is a deterministic Android actuator. The agent is the planner. Clawperator accepts an explicit execution payload, validates it, resolves one target device and Operator package, dispatches the actions, and returns a structured result.
This page is intentionally narrow:
- Use Actions for action-by-action parameter semantics
- Use Selectors for
NodeMatcherand selector flags - Use Errors for exact error codes and recovery
- Use Devices for target selection rules
Execution Payload
Authoritative source: apps/node/src/contracts/execution.ts
Top-level execution fields:
| Field | Type | Meaning |
|---|---|---|
commandId |
string |
Caller-generated correlation id for the whole run. |
taskId |
string |
Caller-generated task id. |
source |
string |
Caller label, such as serve-api or clawperator-action. |
expectedFormat |
"android-ui-automator" |
Required constant. |
timeoutMs |
number |
Execution-level timeout for the whole payload. Current Node limits require 1000 <= timeoutMs <= 120000. |
actions |
ExecutionAction[] |
Ordered action list. |
mode |
"artifact_compiled" | "direct" |
Optional runtime mode marker. |
Each action has:
| Field | Type | Meaning |
|---|---|---|
id |
string |
Step correlation id. |
type |
string |
Canonical action type such as click or snapshot_ui. |
params |
ActionParams |
Optional action-specific parameters. |
Minimum valid payload example:
{
"commandId": "cmd-001",
"taskId": "task-001",
"source": "agent-loop",
"expectedFormat": "android-ui-automator",
"timeoutMs": 30000,
"actions": [
{
"id": "snap-1",
"type": "snapshot_ui"
}
],
"mode": "direct"
}
Success conditions for a valid payload before dispatch:
expectedFormatis exactly"android-ui-automator"timeoutMsis between1000and120000millisecondsactionsis non-empty- every
actions[i].typeis a supported canonical action type after alias normalization - on a live execution path,
commandIdandtaskIdare echoed back in the result envelope for correlation
Input Normalization
Stored payloads should use canonical field names and canonical action types. On input, Node normalizes a small alias set before schema validation.
Accepted top-level execution key aliases:
| Alias | Canonical field |
|---|---|
command_id |
commandId |
task_id |
taskId |
expected_format |
expectedFormat |
timeout_ms |
timeoutMs |
For action-type aliases and parameter aliases such as package -> applicationId, url -> uri, and selector -> matcher, use Actions. For raw matcher-field aliases such as resource_id and content_desc, use Selectors.
Verification pattern:
clawperator exec --validate-only --payload '{"command_id":"cmd-001","task_id":"task-001","source":"docs","expected_format":"android-ui-automator","timeout_ms":30000,"actions":[{"id":"snap-1","type":"snapshot"}]}' --json
Success condition:
- exit code
0 - top-level
ok == true execution.commandId == "cmd-001"execution.taskId == "task-001"execution.expectedFormat == "android-ui-automator"execution.timeoutMs == 30000execution.actions[0].type == "snapshot_ui"
CLI payload-source aliases accepted by clawperator exec:
--payloadis canonical--execution,--input, and--fileare accepted aliases for the same argument
Execution Limits
Current Node-side validation limits are:
- at most
50actions per execution - at most
64000serialized payload bytes timeoutMsmust stay in1000..120000
If validation fails on limits, shorten the action list, split a large workflow into multiple executions, or avoid embedding oversized inline artifacts in the payload.
Result Envelope
Authoritative source: apps/node/src/contracts/result.ts
The Android runtime emits a [Clawperator-Result] envelope. The Node CLI wraps that envelope in a top-level success object for most device commands:
{
"envelope": {
"commandId": "cmd-001",
"taskId": "task-001",
"status": "success",
"stepResults": [
{
"id": "snap-1",
"actionType": "snapshot_ui",
"success": true,
"data": {
"text": "<hierarchy/>"
}
}
],
"error": null
},
"deviceId": "<device_serial>",
"terminalSource": "clawperator_result",
"isCanonicalTerminal": true
}
Inside that wrapper, the canonical envelope shape is:
{
"commandId": "<command_id>",
"taskId": "<task_id>",
"status": "success",
"stepResults": [
{
"id": "<step_id>",
"actionType": "<action_type>",
"success": true,
"data": {}
}
],
"error": null,
"errorCode": null,
"hint": "<optional_hint>"
}
Field meanings:
| Field | Meaning |
|---|---|
status |
Top-level outcome after Node post-processing. "failed" means at least one step failed or the runtime returned a top-level failure. |
stepResults[].success |
Per-step success bit. |
stepResults[].data |
Action-specific string map. Keys and values are strings after parsing and post-processing. |
error |
Human-readable top-level failure summary. |
errorCode |
Stable top-level code when available. Prefer this over matching error. Current runtime envelopes may also surface Android-emitted strings such as SERVICE_UNAVAILABLE, so do not assume this field is limited to the Node enum in errors.ts. |
hint |
Optional recovery hint injected by Node. |
Failure wrapper example from the CLI:
{
"code": "EXECUTION_VALIDATION_FAILED",
"message": "wait_for_navigation requires params.timeoutMs > 0",
"details": {
"path": "actions.0.params.timeoutMs",
"actionId": "wait-1",
"actionType": "wait_for_navigation"
}
}
Non-runtime success wrappers from clawperator exec:
{
"ok": true,
"validated": true,
"execution": {
"commandId": "cmd-001",
"taskId": "task-001",
"source": "agent-loop",
"expectedFormat": "android-ui-automator",
"timeoutMs": 30000,
"actions": [
{ "id": "snap-1", "type": "snapshot_ui" }
]
}
}
{
"ok": true,
"dryRun": true,
"plan": {
"commandId": "cmd-001",
"timeoutMs": 30000,
"actionCount": 1,
"actions": [
{ "id": "snap-1", "type": "snapshot_ui" }
]
}
}
Surface Differences
Use the wrapper shape that matches the surface you called:
| Surface | Success shape |
|---|---|
| CLI device execution commands | { "envelope": ..., "deviceId": "...", "terminalSource": "clawperator_result", "isCanonicalTerminal": true } |
clawperator exec --validate |
{ "ok": true, "validated": true, "execution": ... } |
clawperator exec --dry-run |
{ "ok": true, "dryRun": true, "plan": ... } |
clawperator serve execution endpoints |
{ "ok": true, "deviceId": "...", "terminalSource": "...", "envelope": ... } |
isCanonicalTerminal is a CLI wrapper field. HTTP serve execution responses do not include it.
How To Branch On Results
Branch in this order:
- If the CLI returned a top-level object with
codeand noenvelope, treat it as a host-side failure. Do not retry unchanged. - If
envelope.status == "failed"andenvelope.errorCodeis present, branch onenvelope.errorCode. - If
envelope.status == "failed"andenvelope.stepResultscontains a failed step, branch on the firststepResults[i].success == falseand inspectstepResults[i].data.error. - If
envelope.status == "success"and everystepResults[i].success == true, treat the command as successful. - If the top-level object is
{ ok: true, validated: true, ... }or{ ok: true, dryRun: true, ... }, treat it as a pre-dispatch contract success, not a device execution result.
Exact machine-checkable success condition for most CLI device commands:
- process exit code
0 - top-level JSON contains
envelope envelope.status == "success"- every
envelope.stepResults[i].success == true terminalSource == "clawperator_result"isCanonicalTerminal == true
How status and stepResults Relate
- If any
stepResults[].successisfalse, Node reconcilesstatusto"failed"and setserrorfrom the first failed step. - If all steps succeed, Node reconciles
statusto"success", clears top-level error state, and removeshint. - A top-level failure can also arrive with zero steps, for example when Android returns a failure envelope before a normal step list exists, such as
SERVICE_UNAVAILABLE. - Execution timeout is different:
runExecution()returns a top-level host-side error object such asRESULT_ENVELOPE_TIMEOUT, not a zero-step success-wrapper envelope. - Node may modify step data after the runtime returns. Examples:
snapshot_uisuccess steps getdata.textattached from extracted log output- missing snapshot text is converted into
SNAPSHOT_EXTRACTION_FAILED - successful
take_screenshotsteps getdata.path - successful pre-flight
close_appsteps are normalized todata.application_id
Execution Flow
- Agent constructs an execution payload with stable
commandId,taskId, and orderedactions. - Node validates the payload size and action schema before any adb dispatch.
- Node resolves one target device and one Operator package.
- Node sends the payload to Android and waits for a
[Clawperator-Result]envelope. - Node post-processes known cases such as snapshot extraction, screenshot capture, settle warnings, and
close_appnormalization. - CLI commands return a JSON wrapper containing the envelope,
deviceId,terminalSource, andisCanonicalTerminal.
Worked Example
Command:
clawperator snapshot --json --device <device_serial>
Success output shape:
{
"envelope": {
"commandId": "snapshot-1700000000000-abcd123",
"taskId": "snapshot-1700000000000-abcd123",
"status": "success",
"stepResults": [
{
"id": "snap",
"actionType": "snapshot_ui",
"success": true,
"data": {
"text": "<hierarchy rotation=\"0\">...</hierarchy>"
}
}
],
"error": null
},
"deviceId": "<device_serial>",
"terminalSource": "clawperator_result",
"isCanonicalTerminal": true
}
Agent-side success test:
envelope.status == "success"envelope.stepResults[0].actionType == "snapshot_ui"envelope.stepResults[0].success == true"text" in envelope.stepResults[0].data