Type something to search...

Longarm Batch Tasks

Back to longarm Docs

Batch tasks let longarm save reusable multi-step workflows and run them later from the app, REST API, or MCP.

Key behavior

  • one batch run can be active at a time
  • run requests return immediately with a runId
  • progress is checked through status and history
  • screenshots captured by screenshot steps are stored in batch history
  • default history retention is 30 runs

Recorded tasks

Stopping a recording in the Batch tab creates a normal batch task containing the classified tap, long-press, swipe, and delay steps. A recorded task is not a special immutable source — it can be edited, duplicated, deleted, run, or updated through the same app, REST, and MCP operations as any manually created task.

When UI inspection is enabled in settings at recording start, each recorded gesture is preceded by a ui_context annotation describing the UI state right before that gesture. It is shown as read-only JSON in the app and preserved in saved tasks, JSON exports/imports, and REST/MCP task data. The runner skips the entire annotation during execution — it contributes no execution count or step log, and the original coordinate gesture is unchanged. AI editors can use the annotation’s node text, viewId, class, bounds, state, and actions to refine a recorded gesture into a semantic target.

With UI inspection disabled at recording time, recording saves gestures only, with no annotation. Older saved tasks with legacy recording metadata load as ordinary batch tasks; the obsolete metadata is dropped the next time the task is saved.

Task JSON shape

Top-level task:

{
  "id": "bt_...",
  "name": "Open Settings and capture",
  "steps": [],
  "repeat": 1,
  "variables": {},
  "createdAt": "2026-07-13T00:00:00.000Z",
  "updatedAt": "2026-07-13T00:00:00.000Z"
}

For POST /api/batch and batch_save, provide name, steps, optional repeat, and optional variables. The server can assign IDs and timestamps.

Step JSON shape

{
  "id": "step_1",
  "type": "tap",
  "params": { "x": 540, "y": 1200, "duration": 80 },
  "repeat": 1,
  "delayAfterMs": 0,
  "continueOnError": false,
  "children": []
}

children holds nested steps and is used by loop.

Supported step types

Free:

  • tap
  • long_press
  • screenshot
  • open_app
  • open_intent
  • app_function_query
  • app_function_call
  • overlay_show
  • overlay_hide
  • delay
  • set
  • ui_action
  • set_text
  • ui_scroll
  • wait_for_ui
  • loop

Plus:

  • swipe
  • pinch
  • two_finger_swipe
  • rotate

Common examples

Tap

{
  "type": "tap",
  "params": { "x": 540, "y": 1200, "duration": 80 }
}

Target-based tap:

{
  "type": "tap",
  "params": {
    "target": { "text": "Sign in", "role": "button" },
    "mode": "semantic_then_gesture"
  }
}

Swipe (Plus)

{
  "type": "swipe",
  "params": {
    "startX": 540,
    "startY": 1800,
    "endX": 540,
    "endY": 600,
    "duration": 700
  }
}

Screenshot

{
  "type": "screenshot",
  "params": {
    "gridSize": 1,
    "gridUnit": "cm",
    "scale": true
  }
}

Open app

{
  "type": "open_app",
  "params": {
    "packageName": "com.android.settings"
  }
}

Delay

{
  "type": "delay",
  "params": { "ms": 1500 }
}

Query and call an AppFunction

app_function_query can save its complete discovery result into a batch variable, and app_function_call can reference it and optionally save the decoded return value:

{
  "steps": [
    {
      "type": "app_function_query",
      "params": {
        "packageName": "com.example.notes",
        "maxResults": 20,
        "resultVariable": "availableFunctions"
      }
    },
    {
      "type": "app_function_call",
      "params": {
        "packageName": "com.example.notes",
        "functionId": "{{availableFunctions.functions.0.functionId}}",
        "arguments": { "title": "Groceries" },
        "resultVariable": "createdNote"
      }
    }
  ]
}

These steps do not require accessibility. They still require Android AppFunctions support and Android authorization for longarm and the target function.

UI action

{
  "type": "ui_action",
  "params": {
    "selector": { "text": "Continue" },
    "action": "click",
    "fallback": "none"
  }
}

Wait for UI

{
  "type": "wait_for_ui",
  "params": {
    "selector": { "text": "Welcome" },
    "condition": "exists",
    "timeoutMs": 5000,
    "stableForMs": 0
  }
}

Variables

Use {{name}} substitution inside string params:

{
  "variables": {
    "packageName": "com.android.settings"
  },
  "steps": [
    {
      "type": "open_app",
      "params": { "packageName": "{{packageName}}" }
    }
  ]
}

A param whose entire value is a single {{name}} placeholder preserves the variable’s original JSON type (number, boolean, object) instead of converting it to a string. Nested lookups are also supported, such as {{user.name}}.

Loops

loop steps execute their children, and support four modes:

Fixed count:

{
  "type": "loop",
  "params": { "count": 3 },
  "children": [
    { "type": "tap", "params": { "x": 540, "y": 1800 } }
  ]
}

Numeric range:

{
  "type": "loop",
  "params": { "var": "i", "from": 1, "to": 5, "step": 1 },
  "children": [
    { "type": "delay", "params": { "ms": 250 } }
  ]
}

For-each over a list:

{
  "type": "loop",
  "params": {
    "var": "pkg",
    "in": ["com.android.settings", "com.android.chrome"]
  },
  "children": [
    { "type": "open_app", "params": { "packageName": "{{pkg}}" } }
  ]
}

While-like loop:

{
  "type": "loop",
  "params": { "whileVar": "keepGoing", "max": 10 },
  "children": [
    { "type": "delay", "params": { "ms": 500 } }
  ]
}

Notes: loop nesting depth is limited, a loop with step: 0 is invalid, and whileVar loops require max.

Full example

{
  "name": "Open Google News and scroll",
  "variables": {
    "packageName": "com.google.android.apps.magazines"
  },
  "steps": [
    {
      "type": "open_app",
      "params": { "packageName": "{{packageName}}" },
      "delayAfterMs": 2500
    },
    {
      "type": "swipe",
      "params": {
        "startX": 540,
        "startY": 1900,
        "endX": 540,
        "endY": 650,
        "duration": 700
      }
    },
    {
      "type": "screenshot",
      "params": { "gridSize": 1, "gridUnit": "cm", "scale": true }
    }
  ]
}

REST operations

Save and list tasks:

  • GET /api/batch
  • POST /api/batch
  • GET /api/batch/<id>
  • PUT /api/batch/<id>
  • DELETE /api/batch/<id>

Task validation requires a name and a non-empty steps array.

Run a task — saved task with POST /api/batch/<id>/run, or an inline unsaved task with POST /api/batch/run. Both accept an optional keepScreenOn boolean (JSON body or ?keepScreenOn=true query parameter). When true, longarm holds the screen on for the whole run and releases it as soon as the run finishes, fails, or is stopped — useful for long runs that would otherwise hit the device’s screen timeout. Pressing the power button still turns the screen off. This matches the Screen on item in a task’s menu in the app; it defaults to false for API-triggered runs.

{ "runId": "run_123", "status": "processing" }

If another run is already active, the server returns HTTP 409 Conflict with { "error": "Another batch task is already running" }.

Runner status — GET /api/batch/status:

{
  "state": "running",
  "currentTaskId": "bt_123",
  "currentRunId": "run_123",
  "currentStepIndex": 2,
  "totalSteps": 5
}

state is one of idle, running, stopping.

Batch history

List history with GET /api/batch/history, or one run with GET /api/batch/history/<runId>:

{
  "runId": "run_123",
  "taskId": "bt_123",
  "taskName": "Open Google News and scroll",
  "status": "finished",
  "startedAt": "2026-07-13T00:00:00.000Z",
  "endedAt": "2026-07-13T00:00:05.000Z",
  "totalSteps": 3,
  "executedSteps": 3,
  "message": "Completed",
  "screenshots": [
    {
      "id": "shot_1",
      "stepId": "step_3",
      "stepIndex": 3,
      "capturedAt": "2026-07-13T00:00:04.000Z"
    }
  ]
}

Run status values: processing, finished, failed, aborted.

Delete history with DELETE /api/batch/history/<runId> (one run) or DELETE /api/batch/history (all runs).

Screenshot export requires Plus:

  • GET /api/batch/history/<runId>/screenshots/<screenshotId> — single screenshot
  • GET /api/batch/history/<runId>/export — all screenshots as a zip

MCP equivalents

The same batch capabilities are available through MCP tools: batch_list, batch_get, batch_save, batch_delete, batch_run, batch_run_inline, batch_status, batch_history_list, batch_history_get, batch_history_export, batch_history_delete.