# Agent Source: https://docs.mobilerun.ai/agent Configure and run the Mobilerun agent to automate tasks on your devices. The Mobilerun agent is an AI-powered automation system that executes tasks on Android devices. Configure the agent with a prompt and settings, then run it on any connected device. ## Overview The agent: * Interprets natural language instructions * Interacts with device UI elements * Executes multi-step workflows autonomously * Returns results and structured output ## Configuration ### Task Prompt The prompt describes what the agent should do. Write clear, specific instructions for best results. ``` Open the Settings app, navigate to Display settings, and enable dark mode ``` ### Model Selection Choose the LLM model that powers the agent. Different models offer different tradeoffs: | Model Type | Characteristics | | ------------------- | -------------------------------------------------------------- | | **Faster models** | Lower latency, lower cost per step, good for simple tasks | | **Advanced models** | Better reasoning, higher accuracy, ideal for complex workflows | Pass one of the following identifiers as the `llmModel` parameter in the REST API or the SDK, or select it in the Playground. This table is the canonical catalog of models available for cloud agent tasks. | Model | `llmModel` identifier | | --------------------------------- | --------------------------------- | | Mobilerun Mobile Agent (Fast) | `mobilerun/mobile-agent-fast` | | Mobilerun Mobile Agent (Thinking) | `mobilerun/mobile-agent-thinking` | | Anthropic Claude Opus 4.6 | `anthropic/claude-opus-4.6` | | Anthropic Claude Opus 4.8 | `anthropic/claude-opus-4.8` | | Anthropic Claude Sonnet 4.6 | `anthropic/claude-sonnet-4.6` | | Google Gemini 3.1 Flash Lite | `google/gemini-3.1-flash-lite` | | Google Gemini 3.1 Pro (Preview) | `google/gemini-3.1-pro-preview` | | Google Gemini 3.5 Flash | `google/gemini-3.5-flash` | | Moonshot Kimi K2.6 | `moonshotai/kimi-k2.6` | | OpenAI GPT-5.4 | `openai/gpt-5.4` | | OpenAI GPT-5.4 Mini | `openai/gpt-5.4-mini` | The model lineup changes over time. To see the live list your account can use, call `GET /v1/models`, or use `client.models.list()` in the SDK. That endpoint is the source of truth whenever an identifier here returns a "model unavailable" error. These provider prefixed identifiers are specific to the **cloud** API. The open source [Framework](/framework/quickstart) selects models in a different way, by provider class and model ID such as `GoogleGenAI` with `gemini-3.1-flash-lite-preview`. See the [SDK configuration](/framework/sdk/configuration#llm-configuration) for details. ### Execution Limits | Setting | Description | | --------------------- | ----------------------------------------------------------------------- | | **Max Steps** | Maximum number of actions the agent can take (1-10,000) | | **Execution Timeout** | Time limit in seconds before the task is stopped | | **Temperature** | Controls response randomness (0-2). Lower values are more deterministic | ### Features Enables logical thinking and step-by-step analysis. The agent will reason through complex tasks before acting. Allows the agent to process and understand visual content on screen. Required for most UI automation tasks. Enable human-like device interactions for automations where detection matters. Cross-task memory personalization. Use `memoryNamespace` to isolate memory between different workflows. ### Platform Settings | Setting | Description | | --------------- | -------------------------------------------------------------- | | **Device** | Choose a [device](/device-types) from your account | | **Apps** | Select which [apps](/apps) should be available on the device | | **Credentials** | Attach [credentials](/credentials) for authenticated workflows | | **VPN Country** | Route traffic through a specific country | ### Structured Output Define a JSON schema to receive structured responses from the agent. This is useful for: * Data extraction tasks * Validation workflows * Integration with downstream systems When a schema is defined, the agent formats its output to match the specified structure. ## Running the Agent ### Via Playground Use the [Playground](/playground) for interactive testing: 1. Configure your task and settings 2. Select a device 3. Click **Run** 4. Monitor execution in the task stream ### Via API Run tasks programmatically using the [API](/api-keys): ```json theme={null} { "task": "Open Settings and enable dark mode", "llmModel": "mobilerun/mobile-agent-fast", "maxSteps": 50, "executionTimeout": 300, "temperature": 0.5, "reasoning": true, "vision": true, "stealth": true, "memoryNamespace": "my-workflow", "continueOnFailure": false, "apps": ["com.android.settings"], "credentials": [], "outputSchema": { "type": "object", "properties": { "success": { "type": "boolean" }, "darkModeEnabled": { "type": "boolean" } } } } ``` | Parameter | Type | Default | Description | | ------------------- | --------- | ----------- | -------------------------------------------------------------------------------------------------------------------------- | | `task` | string | required | The prompt describing what the agent should do | | `llmModel` | string | required | LLM model identifier such as `"mobilerun/mobile-agent-fast"`. See the [model catalog](#model-selection) for the full list. | | `maxSteps` | integer | `100` | Maximum number of actions the agent can take | | `executionTimeout` | integer | `1000` | Time limit in seconds before the task is stopped | | `temperature` | number | `0.5` | Controls response randomness (0-2) | | `reasoning` | boolean | `false` | Enable step-by-step reasoning mode | | `vision` | boolean | `false` | Enable visual understanding of screen content | | `stealth` | boolean | `false` | Enable stealth mode for human-like device interactions | | `memoryNamespace` | string | `"default"` | Memory namespace for cross-task personalization | | `continueOnFailure` | boolean | `false` | Whether the agent should keep executing if an individual action fails, instead of stopping the task | | `apps` | string\[] | `[]` | Package names of apps to make available on the device | | `credentials` | object\[] | `[]` | Credentials to attach for authenticated workflows | | `outputSchema` | object | `null` | JSON Schema defining the structure of the agent's output | | `vpnCountry` | string | `null` | Route device traffic through a specific country (`"US"`, `"DE"`, `"JP"`, etc.) | ### Via MCP Use the [MCP integration](/mcp-server) to run tasks from AI assistants and IDEs. ## Execution Flow 1. **Initialization** - Agent connects to the selected device 2. **Observation** - Agent captures the current screen state 3. **Planning** - Agent determines the next action based on the prompt 4. **Action** - Agent executes the action (tap, type, scroll, etc.) 5. **Repeat** - Steps 2-4 repeat until the task is complete or limits are reached 6. **Output** - Agent returns results and any structured output ## Best Practices | Practice | Description | | ------------------------- | --------------------------------------------------------------------- | | **Be specific** | Clear prompts lead to more reliable execution | | **Set reasonable limits** | Use appropriate max steps and timeouts for your task complexity | | **Use structured output** | Define schemas when you need to process results programmatically | | **Enable reasoning** | Turn on for complex multi-step tasks that require planning | | **Attach credentials** | Pre-configure authentication rather than including secrets in prompts | # API Keys Source: https://docs.mobilerun.ai/api-keys Create and manage API keys for programmatic access to the Mobilerun API. The API Keys tab allows you to generate and manage authentication keys for accessing the Mobilerun API. Use API keys to integrate Mobilerun into your applications, CI/CD pipelines, and automation workflows. ## Overview API keys provide: * Authentication for API requests * Programmatic access to all Mobilerun features * Integration with external tools and services ## Creating an API Key To generate a new API key: 1. Click **Create API Key** 2. Enter a descriptive name for the key (e.g., "CI Pipeline", "Production Server") 3. Click **Create** 4. Copy the generated key immediately API keys are only displayed once at creation. Store your key securely as you will not be able to view it again. Mobilerun cloud keys always start with the prefix **`dr_sk_`**. Pass the full key, including the prefix, as a bearer token in the form `Authorization: Bearer dr_sk_...`. This is a different credential from your LLM provider keys such as `GOOGLE_API_KEY` or `OPENAI_API_KEY`, which are only used by the local [Framework](/framework/quickstart). See [the two kinds of API keys](/quickstart#two-kinds-of-api-keys) for guidance on when to use each one. ## Managing API Keys Your API key list displays: | Field | Description | | ------------- | -------------------------------------- | | **Name** | The descriptive name you assigned | | **Created** | When the key was generated | | **Last Used** | Most recent API request using this key | ### Revoke Keys Delete API keys that are no longer needed or may have been compromised. Revoked keys immediately stop working for all API requests. Revoking a key cannot be undone. You will need to create a new key and update any integrations that used the old key. ## Using API Keys Include your API key in the request headers when calling the Mobilerun API: ```bash theme={null} curl -X POST https://api.mobilerun.ai/v1/tasks \ -H "Authorization: Bearer dr_sk_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"task": "Open Settings app", "llmModel": "mobilerun/mobile-agent-fast"}' ``` If you are new to the API, the [Cloud Quickstart](/cloud/quickstart) walks through the full flow in TypeScript, Python, and cURL. It covers finding a device, running a task, and reading the result. ### Authentication Header All API requests require the `Authorization` header: ``` Authorization: Bearer dr_sk_YOUR_API_KEY ``` ## API Documentation The complete API reference is available via OpenAPI specification. Access the full documentation to explore all available endpoints: * **Tasks** - Create, monitor, and manage agent tasks * **Devices** - List and manage your devices * **Apps** - Upload and install applications * **Credentials** - Manage stored credentials * **Hooks** - Configure webhooks for task events View the complete API documentation with request/response schemas and examples. ## Best Practices | Practice | Description | | ------------------------- | ------------------------------------------------------------------------- | | **Use descriptive names** | Name keys by their purpose (e.g., "GitHub Actions", "Monitoring Service") | | **Separate environments** | Create distinct keys for development, staging, and production | | **Rotate periodically** | Generate new keys and revoke old ones on a regular schedule | | **Limit exposure** | Store keys in environment variables or secret managers, never in code | | **Monitor usage** | Check the "Last Used" timestamp to identify inactive keys | ## Environment Variables Store your API key as an environment variable rather than hardcoding it: ```bash theme={null} export MOBILERUN_API_KEY="your_api_key_here" ``` Then reference it in your code or scripts: ```bash theme={null} curl -X POST https://api.mobilerun.ai/v1/tasks \ -H "Authorization: Bearer $MOBILERUN_API_KEY" \ -H "Content-Type: application/json" \ -d '{"task": "Open Settings app", "llmModel": "mobilerun/mobile-agent-fast"}' ``` # Get a catalog entry Source: https://docs.mobilerun.ai/api-reference/action-catalog/get-a-catalog-entry /api-reference/workflows.yaml get /action-catalog/{catalogEntryId} Fetch a single action catalog entry by its ID, including its service, method, and parameter schema. Returns 404 if no entry matches. # List action catalog entries Source: https://docs.mobilerun.ai/api-reference/action-catalog/list-action-catalog-entries /api-reference/workflows.yaml get /action-catalog Return a paginated list of catalog entries — the service/method templates that actions are created from, each carrying its parameter schema. Supports filtering by `service`. # Create an action Source: https://docs.mobilerun.ai/api-reference/actions/create-an-action /api-reference/workflows.yaml post /actions Create a reusable action from a catalog entry (`catalogEntryId`), with an optional `params` object supplying the values for that entry's service method. Returns 400 if the params are invalid for the chosen catalog entry. # Delete an action Source: https://docs.mobilerun.ai/api-reference/actions/delete-an-action /api-reference/workflows.yaml delete /actions/{actionId} Delete an action by its ID. Returns 404 if no action matches. # Get an action Source: https://docs.mobilerun.ai/api-reference/actions/get-an-action /api-reference/workflows.yaml get /actions/{actionId} Fetch a single action by its ID, including its configured service, method, and params. Returns 404 if no action matches. # List actions Source: https://docs.mobilerun.ai/api-reference/actions/list-actions /api-reference/workflows.yaml get /actions Return a paginated list of actions. Supports filtering by `service`, free-text `search`, and ordering by name, createdAt, or updatedAt. # List allowed methods for a service Source: https://docs.mobilerun.ai/api-reference/actions/list-allowed-methods-for-a-service /api-reference/workflows.yaml get /actions/services/{service}/methods Return the methods allowed for the given service, each with its parameter definitions (name, type, whether required, description, and optional default/example). Returns 404 if the service is unknown. # List available services Source: https://docs.mobilerun.ai/api-reference/actions/list-available-services /api-reference/workflows.yaml get /actions/services Return the names of the services that actions can be built against. Use these values to look up each service's allowed methods. # Update an action Source: https://docs.mobilerun.ai/api-reference/actions/update-an-action /api-reference/workflows.yaml patch /actions/{actionId} Partially update an action's name, description, or params; all fields are optional. Returns 404 if the action does not exist. # Add a credential field Source: https://docs.mobilerun.ai/api-reference/app-credentials/add-a-credential-field /api-reference/phones.yaml post /credentials/packages/{packageName}/credentials/{credentialName}/fields Adds a single field to an existing credential. The body specifies a `fieldType` (one of the supported field types) and its value. Returns a conflict if a field of that type already exists on the credential. # Create a credential Source: https://docs.mobilerun.ai/api-reference/app-credentials/create-a-credential /api-reference/phones.yaml post /credentials/packages/{packageName} Creates a credential under the given package with a `credentialName` and at least one field. Each field has a `fieldType` (email, username, password, api_token, phone_number, two_factor_secret, or backup_codes) and a value. # Delete a credential and all its fields Source: https://docs.mobilerun.ai/api-reference/app-credentials/delete-a-credential-and-all-its-fields /api-reference/phones.yaml delete /credentials/packages/{packageName}/credentials/{credentialName} Permanently deletes the credential identified by `packageName` and `credentialName`, removing all of its fields. Returns the deleted credential. # Delete a field from a credential Source: https://docs.mobilerun.ai/api-reference/app-credentials/delete-a-field-from-a-credential /api-reference/phones.yaml delete /credentials/packages/{packageName}/credentials/{credentialName}/fields/{fieldType} Removes a single field of the given `fieldType` from the specified credential while leaving the credential itself intact. Returns the updated credential. # Get a credential Source: https://docs.mobilerun.ai/api-reference/app-credentials/get-a-credential /api-reference/phones.yaml get /credentials/packages/{packageName}/credentials/{credentialName} Fetches a single credential by `packageName` and `credentialName`, including all of its stored fields. Returns not found if no matching credential exists. # Initialize a new package/app Source: https://docs.mobilerun.ai/api-reference/app-credentials/initialize-a-new-packageapp /api-reference/phones.yaml post /credentials/packages Creates a new package (identified by `packageName`) under which credentials can be grouped. Returns a conflict if a package with the same name already exists for the user. # List credentials Source: https://docs.mobilerun.ai/api-reference/app-credentials/list-credentials /api-reference/phones.yaml get /credentials Returns a paginated list of all credentials belonging to the authenticated user across every package. Accepts standard pagination query parameters and responds with the credential items plus pagination metadata. # List credentials for a specific package Source: https://docs.mobilerun.ai/api-reference/app-credentials/list-credentials-for-a-specific-package /api-reference/phones.yaml get /credentials/packages/{packageName} Returns all credentials stored under the given `packageName`. Each credential includes its name, secret path, and the list of fields it holds. # List packages Source: https://docs.mobilerun.ai/api-reference/app-credentials/list-packages /api-reference/phones.yaml get /credentials/packages Returns the names of all packages (apps) the authenticated owner has credentials grouped under. Use this to discover which `packageName` values are valid for the per-package credential routes. # Update the value of a credential field Source: https://docs.mobilerun.ai/api-reference/app-credentials/update-the-value-of-a-credential-field /api-reference/phones.yaml patch /credentials/packages/{packageName}/credentials/{credentialName}/fields/{fieldType} Updates the value of an existing field on a credential, identified by `packageName`, `credentialName`, and `fieldType` in the path. The body carries the new value and returns the updated credential. # Get an app event Source: https://docs.mobilerun.ai/api-reference/app-events/get-an-app-event /api-reference/workflows.yaml get /app-events/{id} Fetch a single structured app event by its ID, including its typed payload, source, and originating device. Returns 404 if no event matches. # Get one app-event catalog entry by type/name Source: https://docs.mobilerun.ai/api-reference/app-events/get-one-app-event-catalog-entry-by-typename /api-reference/workflows.yaml get /app-events/catalog/{appEventType} Fetch a single selectable app event by its appEventType (e.g. app.whatsapp.message_received). # List structured app events Source: https://docs.mobilerun.ai/api-reference/app-events/list-structured-app-events /api-reference/workflows.yaml get /app-events Structured, app-scoped events (e.g. app.whatsapp.message_received) derived from raw device notifications. Typed columns — not raw payloads (those stay in the event log). # List the app-event catalog Source: https://docs.mobilerun.ai/api-reference/app-events/list-the-app-event-catalog /api-reference/workflows.yaml get /app-events/catalog Selectable app-based trigger events (e.g. app.whatsapp.message_received) with their predefined payload — served from the JSON definition registry (always in sync, no DB). # Confirm successful app upload Source: https://docs.mobilerun.ai/api-reference/apps-cloud-storage/confirm-successful-app-upload /api-reference/phones.yaml post /apps/{id}/confirm-upload Verifies the APK file exists in R2 and sets the app status to available. # Create a signed R2 upload URL for an app Source: https://docs.mobilerun.ai/api-reference/apps-cloud-storage/create-a-signed-r2-upload-url-for-an-app /api-reference/phones.yaml post /apps/create-signed-upload-url Creates or updates an app and returns pre-signed Cloudflare R2 upload URLs for each file # Delete uploaded app Source: https://docs.mobilerun.ai/api-reference/apps-cloud-storage/delete-uploaded-app /api-reference/phones.yaml delete /apps/{id} Deletes an uploaded app by ID. Removes files from R2 storage and the database entry. # Get app by ID Source: https://docs.mobilerun.ai/api-reference/apps-cloud-storage/get-app-by-id /api-reference/phones.yaml get /apps/{id} Retrieves an app by its ID # Get the user’s storage usage Source: https://docs.mobilerun.ai/api-reference/apps-cloud-storage/get-the-user’s-storage-usage /api-reference/phones.yaml get /apps/storage-usage Returns the user’s total storage quota, bytes used, and remaining bytes — the reliable maximum size for the next upload. # List apps Source: https://docs.mobilerun.ai/api-reference/apps-cloud-storage/list-apps /api-reference/phones.yaml get /apps Retrieves a paginated list of apps with filtering and search capabilities # List versions for an app Source: https://docs.mobilerun.ai/api-reference/apps-cloud-storage/list-versions-for-an-app /api-reference/phones.yaml get /apps/{id}/versions Retrieves all versions of an app visible to the user (own uploads + system versions) # Mark app upload as failed Source: https://docs.mobilerun.ai/api-reference/apps-cloud-storage/mark-app-upload-as-failed /api-reference/phones.yaml post /apps/{id}/mark-failed Sets the app status to failed. # Delete app Source: https://docs.mobilerun.ai/api-reference/apps/delete-app /api-reference/phones/tools.yaml delete /devices/{deviceId}/apps/{packageName} Uninstalls the app identified by the path package name from the device. Protected packages cannot be deleted. # Grant app permission Source: https://docs.mobilerun.ai/api-reference/apps/grant-app-permission /api-reference/phones/tools.yaml put /devices/{deviceId}/apps/{packageName}/permissions/{permission} Grants an Android runtime permission to the package named in the path. The permission is given by its short name (e.g. POST_NOTIFICATIONS). # Install app Source: https://docs.mobilerun.ai/api-reference/apps/install-app /api-reference/phones/tools.yaml post /devices/{deviceId}/apps Requests an app install on the device. The request body must supply exactly one of an Android packageName or an iOS bundleId; protected packages are rejected. background (default false) selects the response contract: false installs inline and returns the outcome directly (200 on success, an error status on failure); true accepts the request and runs the download + install in the background, returning 202 immediately — poll list-app-installs for the backend's view of that attempt's status. Refuses with 409 once 2 other installs are already running on the device, in either mode; a repeat request for an app that already has an install running is also refused with 409 rather than superseding it — retry once that attempt reaches a terminal state. # List app installs Source: https://docs.mobilerun.ai/api-reference/apps/list-app-installs /api-reference/phones/tools.yaml get /devices/{deviceId}/apps/installs Reports the backend's view of background app-install attempts on this device — status reflects the install ATTEMPT, not device ground truth; list-apps remains authoritative for what is actually installed. Records are in-memory and lost on service restart; terminal records are kept ~15 minutes. Not gated on device readiness, so it also answers while the device is offline or crashed. # List apps Source: https://docs.mobilerun.ai/api-reference/apps/list-apps /api-reference/phones/tools.yaml get /devices/{deviceId}/apps Returns detailed information about apps installed on the device, including package name and label. System and protected apps are excluded unless the corresponding query parameters are set. # List packages Source: https://docs.mobilerun.ai/api-reference/apps/list-packages /api-reference/phones/tools.yaml get /devices/{deviceId}/packages Returns the package names of apps installed on the device. System and protected packages are excluded unless the corresponding query parameters are set. # Open a deep link Source: https://docs.mobilerun.ai/api-reference/apps/open-a-deep-link /api-reference/phones/tools.yaml post /devices/{deviceId}/apps/open-deep-link Opens a deep link on the device. On Android the link is dispatched as an intent — packageName optionally pins it to a specific app and action overrides the default android.intent.action.VIEW. On iOS the URL is opened directly and the optional fields must be omitted. Protected packages are rejected. # Revoke app permission Source: https://docs.mobilerun.ai/api-reference/apps/revoke-app-permission /api-reference/phones/tools.yaml delete /devices/{deviceId}/apps/{packageName}/permissions/{permission} Revokes an Android runtime permission from the package named in the path. The permission is given by its short name (e.g. POST_NOTIFICATIONS). # Start app Source: https://docs.mobilerun.ai/api-reference/apps/start-app /api-reference/phones/tools.yaml put /devices/{deviceId}/apps/{packageName} Launches the app identified by the path package name, optionally starting a specific activity given in the request body. Protected packages cannot be started. # Stop app Source: https://docs.mobilerun.ai/api-reference/apps/stop-app /api-reference/phones/tools.yaml patch /devices/{deviceId}/apps/{packageName} Force-stops the app identified by the path package name. When clearData is set in the request body, the app's data is also cleared. Protected packages cannot be stopped. # Execute JavaScript in the device's Chrome browser (CDP) Source: https://docs.mobilerun.ai/api-reference/browser/execute-javascript-in-the-devices-chrome-browser-cdp /api-reference/phones/tools.yaml post /devices/{deviceId}/browser/execute-script Evaluates a JavaScript expression in the device's foreground Chrome tab via the Chrome DevTools Protocol and returns its JSON-serialized result. Devices without browser support return an unsupported-feature error. # List available countries Source: https://docs.mobilerun.ai/api-reference/countries/list-available-countries /api-reference/mobilerun-proxy.yaml get /connect/countries Lookup of countries that can be selected when creating a proxy. Each country lists the proxy types available there; without a ?type filter, every covered country is returned. # Count claimed devices Source: https://docs.mobilerun.ai/api-reference/devices/count-claimed-devices /api-reference/phones.yaml get /devices/count Returns the number of claimed devices for the user, broken down by device type. # Get capabilities for a specific device Source: https://docs.mobilerun.ai/api-reference/devices/get-capabilities-for-a-specific-device /api-reference/phones.yaml get /devices/{deviceId}/capabilities Returns the set of capabilities supported by this device. For a legacy device this reflects the live instance's actual tools rather than its static type; for a core-managed device it is resolved from provider/pool configuration without guaranteeing a live instance. Used to determine which tools and features are available for the device. # Get device info Source: https://docs.mobilerun.ai/api-reference/devices/get-device-info /api-reference/phones.yaml get /devices/{deviceId} Returns the current state and metadata for a single device, including its lifecycle state, type, stream URL, billing strategy, and timestamps. A stream token is included while the device is active. # List devices Source: https://docs.mobilerun.ai/api-reference/devices/list-devices /api-reference/phones.yaml get /devices Returns a paginated list of the user's devices along with pagination metadata. # List tasks for a device Source: https://docs.mobilerun.ai/api-reference/devices/list-tasks-for-a-device /api-reference/phones.yaml get /devices/{deviceId}/tasks Returns a paginated list of tasks that have run on the device, along with pagination metadata. # Provision a new device Source: https://docs.mobilerun.ai/api-reference/devices/provision-a-new-device /api-reference/phones.yaml post /devices Requests a new device for the authenticated user from the device spec in the request body. Optional query parameters select the canonical device type, target country, billing mode, and a profile to use as the base spec; deprecated device-type aliases remain accepted only during the documented compatibility grace period. The response returns the device and its stream token. # Reboot a device Source: https://docs.mobilerun.ai/api-reference/devices/reboot-a-device /api-reference/phones.yaml post /devices/{deviceId}/reboot Triggers a reboot of the device. The device transitions through its reboot lifecycle and becomes ready again once the restart completes. # Reset a device to a fresh state Source: https://docs.mobilerun.ai/api-reference/devices/reset-a-device-to-a-fresh-state /api-reference/phones.yaml post /devices/{deviceId}/reset Resets the device back to a clean state, clearing installed apps and user data accumulated during the session. The device transitions through its reset lifecycle before becoming ready again. # Terminate a device Source: https://docs.mobilerun.ai/api-reference/devices/terminate-a-device /api-reference/phones.yaml delete /devices/{deviceId} Terminates the device and releases its resources. Termination can be scheduled for a future time or chained from a previous device via the request body, in which case a service key is required. # Update device name Source: https://docs.mobilerun.ai/api-reference/devices/update-device-name /api-reference/phones.yaml put /devices/{deviceId}/name Sets the display name for a device from the name in the request body and returns the updated device. # Wait for device to be ready Source: https://docs.mobilerun.ai/api-reference/devices/wait-for-device-to-be-ready /api-reference/phones.yaml get /devices/{deviceId}/wait Blocks until the device reaches the ready state, then returns the same payload as Get device info. The call returns early with an error if the wait is cancelled or times out. # Ingest an event Source: https://docs.mobilerun.ai/api-reference/events/ingest-an-event /api-reference/workflows.yaml post /events/ingest Ingest an event for trigger evaluation. Returns immediately with 202 Accepted. # Simulate event matching (dry run) Source: https://docs.mobilerun.ai/api-reference/events/simulate-event-matching-dry-run /api-reference/workflows.yaml post /events/dry-run Simulate an event against all configured flows. Returns which flows would match and what actions would run, without storing the event or enqueuing jobs. # Abort a running or pending execution Source: https://docs.mobilerun.ai/api-reference/executions/abort-a-running-or-pending-execution /api-reference/workflows.yaml post /executions/{executionId}/abort Signals the worker to stop the execution between steps and marks it cancelled. Idempotent-ish: already-terminal executions return 409. # Get execution details Source: https://docs.mobilerun.ai/api-reference/executions/get-execution-details /api-reference/workflows.yaml get /executions/{executionId} Fetch a single flow execution by its ID, including its status, kind, result or error, and start/finish timestamps. Returns 404 if no execution matches. # Get execution metrics Source: https://docs.mobilerun.ai/api-reference/executions/get-execution-metrics /api-reference/workflows.yaml get /executions/metrics Return aggregate execution metrics — total count, counts by status, average duration, and the last execution time. Can be scoped by `flowId`, `triggerId`, and a `from`/`to` time range. # List flow executions Source: https://docs.mobilerun.ai/api-reference/executions/list-flow-executions /api-reference/workflows.yaml get /executions Return a paginated history of flow executions. Supports filtering by `flowId`, `triggerId`, `status`, and a `from`/`to` time range, plus free-text `search` and ordering by startedAt, finishedAt, or status. # Delete file Source: https://docs.mobilerun.ai/api-reference/files/delete-file /api-reference/phones/tools.yaml delete /devices/{deviceId}/files Deletes the file at the path given in the path query parameter from the device. # Download file Source: https://docs.mobilerun.ai/api-reference/files/download-file /api-reference/phones/tools.yaml get /devices/{deviceId}/files/download Pulls the file at the given path query parameter from the device and returns its raw bytes as an octet-stream. # List files Source: https://docs.mobilerun.ai/api-reference/files/list-files /api-reference/phones/tools.yaml get /devices/{deviceId}/files Lists the files at the directory path given in the path query parameter, returning each entry's metadata along with the path and total count. # Upload file Source: https://docs.mobilerun.ai/api-reference/files/upload-file /api-reference/phones/tools.yaml post /devices/{deviceId}/files Uploads a file to the device via multipart form data, writing it into the directory given by the path query parameter using the uploaded file's name. # Add an action to a flow Source: https://docs.mobilerun.ai/api-reference/flows/add-an-action-to-a-flow /api-reference/workflows.yaml post /flows/{flowId}/actions Append a single action to a flow at the given `position`, optionally nesting it under a `parentFlowActionId` or supplying its own `children`. Supports a `nameOverride`, param `overrides`, and `continueOnError`. Returns 404 if the flow does not exist. # Clone a flow Source: https://docs.mobilerun.ai/api-reference/flows/clone-a-flow /api-reference/workflows.yaml post /flows/{flowId}/clone Create a copy of an existing flow, including its actions and settings. The optional body can override the new flow's `name` and target `deviceIds`. Returns 404 if the source flow does not exist. # Create a flow Source: https://docs.mobilerun.ai/api-reference/flows/create-a-flow /api-reference/workflows.yaml post /flows Create a flow that binds a trigger (`triggerId`) to an ordered list of actions, with at least one action required. Optional settings include target `deviceIds`, a cooldown (`cooldownSeconds`/`cooldownScope`), and webhook notifications on success or failure. # Delete a flow Source: https://docs.mobilerun.ai/api-reference/flows/delete-a-flow /api-reference/workflows.yaml delete /flows/{flowId} Delete a flow by its ID. Returns 404 if no flow matches. # Dry-run a flow Source: https://docs.mobilerun.ai/api-reference/flows/dry-run-a-flow /api-reference/workflows.yaml post /flows/{flowId}/dry-run Simulate this flow firing without storing events, enqueuing jobs, or consuming cooldown/rate-limit slots. Works for every trigger activation type: - `event`: validates the payload against the event catalog schema and evaluates the trigger conditions. - `custom`: validates the payload against the custom payload schema (conditions do not apply). - `schedule`: ignores the payload and reports the next fire time. The response reports `wouldFire` — whether the flow would actually run right now — alongside the gates that decide it (enabled, device attached, blocked, cooldown). `rateLimited` is informational and is not folded into `wouldFire`. # Get a flow Source: https://docs.mobilerun.ai/api-reference/flows/get-a-flow /api-reference/workflows.yaml get /flows/{flowId} Fetch a single flow by its ID, including its trigger binding, configuration, and current status. Returns 404 if no flow matches. # List actions for a flow Source: https://docs.mobilerun.ai/api-reference/flows/list-actions-for-a-flow /api-reference/workflows.yaml get /flows/{flowId}/actions Return the ordered list of actions attached to a flow, including any nested child actions. Returns 404 if the flow does not exist. # List flows Source: https://docs.mobilerun.ai/api-reference/flows/list-flows /api-reference/workflows.yaml get /flows Return a paginated list of flows. Supports filtering by `triggerId`, `enabled`, one or more health `status` values (healthy, failing, blocked), `mine` (flows created by the calling actor), `createdBy` (flows created by a given actor id — mutually exclusive with `mine`), plus free-text `search` and ordering. # List self-healing repair episodes Source: https://docs.mobilerun.ai/api-reference/flows/list-self-healing-repair-episodes /api-reference/workflows.yaml get /flows/{flowId}/repairs # Remove an action from a flow Source: https://docs.mobilerun.ai/api-reference/flows/remove-an-action-from-a-flow /api-reference/workflows.yaml delete /flows/{flowId}/actions/{flowActionId} Remove a single action from a flow by its `flowActionId`. Returns 404 if the flow or flow action does not exist. # Replace all actions for a flow Source: https://docs.mobilerun.ai/api-reference/flows/replace-all-actions-for-a-flow /api-reference/workflows.yaml put /flows/{flowId}/actions Replace a flow's entire action list with the supplied set (at least one required). Each action references an `actionId` and a unique `position`, and may include nested `children`, a `nameOverride`, param `overrides`, and a `continueOnError` flag. Returns 404 if the flow does not exist. # Unblock a flow Source: https://docs.mobilerun.ai/api-reference/flows/unblock-a-flow /api-reference/workflows.yaml post /flows/{flowId}/unblock Clear a flow's blocked status after fixing the underlying issue. Idempotent — safe to call on already-healthy flows. # Update a flow Source: https://docs.mobilerun.ai/api-reference/flows/update-a-flow /api-reference/workflows.yaml patch /flows/{flowId} Partially update a flow's settings — name, trigger binding, enabled state, target devices, cooldown, or notifications; all fields are optional. Actions are managed through the flow-actions endpoints, not here. Returns 404 if the flow does not exist. # Check if overlay is visible Source: https://docs.mobilerun.ai/api-reference/misc/check-if-overlay-is-visible /api-reference/phones/tools.yaml get /devices/{deviceId}/overlay Returns whether the accessibility overlay is currently visible on the device. # Device fingerprint snapshot Source: https://docs.mobilerun.ai/api-reference/misc/device-fingerprint-snapshot /api-reference/phones/tools.yaml get /devices/{deviceId}/fingerprint Returns a live snapshot of the device's spoofed identity, including model, display, identifiers, and carrier. Devices without fingerprint support return an unsupported-feature error. # Device time Source: https://docs.mobilerun.ai/api-reference/misc/device-time /api-reference/phones/tools.yaml get /devices/{deviceId}/time Returns the device's current wall-clock time as an RFC 3339 timestamp. # Disable kiosk (lock-task) mode Source: https://docs.mobilerun.ai/api-reference/misc/disable-kiosk-lock-task-mode /api-reference/phones/tools.yaml delete /devices/{deviceId}/kiosk Disables Android lock-task (kiosk) mode on the device, releasing it from the locked app. # Enable kiosk (lock-task) mode Source: https://docs.mobilerun.ai/api-reference/misc/enable-kiosk-lock-task-mode /api-reference/phones/tools.yaml put /devices/{deviceId}/kiosk Locks the device to the package named in the request body using Android lock-task (kiosk) mode, preventing the user from leaving the app. # Get device language/locale Source: https://docs.mobilerun.ai/api-reference/misc/get-device-languagelocale /api-reference/phones/tools.yaml get /devices/{deviceId}/language Returns the device's current language/locale as a BCP-47 locale string. # Get device location Source: https://docs.mobilerun.ai/api-reference/misc/get-device-location /api-reference/phones/tools.yaml get /devices/{deviceId}/location Returns the device's current simulated GPS location as latitude and longitude. Devices without geo support return an unsupported-feature error. # Get device timezone Source: https://docs.mobilerun.ai/api-reference/misc/get-device-timezone /api-reference/phones/tools.yaml get /devices/{deviceId}/timezone Returns the device's current timezone identifier. Devices that do not support timezone control return an unsupported-feature error. # Reset the device location to default Source: https://docs.mobilerun.ai/api-reference/misc/reset-the-device-location-to-default /api-reference/phones/tools.yaml delete /devices/{deviceId}/location Clears any simulated GPS location and restores the device's default location behavior. Devices without geo support return an unsupported-feature error. # Set device language/locale Source: https://docs.mobilerun.ai/api-reference/misc/set-device-languagelocale /api-reference/phones/tools.yaml post /devices/{deviceId}/language Sets the device language/locale to the BCP-47 locale in the request body. An optional restart flag applies the change immediately by restarting the zygote instead of waiting for the next reboot. # Set device location Source: https://docs.mobilerun.ai/api-reference/misc/set-device-location /api-reference/phones/tools.yaml post /devices/{deviceId}/location Sets the device's simulated GPS location to the latitude and longitude in the request body. Devices without geo support return an unsupported-feature error. # Set device timezone Source: https://docs.mobilerun.ai/api-reference/misc/set-device-timezone /api-reference/phones/tools.yaml post /devices/{deviceId}/timezone Sets the device timezone to the identifier in the request body. Devices that do not support timezone control return an unsupported-feature error. # Set overlay visibility Source: https://docs.mobilerun.ai/api-reference/misc/set-overlay-visibility /api-reference/phones/tools.yaml post /devices/{deviceId}/overlay Shows or hides the accessibility overlay on the device based on the visibility flag in the request body. # Get all LLM models Source: https://docs.mobilerun.ai/api-reference/models/get-all-llm-models /api-reference/tasks.yaml get /models Get all LLM models # Clear input Source: https://docs.mobilerun.ai/api-reference/navigation/clear-input /api-reference/phones/tools.yaml delete /devices/{deviceId}/keyboard Clears the contents of the currently focused text input field. # Input key Source: https://docs.mobilerun.ai/api-reference/navigation/input-key /api-reference/phones/tools.yaml put /devices/{deviceId}/keyboard Sends a single Android key event to the device, identified by its key code. # Input text Source: https://docs.mobilerun.ai/api-reference/navigation/input-text /api-reference/phones/tools.yaml post /devices/{deviceId}/keyboard Types the given text into the focused input field. Supports optionally clearing the field first and a stealth mode that emulates human typing speed and error rate on supported devices. # Perform a global action Source: https://docs.mobilerun.ai/api-reference/navigation/perform-a-global-action /api-reference/phones/tools.yaml post /devices/{deviceId}/global Performs a global system action on the device, such as navigating back or going to the home screen, identified by an action code. # Swipe Source: https://docs.mobilerun.ai/api-reference/navigation/swipe /api-reference/phones/tools.yaml post /devices/{deviceId}/swipe Swipes from a start coordinate to an end coordinate over the given duration in milliseconds. An optional stealth flag applies human-like jitter and curved paths on devices that support it. # Take screenshot Source: https://docs.mobilerun.ai/api-reference/navigation/take-screenshot /api-reference/phones/tools.yaml get /devices/{deviceId}/screenshot Captures the device screen and returns it as a PNG image. An optional hideOverlay query parameter excludes the accessibility overlay from the capture. # Tap by coordinates Source: https://docs.mobilerun.ai/api-reference/navigation/tap-by-coordinates /api-reference/phones/tools.yaml post /devices/{deviceId}/tap Taps the device screen at the given x/y coordinates. An optional stealth flag routes the tap through human-like input on devices that support it. # UI state Source: https://docs.mobilerun.ai/api-reference/navigation/ui-state /api-reference/phones/tools.yaml get /devices/{deviceId}/ui-state Returns the current accessibility UI state of the device as a structured tree of on-screen elements. An optional filter query reduces the result to interactive elements. # Get notification preferences Source: https://docs.mobilerun.ai/api-reference/notifications/get-notification-preferences /api-reference/webhooks.yaml get /notifications/preferences Returns your current notification preferences, expressed as the list of event types you have muted. An empty list means notifications are enabled for all notifiable event types. # List notification event types Source: https://docs.mobilerun.ai/api-reference/notifications/list-notification-event-types /api-reference/webhooks.yaml get /notifications/catalog Returns the catalog of notifiable event types grouped by source category. Each event lists its type identifier, label, and description, which can be referenced when muting event types in notification preferences. # Update notification preferences Source: https://docs.mobilerun.ai/api-reference/notifications/update-notification-preferences /api-reference/webhooks.yaml patch /notifications/preferences Replaces your set of muted event types with the supplied list. Any unknown or non-notifiable types are dropped, and the response returns the muted types that were actually stored. # Cancel a phone-number purchase or an active rental Source: https://docs.mobilerun.ai/api-reference/numbers/cancel-a-phone-number-purchase-or-an-active-rental /api-reference/mobilerun-numbers.yaml delete /numbers/phones/{id} Cancels a Mobilerun Phone. The outcome depends on the number's current state: - If the number is still awaiting payment and no payment for it is currently being processed, the checkout is closed immediately and the number is retired. - If the number is on the standard paid plan and already paid and in service, cancellation is scheduled for the end of the current billing period rather than taking effect immediately. The number stays usable through the period already paid for, with no partial refund. Calling this again while a cancellation is already scheduled is a no-op that returns the same result. The response's `state` reflects this as `cancel_scheduled` with `cancelAtPeriodEnd: true`; `currentPeriodEnd` is populated once billing confirms the cancellation. Any other state (already refunding, a permanent billing failure, a payment currently being processed, an included-plan number, or a non-hosted/BYO number) returns 409 `not_cancellable`. Returns 404 if the number doesn't exist or isn't owned by the caller. # Get phone number by id Source: https://docs.mobilerun.ai/api-reference/numbers/get-phone-number-by-id /api-reference/mobilerun-numbers.yaml get /numbers/phones/{id} Retrieves a single phone number. # List dedicated-number countries Source: https://docs.mobilerun.ai/api-reference/numbers/list-dedicated-number-countries /api-reference/mobilerun-numbers.yaml get /numbers/phones/countries Lists the countries currently offered for a dedicated Mobilerun Phone, with live stock status. Pass `country` as the `country` field on POST /numbers/phones. # List messages (inbox + outbox) on a number Source: https://docs.mobilerun.ai/api-reference/numbers/list-messages-inbox-+-outbox-on-a-number /api-reference/mobilerun-numbers.yaml get /numbers/phones/{id}/messages Returns SMS messages on the number, inbound and outbound, scoped to the authenticated user. Newest first. Messages stay with the number across device switches. # List phone numbers Source: https://docs.mobilerun.ai/api-reference/numbers/list-phone-numbers /api-reference/mobilerun-numbers.yaml get /numbers/phones Lists phone numbers owned by the authenticated user — both BYO (`user`) and provisioned (`mobilerun`) numbers. # List phone purposes Source: https://docs.mobilerun.ai/api-reference/numbers/list-phone-purposes /api-reference/mobilerun-numbers.yaml get /numbers/phones/purposes Lists the optional purposes currently available for a Mobilerun Phone. # Purchase a phone number Source: https://docs.mobilerun.ai/api-reference/numbers/purchase-a-phone-number /api-reference/mobilerun-numbers.yaml post /numbers/phones Starts a Mobilerun Phone purchase for the authenticated owner. Accepted requests always return the same asynchronous envelope; poll GET /numbers/phones/{id} for its business state. `purpose` and `country` are mutually exclusive. # Create a proxy Source: https://docs.mobilerun.ai/api-reference/proxies/create-a-proxy /api-reference/mobilerun-proxy.yaml post /connect/proxies Provisions a proxy of the requested type for the caller in the selected country. # Delete a proxy Source: https://docs.mobilerun.ai/api-reference/proxies/delete-a-proxy /api-reference/mobilerun-proxy.yaml delete /connect/proxies/{id} Deletes the proxy identified by the path ID and releases its provisioning. Returns 404 if no such proxy exists for the caller. # Get proxy by ID Source: https://docs.mobilerun.ai/api-reference/proxies/get-proxy-by-id /api-reference/mobilerun-proxy.yaml get /connect/proxies/{id} Returns the proxy identified by the path ID. The response includes the proxy's password. # Latency check Source: https://docs.mobilerun.ai/api-reference/proxies/latency-check /api-reference/mobilerun-proxy.yaml get /connect/proxies/{id}/ping Returns the most recent cached network-latency measurement for the proxy, sampled periodically by connecting through the proxy to a fixed target. `latency` is null when no measurement is available yet (e.g. the proxy is not active, or it has not been sampled since coming online). # List proxies Source: https://docs.mobilerun.ai/api-reference/proxies/list-proxies /api-reference/mobilerun-proxy.yaml get /connect/proxies Returns proxies owned by the calling tenant (the X-Owner-Id header, falling back to X-User-ID). Credentials are omitted from the list. # List Proxy Connections Source: https://docs.mobilerun.ai/api-reference/proxies/list-proxy-connections /api-reference/mobilerun-proxy.yaml get /connect/proxies/{id}/connections Returns the connection history recorded for this proxy, one item per connection (aggregated across the connection's lifetime). Supports filtering on every property plus ordering and pagination. Returns 503 when the connection-insights backend is disabled or unreachable. # Connect proxy Source: https://docs.mobilerun.ai/api-reference/proxy/connect-proxy /api-reference/phones/tools.yaml post /devices/{deviceId}/proxy Routes the device's traffic through a SOCKS5 proxy supplied in the request body, replacing any existing connection. A smartIp option can be used to select an IP automatically; the legacy flat host/port/user/password fields remain supported. # Disconnect proxy Source: https://docs.mobilerun.ai/api-reference/proxy/disconnect-proxy /api-reference/phones/tools.yaml delete /devices/{deviceId}/proxy Disconnects the device's active proxy connection and clears its stored proxy state. Returns successfully if no proxy is connected. # Get proxy connection state Source: https://docs.mobilerun.ai/api-reference/proxy/get-proxy-connection-state /api-reference/phones/tools.yaml get /devices/{deviceId}/proxy Returns the device's current proxy connection state, including whether a proxy is connected and its protocol and name. # Delete a device recording Source: https://docs.mobilerun.ai/api-reference/recordings/delete-a-device-recording /api-reference/phones/tools.yaml delete /devices/{deviceId}/recordings/{recordingId} # Get a device recording Source: https://docs.mobilerun.ai/api-reference/recordings/get-a-device-recording /api-reference/phones/tools.yaml get /devices/{deviceId}/recordings/{recordingId} # Get a device recording trajectory Source: https://docs.mobilerun.ai/api-reference/recordings/get-a-device-recording-trajectory /api-reference/phones/tools.yaml get /devices/{deviceId}/recordings/{recordingId}/trajectory # Get a device recording video Source: https://docs.mobilerun.ai/api-reference/recordings/get-a-device-recording-video /api-reference/phones/tools.yaml get /devices/{deviceId}/recordings/{recordingId}/video # List device recordings Source: https://docs.mobilerun.ai/api-reference/recordings/list-device-recordings /api-reference/phones/tools.yaml get /devices/{deviceId}/recordings # Start a device recording Source: https://docs.mobilerun.ai/api-reference/recordings/start-a-device-recording /api-reference/phones/tools.yaml post /devices/{deviceId}/recordings # Stop a device recording Source: https://docs.mobilerun.ai/api-reference/recordings/stop-a-device-recording /api-reference/phones/tools.yaml post /devices/{deviceId}/recordings/{recordingId} # Attach Task Source: https://docs.mobilerun.ai/api-reference/tasks/attach-task /api-reference/tasks.yaml get /tasks/{task_id}/attach Attach to a running task and receive its events as an SSE stream. # Get Task Source: https://docs.mobilerun.ai/api-reference/tasks/get-task /api-reference/tasks.yaml get /tasks/{task_id} Get full details of a task by ID. # Get Task Screenshot Source: https://docs.mobilerun.ai/api-reference/tasks/get-task-screenshot /api-reference/tasks.yaml get /tasks/{task_id}/screenshots/{index} Get a specific screenshot by index. # Get Task Screenshots Source: https://docs.mobilerun.ai/api-reference/tasks/get-task-screenshots /api-reference/tasks.yaml get /tasks/{task_id}/screenshots List all screenshot URLs for a task. # Get Task Status Source: https://docs.mobilerun.ai/api-reference/tasks/get-task-status /api-reference/tasks.yaml get /tasks/{task_id}/status Get the status of a task. # Get Task Trajectory Source: https://docs.mobilerun.ai/api-reference/tasks/get-task-trajectory /api-reference/tasks.yaml get /tasks/{task_id}/trajectory Get the trajectory of a task. # Get Task Ui State Source: https://docs.mobilerun.ai/api-reference/tasks/get-task-ui-state /api-reference/tasks.yaml get /tasks/{task_id}/ui_states/{index} Get a specific UI state by index. # Get Task Ui States Source: https://docs.mobilerun.ai/api-reference/tasks/get-task-ui-states /api-reference/tasks.yaml get /tasks/{task_id}/ui_states List all UI state URLs for a task. # List Tasks Source: https://docs.mobilerun.ai/api-reference/tasks/list-tasks /api-reference/tasks.yaml get /tasks List tasks with optional filtering, sorting, and pagination. # Run Streamed Task Source: https://docs.mobilerun.ai/api-reference/tasks/run-streamed-task /api-reference/tasks.yaml post /tasks/stream Create and dispatch a new agent task, returning an SSE stream of task events. Cancels the task if the client disconnects. # Run Task Source: https://docs.mobilerun.ai/api-reference/tasks/run-task /api-reference/tasks.yaml post /tasks Create and dispatch a new agent task. Returns the task ID and device stream details. # Send Message Source: https://docs.mobilerun.ai/api-reference/tasks/send-message /api-reference/tasks.yaml post /tasks/{task_id}/message Send a message to a running agent task. The message ID is delivered via SSE (UserMessageEvent with action=queued). # Stop Task Source: https://docs.mobilerun.ai/api-reference/tasks/stop-task /api-reference/tasks.yaml post /tasks/{task_id}/cancel Cancel a running task. Returns an error if the task is already in a terminal state. # Create a trigger Source: https://docs.mobilerun.ai/api-reference/triggers/create-a-trigger /api-reference/workflows.yaml post /triggers Create a trigger with an activation type of `event`, `schedule`, or `custom`. Each type requires its own fields (e.g. `eventType` and optional `conditions` for events, `scheduleRule` and `timezone` for schedules, `customPayloadSchema` for custom triggers); mismatched fields are rejected. # Delete a trigger Source: https://docs.mobilerun.ai/api-reference/triggers/delete-a-trigger /api-reference/workflows.yaml delete /triggers/{triggerId} Delete a trigger by its ID. Returns 404 if no trigger matches. # Fire a custom trigger with payload Source: https://docs.mobilerun.ai/api-reference/triggers/fire-a-custom-trigger-with-payload /api-reference/workflows.yaml post /triggers/{triggerId}/fire Invoke a custom trigger directly with an arbitrary JSON payload. Fan-out: a trigger may be referenced by multiple flows (workflows). Firing it enqueues one execution per enabled, non-deleted flow attached to this trigger, each receiving the same payload. The `enqueuedCount` in the response reports how many were enqueued (0 if no flows are attached, or if all matching flows are gated by a cooldown). Payload validation: - If the trigger has a `customPayloadSchema`, the payload is validated against it (JSON Schema via AJV). - If no schema is configured, the payload only needs to be a JSON object — any keys and values are accepted. Only triggers with `activation = "custom"` can be fired through this endpoint; event and schedule triggers return 409. # Get a trigger Source: https://docs.mobilerun.ai/api-reference/triggers/get-a-trigger /api-reference/workflows.yaml get /triggers/{triggerId} Fetch a single trigger by its ID, including its activation type and type-specific configuration. Returns 404 if no trigger matches. # List triggers Source: https://docs.mobilerun.ai/api-reference/triggers/list-triggers /api-reference/workflows.yaml get /triggers Return a paginated list of triggers. Supports filtering by `activation` and `eventType`, free-text `search`, and ordering by name, createdAt, or updatedAt. # Update a trigger Source: https://docs.mobilerun.ai/api-reference/triggers/update-a-trigger /api-reference/workflows.yaml patch /triggers/{triggerId} Partially update a trigger; all fields are optional. When `activation` is changed, the type-specific field rules are re-validated. Returns 404 if the trigger does not exist. # Create a SOCKS5 user Source: https://docs.mobilerun.ai/api-reference/users/create-a-socks5-user /api-reference/mobilerun-proxy.yaml post /connect/users Creates a SOCKS5 credential, optionally bound to a proxy for dedicated routing. Username and password are generated when omitted. # Delete a SOCKS5 user Source: https://docs.mobilerun.ai/api-reference/users/delete-a-socks5-user /api-reference/mobilerun-proxy.yaml delete /connect/users/{id} Deletes the SOCKS5 user identified by the path ID, revoking its credentials and any proxy binding. Returns 404 if no such user exists for the caller. # Get a SOCKS5 user by ID Source: https://docs.mobilerun.ai/api-reference/users/get-a-socks5-user-by-id /api-reference/mobilerun-proxy.yaml get /connect/users/{id} Returns the SOCKS5 user identified by the path ID. The response includes the user's password. # List connections by SOCKS5 user Source: https://docs.mobilerun.ai/api-reference/users/list-connections-by-socks5-user /api-reference/mobilerun-proxy.yaml get /connect/users/{id}/connections Returns the connection history recorded for this user, one item per connection (aggregated across the connection's lifetime). Supports filtering on every property plus ordering and pagination. Returns 503 when the connection-insights backend is disabled or unreachable. # List SOCKS5 users Source: https://docs.mobilerun.ai/api-reference/users/list-socks5-users /api-reference/mobilerun-proxy.yaml get /connect/users Returns SOCKS5 users owned by the caller. Passwords are omitted from the list. # Update a SOCKS5 user Source: https://docs.mobilerun.ai/api-reference/users/update-a-socks5-user /api-reference/mobilerun-proxy.yaml patch /connect/users/{id} Rebind the user to a different proxy (or detach it by passing null). # Get a delivery with its attempts Source: https://docs.mobilerun.ai/api-reference/webhook-deliveries/get-a-delivery-with-its-attempts /api-reference/webhooks.yaml get /webhooks/{id}/deliveries/{deliveryId} Returns a single delivery for a webhook subscription along with the full list of captured attempt records. Each attempt includes the request URL, method, headers and body, whether it was signed, and the response status, headers, and snippet. # Get delivery statistics Source: https://docs.mobilerun.ai/api-reference/webhook-deliveries/get-delivery-statistics /api-reference/webhooks.yaml get /webhooks/deliveries/stats Returns aggregate delivery statistics across all of your webhooks, including the total count, a breakdown by status (pending, success, skipped, dead), and the overall success rate. An optional `since` timestamp narrows the reporting window. # List deliveries across all your webhooks Source: https://docs.mobilerun.ai/api-reference/webhook-deliveries/list-deliveries-across-all-your-webhooks /api-reference/webhooks.yaml get /webhooks/deliveries Returns a paginated feed of webhook deliveries across all of your subscriptions, with the originating endpoint URL included on each record. Results can be filtered by delivery status (pending, success, skipped, or dead), by a `since` timestamp, and by `eventId` (exact match against the originating event id). # List deliveries for a webhook Source: https://docs.mobilerun.ai/api-reference/webhook-deliveries/list-deliveries-for-a-webhook /api-reference/webhooks.yaml get /webhooks/{id}/deliveries Returns a paginated list of deliveries for a single webhook subscription, identified by its id. Each record reports the event, delivery status, attempt count, and the last response code or error. Results can be filtered by `eventId` (exact match against the originating event id). # List subscribable event types per source Source: https://docs.mobilerun.ai/api-reference/webhook-event-types/list-subscribable-event-types-per-source /api-reference/webhooks.yaml get /event-types Returns the catalog of event types that webhook subscriptions can subscribe to, grouped by source. Use the returned type identifiers as the `eventTypes` values when creating or updating a webhook. # Delete a webhook subscription Source: https://docs.mobilerun.ai/api-reference/webhooks/delete-a-webhook-subscription /api-reference/webhooks.yaml delete /webhooks/{id} Deletes a webhook subscription so it stops receiving deliveries. Returns 204 No Content on success. # Get a webhook subscription Source: https://docs.mobilerun.ai/api-reference/webhooks/get-a-webhook-subscription /api-reference/webhooks.yaml get /webhooks/{id} Returns a single webhook subscription by id, including its URL, subscribed event types, state, and system-observed delivery health. The signing secret is never included. # List your webhook subscriptions Source: https://docs.mobilerun.ai/api-reference/webhooks/list-your-webhook-subscriptions /api-reference/webhooks.yaml get /webhooks Returns a paginated list of your webhook subscriptions, optionally filtered by status (active, failing, blocked, or disabled) and/or by `search` (a case-insensitive substring match against the URL or description). The response also includes per-status counts across all of your subscriptions. # Register a webhook subscription Source: https://docs.mobilerun.ai/api-reference/webhooks/register-a-webhook-subscription /api-reference/webhooks.yaml post /webhooks Creates a webhook subscription with a delivery URL and an optional list of event types to subscribe to (defaults to all when omitted). The response includes the generated signing secret, which is returned only once at creation time and cannot be retrieved later. # Rotate signing secret Source: https://docs.mobilerun.ai/api-reference/webhooks/rotate-signing-secret /api-reference/webhooks.yaml post /webhooks/{id}/rotate-secret Generates a new signing secret for the webhook subscription and returns it once in the response. The previous secret is replaced immediately, so any signature verification on your endpoint must be updated to use the new value. # Send a one-shot test delivery Source: https://docs.mobilerun.ai/api-reference/webhooks/send-a-one-shot-test-delivery /api-reference/webhooks.yaml post /webhooks/{id}/test Sends a single test payload to the webhook subscription URL to verify connectivity. The response reports whether the attempt succeeded along with the returned HTTP status code or error, if any. # Update a webhook subscription Source: https://docs.mobilerun.ai/api-reference/webhooks/update-a-webhook-subscription /api-reference/webhooks.yaml patch /webhooks/{id} Updates a webhook subscription. Any combination of the subscribed event types, state (ACTIVE or DISABLED), and description may be changed, and at least one field must be supplied. Setting state to ACTIVE re-enables a subscription that was auto-blocked after sustained delivery failures. # Apps Source: https://docs.mobilerun.ai/apps Manage your app library by uploading custom APKs to deploy to your devices. The Apps tab is your central hub for managing Android applications. Upload your own APKs to build your app library, then deploy to your devices. ## Overview The Apps tab allows you to: * View all apps available in your user space * Upload custom APK files * Deploy apps to your devices ## App Library Your app library displays all applications you have access to. Each app entry shows: | Field | Description | | ---------------- | ---------------------------------------------------- | | **App Name** | The application display name | | **Package Name** | Android package identifier (e.g., `com.example.app`) | | **Version** | Installed app version | ## Uploading Custom APKs To upload your own APK: 1. Click the **Upload** button 2. Select your APK file from your local machine 3. Wait for the upload and processing to complete 4. The app appears in your library once ready Uploaded APKs are stored securely in your user space and are only accessible to your account. ## Deploying Apps to Devices Once an app is in your library, you can install it on any of your devices: | Device Type | Deployment | | ------------------ | ----------------------------------------------------------------------------------------------- | | **Cloud Phone** | Apps persist across sessions on your dedicated virtual devices | | **Physical Phone** | Apps install to your dedicated real hardware in the Mobilerun data center | | **Personal Phone** | Apps install to your connected hardware via the [Mobilerun Portal App](/guides/connect-android) | ### Installation Methods **From the Apps tab** Select an app and choose which device to install it on. **From the Playground** Reference apps by package name in your agent prompts. The agent can install and interact with any app in your library. **Via API** Specify apps in your task configuration when running tasks programmatically. ## Managing Apps ### Remove Apps Delete apps from your library when no longer needed. This removes the app from your user space but does not uninstall it from devices where it was previously deployed. ### Update Apps Upload a new version of an APK to replace the existing one in your library. ### View App Details Click on any app to see: * Full package information * Installation history * Devices where the app is currently installed # Browser Automation Source: https://docs.mobilerun.ai/browser-automation Drive Chrome on a Mobilerun device with Puppeteer or Playwright over the Chrome DevTools Protocol. Mobilerun exposes a per-device WebSocket that reverse-proxies Chrome-for-Android's DevTools (CDP) endpoint. Off-the-shelf browser automation libraries — [Puppeteer](https://pptr.dev) and [Playwright](https://playwright.dev) — connect to it with a single URL, just like they would to a local Chrome instance. Use this when you want to script Chrome on a device directly (navigate, evaluate JavaScript, intercept network requests, scrape content) instead of going through the agent. ## Endpoint ``` wss://api.mobilerun.ai/v1/devices/{deviceId}/browser/cdp ``` The endpoint is a **browser-level** CDP socket. Pages are multiplexed over the CDP `Target` domain — you do not need a separate URL per tab. ### Authentication Pass your Mobilerun API key as a request header during the WebSocket handshake: ``` Authorization: Bearer dr_sk_YOUR_API_KEY ``` Always send the key as a header. Never put credentials in the URL — query-string tokens leak into logs, traces, and proxy access records. ### Requirements * The device must be in the `ready` state. See [Devices](/devices) for state details. * Chrome must be installed and have remote debugging available on the device. If Chrome's CDP socket is not reachable, the connection fails at handshake with a clear error. * You need a Mobilerun [API key](/api-keys). ## Connect with Puppeteer ```js theme={null} import puppeteer from 'puppeteer-core'; const browser = await puppeteer.connect({ browserWSEndpoint: `wss://api.mobilerun.ai/v1/devices/${deviceId}/browser/cdp`, headers: { Authorization: `Bearer ${process.env.MOBILERUN_API_KEY}`, }, }); const page = await browser.newPage(); await page.goto('https://example.com'); const title = await page.title(); console.log(title); await browser.disconnect(); ``` ## Connect with Playwright ```js theme={null} import { chromium } from 'playwright'; const browser = await chromium.connectOverCDP( `wss://api.mobilerun.ai/v1/devices/${deviceId}/browser/cdp`, { headers: { Authorization: `Bearer ${process.env.MOBILERUN_API_KEY}`, }, } ); const context = browser.contexts()[0] ?? await browser.newContext(); const page = await context.newPage(); await page.goto('https://example.com'); console.log(await page.title()); await browser.close(); ``` ## How it works The endpoint transparently bridges the WebSocket to Chrome's `chrome_devtools_remote` socket on the device. There is no local port forwarding to set up and no separate ADB tunnel to manage — once the device is connected to Mobilerun, the CDP endpoint is available automatically on every adb-backed device type ([Cloud Phone](/device-types#cloud-phone) and [Physical Phone](/device-types#physical-phone)). Standard CDP semantics apply: connect once at the browser level, then attach to individual tabs through `Target.getTargets` / `Target.attachToTarget` (Puppeteer and Playwright handle this for you). ## Limitations * **Chrome only.** WebView debugging (`webview_devtools_remote_`) is not exposed. * **Browser-level only.** There is no per-page `/json/list` proxy — use the CDP `Target` domain to enumerate and attach to pages. * **Chrome must be debuggable.** A device without an active Chrome CDP socket fails at connect time. # Cloud Quickstart Source: https://docs.mobilerun.ai/cloud/quickstart Run your first autonomous task on a hosted device using the TypeScript SDK, Python, or cURL. This guide takes you from zero to a completed cloud task. You will find a ready device, submit an autonomous goal, wait for it to finish, and read the result. You can do this in **TypeScript**, in **Python**, or with plain **cURL**. Everything here uses the Mobilerun **Cloud**, which gives you hosted devices and a managed agent. There is no local install, no adb, and no Portal APK. If you would rather run the open source package on your own machine, follow the [Framework Quickstart](/framework/quickstart) instead. ## Prerequisites * A Mobilerun account. You can sign up at [cloud.mobilerun.ai](https://cloud.mobilerun.ai). * A **cloud API key** that starts with `dr_sk_` from the [API Keys](/api-keys) page. This is the only credential you need, because the agent's LLM usage is billed from your [credit](/credits) balance and you do **not** need your own model key. * A **ready device**. You can connect your own phone with the [Portal app](/guides/connect-android), or provision a [Cloud Phone](/device-types#cloud-phone) or a [Physical Phone](/device-types#physical-phone) in the dashboard. Autonomous tasks consume [credits](/credits) at roughly 0.5 credits per agent step. New accounts and device subscriptions include a monthly credit allowance. ## 1. Set your API key The examples below read your key from the `MOBILERUN_API_KEY` environment variable. ```bash theme={null} export MOBILERUN_API_KEY=dr_sk_your_key_here ``` If you call `new Mobilerun()` without an `apiKey`, the TypeScript SDK auto-detects the `MOBILERUN_CLOUD_API_KEY` variable instead. The examples pass the key explicitly, so either name works. The TypeScript example uses top level `await`, so run it as an ES module. Set `"type": "module"` in your `package.json` or use a `.mts` file, and then run it with `npx tsx quickstart.ts`. ## 2. Install ```bash npm theme={null} npm install @mobilerun/sdk ``` ```bash pip theme={null} # The Python example below uses only the standard requests library pip install requests ``` ## 3. Run your first task Each example does the same four things. It lists a ready device, submits a task, polls until the task finishes, and prints the result. ```typescript TypeScript theme={null} import Mobilerun from '@mobilerun/sdk'; // Pass your dr_sk_ key explicitly (or set MOBILERUN_CLOUD_API_KEY and call new Mobilerun()) const client = new Mobilerun({ apiKey: process.env.MOBILERUN_API_KEY }); // 1. Find a device that's ready to accept tasks const devices = await client.devices.list({ state: ['ready'] }); const device = devices.items[0]; if (!device) throw new Error('No ready device. Connect one in the dashboard first.'); // 2. Submit an autonomous task const task = await client.tasks.run({ deviceId: device.id, task: 'Open Settings and tell me the Android version', llmModel: 'mobilerun/mobile-agent-fast', maxSteps: 50, }); console.log('Task started:', task.id); // 3. Wait for it to finish let status = await client.tasks.getStatus(task.id); while (!['completed', 'failed', 'cancelled'].includes(status.status)) { await new Promise((r) => setTimeout(r, 3000)); status = await client.tasks.getStatus(task.id); } console.log('Final status:', status.status); // 4. Read the result from the trajectory const { trajectory } = await client.tasks.getTrajectory(task.id); const result = trajectory.find((e) => e.event === 'ResultEvent'); console.log('Result:', result?.data); ``` ```python Python theme={null} import os, time, requests API = "https://api.mobilerun.ai/v1" HEADERS = { "Authorization": f"Bearer {os.environ['MOBILERUN_API_KEY']}", "Content-Type": "application/json", } # 1. Find a device that's ready to accept tasks devices = requests.get(f"{API}/devices", headers=HEADERS, params={"state": "ready"}).json() device_id = devices["items"][0]["id"] # 2. Submit an autonomous task task = requests.post( f"{API}/tasks", headers=HEADERS, json={ "deviceId": device_id, "task": "Open Settings and tell me the Android version", "llmModel": "mobilerun/mobile-agent-fast", "maxSteps": 50, }, ).json() task_id = task["id"] print("Task started:", task_id) # 3. Poll until the task finishes while True: status = requests.get(f"{API}/tasks/{task_id}/status", headers=HEADERS).json() if status["status"] in ("completed", "failed", "cancelled"): break time.sleep(3) print("Final status:", status["status"]) # 4. Read the result from the trajectory trajectory = requests.get(f"{API}/tasks/{task_id}/trajectory", headers=HEADERS).json()["trajectory"] result = next((e for e in trajectory if e["event"] == "ResultEvent"), None) print("Result:", result["data"] if result else None) ``` ```bash cURL theme={null} # 1. Find a ready device and grab its id curl -s https://api.mobilerun.ai/v1/devices \ -H "Authorization: Bearer $MOBILERUN_API_KEY" | jq '.items[0].id' # 2. Submit a task (use the device id from step 1) curl -s -X POST https://api.mobilerun.ai/v1/tasks \ -H "Authorization: Bearer $MOBILERUN_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "deviceId": "", "task": "Open Settings and tell me the Android version", "llmModel": "mobilerun/mobile-agent-fast", "maxSteps": 50 }' # 3. Check status (use the task id returned in step 2) curl -s https://api.mobilerun.ai/v1/tasks//status \ -H "Authorization: Bearer $MOBILERUN_API_KEY" # 4. Once status is "completed", read the result from the trajectory curl -s https://api.mobilerun.ai/v1/tasks//trajectory \ -H "Authorization: Bearer $MOBILERUN_API_KEY" \ | jq '.trajectory[] | select(.event == "ResultEvent") | .data' ``` The trajectory is returned as `{ "trajectory": [ ... ] }`, which is an array of events. The final result is the event whose `event` field equals `"ResultEvent"`, and its `data` holds the fields `success`, `message`, `structured_output`, and `steps`. For data extraction tasks you can pass an `outputSchema` in JSON Schema form when you create the task, and then `structured_output` is populated with data matching it. See [Agent configuration](/agent#structured-output) for more. ## Read the result without the trajectory The trajectory is the full step by step record, which is useful when you want to inspect every screenshot and action. For most tasks you do not need it, because the status endpoint already returns the answer. A `GET /tasks/{id}/status` call returns the task outcome directly. ```json theme={null} { "status": "completed", "succeeded": true, "message": "The Android version on your device is 15.", "output": null, "steps": 5 } ``` The `message` field holds the agent's natural language answer. The `output` field holds your structured data when you submitted an `outputSchema`, and it is `null` otherwise. Because the polling loop in step 3 already calls this endpoint, you can read `message` and `output` straight from the final poll and skip the trajectory call entirely. Use the status endpoint for the answer and for polling. Reach for the trajectory only when you need the detailed run, such as the screenshots, the manager plan, or each executor action. ## Choosing a model Pass `llmModel` to pick the model that drives the agent. Use one of the identifiers from the [cloud model catalog](/agent#model-selection). For example, `mobilerun/mobile-agent-fast` is tuned for speed and cost, while `mobilerun/mobile-agent-thinking` gives stronger reasoning for complex flows. Omit the field to use your account default. ## Next steps All task parameters, including models, vision, reasoning, stealth, memory, and structured output. Full REST schemas for every endpoint, with request and response examples. Drive Mobilerun from Cursor, Claude Desktop, and other MCP clients. Get notified when tasks change state instead of polling. Server-side SDK calls, a live `@mobilerun/react` device stream, and status polling with cancellation. HMAC verification, replay protection, and secure file downloads. # Credentials Source: https://docs.mobilerun.ai/credentials Securely store and manage authentication credentials for automated app interactions. The Credentials tab allows you to create and manage secure credentials that your agent can use when automating apps. Store login information, API tokens, and other secrets without exposing them in your prompts. ## Overview The Credentials tab provides: * Secure storage for app credentials * Per-app credential organization * Encrypted secret management * Credential attachment for agent tasks ## Security Model Credentials are always encrypted at rest and in transit. The agent never has access to raw passwords or secrets. When the agent needs to authenticate: 1. The agent identifies a login field in the app 2. It requests the credential from the secure vault 3. The credential is injected directly into the field 4. The raw value is never exposed to the agent's context This architecture ensures your secrets remain protected even during automated interactions. ## Credential Structure Credentials are organized by app package name. Each credential can contain multiple fields: | Field Type | Description | | --------------------- | --------------------------------- | | **Username** | Account username or identifier | | **Email** | Email address for login | | **Password** | Account password (encrypted) | | **API Token** | API keys or access tokens | | **Phone Number** | Phone number for SMS verification | | **Two-Factor Secret** | TOTP secret for 2FA codes | | **Backup Codes** | Recovery codes for account access | ## Creating Credentials To add credentials for an app: 1. Click **Add Credential** 2. Select the app package name from your [app library](/apps) 3. Enter a credential name (e.g., "Production Account", "Test User") 4. Add the required fields (username, password, etc.) 5. Save the credential You can create multiple credentials per app. This is useful for testing different account types or environments. ## Managing Credentials ### View Credentials Your credential list shows: * App package name * Credential name * Field types configured (without revealing values) * Last modified date ### Edit Credentials Update credential fields when passwords change or tokens expire. Click on any credential to modify its fields. ### Delete Credentials Remove credentials that are no longer needed. This action is permanent and cannot be undone. ## Using Credentials in Tasks Attach credentials to your agent tasks to enable authenticated automation: ### In the Playground Select the credentials to use before running your task. The agent will have access to inject these credentials when it encounters login screens. ### Via API Specify credentials in your task configuration: ```json theme={null} { "task": "Log into the app and check notifications", "credentials": [ { "packageName": "com.example.app", "credentialNames": ["Production Account"] } ] } ``` ### Multiple Credentials You can attach credentials for multiple apps in a single task. The agent will use the appropriate credential based on which app it is interacting with. ## Best Practices | Practice | Description | | ------------------------- | ------------------------------------------------------------ | | **Use descriptive names** | Name credentials clearly (e.g., "QA Test Account") | | **Separate environments** | Create distinct credentials for dev, staging, and production | | **Rotate regularly** | Update credentials when passwords change | | **Minimal access** | Only attach credentials the task actually needs | # Credits Source: https://docs.mobilerun.ai/credits Understand how credits work and what they are used for on Mobilerun. Credits are the currency used to pay for usage on Mobilerun. 1 credit = \$0.01 USD. You receive credits with your device subscriptions or can top up your balance as needed. ## How Credits Work Credits are consumed when you use Mobilerun services. Your credit balance decreases as you run tasks. ## Credit Usage ### Agent Execution (LLM Tokens) Each action the agent takes during task execution consumes credits based on the LLM tokens used. On average, agent steps cost approximately **\~0.5 credits per step**. The exact cost per step varies depending on the model you select, the length of the conversation context, and whether features like vision or reasoning are enabled. You can monitor your LLM credit consumption in the Billing section of the dashboard. ## Getting Credits ### Device Subscriptions Each device subscription includes a monthly credit allocation that refreshes every billing cycle: | Device | Monthly Credits | Price | | ---------------------------------------------- | --------------- | ----------- | | [Personal Phone](/device-types#personal-phone) | 250 | \$5/month | | [Cloud Phone](/device-types#cloud-phone) | 2,500 | \$50/month | | [Physical Phone](/device-types#physical-phone) | 5,000 | \$150/month | No base plan is required — sign up for free and add devices as needed. ### Top Up Purchase additional credits when you need more capacity: * **\$5 per 500 credits** (one-time purchase) * Credits are added to your balance immediately * Top-up credits do not expire ## Monitoring Usage Track your credit consumption in the Billing section of the dashboard: * Current credit balance * Usage breakdown by category * Historical consumption trends ## Cost Optimization | Strategy | Description | | ----------------------------- | ------------------------------------------------------------ | | **Select appropriate models** | Use faster, cheaper models for simple tasks | | **Optimize prompts** | Well-written prompts reduce the number of agent steps needed | # Device Types Source: https://docs.mobilerun.ai/device-types Learn about the different device types available on Mobilerun and choose the one that best fits your automation needs. Mobilerun offers three device types, each designed for different use cases and requirements. Understanding these options will help you select the right infrastructure for your mobile automation workflows. ## Personal Phone Connect your own physical smartphone to the cloud. Follow the [Connect an iPhone](/guides/connect-iphone) or [Connect an Android](/guides/connect-android) guide depending on your device. | Feature | Description | | ---------------- | ------------------------------------------------- | | **Provisioning** | BYO — bring your own device | | **Persistence** | Persistent — your device keeps its state | | **Cost** | \$5/month per device (includes 250 credits/month) | | **Hardware** | Remote physical (your own device) | | **Best For** | Quick testing on your own hardware | Personal phones are ideal when you want to: * Use your existing hardware for automation * Maintain persistent device state across sessions * Test on specific device models you own Each Personal Phone subscription includes 250 credits per month. *** ## Cloud Phone A high-performance virtual Android device with dedicated resources, persistent state, and advanced automation capabilities. | Feature | Description | | ---------------- | ---------------------------------------------------- | | **Provisioning** | Dedicated — always-on, reserved for your account | | **Persistence** | Persistent — full state persistence across sessions | | **Cost** | \$50/month per device (includes 2,500 credits/month) | | **Hardware** | Virtual device | | **Location** | EU | | **Best For** | Mobile RPA and scalable automation | Cloud Phones are ideal when you need: * Persistent app installations and configurations * Scalable automation with multiple devices * Profile support for managing multiple identities * Predictable performance without cold-start delays Cloud Phones support **profiles**, allowing you to manage multiple device identities from a single subscription. See the [Cloud Phone Setup](/guides/cloud-phone-setup) guide for setup best practices, including proxy and Google account configuration. *** ## Physical Phone A dedicated, premium real Android device hosted in the Mobilerun data center, optimized for reliable Mobile RPA operations. | Feature | Description | | ---------------- | ----------------------------------------------------- | | **Provisioning** | Dedicated — always-on, reserved for your account | | **Persistence** | Persistent — full state persistence across sessions | | **Cost** | \$150/month per device (includes 5,000 credits/month) | | **Hardware** | Premium real device | | **Location** | EU / US | | **Best For** | Social media automation and stealth operations | Physical Phones are the right choice when you need: * Real hardware for maximum realism and stealth * eSIM support for cellular connectivity * GPS location spoofing and proxy integration * Production-grade automation on real devices See the [Physical Phone Setup](/guides/physical-phone-setup) guide for setup best practices, including proxy, location, and eSIM configuration. *** ## Comparison | Aspect | Personal Phone | Cloud Phone | Physical Phone | | -------------------- | --------------- | ----------- | ------------------- | | **Hardware** | Your own device | Virtual | Premium real device | | **State** | Persistent | Persistent | Persistent | | **Deployment** | BYO | Dedicated | Dedicated | | **Location** | Yours | EU | EU / US | | **Scalable** | No | Yes | No | | **eSIM** | No | No | Yes | | **Profiles** | No | Yes | No | | **App Support** | Yes | Yes | Yes | | **Cost** | \$5/mo | \$50/mo | \$150/mo | | **Included Credits** | 250/mo | 2,500/mo | 5,000/mo | ## Choosing the Right Device Type Choose this to connect your own physical smartphone for testing on your own hardware. Choose this for scalable, persistent virtual devices with profile support and dedicated resources. Choose this when you need real hardware, eSIM, and maximum stealth for production automation. # Devices Source: https://docs.mobilerun.ai/devices View and manage all your devices and monitor active device streams. The Devices tab provides a centralized overview of all devices associated with your account. Use it to monitor device status, manage your device inventory, and access active streams. ## Overview The Devices tab displays: * All devices allocated to your account * Currently running devices and their streams * Device status and availability ## Device Inventory Your device inventory shows every device you have access to, organized by type: | Device Type | Description | | ------------------ | ---------------------------------------------------------------------------------------------- | | **Personal Phone** | Your own physical smartphone connected via the [Mobilerun Portal App](/guides/connect-android) | | **Cloud Phone** | Dedicated, persistent virtual devices with profile support | | **Physical Phone** | Premium real Android hardware hosted in the Mobilerun data center | For detailed information about each device type, including pricing and capabilities, see the [Device Types](/device-types) documentation. ## Device Status Each device in your inventory displays its current status: | Status | Description | | ------------- | --------------------------------------------------------- | | **Available** | Device is ready to accept new tasks | | **Running** | Device is actively executing a task | | **Offline** | Device is not currently connected (physical devices only) | These are the human readable labels shown in the dashboard. The API and SDK use lowercase state values instead. The most important one is **`ready`**, which you filter for with `?state=ready` in REST or with `client.devices.list({ state: ['ready'] })` in the SDK. Other API states include `creating`, `assigned`, `rebooting`, `resetting`, `terminated`, and `maintenance`. ## Live Streams When a device is running, you can view its live stream directly from the Devices tab. The stream shows: * Real-time device screen output * Current task execution progress * Agent actions as they occur Click on any running device to open its stream view. ## Managing Devices From the Devices tab, you can: ### View Device Details Click on any device to see: * Device specifications (model, OS version, screen resolution) * Current task information (if running) * Session history ### Connect Your Own Device With a Personal Phone subscription, connect your own physical Android or iOS device using the [Mobilerun Portal App](/guides/connect-android). Connected devices appear in your inventory alongside cloud-hosted devices. ### Monitor Usage Track your device utilization: * Active session time * Task execution history # Architecture Source: https://docs.mobilerun.ai/framework/concepts/architecture Understanding the Mobilerun multi-agent system for device automation. ## What is Mobilerun? Mobilerun uses a **multi-agent architecture** where specialized agents work together to complete tasks. Instead of one agent doing everything, different agents handle planning, execution, and computation. ``` MobileAgent (orchestrator) ├── Reasoning Mode: ManagerAgent → ExecutorAgent └── Direct Mode: FastAgent ``` ## Execution Modes ### Reasoning Mode (`reasoning=True`) Manager creates plans, Executor takes actions. Best for complex multi-step tasks. ``` Goal → Manager (plan) → Executor (action) → Manager (check) → Executor (next) → ... ``` ### Direct Mode (`reasoning=False`) FastAgent executes immediately via XML tool-calling without planning overhead. Best for simple tasks. ``` Goal → FastAgent (XML tool-calling) → Done ``` ## Core Agents ### MobileAgent (Orchestrator) Main coordinator that routes between agents based on mode. **Location**: `mobilerun/agent/droid/droid_agent.py` ### ManagerAgent (Planner) Creates strategic plans and breaks tasks into subgoals. Reasoning mode only. **Location**: `mobilerun/agent/manager/manager_agent.py` **Workflow**: `prepare_context()` → `get_response()` → `process_response()` → `finalize()` ### ExecutorAgent (Actor) Executes atomic actions for each subgoal. Reasoning mode only. **Location**: `mobilerun/agent/executor/executor_agent.py` **Workflow**: `prepare_context()` → `get_response()` → `process_response()` → `execute()` → `finalize()` ### FastAgent (Direct Executor) Uses XML tool-calling for device interaction. Direct mode only. **Location**: `mobilerun/agent/fast_agent/fast_agent.py` **Available Actions**: ```python theme={null} click(index), click_at(x, y), click_area(x1, y1, x2, y2), long_press(index), long_press_at(x, y), type(text, index), type_secret(secret_id, index), swipe(coordinate, coordinate2), system_button(button), wait(duration), open_app(text), remember(information), complete(success, reason) ``` ## Configuration Configure different LLMs per agent: ```yaml theme={null} llm_profiles: manager: provider: Anthropic model: claude-sonnet-4 executor: provider: OpenAI model: gpt-4o fast_agent: provider: GoogleGenAI model: gemini-3.1-flash-lite-preview agent: reasoning: true # Enable Manager/Executor workflow max_steps: 15 # Maximum execution steps (global) manager: vision: true # Send screenshots to Manager executor: vision: true # Send screenshots to Executor fast_agent: vision: false parallel_tools: true ``` ## When to Use Each Mode **Use Reasoning Mode for:** * Multi-step tasks (booking flights, configuring settings) * Tasks requiring planning and adaptation * Complex workflows across multiple apps **Use Direct Mode for:** * Simple actions (screenshots, sending messages) * Fast execution without planning overhead * Well-defined single-step tasks ## Shared State All agents share `MobileAgentState` for coordination: * Action history and outcomes * Error tracking and recovery * Memory and context * Current plan and progress ## Quick Reference | Agent | Role | Best For | Mode | Config Key | | ------------- | ------------ | ------------------ | --------- | -------------------- | | MobileAgent | Orchestrator | Entry point | Both | `agent.*` | | ManagerAgent | Planner | Strategy, recovery | Reasoning | `agent.manager.*` | | ExecutorAgent | Actor | Action execution | Reasoning | `agent.executor.*` | | FastAgent | Direct | Simple tasks | Direct | `agent.fast_agent.*` | # Event Streaming Source: https://docs.mobilerun.ai/framework/concepts/events-and-workflows How to consume real-time events from MobileAgent execution. ## Overview Mobilerun provides **real-time event streaming** that gives you visibility into agent execution as it happens. This allows you to build UIs, logging systems, or monitoring tools that react to agent actions in real-time. Under the hood, Mobilerun uses [llama-index workflows](https://docs.llamaindex.ai/en/stable/understanding/workflows/) - an event-driven orchestration system that powers the agent architecture. ## Basic Usage ```python theme={null} from mobilerun.agent.droid import MobileAgent # Create and run agent agent = MobileAgent(goal="Open Gmail and check inbox", config=config) handler = agent.run() # Stream events in real-time async for event in handler.stream_events(): if isinstance(event, ManagerPlanDetailsEvent): print(f"📋 Plan: {event.plan}") print(f"🎯 Current subgoal: {event.subgoal}") elif isinstance(event, ExecutorActionEvent): print(f"⚡ Action: {event.description}") print(f"💭 Thought: {event.thought}") elif isinstance(event, ScreenshotEvent): save_screenshot(event.screenshot, "screenshot.png") elif isinstance(event, FastAgentResponseEvent): if event.code: print(f"🔧 Tool calls: {event.code}") if event.thought: print(f"💭 Thought: {event.thought}") # Wait for final result result = await handler print(f"✅ Success: {result.success}") print(f"📝 Reason: {result.reason}") ``` ## Event Types Used for workflow coordination between MobileAgent and its child agents. ```python theme={null} # Main workflow class FastAgentExecuteEvent(Event): instruction: str class FastAgentResultEvent(Event): success: bool reason: str instruction: str class FinalizeEvent(Event): success: bool reason: str class ResultEvent(StopEvent): success: bool reason: str steps: int structured_output: BaseModel | None # Manager/Executor coordination class ManagerInputEvent(Event): pass class ManagerPlanEvent(Event): plan: str current_subgoal: str thought: str answer: str = "" success: bool | None = None class ExecutorInputEvent(Event): current_subgoal: str class ExecutorResultEvent(Event): action: Dict outcome: bool error: str summary: str # External user message events class ExternalUserMessageAppliedEvent(Event): message_ids: List[str] consumer: str step_number: int class ExternalUserMessageDroppedEvent(Event): message_ids: List[str] reason: str step_number: int ``` Internal to ManagerAgent, streamed to frontend/logging. ```python theme={null} class ManagerContextEvent(Event): pass class ManagerResponseEvent(Event): response: str usage: Optional[UsageResult] = None class ManagerPlanDetailsEvent(Event): plan: str subgoal: str thought: str answer: str = "" memory_update: str = "" progress_summary: str = "" success: bool | None = None full_response: str = "" ``` Internal to ExecutorAgent, streamed to frontend/logging. ```python theme={null} class ExecutorContextEvent(Event): subgoal: str class ExecutorResponseEvent(Event): response: str usage: Optional[UsageResult] = None class ExecutorActionEvent(Event): action_json: str thought: str description: str full_response: str = "" class ExecutorActionResultEvent(Event): action: Dict success: bool error: str summary: str thought: str = "" full_response: str = "" ``` Internal to FastAgent, used in direct execution mode. ```python theme={null} class FastAgentInputEvent(Event): pass class FastAgentResponseEvent(Event): thought: str code: Optional[str] = None usage: Optional[UsageResult] = None class FastAgentToolCallEvent(Event): tool_calls_repr: str class FastAgentOutputEvent(Event): output: str class FastAgentEndEvent(Event): success: bool reason: str tool_call_count: int = 0 ``` ```python theme={null} # Common events (mobilerun/agent/common/events.py) class ScreenshotEvent(Event): screenshot: bytes class RecordUIStateEvent(Event): ui_state: list[Dict[str, Any]] class ToolExecutionEvent(Event): tool_name: str tool_args: Dict[str, Any] success: bool summary: str # Telemetry events (when enabled) class MobileAgentInitEvent(TelemetryEvent): goal: str llms: Dict[str, str] tools: str max_steps: int timeout: int vision: Dict[str, bool] reasoning: bool enable_tracing: bool debug: bool save_trajectories: str runtype: str custom_prompts: Optional[Dict[str, str]] class PackageVisitEvent(TelemetryEvent): package_name: str activity_name: str step_number: int class MobileAgentFinalizeEvent(TelemetryEvent): success: bool reason: str steps: int unique_packages_count: int unique_activities_count: int # Usage tracking class UsageResult(BaseModel): request_tokens: int response_tokens: int total_tokens: int requests: int ``` ## Common Patterns ### Building a Live UI ```python theme={null} async def run_with_ui(goal: str): agent = MobileAgent(goal=goal, config=config) handler = agent.run() async for event in handler.stream_events(): if isinstance(event, ManagerPlanDetailsEvent): ui.update_plan(event.plan) ui.update_current_step(event.subgoal) elif isinstance(event, ExecutorActionEvent): ui.add_action_log(event.description, event.thought) elif isinstance(event, ScreenshotEvent): ui.update_screenshot(event.screenshot) result = await handler ui.show_completion(result.success, result.reason) ``` ### Tracking Token Usage ```python theme={null} async def track_token_usage(goal: str): agent = MobileAgent(goal=goal, config=config) handler = agent.run() total_tokens = 0 total_requests = 0 async for event in handler.stream_events(): # Check for events that contain usage information if hasattr(event, 'usage') and event.usage: total_tokens += event.usage.total_tokens total_requests += event.usage.requests print(f"LLM call - Input: {event.usage.request_tokens}, " f"Output: {event.usage.response_tokens}, " f"Total: {event.usage.total_tokens}") result = await handler print(f"\n📊 Total tokens used: {total_tokens}") print(f"📊 Total LLM requests: {total_requests}") ``` ### Logging and Monitoring ```python theme={null} import logging logger = logging.getLogger("mobilerun.monitor") async def monitor_execution(goal: str): agent = MobileAgent(goal=goal, config=config) handler = agent.run() start_time = time.time() action_count = 0 async for event in handler.stream_events(): if isinstance(event, ExecutorActionEvent): action_count += 1 logger.info(f"Action {action_count}: {event.description}") elif isinstance(event, FastAgentOutputEvent): logger.info(f"Tool execution result: {event.output}") result = await handler duration = time.time() - start_time logger.info(f"Task completed in {duration:.2f}s with {action_count} actions") logger.info(f"Result: {result.success} - {result.reason}") ``` ## Notes ### Event Streaming Behavior * Events are **streamed in real-time** as the agent executes * Not all events are emitted in every execution (depends on mode and actions) * All events are **Pydantic models** with full type safety * The `handler` object is **async** - always use `await handler` to get the final result ### Event Emission by Mode **Reasoning Mode** (`reasoning=True`) emits: * Coordination: `ManagerInputEvent`, `ManagerPlanEvent`, `ExecutorInputEvent`, `ExecutorResultEvent` * Internal Manager: `ManagerContextEvent`, `ManagerResponseEvent`, `ManagerPlanDetailsEvent` * Internal Executor: `ExecutorContextEvent`, `ExecutorResponseEvent`, `ExecutorActionEvent`, `ExecutorActionResultEvent` * Tool execution: `ToolExecutionEvent` (after every tool dispatch) * Visual: `ScreenshotEvent`, `RecordUIStateEvent` (when enabled) **Direct Mode** (`reasoning=False`) emits: * Coordination: `FastAgentExecuteEvent`, `FastAgentResultEvent` * Internal FastAgent: `FastAgentInputEvent`, `FastAgentResponseEvent`, `FastAgentToolCallEvent`, `FastAgentOutputEvent`, `FastAgentEndEvent` * Tool execution: `ToolExecutionEvent` (after every tool dispatch) * Visual: `ScreenshotEvent`, `RecordUIStateEvent` (when enabled) **All Modes** emit: * Finalization: `FinalizeEvent`, `ResultEvent` * Telemetry: `MobileAgentInitEvent`, `PackageVisitEvent`, `MobileAgentFinalizeEvent` (when telemetry enabled) ### Event Categories **Coordination Events** - Used for workflow routing between agents (minimal data) * Located in `mobilerun/agent/droid/events.py` * Examples: `ManagerPlanEvent`, `ExecutorResultEvent`, `ExternalUserMessageAppliedEvent` **Internal Events** - Used for streaming to frontend/logging (full debug data) * Located in agent-specific event files * Examples: `ManagerPlanDetailsEvent`, `ExecutorActionEvent`, `FastAgentResponseEvent` **Common Events** - Emitted during execution (for screenshots, UI state recording, and tool tracking) * Located in `mobilerun/agent/common/events.py` * Examples: `ScreenshotEvent`, `RecordUIStateEvent`, `ToolExecutionEvent` **Telemetry Events** - Captured for analytics (when enabled) * Located in `mobilerun/telemetry/events.py` * Examples: `MobileAgentInitEvent`, `PackageVisitEvent`, `MobileAgentFinalizeEvent` ## Learn More * [LlamaIndex Workflows](https://docs.llamaindex.ai/en/stable/understanding/workflows/) - The underlying orchestration system # Prompt Templates Source: https://docs.mobilerun.ai/framework/concepts/prompts Customizing agent behavior with Jinja2 prompt templates. ## Overview Mobilerun uses **Jinja2 templates** for agent prompts. You can customize agent behavior by passing custom template strings to `MobileAgent`: ```python theme={null} custom_prompts = { "manager_system": "Your Jinja2 template here...", "executor_system": "Another template...", "fast_agent_system": "...", "fast_agent_user": "..." } agent = MobileAgent( goal="Send an email", config=config, prompts=custom_prompts # Pass template strings, not file paths ) ``` **Important**: The `prompts` parameter accepts Jinja2 **template strings**, not file paths. ## How It Works 1. `MobileAgent` creates a `PromptResolver` with your custom prompts 2. Each agent checks if you provided a custom template for its key (e.g., "manager\_system") 3. If found: uses your custom template 4. If not found: loads the default template from Mobilerun's built-in files 5. Templates are rendered with context variables specific to each agent ## Available Prompt Keys | Key | Agent | When Used | | ------------------- | --------- | ----------------------------------------------- | | `manager_system` | Manager | Planning and reasoning (only in reasoning mode) | | `executor_system` | Executor | Action selection (only in reasoning mode) | | `fast_agent_system` | FastAgent | Direct execution (always used) | | `fast_agent_user` | FastAgent | Task input formatting (always used) | ## Context Variables Each agent has access to different variables in its templates: ### Manager * `instruction` - User's goal * `device_date` - Current device date/time * `app_card` - App-specific guidance (empty if none available) * `error_history` - List of recent failed actions with details * `custom_tools_descriptions` - Custom tool documentation * `available_secrets` - Available credential IDs * `variables` - Custom variables passed to MobileAgent * `output_schema` - Pydantic model schema (if provided) ### Executor * `instruction` - User's goal * `device_state` - Current UI tree * `plan` - Current plan from Manager * `subgoal` - Current subgoal from Manager * `progress_status` - Cumulative progress summary * `atomic_actions` - Available actions (includes custom tools) * `action_history` - Recent actions with outcomes * `available_secrets` - Available credential IDs * `variables` - Custom variables passed to MobileAgent ### FastAgent **System prompt:** * `tool_descriptions` - Available tool signatures * `available_secrets` - Credential IDs * `variables` - Custom variables * `output_schema` - Output model schema (if provided) **User prompt:** * `goal` - Task description * `variables` - Custom variables ## Example: Custom Manager Prompt ```python theme={null} custom_prompts = { "manager_system": """ You are a mobile automation planning agent. Task: {{ instruction }} Date: {{ device_date }} {% if app_card %} App guidance: {{ app_card }} {% endif %} {% if error_history %} Recent errors (you may be stuck): {% for error in error_history %} - Action: {{ error.action }} Error: {{ error.error }} {% endfor %} {% endif %} {% if custom_tools_descriptions %} Custom tools: {{ custom_tools_descriptions }} {% endif %} {% if variables.domain %} Domain: {{ variables.domain }} {% endif %} Output format: Your reasoning 1. First step 2. Second step 3. DONE Or if complete: Task is done. Answer: ... """ } agent = MobileAgent( goal="Send an email", config=config, prompts=custom_prompts, variables={"domain": "finance"} ) ``` ## Example: Using Custom Variables Custom variables let you inject dynamic context into prompts: ```python theme={null} custom_prompts = { "manager_system": """ Task: {{ instruction }} {% if variables.budget %} Budget limit: ${{ variables.budget }} {% endif %} {% if variables.priority %} Priority: {{ variables.priority }} {% endif %} Guidelines: {% for rule in variables.rules %} - {{ rule }} {% endfor %} """ } agent = MobileAgent( goal="Buy a phone", config=config, prompts=custom_prompts, variables={ "budget": 1000, "priority": "high", "rules": ["Check reviews", "Compare prices", "Use coupons"] } ) ``` ## Jinja2 Syntax Reference ### Variables ```jinja2 theme={null} {{ instruction }} {{ variables.my_var }} ``` ### Conditionals ```jinja2 theme={null} {% if app_card %} {{ app_card }} {% endif %} {% if error_history %} You have {{ error_history | length }} errors {% endif %} ``` ### Loops ```jinja2 theme={null} {% for error in error_history %} - {{ error.action }}: {{ error.error }} {% endfor %} ``` ### Filters ```jinja2 theme={null} {{ instruction | upper }} {{ available_secrets | join(', ') }} {{ error_history | length }} ``` ## Best Practices ### 1. Use Clear Structure ```jinja2 theme={null} {{ instruction }} 1. Rule one 2. Rule two Expected format ``` ### 2. Handle Missing Data with Conditionals ```jinja2 theme={null} {% if app_card %} {{ app_card }} {% else %} No app-specific guidance available {% endif %} ``` ### 3. Document Expected Variables ```jinja2 theme={null} {# Expected variables: - instruction: str - User's goal - device_date: str - Current date/time - app_card: str - App guidance (may be empty) #} ``` ### 4. Use Variables for Dynamic Behavior ```jinja2 theme={null} {% if variables.strict_mode %} Follow instructions exactly. Do not make assumptions. {% endif %} ``` ## Complete Example ```python theme={null} from mobilerun import MobileAgent from mobilerun.config_manager import MobileConfig # E-commerce automation with custom prompts ecommerce_prompts = { "manager_system": """ You are an e-commerce automation specialist. Task: {{ instruction }} Budget: ${{ variables.budget }} {% if app_card %} App info: {{ app_card }} {% endif %} {% if error_history %} Errors encountered: {% for error in error_history %} - {{ error.action }}: {{ error.summary }} - {{ error.error }} {% endfor %} Consider changing your approach. {% endif %} Rules: 1. Verify product names exactly 2. Check prices before purchasing 3. Store order confirmations in memory 4. Never exceed budget Output: Your reasoning 1. Step 2. DONE """ } config = MobileConfig() agent = MobileAgent( goal="Buy iPhone 15 Pro from Amazon", config=config, prompts=ecommerce_prompts, variables={"budget": 1200} ) result = await agent.run() ``` ## Key Points * Pass Jinja2 template **strings** (not file paths) to `MobileAgent(prompts={...})` * Each agent has different available variables in its template * Use `variables` parameter to inject custom context * Templates are rendered at runtime with current state * If no custom prompt provided, default templates are used * Supports full Jinja2 syntax (conditionals, loops, filters) # Shared State Source: https://docs.mobilerun.ai/framework/concepts/shared-state MobileAgentState - the coordination mechanism for multi-agent workflow communication. ## What is Shared State? **MobileAgentState** is a Pydantic model that serves as the **central coordination mechanism** for Mobilerun's multi-agent workflow. It's a shared data structure that all agents (Manager, Executor, FastAgent) can read from and write to. Shared state enables: * **Cross-agent communication**: Agents share information about actions, results, and errors * **Progress tracking**: Step counts, action history, visited apps/screens * **Memory management**: Agent memory, custom variables, user session data * **Error coordination**: Error flags, escalation thresholds, error descriptions **Key insight**: Shared state replaces complex message passing. Instead of sending data back and forth, agents update a single shared object. ## Core State Fields ```python theme={null} class MobileAgentState(BaseModel): # Task context instruction: str = "" # Original task step_number: int = 0 # Current step # Device state formatted_device_state: str = "" # Human-readable state current_package_name: str = "" # Current app current_activity_name: str = "" # Current screen # Action tracking action_history: List[Dict] = [] # All actions taken action_outcomes: List[bool] = [] # Success/failure summary_history: List[str] = [] # Action summaries # Memory manager_memory: str = "" # Manager's planning notes (append-only) fast_memory: List[str] = [] # FastAgent remember() items (max 10) # Planning (Manager) plan: str = "" # Current plan current_subgoal: str = "" # Current subgoal answer: str = "" # Final answer (manager completion or complete() tool) # Completion (set by complete() tool) finished: bool = False # Whether task is done success: Optional[bool] = None # Whether task succeeded # Error handling error_flag_plan: bool = False # Signal error to Manager error_descriptions: List[str] = [] # Error messages err_to_manager_thresh: int = 2 # Consecutive errors before escalation # Message history (for stateful agents) message_history: List[ChatMessage] = [] # Preserves ThinkingBlock, ImageBlock, etc. # External user messages (mid-run injection queue) pending_user_messages: List[QueuedUserMessage] = [] # Queued external messages workflow_completed: bool = False # Set True at finalization # Custom variables custom_variables: Dict = {} # User-defined data ``` # App Instruction Cards Source: https://docs.mobilerun.ai/framework/features/app-cards App cards give your agents app-specific knowledge to operate apps more effectively. They automatically load when agents work with specific apps, improving success rates for navigation and complex tasks. ## What Are App Cards? App cards are **app-specific instruction guides** that teach agents how to use apps effectively. Think of them as cheat sheets that help your agent understand: * How to navigate the app's UI * Where to find buttons and features * App-specific shortcuts and gestures * Search syntax and filters (for apps like Gmail) * Common workflows and best practices **Example:** When your agent opens Gmail, it automatically loads the Gmail app card and learns that: * The compose button is at the bottom-right * Search supports filters like `from:sender@email.com` or `has:attachment` * Swiping right archives emails, swiping left deletes them This knowledge helps agents complete tasks faster and more reliably. *** ## Why Use App Cards? **Without app cards:** * Agents guess how to navigate unfamiliar apps * Trial-and-error wastes time and tokens * Success rates drop for complex workflows **With app cards:** * ✅ Agents know exactly where to find features * ✅ First-attempt success for common tasks * ✅ Reduced token usage (less exploration needed) * ✅ Better handling of app-specific quirks *** ## Quick Start Mobilerun includes a sample Gmail app card to demonstrate how app cards work: ```bash theme={null} # App cards are enabled by default mobilerun run "Send an email to john@example.com" --reasoning ``` When the agent opens Gmail, the Gmail app card automatically loads and guides the workflow. **Sample app card included:** * **Gmail** (`com.google.android.gm`) - Email navigation, search, composition You can use this as a template to create cards for other apps (see "Creating Custom App Cards" below). *** ## How App Cards Work ### Automatic Loading 1. **Detection:** Agent detects the current foreground app (e.g., Gmail) 2. **Loading:** Mobilerun loads the app card for that package name 3. **Injection:** App card content is added to the agent's prompt 4. **Guidance:** Agent uses the instructions to make better decisions **Technical note:** App cards are loaded asynchronously and cached in memory. Loading happens in the background and doesn't block agent execution. ### When Are They Used? App cards are used by the **Manager Agent** when running in reasoning mode (`--reasoning` flag or `reasoning: true` in config): ```bash theme={null} # App cards enabled (Manager uses them for planning) mobilerun run "Archive all unread emails" --reasoning # App cards not used (direct execution mode) mobilerun run "Tap the button" ``` *** ## Creating Custom App Cards Want to add an app card for your favorite app? Here's how: ### Step 1: Find the Package Name ```bash theme={null} # Get all apps adb shell pm list packages # Or search for a specific app adb shell pm list packages | grep keyword ``` **Common package names:** * Chrome: `com.android.chrome` * WhatsApp: `com.whatsapp` * Instagram: `com.instagram.android` * YouTube: `com.google.android.youtube` ### Step 2: Create the Files Create the app cards directory and files: ```bash theme={null} mkdir -p config/app_cards touch config/app_cards/app_cards.json touch config/app_cards/chrome.md ``` **Example structure:** ```markdown theme={null} # Chrome App Guide ## Navigation - Address bar at the top for entering URLs - Three-dot menu (top-right) for settings and history - Tabs button (top-right) to switch between tabs ## Search - Type queries directly in the address bar - Use voice search via the microphone icon ## Common Actions - **New Tab**: Tap the tabs button → Plus icon - **Close Tab**: Swipe tab away in tab switcher - **Refresh**: Pull down from the top of the page - **Bookmarks**: Three-dot menu → Bookmarks ## Tips - Incognito mode available via three-dot menu - Downloads accessible via three-dot menu → Downloads ``` ### Step 3: Register the App Card Add your app mapping to `config/app_cards/app_cards.json`: ```json theme={null} { "com.google.android.gm": "gmail.md", "com.android.chrome": "chrome.md" } ``` **Using subdirectories:** ```json theme={null} { "com.whatsapp": "social/whatsapp.md", "com.instagram.android": "social/instagram.md" } ``` ### Step 4: Test ```bash theme={null} mobilerun run "Open Chrome and search for mobilerun" --reasoning --debug ``` Look for this log message: ``` Loaded app card for com.android.chrome from config/app_cards/chrome.md ``` *** ## Configuration App cards are enabled by default and load from `config/app_cards/`: ```yaml theme={null} agent: app_cards: enabled: true app_cards_dir: config/app_cards ``` **To disable:** ```yaml theme={null} agent: app_cards: enabled: false ``` *** ## App Card Best Practices ### Content Guidelines **Do:** * Be concise and actionable * Focus on UI patterns and workflows * Include search syntax and special features * Mention common pitfalls or quirks * Use bullet points and clear headings **Don't:** * Write essays or lengthy explanations * Describe every single feature * Include information that changes frequently (version-specific details) * Duplicate general Android knowledge (agents already know how to tap, swipe, etc.) ### Example: Good vs Bad **❌ Bad (too verbose):** ```markdown theme={null} Gmail is an email application developed by Google. It has many features including the ability to send and receive emails. To compose an email, you need to first understand that Gmail uses a material design interface with a floating action button, which is a circular button... ``` **✅ Good (concise and actionable):** ```markdown theme={null} ## Composing Emails - Tap the floating compose button (bottom-right) - Fill recipient, subject, and body - Send via paper plane icon (top-right) ``` *** ## Troubleshooting ### App Card Not Loading **Check these:** 1. **Is the package name correct?** ```bash theme={null} adb shell dumpsys window windows | grep -E 'mCurrentFocus' ``` 2. **Is the mapping correct in app\_cards.json?** ```json theme={null} { "com.your.app": "yourapp.md" } ``` 3. **Does the markdown file exist?** ```bash theme={null} ls config/app_cards/yourapp.md ``` 4. **Are app cards enabled?** ```yaml theme={null} agent: app_cards: enabled: true ``` 5. **Are you using reasoning mode?** ```bash theme={null} mobilerun run "command" --reasoning ``` ### Debug Mode Run with `--debug` to see app card loading: ```bash theme={null} mobilerun run "Open Gmail" --reasoning --debug ``` Look for these log messages: ``` Loaded app_cards.json with 2 entries Loaded app card for com.google.android.gm from config/app_cards/gmail.md ``` *** ## Related Documentation * [CLI Usage](/framework/guides/cli) - Mobilerun CLI command reference * [Configuration](/framework/sdk/configuration) - Configuration system details * [Agent Architecture](/framework/concepts/architecture) - How agents work * [Manager Agent](/framework/concepts/architecture#manageragent-planner) - Agent that uses app cards *** **Help your agents become app experts with well-crafted app cards!** # Credential Management Source: https://docs.mobilerun.ai/framework/features/credentials Extend Mobilerun with secure credential management ## Overview Secure storage for passwords, API keys, and tokens. * Stored in YAML files or in-memory dicts * Never logged or exposed * Auto-injected as `type_secret` action * Simple string or dict format ## Quick Start ### Method 1: In-Memory (Recommended for SDK) ```python theme={null} import asyncio from mobilerun import MobileAgent, MobileConfig async def main(): # Define credentials directly credentials = { "MY_PASSWORD": "secret123", "API_KEY": "sk-1234567890" } config = MobileConfig() agent = MobileAgent( goal="Login to my app", config=config, credentials=credentials # Pass directly ) result = await agent.run() print(result.success) asyncio.run(main()) ``` ### Method 2: YAML File 1. **Create credentials file:** ```yaml theme={null} # credentials.yaml secrets: # Dict format (recommended) MY_PASSWORD: value: "your_password_here" enabled: true GMAIL_PASSWORD: value: "gmail_pass_123" enabled: true # Simple string format (auto-enabled) API_KEY: "sk-1234567890abcdef" # Disabled secret OLD_PASSWORD: value: "old_pass" enabled: false # Not loaded ``` 2. **Enable in config.yaml:** ```yaml theme={null} # config.yaml credentials: enabled: true file_path: config/credentials.yaml ``` 3. **Use in code:** ```python theme={null} from mobilerun import MobileAgent, MobileConfig # Config loads credentials from file config = MobileConfig.from_yaml("config.yaml") agent = MobileAgent( goal="Login to Gmail", config=config # Credentials loaded automatically ) ``` *** ## How Agents Use Credentials When credentials are provided, the `type_secret` action is **automatically available**: ### Executor/Manager Mode ```json theme={null} { "action": "type_secret", "secret_id": "MY_PASSWORD", "index": 5 } ``` ### FastAgent Mode ```xml theme={null} MY_PASSWORD 5 ``` The agent never sees the actual value - only the secret ID. *** ## Example: Login Automation ```python theme={null} import asyncio from mobilerun import MobileAgent, MobileConfig async def main(): credentials = { "EMAIL_USER": "user@example.com", "EMAIL_PASS": "secret_password" } config = MobileConfig() agent = MobileAgent( goal="Open Gmail and login with my credentials", config=config, credentials=credentials ) result = await agent.run() print(f"Success: {result.success}") asyncio.run(main()) ``` **What the agent does:** 1. Opens Gmail: `open_app("Gmail")` 2. Clicks email field: `click(index=3)` 3. Types email: `type("user@example.com", index=3)` 4. Clicks password field: `click(index=5)` 5. Types password securely: `type_secret("EMAIL_PASS", index=5)` 6. Clicks login: `click(index=7)` ## Credentials vs Variables | Feature | Credentials | Variables | | ------------ | ---------------------- | ------------------ | | **Purpose** | Passwords, API keys | Non-sensitive data | | **Storage** | YAML or in-memory | In-memory only | | **Logging** | Never logged | May appear in logs | | **Access** | Via `type_secret` tool | In shared state | | **Security** | Protected | No protection | **Example: Using Variables** ```python theme={null} variables = { "target_email": "john@example.com", "subject_line": "Monthly Report" } agent = MobileAgent( goal="Compose email to {{target_email}}", config=config, variables=variables # Non-sensitive ) ``` *** ## Troubleshooting ### Error: Credential manager not initialized **Solution:** ```yaml theme={null} # config.yaml credentials: enabled: true # Must be true file_path: config/credentials.yaml ``` Or: ```python theme={null} agent = MobileAgent(..., credentials={"PASSWORD": "secret"}) ``` ### Error: Secret 'X' not found **Check available secrets:** ```python theme={null} from mobilerun.credential_manager import FileCredentialManager cm = FileCredentialManager("config/credentials.yaml") print(await cm.get_keys()) ``` **Verify in YAML:** ```yaml theme={null} secrets: X: value: "your_value" enabled: true # Must be true ``` *** ## Custom Credential Managers Extend `CredentialManager` for custom secret storage: ```python theme={null} from mobilerun.credential_manager import CredentialManager class MyCredentialManager(CredentialManager): def __init__(self, api_key): self.api_key = api_key async def resolve_key(self, key: str) -> str: # Implement your own credential retrieval logic return await fetch_from_service(key, self.api_key) async def get_keys(self) -> list[str]: # Return list of available credential keys return await fetch_available_keys(self.api_key) # Use it credentials = MyCredentialManager(api_key="...") agent = MobileAgent(goal="Login", config=config, credentials=credentials) ``` Implement any custom secret storage backend. *** ## Related See [Configuration Guide](/framework/sdk/configuration) for credential setup. See [Custom Variables](/framework/features/custom-variables) for non-sensitive data. # Custom Tools Source: https://docs.mobilerun.ai/framework/features/custom-tools Extend Mobilerun with custom Python functions ## Overview Custom tools are Python functions that extend agent capabilities beyond built-in atomic actions (click, type, swipe). **Use cases:** * External API calls (webhooks, REST services) * Data processing and calculations * Database operations * Domain-specific logic *** ## Quick Start ### Basic Example Simple custom tool without device access: ```python theme={null} import asyncio from mobilerun import MobileAgent, MobileConfig def calculate_tax(amount: float, rate: float, **kwargs) -> str: """Calculate tax for a given amount.""" tax = amount * rate total = amount + tax return f"Tax: ${tax:.2f}, Total: ${total:.2f}" custom_tools = { "calculate_tax": { "parameters": { "amount": {"type": "number", "required": True}, "rate": {"type": "number", "required": True}, }, "description": "Calculate tax for a given amount and rate", "function": calculate_tax } } async def main(): config = MobileConfig() agent = MobileAgent( goal="Calculate tax for $100 at 8% rate", config=config, custom_tools=custom_tools ) result = await agent.run() print(result.success, result.reason) asyncio.run(main()) ``` *** ## Tool Structure All custom tools follow this format: ```python theme={null} custom_tools = { "tool_name": { "parameters": { # Parameter definitions "arg1": {"type": "string", "required": True}, "arg2": {"type": "string", "required": True}, }, "description": "Tool description...", # For LLM prompt "function": callable_function # Python function } } ``` **Function signature:** ```python theme={null} async def tool_name(arg1: type, arg2: type, *, ctx: ActionContext, **kwargs) -> str: """ Args: arg1: Your parameter arg2: Another parameter ctx: ActionContext (injected automatically by the registry) """ # Implementation return "result" ``` **Key points:** * List only user parameters in `"parameters"` (not `ctx`) * `ctx` (an `ActionContext` instance) is injected automatically as a keyword argument * Access device via `ctx.driver`, shared state via `ctx.shared_state`, credentials via `ctx.credential_manager` * Use `**kwargs` for forward compatibility * Return type should be `str` *** ## Using ActionContext Access the device and state via the `ctx` parameter (an `ActionContext` instance injected automatically): ```python theme={null} async def screenshot_and_count(*, ctx, **kwargs) -> str: """Take screenshot and count UI elements.""" # Take screenshot via the driver screenshot = await ctx.driver.screenshot() # Get UI state via the state provider ui_state = await ctx.state_provider.get_state() element_count = len(ui_state.elements) if ui_state else 0 return f"Screenshot taken. Found {element_count} UI elements" custom_tools = { "screenshot_and_count": { "parameters": {}, "description": "Take screenshot and count UI elements on screen", "function": screenshot_and_count } } ``` **Available via `ctx`:** * `ctx.driver` - `DeviceDriver` for raw device I/O (screenshot, tap, swipe, etc.) * `ctx.state_provider` - `StateProvider` to fetch/parse UI state * `ctx.ui` - Current `UIState` (refreshed each step) * `ctx.shared_state` - `MobileAgentState` for agent coordination * `ctx.credential_manager` - `CredentialManager` for secrets *** ## Accessing Shared State Access agent state via `ctx.shared_state`: ```python theme={null} async def check_action_history(action_name: str, *, ctx, **kwargs) -> str: """Check if action was recently performed.""" shared_state = ctx.shared_state # Check recent actions recent_actions = shared_state.action_history[-5:] already_done = any(a.get("action") == action_name for a in recent_actions) if already_done: return f"Action '{action_name}' was already performed recently" # Check step count if shared_state.step_number > 10: return "Warning: Task taking too many steps" # Access fast memory if any("skip_validation" in m for m in shared_state.fast_memory): return "Validation skipped per memory" return f"Action '{action_name}' not yet performed" custom_tools = { "check_action_history": { "parameters": { "action_name": {"type": "string", "required": True}, }, "description": "Check if a specific action was recently performed in agent history", "function": check_action_history } } ``` **MobileAgentState fields:** * `step_number` - Current execution step * `action_history` - List of executed actions * `action_outcomes` - Success/failure per action * `manager_memory` - Manager planning notes (append-only string) * `fast_memory` - FastAgent remember() items (list of strings, max 10) * `custom_variables` - User-provided variables * `visited_packages` - Apps visited * `current_package_name` - Current app package * `plan` - Current Manager plan * More in `mobilerun/agent/droid/state.py` *** ## Common Patterns ### API Integration ```python theme={null} import requests def fetch_weather(city: str, **kwargs) -> str: """Fetch weather data from API.""" try: # Using OpenWeatherMap API example api_key = "your_api_key" url = f"https://api.openweathermap.org/data/2.5/weather?q={city}&appid={api_key}" response = requests.get(url, timeout=10) response.raise_for_status() data = response.json() temp = data["main"]["temp"] - 273.15 # Convert to Celsius weather = data["weather"][0]["description"] return f"Weather in {city}: {weather}, {temp:.1f}°C" except Exception as e: return f"Error: {str(e)}" custom_tools = { "fetch_weather": { "parameters": { "city": {"type": "string", "required": True}, }, "description": "Fetch current weather data for a given city", "function": fetch_weather } } ``` ### Database Query ```python theme={null} import sqlite3 def query_database(query: str, **kwargs) -> str: """Query local database.""" try: conn = sqlite3.connect("app.db") cursor = conn.execute(query) results = cursor.fetchall() conn.close() return f"Found {len(results)} results" except Exception as e: return f"Database error: {str(e)}" custom_tools = { "query_database": { "parameters": { "query": {"type": "string", "required": True}, }, "description": "Execute SQL query on local database and return results", "function": query_database } } ``` ### Async Operations ```python theme={null} import aiohttp async def fetch_async(url: str, **kwargs) -> str: """Fetch data asynchronously.""" try: async with aiohttp.ClientSession() as session: async with session.get(url, timeout=10) as response: data = await response.text() return f"Fetched {len(data)} bytes from {url}" except Exception as e: return f"Error: {str(e)}" custom_tools = { "fetch_async": { "parameters": { "url": {"type": "string", "required": True}, }, "description": "Asynchronously fetch data from a URL", "function": fetch_async } } ``` *** ## Best Practices ### 1. Clear Descriptions Write descriptive, specific descriptions: ```python theme={null} # Good "description": "Send POST request to webhook URL with JSON data payload" # Bad "description": "Send webhook" ``` ### 2. Error Handling Always catch exceptions: ```python theme={null} def robust_tool(url: str, **kwargs) -> str: try: response = requests.get(url, timeout=10) response.raise_for_status() return f"Success: {response.status_code}" except requests.Timeout: return "Error: Request timed out" except requests.RequestException as e: return f"Error: {str(e)}" except Exception as e: return f"Unexpected error: {str(e)}" ``` ### 3. Argument Validation Validate inputs before processing: ```python theme={null} def validated_tool(count: int, **kwargs) -> str: if not isinstance(count, int): return "Error: count must be integer" if count < 0 or count > 100: return "Error: count must be 0-100" return f"Processed {count} items" ``` ### 4. Logging Use Python logging for debugging: ```python theme={null} import logging logger = logging.getLogger("mobilerun") def logged_tool(data: str, **kwargs) -> str: logger.info(f"Processing: {data[:50]}...") # Process data logger.info("Complete") return "Success" ``` *** ## Advanced Example Combining ActionContext, shared state, and credentials: ```python theme={null} import requests async def send_authenticated_request( url: str, data: str, *, ctx, **kwargs ) -> str: """Send authenticated API request with credentials.""" try: # Access credentials via ActionContext if not ctx.credential_manager: return "Error: Credential manager not available" api_key = await ctx.credential_manager.resolve_key("API_KEY") # Check if we've made too many requests if ctx.shared_state.step_number > 15: return "Error: Too many API calls" # Send authenticated request headers = {"Authorization": f"Bearer {api_key}"} response = requests.post(url, json={"data": data}, headers=headers, timeout=10) response.raise_for_status() return f"Request successful: {response.status_code}" except Exception as e: return f"Error: {str(e)}" custom_tools = { "send_authenticated_request": { "parameters": { "url": {"type": "string", "required": True}, "data": {"type": "string", "required": True}, }, "description": "Send authenticated API request using stored credentials", "function": send_authenticated_request } } # Usage with credentials credentials = {"API_KEY": "sk-1234567890"} agent = MobileAgent( goal="Send data to API", config=config, custom_tools=custom_tools, credentials=credentials ) ``` *** ## Related See [Agent Architecture](/framework/concepts/architecture) for understanding shared state and custom tools integration. # Custom Variables Source: https://docs.mobilerun.ai/framework/features/custom-variables Pass dynamic data to your Mobilerun agents using the `variables` parameter. Variables enable parameterized workflows and reusable automation. Custom variables are accessible in: * **Agent prompts** via custom Jinja2 templates * **Custom tools** via `ctx.shared_state.custom_variables` *** ## Quick Start ```python theme={null} from mobilerun import MobileAgent, MobileConfig # Define custom prompts that render variables custom_prompts = { "manager_system": """ {{ instruction }} {% if variables %} Available variables: {% for key, value in variables.items() %} - {{ key }}: {{ value }} {% endfor %} {% endif %} """ } # Create agent with variables config = MobileConfig() agent = MobileAgent( goal="Send email to recipient with subject", config=config, variables={"recipient": "john@example.com", "subject": "Update"}, prompts=custom_prompts # Required to see variables ) result = await agent.run() ``` **Important:** Default prompts don't render variables. You must provide custom prompts via the `prompts` parameter. *** ## How Variables Work When you pass `variables` to `MobileAgent`: 1. **Stored** in `MobileAgentState.custom_variables` 2. **Passed** to prompt templates as `variables` dict 3. **Rendered** in Jinja2 templates via `{% if variables %}` blocks 4. **Available** to all child agents throughout the workflow **Access Summary:** * ✅ Agent prompts (via custom Jinja2 templates) * ✅ Agents can read and pass to tools as arguments * ✅ Custom tools (via `ctx.shared_state.custom_variables`) *** ## Basic Usage ```python theme={null} from mobilerun import MobileAgent, MobileConfig # Define variables variables = { "recipient": "alice@example.com", "message": "Hello from Mobilerun!" } # Custom prompt to render variables custom_prompts = { "fast_agent_system": """ You are an agent that controls Android devices. {% if variables %} Available variables: {% for key, value in variables.items() %} - {{ key }}: {{ value }} {% endfor %} {% endif %} Use these variables when executing tasks. """ } # Create agent config = MobileConfig() agent = MobileAgent( goal="Send message to recipient", config=config, variables=variables, prompts=custom_prompts ) result = await agent.run() ``` ### Available Prompt Keys Customize these prompts to render variables: * `manager_system` - Manager agent system prompt * `executor_system` - Executor agent system prompt * `fast_agent_system` - FastAgent system prompt * `fast_agent_user` - FastAgent user prompt *** ## Accessing Variables in Custom Tools Custom tools can access variables via the `ctx` keyword argument (an `ActionContext` instance injected automatically): ```python theme={null} from mobilerun import MobileAgent, MobileConfig async def send_notification(title: str, *, ctx, **kwargs): """Send a notification using channel from custom variables. Args: title: Notification title ctx: ActionContext (injected automatically by the registry) """ # Access custom variables via ctx.shared_state channel = ctx.shared_state.custom_variables.get("notification_channel", "default") return f"Sent '{title}' to {channel}" custom_tools = { "send_notification": { "parameters": { "title": {"type": "string", "required": True}, }, "description": "Send a notification with title. Usage: {\"action\": \"send_notification\", \"title\": \"Alert\"}", "function": send_notification } } config = MobileConfig() agent = MobileAgent( goal="Send notification with title 'Alert'", config=config, custom_tools=custom_tools, variables={"notification_channel": "alerts"} ) ``` *** ## Use Cases ### Parameterized Workflows ```python theme={null} # Define reusable workflow with different variables for user in ["alice@example.com", "bob@example.com"]: agent = MobileAgent( goal="Send welcome email", config=config, variables={"recipient": user}, prompts=custom_prompts ) await agent.run() ``` ### Configuration Data ```python theme={null} variables = { "api_endpoint": "https://api.example.com/v2", "timeout": 30 } agent = MobileAgent( goal="Call API endpoint", config=config, variables=variables, prompts=custom_prompts ) ``` *** ## Key Points 1. **Custom prompts required** - Default prompts don't render variables in agent context 2. **Direct access in tools** - Custom tools access `ctx.shared_state.custom_variables` via the `ActionContext` 3. **Available to all agents** - Manager, Executor, FastAgent all receive variables 4. **Jinja2 templates** - Use `{% if variables %}` blocks in custom prompts 5. **Auto-injection** - `ctx` (ActionContext) is injected automatically by the tool registry ## Related Documentation * [Custom Prompts](/framework/concepts/prompts) - How to customize agent prompts * [Custom Tools](/framework/features/custom-tools) - Creating custom tool functions * [MobileAgent SDK](/framework/sdk/droid-agent) - Complete API reference # Structured Output Source: https://docs.mobilerun.ai/framework/features/structured-output Extract structured data from device interactions using Pydantic models ## Quick Start ```python theme={null} import asyncio from pydantic import BaseModel, Field from mobilerun import MobileAgent, MobileConfig # 1. Define output structure class ContactInfo(BaseModel): """Contact information from device.""" name: str = Field(description="Full name of the contact") phone: str = Field(description="Phone number") email: str = Field(description="Email address", default="Not provided") # 2. Create agent with output_model async def main(): config = MobileConfig() agent = MobileAgent( goal="Find John Smith's contact information", config=config, output_model=ContactInfo, ) # 3. Run and access structured output result = await agent.run() if result.success and result.structured_output: contact: ContactInfo = result.structured_output print(f"Name: {contact.name}") print(f"Phone: {contact.phone}") print(f"Email: {contact.email}") asyncio.run(main()) ``` *** ## How It Works ### Two-Stage Process **Stage 1: Task Execution** * MobileAgent performs device actions while collecting required information * System prompt is automatically injected with your Pydantic schema * Agent completes with natural language answer containing the data **Stage 2: Extraction (Post-Completion)** * `StructuredOutputAgent` receives the final answer text * Uses LLM's `astructured_predict()` to extract data into your model * Validates against schema and returns typed object or `None` *** ## Example: Invoice Extraction ```python theme={null} from pydantic import BaseModel, Field from typing import List class Invoice(BaseModel): """Invoice information.""" invoice_number: str = Field(description="Invoice ID") vendor_name: str = Field(description="Vendor name") total_due: float = Field(description="Total amount in dollars") agent = MobileAgent( goal="Open Gmail and extract invoice from Acme Corp email", config=MobileConfig(), output_model=Invoice, ) result = await agent.run() invoice = result.structured_output print(f"Invoice {invoice.invoice_number}: ${invoice.total_due}") ``` *** ## Working with Results ### Accessing Data ```python theme={null} result = await agent.run() if result.success: if result.structured_output: data = result.structured_output # Typed Pydantic object print(f"Extracted: {data}") else: print(f"Extraction failed, text answer: {result.reason}") else: print(f"Task failed: {result.reason}") ``` ### Exporting to JSON ```python theme={null} result = await agent.run() if result.structured_output: # Convert to JSON and save json_str = result.structured_output.model_dump_json(indent=2) with open("output.json", "w") as f: f.write(json_str) ``` *** ## Configuration ### Custom Extraction LLM By default, extraction uses the `structured_output` LLM profile. If not configured, it falls back to the `fast_agent` LLM. You can specify a dedicated `structured_output` profile: **config.yaml:** ```yaml theme={null} llm_profiles: fast_agent: provider: GoogleGenAI model: gemini-3.1-flash-lite-preview temperature: 0.3 structured_output: provider: OpenAI model: gpt-4o-mini temperature: 0.0 # Low temp for consistent extraction ``` **Programmatically:** ```python theme={null} from mobilerun import load_llm config = MobileConfig() llms = { "fast_agent": load_llm("GoogleGenAI", "gemini-3.1-flash-lite-preview"), "structured_output": load_llm("OpenAI", "gpt-4o-mini"), } agent = MobileAgent( goal="Extract contact info for Alice", llms=llms, config=config, output_model=ContactInfo, ) ``` ### Reasoning Mode Works in both direct and reasoning modes: ```python theme={null} # Direct mode config = MobileConfig() config.agent.reasoning = False agent = MobileAgent( goal="Find weather for SF", config=config, output_model=WeatherInfo, ) # Reasoning mode config.agent.reasoning = True agent = MobileAgent( goal="Find weather for SF", config=config, output_model=WeatherInfo, ) ``` *** ## Best Practices **1. Add clear field descriptions** - The LLM uses these to understand what to extract: ```python theme={null} name: str = Field(description="Full name of customer who placed order") ``` **2. Provide defaults for optional fields** - Prevents extraction failures: ```python theme={null} rating: Optional[float] = Field(description="Customer rating (1-5)", default=None) ``` **3. Guide data collection in your goal**: ```python theme={null} agent = MobileAgent( goal="Find contact and get their phone number, email, and full name", config=config, output_model=ContactInfo, ) ``` *** ## Troubleshooting **Extraction returns None:** * Verify `output_model` is passed to `MobileAgent` * Check if task succeeded: `result.success` * Enable debug logging: `config.logging.debug = True` **Partial or incorrect data:** * Add more specific field descriptions * Mention required fields explicitly in the goal **Validation errors:** * Add `Optional` and defaults for uncertain fields *** ## Advanced ### Multiple Items Extract lists of data using a model with `List` fields: ```python theme={null} class ContactList(BaseModel): """Multiple contacts.""" contacts: List[ContactInfo] = Field(description="List of contacts") agent = MobileAgent( goal="Find contacts for John Smith and Jane Doe", config=config, output_model=ContactList, ) ``` ### Workflow Integration Extraction happens automatically in `MobileAgent.finalize()`: ```python theme={null} @step async def finalize(self, ctx: Context, ev: FinalizeEvent) -> ResultEvent: result = ResultEvent( success=ev.success, reason=ev.reason, steps=self.shared_state.step_number, structured_output=None, ) # Extract if model was provided if self.output_model is not None and ev.reason: structured_agent = StructuredOutputAgent( llm=self.structured_output_llm, pydantic_model=self.output_model, answer_text=ev.reason, ) extraction_result = await structured_agent.run() if extraction_result["success"]: result.structured_output = extraction_result["structured_output"] return result ``` *** ## Related Documentation * [MobileAgent API](/framework/sdk/droid-agent) * [Pydantic Documentation](https://docs.pydantic.dev/) * [Configuration Guide](/framework/sdk/configuration) * [Custom Variables](/framework/features/custom-variables) # Telemetry Source: https://docs.mobilerun.ai/framework/features/telemetry Configure anonymous telemetry # Why We Need Telemetry Telemetry helps us: * Identify which features are most used and need improvement * Prioritize bug fixes and new features based on real usage * Ensure Mobilerun works well across different environments We do not collect any personal or sensitive data. All telemetry is strictly anonymized and used only to improve the framework for everyone. If you have questions or concerns, please reach out on [GitHub](https://github.com/droidrun/mobilerun) or review our privacy policy. *** # Toggling Telemetry Mobilerun collects anonymized usage data to help us understand which features are most valuable and where improvements are needed. This data is never used for advertising or tracking individuals, and is only used to make Mobilerun better for the community. ## How to Disable Telemetry You can disable telemetry at any time by setting the following environment variable: ```bash theme={null} export MOBILERUN_TELEMETRY_ENABLED=false ``` Add this line to your shell profile (e.g., `.bashrc`, `.zshrc`, or `.profile`) to make it persistent across sessions. ## How to Enable Telemetry Again To re-enable telemetry, set the environment variable to `true`: ```bash theme={null} export MOBILERUN_TELEMETRY_ENABLED=true ``` Telemetry is currently controlled only by the `MOBILERUN_TELEMETRY_ENABLED` environment variable. While a `telemetry.enabled` config option exists in the configuration schema, it is not currently used by the telemetry system. *** # Tracing Source: https://docs.mobilerun.ai/framework/features/tracing Configure Phoenix/Langfuse tracing and trajectory recording Mobilerun provides multiple monitoring capabilities: 1. **LLM Tracing** - Real-time execution tracing via Arize Phoenix or Langfuse 2. **Trajectory Recording** - Local screenshots and UI state for debugging ## Quick Reference ```sh theme={null} # Enable Phoenix tracing mobilerun run "task" --tracing # Enable trajectory recording mobilerun run "task" --save-trajectory step ``` *** ## LLM Tracing Mobilerun supports two tracing providers for real-time monitoring of LLM calls, agent execution, and tool invocations: * **Arize Phoenix** (default) - Open-source observability platform * **Langfuse** - LLM engineering platform with cloud and self-hosted options Use tracing to debug agent behavior, monitor token usage, and analyze execution flow. *** ### Arize Phoenix Tracing #### Setup **1. Install Phoenix:** ```sh theme={null} uv pip install arize-phoenix ``` **2. Start Phoenix server:** ```sh theme={null} phoenix serve ``` The server starts at `http://localhost:6006` and provides a web UI for viewing traces. **3. Enable tracing in Mobilerun:** **Via CLI:** ```sh theme={null} mobilerun run "Open settings" --tracing ``` **Via config.yaml:** ```yaml theme={null} tracing: enabled: true ``` **Via code:** ```python theme={null} from mobilerun import MobileAgent, MobileConfig config = MobileConfig() config.tracing.enabled = True agent = MobileAgent(goal="Open settings", config=config) await agent.run() ``` **4. View traces:** Navigate to `http://localhost:6006` to see: * LLM calls with prompts, responses, and token counts * Agent workflow execution (Manager, Executor, FastAgent) * Tool invocations and their results * Execution timings and errors For more on using Phoenix, see the [Arize Phoenix documentation](https://docs.arize.com/phoenix). #### Phoenix Configuration Set environment variables to customize Phoenix: ```sh theme={null} # Custom Phoenix server URL (default: http://0.0.0.0:6006) export phoenix_url=http://localhost:6006 # Project name for organizing traces export phoenix_project_name=my_mobilerun_project ``` Environment variable names are lowercase: `phoenix_url` and `phoenix_project_name`. *** ### Langfuse Tracing Langfuse provides LLM observability with features like session tracking, user analytics, and cost monitoring. #### Setup **1. Get Langfuse credentials:** * **Cloud**: Sign up at [cloud.langfuse.com](https://cloud.langfuse.com) * **Self-hosted**: Deploy using [Langfuse docs](https://langfuse.com/docs/deployment/self-host) **2. Configure Mobilerun:** **Via environment variables:** ```sh theme={null} export LANGFUSE_SECRET_KEY=sk-lf-... export LANGFUSE_PUBLIC_KEY=pk-lf-... export LANGFUSE_HOST=https://cloud.langfuse.com ``` **Via config.yaml:** ```yaml theme={null} tracing: enabled: true provider: langfuse langfuse_secret_key: sk-lf-... langfuse_public_key: pk-lf-... langfuse_host: https://cloud.langfuse.com langfuse_user_id: user@example.com # Optional: track by user langfuse_session_id: "" # Optional: custom session ID ``` **Via code:** ```python theme={null} from mobilerun import MobileAgent, MobileConfig, TracingConfig config = MobileConfig( tracing=TracingConfig( enabled=True, provider="langfuse", langfuse_secret_key="sk-lf-...", langfuse_public_key="pk-lf-...", langfuse_host="https://cloud.langfuse.com", langfuse_user_id="user@example.com", ) ) agent = MobileAgent(goal="Open settings", config=config) await agent.run() ``` **3. View traces:** Navigate to your Langfuse dashboard to see: * LLM calls with prompts, completions, and token usage * Agent execution traces and nested workflows * Session-based analytics and cost tracking * User-level metrics (if `langfuse_user_id` is set) For more on using Langfuse, see the [Langfuse documentation](https://langfuse.com/docs). *** ## Trajectory Recording Trajectory recording saves screenshots and UI state locally for offline debugging and analysis. Unlike telemetry (sent to PostHog) and tracing (sent to Phoenix or Langfuse), trajectories stay on your machine. ### Recording Levels | Level | What's Saved | When to Use | | ---------------- | ------------------------------------ | ------------------------------------------------- | | `none` (default) | Nothing | Production use, saves disk space | | `step` | Screenshot + state per agent step | General debugging, recommended for most use cases | | `action` | Screenshot + state per atomic action | Detailed debugging, captures every tap/swipe/type | **Note:** `action` level generates significantly more files than `step` level. ### Enable Recording **Via CLI:** ```sh theme={null} mobilerun run "Open settings" --save-trajectory step ``` **Via config.yaml:** ```yaml theme={null} logging: save_trajectory: step # none | step | action ``` **Via code:** ```python theme={null} from mobilerun import MobileAgent, MobileConfig config = MobileConfig() config.logging.save_trajectory = "action" agent = MobileAgent(goal="Open settings", config=config) await agent.run() ``` ### Output Location Trajectories are saved to `trajectories/` in your working directory: ``` trajectories/ └── 2025-10-17_14-30-45_open_settings/ ├── step_000_screenshot.png ├── step_000_state.json ├── step_001_screenshot.png └── step_001_state.json ``` Each trajectory folder contains: * **Screenshots** - PNG images of the device screen at each step/action * **State files** - JSON files with: * UI accessibility tree (element hierarchy with IDs, text, bounds) * Action executed (e.g., `click(5)`, `type("hello", 3)`) * Agent reasoning and step number * Device state (current app package, activity) Use these files to: * Debug why the agent made specific decisions * Replay failed executions * Analyze UI element detection issues * Build training datasets for agent improvement *** ## Related Documentation * [Configuration System](/framework/sdk/configuration) - Configure tracing and telemetry settings * [Events and Workflows](/framework/concepts/events-and-workflows) - Build custom monitoring integrations * [CLI Usage](/framework/guides/cli) - Command-line flags for monitoring # Vision Mode Source: https://docs.mobilerun.ai/framework/features/vision Screenshots for agents, and how screen coordinates stay accurate With vision enabled, agents receive a screenshot of the device alongside the accessibility tree on every step. ## Enabling vision ```yaml theme={null} agent: manager: vision: true # planning agent sees screenshots executor: vision: true # action agent sees screenshots fast_agent: vision: true # direct-execution agent sees screenshots ``` ```bash theme={null} # CLI: enable for all agents at once mobilerun run "Open settings and enable dark theme" --vision ``` ```python theme={null} # SDK from mobilerun import MobileConfig from mobilerun.config_manager import AgentConfig, FastAgentConfig config = MobileConfig(agent=AgentConfig(fast_agent=FastAgentConfig(vision=True))) ``` `vision_only: true` (CLI: `--vision-only`) is a separate mode that drops the accessibility tree and drives the device from screenshots alone. ## The coordinate contract With vision active, coordinates are handled automatically: 1. The screenshot is resized to the exact dimensions the model will ground on, with a labeled coordinate grid drawn on it. 2. The device state declares that coordinate space, and element bounds in the accessibility tree text are emitted in the same space. 3. Coordinates the model passes to `click_at`, `click_area`, `long_press_at`, and `swipe` are converted back automatically — to device pixels on Android, to points on iOS. 4. Coordinates outside the declared space are rejected with a corrective error and the model retries. No configuration is needed. Element-index actions (`click`, `long_press`) are unaffected; they always tap accessibility-tree bounds. ### Per-model effective screenshot size Some providers downsize images server-side before the model sees them. To keep coordinates exact, mobilerun resolves the screenshot dimensions each active vision model actually grounds on and resizes to the smallest result across all recipients (manager + executor, or the fast agent): | Model family | Longest edge | | ------------------------------------ | ------------ | | Anthropic standard (e.g. Sonnet) | 1568 px | | Anthropic high-res (e.g. Opus, 4.x+) | 2576 px | | OpenAI, Gemini, Ollama | 2048 px | Unknown Anthropic model IDs fall back to the standard 1568 budget. The declared coordinate space always matches the image the model receives, so `convert_point` is exact regardless of which model is configured. ### Capping the screenshot size For local or undocumented vision models that downsize to a size mobilerun can't infer (for example, certain Ollama models), set an explicit cap with `agent.model_screenshot_max_side`. The cap is applied on top of per-model resolution and uses the smaller of the two. ```yaml theme={null} agent: # Cap the model-facing screenshot at 1280 px on the longest edge. model_screenshot_max_side: 1280 ``` ```python theme={null} from mobilerun import MobileConfig from mobilerun.config_manager import AgentConfig config = MobileConfig(agent=AgentConfig(model_screenshot_max_side=1280)) ``` Leave this unset for the supported providers above — the per-model defaults already match what they ground on. ## Coordinate tools and vision `swipe` is always available. `click_at`, `click_area`, and `long_press_at` are disabled by default; enabling vision re-enables `click_at` automatically (non-normalized). To enable all coordinate tools: ```yaml theme={null} tools: disabled_tools: [] ``` ## Normalized coordinates `agent.use_normalized_coordinates: true` makes the model emit `[0-1000]` coordinates instead of pixels; the coordinate contract is inactive in this mode. An occasional "Coordinates ... are outside the ... coordinate space" error is the guardrail correcting the model; it retries automatically. Persistent errors suggest a model that is weak at visual grounding. # CLI Usage Source: https://docs.mobilerun.ai/framework/guides/cli Command-line interface for controlling devices with natural language ## Overview The Mobilerun CLI lets you control Android and iOS devices using natural language commands powered by LLM agents. ### Quick Start ```bash theme={null} # Setup device mobilerun setup # Run a command mobilerun run "Open Spotify and play my Discover Weekly" ``` **Auto-configuration**: If no config exists, Mobilerun creates a default `config.yaml` at `~/.config/mobilerun/config.yaml` automatically. *** ## Commands Execute natural language commands on your device. ### Usage ```bash theme={null} mobilerun run "" [OPTIONS] ``` ### Common Flags | Flag | Description | Default | | ------------------- | ------------------------------------------------------------------- | --------------------------------- | | `--agent`, `-a` | External agent to use (not yet supported — reserved for future use) | None | | `--provider`, `-p` | LLM provider (GoogleGenAI, OpenAI, Anthropic, etc.) | From config | | `--model`, `-m` | Model name | From config | | `--device`, `-d` | Device serial or IP | Auto-detect | | `--steps` | Max execution steps | `15` | | `--reasoning` | Enable planning mode | `false` | | `--vision` | Enable vision for all agents | From config | | `--tcp` | Use TCP instead of content provider | `false` | | `--debug` | Verbose logging | `false` | | `--save-trajectory` | Save execution (`none`, `step`, `action`) | `none` | | `--config`, `-c` | Custom config path | `~/.config/mobilerun/config.yaml` | ### Examples ```bash theme={null} # Simple command mobilerun run "Open Settings" # Multi-step task mobilerun run "Send WhatsApp to John: I'll be late" # Specific device mobilerun run "Check battery" --device emulator-5554 ``` ```bash theme={null} # Google Gemini export GOOGLE_API_KEY=your-key mobilerun run "Archive old emails" \ --provider GoogleGenAI \ --model gemini-3.1-flash-lite-preview # OpenAI export OPENAI_API_KEY=your-key mobilerun run "Create shopping list" \ --provider OpenAI \ --model gpt-4o # Anthropic Claude export ANTHROPIC_API_KEY=your-key mobilerun run "Reply to latest email" \ --provider Anthropic \ --model claude-sonnet-4-5-latest # Local Ollama (free) mobilerun run "Turn on dark mode" \ --provider Ollama \ --model llama3.3:70b \ --base_url http://localhost:11434 ``` ```bash theme={null} # Complex task with planning mobilerun run "Organize inbox by sender" \ --reasoning \ --vision \ --steps 30 # Debug failing command mobilerun run "Book Uber to airport" \ --debug \ --save-trajectory action # Wireless execution mobilerun run "Clear cache" \ --device 192.168.1.100:5555 \ --tcp # Custom config mobilerun run "Enable 2FA" \ --config /path/to/config.yaml ``` ### Provider Options | Provider | Install | Environment Variable | | ----------- | --------------------------------------- | -------------------- | | GoogleGenAI | Included by default | `GOOGLE_API_KEY` | | OpenAI | Included by default | `OPENAI_API_KEY` | | OpenAILike | Included by default | Varies by provider | | OpenRouter | Included by default | `OPENROUTER_API_KEY` | | Ollama | Included by default | None (local) | | Anthropic | `uv pip install 'mobilerun[anthropic]'` | `ANTHROPIC_API_KEY` | | DeepSeek | `uv pip install 'mobilerun[deepseek]'` | `DEEPSEEK_API_KEY` | ### `mobilerun devices` List connected devices. ```bash theme={null} mobilerun devices # Output: # Found 2 connected device(s): # • emulator-5554 # • 192.168.1.100:5555 ``` *** ### `mobilerun setup` Install Portal APK on device. ```bash theme={null} # Auto-detect device mobilerun setup # Specific device mobilerun setup --device emulator-5554 # Custom APK mobilerun setup --path /path/to/portal.apk ``` **What it does:** 1. Downloads compatible Portal APK for your SDK version 2. Installs with all permissions 3. Auto-enables accessibility service 4. Opens settings if manual enable needed *** ### `mobilerun ping` Test Portal connection. ```bash theme={null} # Test default communication mobilerun ping # Test TCP mode mobilerun ping --tcp # Specific device mobilerun ping --device 192.168.1.100:5555 ``` **Success output:** `Portal is installed and accessible. You're good to go!` *** ### `mobilerun connect` Connect to device via TCP/IP. ```bash theme={null} mobilerun connect 192.168.1.100:5555 ``` **Prerequisites:** ```bash theme={null} # Enable wireless debugging (Android 11+) # Settings > Developer options > Wireless debugging # Or via USB: adb tcpip 5555 adb shell ip route | awk '{print $9}' # Get IP mobilerun connect :5555 ``` *** ### `mobilerun disconnect` Disconnect from device. ```bash theme={null} mobilerun disconnect 192.168.1.100:5555 ``` *** ### `mobilerun device` Direct device actions that bypass the LLM agent. ```bash theme={null} # Take a screenshot mobilerun device screenshot # Print the UI accessibility tree mobilerun device ui # Tap at coordinates mobilerun device tap 500 500 # Swipe from point to point mobilerun device swipe 100 500 100 200 --duration 0.5 # Long press at coordinates mobilerun device long-press 500 500 # Type text into focused field mobilerun device type "Hello world" --clear # Press a system button (back, home, enter) mobilerun device press back # List installed apps mobilerun device apps --system # Launch an app by package name mobilerun device start com.example.app ``` All `device` subcommands support `--device`, `--config`, `--tcp`, and `--ios` flags. Record and replay automation sequences. ### `mobilerun macro list` List saved trajectories. ```bash theme={null} # Default directory mobilerun macro list # Custom directory mobilerun macro list /path/to/trajectories ``` **Output:** ``` Found 3 trajectory(s): ┏━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━┓ ┃ Folder ┃ Description ┃ Actions ┃ ┡━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━┩ │ open-settings │ Opens settings app │ 3 │ │ enable-dark-mode │ Navigate to display... │ 8 │ └──────────────────┴───────────────────────────┴─────────┘ ``` *** ### `mobilerun macro replay` Replay recorded macro. ```bash theme={null} # Basic replay mobilerun macro replay trajectories/open-settings # Custom device and timing mobilerun macro replay trajectories/login-flow \ --device emulator-5554 \ --delay 0.5 # Start from specific step mobilerun macro replay trajectories/checkout \ --start-from 5 \ --max-steps 10 # Preview without executing mobilerun macro replay trajectories/test --dry-run ``` **Flags:** | Flag | Description | Default | | -------------------- | ----------------------- | ----------- | | `--device`, `-d` | Device serial | Auto-detect | | `--delay`, `-t` | Seconds between actions | `1.0` | | `--start-from`, `-s` | Start step (1-based) | `1` | | `--max-steps`, `-m` | Max steps to run | All | | `--dry-run` | Preview only | `false` | *** ### Recording Trajectories ```bash theme={null} # Record at action level (most detailed) mobilerun run "Create alarm for 7am" --save-trajectory action # Record at step level mobilerun run "Export contacts" --save-trajectory step ``` **Trajectory structure:** ``` trajectories/2025-10-16_14-30-45/ ├── macro.json # Action sequence ├── step_0.png # Screenshots ├── step_1.png └── ... ``` *** ## Configuration ### Override Priority 1. **CLI flags** (highest) 2. **Config file** (`~/.config/mobilerun/config.yaml`) 3. **Defaults** (lowest) ### Common Patterns ```bash Quick Test theme={null} mobilerun run "Turn on dark mode" \ --provider GoogleGenAI \ --model gemini-3.1-flash-lite-preview ``` ```bash Debug Task theme={null} mobilerun run "Book ride to airport" \ --debug \ --reasoning \ --vision \ --save-trajectory action ``` ```bash Cost Optimization theme={null} mobilerun run "Set alarm" \ --provider GoogleGenAI \ --model gemini-3.1-flash-lite-preview \ --no-vision ``` ```bash Multiple Devices theme={null} for device in $(adb devices | awk 'NR>1 {print $1}'); do mobilerun run "Clear notifications" --device $device done ``` *** ## Troubleshooting ```bash theme={null} # Check ADB adb devices # If unauthorized: Accept prompt on device # If not listed: Try different USB port/cable # Restart ADB adb kill-server && adb start-server ``` ```bash theme={null} # Verify installation adb shell pm list packages | grep mobilerun # Reinstall mobilerun setup # Enable accessibility manually adb shell settings put secure enabled_accessibility_services \ com.mobilerun.portal/com.mobilerun.portal.service.MobilerunAccessibilityService ``` ```bash theme={null} # Install provider uv pip install 'mobilerun[anthropic]' # Check API key echo $GOOGLE_API_KEY # Set if missing export GOOGLE_API_KEY=your-key ``` ```bash theme={null} # Increase steps mobilerun run "Complex task" --steps 50 # Enable debug mode mobilerun run "Task" --debug # Try reasoning mode mobilerun run "Multi-step task" --reasoning ``` ```bash theme={null} # Enable TCP mode (USB connected first) adb tcpip 5555 # Get device IP adb shell ip route | awk '{print $9}' # Connect mobilerun connect :5555 # Verify mobilerun ping --tcp ``` *** ## Environment Variables For first-time setup, use `mobilerun configure` to interactively choose your provider and credentials. Environment variables below are useful for overrides and CI/CD. | Variable | Description | Default | | ------------------- | --------------------- | --------------------------------- | | `GOOGLE_API_KEY` | Google Gemini API key | None | | `OPENAI_API_KEY` | OpenAI API key | None | | `ANTHROPIC_API_KEY` | Anthropic API key | None | | `DEEPSEEK_API_KEY` | DeepSeek API key | None | | `MOBILERUN_CONFIG` | Config file path | `~/.config/mobilerun/config.yaml` | **Setting variables:** ```bash Linux/macOS theme={null} export GOOGLE_API_KEY=your-key ``` ```bash Windows PowerShell theme={null} $env:GOOGLE_API_KEY="your-key" ``` ```bash Permanent (Linux/macOS) theme={null} echo 'export GOOGLE_API_KEY=your-key' >> ~/.bashrc source ~/.bashrc ``` *** ## Next Steps * [Configuration Guide](/framework/sdk/configuration) - Customize behavior * [Device Setup](/framework/guides/device-setup) - Detailed setup instructions * [Agent Architecture](/framework/concepts/architecture) - How it works * [Custom Tools](/framework/features/custom-tools) - Extend functionality # Device Setup Source: https://docs.mobilerun.ai/framework/guides/device-setup Setting up Android and iOS devices for Mobilerun automation ## Overview Mobilerun controls devices through a specialized Portal app that bridges your computer and the device. ## Prerequisites **macOS**: `brew install android-platform-tools` **Linux**: `sudo apt install adb` **Windows**: Download from [Android Developer Site](https://developer.android.com/studio/releases/platform-tools) Verify: `adb version` 1. Go to **Settings** > **About phone** 2. Tap **Build number** 7 times (enables Developer options) 3. Go to **Settings** > **Developer options** 4. Enable **USB debugging** 5. Connect device and tap **Always allow** Verify: `adb devices` ```bash theme={null} # Automatic setup (downloads compatible Portal APK) mobilerun setup # Or specify device mobilerun setup --device SERIAL_NUMBER ``` This will: * Download the compatible Portal APK for your SDK version * Install with all permissions granted * Enable accessibility service automatically ```bash theme={null} mobilerun ping # Output: Portal is installed and accessible. You're good to go! ``` *** ## Portal App The Mobilerun Portal (`com.mobilerun.portal`) provides: * **Accessibility Tree** - Extracts UI elements and their properties * **Device State** - Tracks current activity, keyboard visibility * **Action Execution** - Tap, swipe, text input, and other actions * **Dual Communication** - TCP (faster) or Content Provider (fallback) The Portal only communicates locally via ADB. No data is sent to external servers. *** ## Communication Modes **How it works:** * Portal runs HTTP server on device port 8080 * ADB forwards local port → device port 8080 * Mobilerun sends HTTP requests to `localhost:PORT` **Enable:** ```bash theme={null} # CLI mobilerun run "your command" --tcp # Python config = MobileConfig(device=DeviceConfig(serial="DEVICE_SERIAL", use_tcp=True)) ``` **Troubleshooting:** ```bash theme={null} # Check port forwarding adb forward --list # Test Portal server adb shell netstat -an | grep 8080 # Remove all forwards and retry adb forward --remove-all mobilerun ping --tcp ``` **How it works:** * Portal exposes content provider at `content://com.mobilerun.portal/` * Commands sent via ADB shell: `content query --uri ...` * JSON responses parsed from shell output **Usage:** ```bash theme={null} # Default mode (no flag needed) mobilerun ping # Python config = MobileConfig(device=DeviceConfig(serial="DEVICE_SERIAL", use_tcp=False)) ``` **Troubleshooting:** ```bash theme={null} # Test content provider directly adb shell content query --uri content://com.mobilerun.portal/state # Should show: Row: 0 result={"data": "{...}"} ``` *** ## Advanced Setup ### Setup 1. **Settings** > **Developer options** > **Wireless debugging** 2. Note IP address and port (e.g., `192.168.1.100:37757`) **QR Code Method:** ```bash theme={null} adb pair ``` **Pairing Code Method:** 1. Tap **Pair device with pairing code** 2. Note pairing code and IP:port 3. Run: `adb pair IP:PORT` 4. Enter pairing code ```bash theme={null} adb connect IP:PORT mobilerun ping --device IP:PORT ``` ### Common Issues * Connection refused → Check same WiFi network and firewall * Frequent drops → Use 5GHz WiFi or stay near router * Can't find IP → Run `adb shell ip addr show wlan0 | grep "inet "` via USB ```bash theme={null} # Connect via USB first adb tcpip 5555 ``` ```bash theme={null} adb shell ip addr show wlan0 | grep inet ``` ```bash theme={null} # Disconnect USB cable adb connect DEVICE_IP:5555 mobilerun ping --device DEVICE_IP:5555 ``` ### List Devices ```bash theme={null} mobilerun devices # Found 2 connected device(s): # • emulator-5554 # • 192.168.1.100:5555 ``` ### Target Specific Device ```bash theme={null} # CLI mobilerun run "your command" --device emulator-5554 # Python config = MobileConfig(device=DeviceConfig(serial="emulator-5554")) agent = MobileAgent(goal="your task", config=config) ``` ### Parallel Control ```python theme={null} import asyncio from mobilerun import DeviceConfig, MobileConfig, MobileAgent from async_adbutils import adb async def control_device(serial: str, command: str): device_config = DeviceConfig(serial=serial) config = MobileConfig(device=device_config) agent = MobileAgent(goal=command, config=config) return await agent.run() async def main(): devices = await adb.list() tasks = [ control_device(devices[0].serial, "Open settings"), control_device(devices[1].serial, "Check battery"), ] results = await asyncio.gather(*tasks) print(results) asyncio.run(main()) ``` *** ## Troubleshooting **Symptoms:** `adb devices` shows no devices or `unauthorized` **Solutions:** 1. Unplug/replug USB cable, try different port 2. Revoke USB debugging authorizations (Developer options) 3. Reconnect and tap "Always allow" 4. Restart ADB: `adb kill-server && adb start-server` 5. **Windows**: Install [Google USB Driver](https://developer.android.com/studio/run/win-usb) **Symptoms:** `mobilerun ping` fails with "Portal is not installed" **Solutions:** 1. Reinstall: `mobilerun setup` 2. Check: `adb shell pm list packages | grep mobilerun` 3. Verify APK architecture matches device (arm64-v8a for most devices) **Symptoms:** `mobilerun ping` fails with "accessibility service not enabled" **Solutions:** 1. Auto-enable: ```bash theme={null} adb shell settings put secure enabled_accessibility_services \ com.mobilerun.portal/com.mobilerun.portal.service.MobilerunAccessibilityService adb shell settings put secure accessibility_enabled 1 ``` 2. Manual: Settings > Accessibility > Mobilerun Portal > Toggle ON 3. Verify: ```bash theme={null} adb shell settings get secure enabled_accessibility_services # Should contain: com.mobilerun.portal/... ``` **Symptoms:** `input_text()` fails or types gibberish **Solutions:** 1. Keyboard auto-enabled by `AndroidDriver` initialization: ```bash theme={null} # Verify adb shell settings get secure default_input_method # Should show: com.mobilerun.portal/.input.MobilerunKeyboardIME ``` 2. Manual switch: Long press space bar → Select "Mobilerun Keyboard" 3. Focus the element first (tap it), then input text **Symptoms:** `get_state()` returns empty or incomplete UI tree **Solutions:** 1. Verify accessibility: `mobilerun ping` 2. Some apps block accessibility services (WebViews, games, custom UI) 3. Wait for UI: `time.sleep(1)` after tap/swipe 4. Enable Portal overlay to see detected elements iOS support is stable and provides the same core device automation capabilities as Android through `mobilerun-ios`. Platform-specific differences are listed under [Supported Features](#supported-features). *** ## Prerequisites * **macOS** with **Xcode** installed * **Apple ID** with signing capability (a personal team is sufficient) * **iPhone** with **Developer Mode** enabled * **USB data cable** to connect the iPhone to your Mac * **Mobilerun WebDriverAgent** installed and signed on the iPhone (see the [Connect an iPhone guide](/guides/connect-iphone)) * **`mobilerun-ios`** installed on your Mac ### Enable Developer Mode on your device 1. Go to **Settings** > **Privacy & Security** > **Developer Mode** 2. Toggle **Developer Mode** on 3. Restart the device when prompted 4. After restart, confirm the prompt to enable Developer Mode For the complete device preparation and WebDriverAgent signing instructions, follow the [Connect an iPhone guide](/guides/connect-iphone). You only need to complete the device, WebDriverAgent, and `mobilerun-ios` installation steps; logging in is not required for local mode. *** ## Install and Run `mobilerun-ios` `mobilerun-ios --local` connects to WebDriverAgent over USB and exposes the device API on your Mac. The framework discovers that API automatically. Choose either Homebrew or the installation script: ```bash Homebrew theme={null} brew install droidrun/tap/mobilerun-ios ``` ```bash Installation script theme={null} curl -fsSL https://github.com/droidrun/mobilerun-ios-releases/releases/latest/download/install.sh | sh ``` Verify the installation: ```bash theme={null} mobilerun-ios --version ``` Connect and unlock the iPhone, accept the **Trust This Computer** prompt, then run: ```bash theme={null} mobilerun-ios list ``` Copy the UDID shown for the iPhone you want to control. ```bash theme={null} mobilerun-ios --local ``` Keep this command running. By default, it serves the device API at `http://127.0.0.1:8080`. ```bash theme={null} curl http://127.0.0.1:8080/ping ``` The response should contain `"result":"pong"`. Then test the framework: ```bash theme={null} mobilerun run "Take a screenshot" --ios ``` To select the server explicitly, pass its URL: ```bash theme={null} mobilerun run "Take a screenshot" --ios --device http://127.0.0.1:8080 ``` When several iPhones are attached, ports advance per device (`8080`, `8081`, and so on). Each device URL is printed when `mobilerun-ios` starts. Framework auto-discovery scans ports `8080` through `8089`. Loopback local mode does not require authentication. If you bind to a non-loopback address, `mobilerun-ios` requires `--local-token`. Set the same token in `MOBILERUN_DEVICE_TOKEN` or `device.auth_token` in the framework configuration. *** ## Architecture The iOS local driver uses a different architecture than Android: | Feature | Android | iOS | | ------------- | -------------------------- | ---------------------------------------- | | Communication | ADB + TCP/Content Provider | Local HTTP API (port 8080 by default) | | Device bridge | Portal APK | `mobilerun-ios` backed by WebDriverAgent | | Setup tool | `mobilerun setup` | `mobilerun-ios --local ` | | Accessibility | Android Accessibility API | WebDriverAgent / XCUITest | | Text Input | Custom keyboard IME | Direct XCUITest text input | | Connection | ADB over USB/TCP | USB through `mobilerun-ios` | `mobilerun-ios` uses WebDriverAgent to extract accessibility trees, perform gestures, manage apps, and capture screenshots. Local mode exposes these operations through the portal HTTP contract used by the framework. *** ## Usage ### CLI ```bash theme={null} # Auto-discovers mobilerun-ios on ports 8080-8089 mobilerun run "your command" --ios # Explicit local server URL mobilerun run "your command" --ios --device http://127.0.0.1:8080 ``` ### Python API ```python theme={null} from mobilerun import MobileAgent, MobileConfig, DeviceConfig config = MobileConfig( device=DeviceConfig( platform="ios", serial="http://127.0.0.1:8080", # optional, auto-discovered if omitted ) ) agent = MobileAgent( goal="Open Settings and check WiFi", config=config ) result = await agent.run() ``` *** ## Supported Features | Feature | Status | Notes | | ----------------- | ------ | --------------------------------------------------- | | `tap()` | ✅ | | | `swipe()` | ✅ | | | `input_text()` | ✅ | | | `screenshot()` | ✅ | | | `get_state()` | ✅ | Accessibility tree extraction | | `start_app()` | ✅ | By bundle identifier | | `stop_app()` | ✅ | By bundle identifier | | `get_apps()` | ✅ | Includes system-app filtering | | `get_date()` | ✅ | | | `press_button()` | ✅ | `home`, `back`, `enter`, `delete`, and `app_switch` | | `drag()` | ❌ | | | `install_app()` | ✅ | From a path or URL accessible to `mobilerun-ios` | | `uninstall_app()` | ❌ | Not exposed by the local API | *** ## Troubleshooting **Symptoms:** The connected iPhone does not appear in the device list **Solutions:** 1. Unlock the iPhone and accept the **Trust This Computer** prompt 2. Confirm the USB cable supports data, not only charging 3. Open Xcode once with the device connected and verify it appears as a run destination **Symptoms:** `mobilerun-ios --local` times out while waiting for WebDriverAgent **Solutions:** 1. Re-sign and build `WebDriverAgentRunner` in Xcode 2. Trust the developer certificate under **Settings** > **General** > **VPN & Device Management** 3. Keep the device unlocked and run `mobilerun-ios --local ` again 4. Review the [Connect an iPhone guide](/guides/connect-iphone) for the complete signing procedure **Symptoms:** `curl http://127.0.0.1:8080/ping` times out or is refused **Solutions:** 1. Confirm `mobilerun-ios --local ` is still running 2. Check its startup output for the actual server URL 3. If another process uses port 8080, choose an available high port, for example: `mobilerun-ios --local --local-addr 127.0.0.1:18080 ` 4. Test the printed URL with `/ping`. Because custom ports are not auto-discovered, pass it to the framework explicitly with `--device http://127.0.0.1:18080` **Symptoms:** `mobilerun run ... --ios` cannot find the iPhone **Solutions:** 1. Confirm the local server is on one of the automatically scanned ports, `8080` through `8089` 2. Pass the URL explicitly: `--device http://127.0.0.1:8080` 3. If the server uses a token, export `MOBILERUN_DEVICE_TOKEN` before starting the framework *** ## Next Steps * Learn about the [Agent System](/framework/concepts/architecture) * Explore [Configuration Options](/framework/sdk/configuration) * Try [Custom Tools](/framework/features/custom-tools) * Implement [Structured Output](/framework/features/structured-output) # Quickstart with Docker Source: https://docs.mobilerun.ai/framework/guides/docker Get up and running with Mobilerun in Docker quickly and effectively This guide will help you get Mobilerun running in a Docker container, controlling your Android device through natural language without installing it natively. ### Host configuration Follow the steps below to ensure your host system is properly configured to mount the smartphone inside the container. 1. Make sure you’ve completed the prerequisite steps in the [Quickstart](/framework/quickstart). Then verify the ADB connection with `adb devices`. This should list your phone as “device” along with its Serial Number. If the phone appears as `unauthorized` make sure to accept the prompt on your phone. 2. Confirm that the ADB keys have been correctly created. Check for an `.android` folder under your `$HOME` directory that contains: `adb.5037`, `adbkey`, and `adbkey.pub`. 3. Set up a custom rule to uniquely identify your phone and map it to a static path: * Open a terminal and find your device’s `idVendor` and `idProduct` using the `lsusb` command. In the example below, the `idVendor` and `idProduct` are `18d1` and `4ee2`, respectively. ```bash theme={null} > lsusb Bus 003 Device 002: ID 18d1:4ee2 Google Inc. Nexus/Pixel Device (MTP + debug) ``` * Create a new file (sudo may be required) under `/etc/udev/rules.d` and name it `51-android.rules`. * Add the following content and save the file: ```bash theme={null} SUBSYSTEM=="usb", ATTR{idVendor}=="XXXX", ATTR{idProduct}=="YYYY", ATTR{serial}=="SSSS", MODE="0666", GROUP="plugdev", SYMLINK+="phone1/phone" ``` where `XXXX`, `YYYY`, and `SSSS` are the vendor ID, product ID, and serial number of your phone. This static mapping is required because otherwise the phone’s mount path will change across reconnections! * Reload the rules ```bash theme={null} sudo udevadm control --reload-rules sudo udevadm trigger ``` 4. Kill the ADB server on the host so that the container can actually detect the phone: ```bash theme={null} adb kill-server ``` ### Usage Pull the image from the GitHub Container Registry: ```bash theme={null} docker pull ghcr.io/droidrun/mobilerun:latest ``` Before running the container, note that the following options are always required when launching Mobilerun in Docker: | Docker option | What it does | Why it matters for Mobilerun | | ---------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | | `--group-add plugdev` | Adds the host user to the container’s `plugdev` group. | Gives the container permission to access USB devices (e.g., your Android phone) without requiring root. | | `--device /dev/phone1/phone:/dev/phone` | Maps a specific USB device file from the host into the container. Thanks to our udev rule, the phone will always be mapped here. | Allows Mobilerun to see the phone as `/dev/phone` inside the container, so it can communicate with the device. | | `--volume /dev/bus/usb:/dev/bus/usb` | Mounts the entire USB bus directory. | Provides access to all connected USB devices, enabling Mobilerun to discover and interact with the phone. | | `--volume ~/.android:/home/mobilerun/.android` | Mounts the host’s Android configuration directory into the container. | Stores ADB keys and settings so that once you grant permission on the host, Mobilerun doesn’t prompt for it again inside the container. | The Docker image uses the `mobilerun` entrypoint, which means you can use any of the CLI commands described in the [CLI Usage](/framework/guides/cli) section and append it to the following base command: ```bash theme={null} docker run \ --group-add plugdev \ --device /dev/phone1/phone:/dev/phone \ --volume /dev/bus/usb:/dev/bus/usb \ --volume ~/.android:/home/mobilerun/.android \ ghcr.io/droidrun/mobilerun:latest \ ``` #### Set Up the Portal APK Simply run the container with the `setup` CLI command. ```bash theme={null} docker run \ --group-add plugdev \ --device /dev/phone1/phone:/dev/phone \ --volume /dev/bus/usb:/dev/bus/usb \ --volume ~/.android:/home/mobilerun/.android \ ghcr.io/droidrun/mobilerun:latest \ setup ``` #### Verify the setup Simply run the container with the `ping` CLI command. ```bash theme={null} docker run \ --group-add plugdev \ --device /dev/phone1/phone:/dev/phone \ --volume /dev/bus/usb:/dev/bus/usb \ --volume ~/.android:/home/mobilerun/.android \ ghcr.io/droidrun/mobilerun:latest \ ping ``` #### Run some agents Now you’re ready to control your device using Docker and natural language: ```bash theme={null} # Using default configuration with Google API key docker run --group-add plugdev --device /dev/phone1/phone:/dev/phone --volume /dev/bus/usb:/dev/bus/usb --volume ~/.android:/home/mobilerun/.android --env GOOGLE_API_KEY=your-api-key-here ghcr.io/droidrun/mobilerun:latest run "Open the settings app and tell me the Android version" # Override provider and model docker run --group-add plugdev --device /dev/phone1/phone:/dev/phone --volume /dev/bus/usb:/dev/bus/usb --volume ~/.android:/home/mobilerun/.android --env OPENAI_API_KEY=your-api-key-here ghcr.io/droidrun/mobilerun:latest run "Open a browser and search for mobilerun" -p OpenAI -m gpt-4o # Use a locally-running LLM, example for LM Studio running in the LAN docker run --group-add plugdev --device /dev/phone1/phone:/dev/phone --volume /dev/bus/usb:/dev/bus/usb --volume ~/.android:/home/mobilerun/.android --network host ghcr.io/droidrun/mobilerun:latest run "Open a browser and search for mobilerun" -p OpenAILike -m gpt-oss --api_base http://IP-of-LMStudio-server:PORT/v1 ``` #### Troubleshooting * Error response from daemon If you encounter the following error: ``` docker: Error response from daemon: error gathering device information while adding custom '/dev/phone1/phone': no such file or directory ``` it usually indicates one of two things: * The phone isn’t mounted at the expected custom path. Verify that `/dev/phone1/phone` exists. * An ADB server is still running on the host. Stop it with `adb kill-server`. *** ## Next Steps * [CLI Usage](/framework/guides/cli) - Dive into the various parameters and start building your own projects with Docker! # Overview Source: https://docs.mobilerun.ai/framework/guides/overview Welcome to the Mobilerun Guides! This section provides step-by-step instructions and best practices for using Mobilerun. Each guide focuses on a specific aspect of the framework, from device setup to advanced automation patterns. *** ## Available Guides ### Getting Started **[CLI Reference](./cli)** - Complete command-line interface guide * All CLI commands (`run`, `setup`, `devices`, `connect`, `disconnect`, `ping`, `device`, `macro`, `doctor`, `tui`) * Configuration overrides and flags * Environment variables and API keys * Common workflows and troubleshooting **[Device Setup](./device-setup)** - Set up Android and iOS devices * Portal app installation and configuration * Accessibility service enablement * TCP vs Content Provider communication * Wireless debugging and multi-device management **[Configuration System](/framework/sdk/configuration)** - Master the config-driven architecture * YAML configuration structure * LLM profiles per agent (Manager, Executor, FastAgent) * Mixing LLM providers (OpenAI, Anthropic, Google, Ollama, DeepSeek) * Prompts and app cards *** ### Templates Explore real-world examples and starter projects in the [mobilerun-framework-examples repository](https://github.com/droidrun/mobilerun-framework-examples): * **[LinkedInJobsScraper](https://github.com/droidrun/mobilerun-framework-examples/tree/main/LinkedInJobsScraper)** - Agentic workflow that searches LinkedIn for roles, evaluates matches, and prepares tailored applications * **[LinkedInLeads](https://github.com/droidrun/mobilerun-framework-examples/tree/main/LinkedInLeads)** - End-to-end lead discovery and enrichment for LinkedIn companies and roles * **[TwitterPost](https://github.com/droidrun/mobilerun-framework-examples/tree/main/TwitterPost)** - Finds trending topics, drafts posts, and generates images to publish on X/Twitter * **[play2048](https://github.com/droidrun/mobilerun-framework-examples/tree/main/play2048)** - MobileAgent that plays the 2048 game on play2048.co Each example includes a self-contained workflow with entrypoint, configuration, and sample data. See the [README](https://github.com/droidrun/mobilerun-framework-examples) for setup instructions and contribution guidelines. These workflow examples target droidrun framework v0.4.0. See the repository's `legacy/` folder for examples built against earlier versions. *** Need help? Join our [Discord community](https://discord.gg/ZZbKEZZkwK) for support and discussions. # Overview Source: https://docs.mobilerun.ai/framework/overview Mobilerun is a powerful framework that enables you to control Android and iOS devices through intelligent LLM agents. Build sophisticated mobile automation workflows with natural language commands. Get up and running with Mobilerun in minutes View Templates for example Use Cases Understand the hierarchical agent system Explore the complete API documentation ## Core Features Learn about the powerful features that make Mobilerun a comprehensive mobile automation framework. These capabilities enable you to build sophisticated, production-ready automation workflows. Extract typed data with Pydantic Store and manage API keys securely Provide app-specific guidance Extend capabilities with custom functions ## Runtime Choose your preferred environment for running Mobilerun automations, from local physical devices to cloud-based solutions. * Connect your own physical Android device for direct automation * Access our managed cloud environment for instant mobile app automation without any setup. # Quickstart Source: https://docs.mobilerun.ai/framework/quickstart Get up and running with Mobilerun quickly and effectively