# Android Automation REST API: Runs, Cloud Devices, Profiles

> Call the MAS Android automation REST API with an API key for run history, cloud device runs, code sync, device lists, settings profiles, proxies and webhooks.

Source: https://automationmacro.com/docs/rest-api (Integrate, updated 2026-09-04)

The Macro Automation Studio (MAS) REST API is the Android automation API you can call with an API key. It covers what an integration needs: the history of your runs, starting and stopping macro runs on cloud devices, syncing code before a cloud run, listing your devices and groups, and managing settings profiles, proxies and webhooks. Everything else stays in the app. This page lists every route that accepts a key, with the fields you send and the fields you get back.

## Authenticate to the Android automation API

Send your key in an `X-API-KEY` header on every request. The base URL is `https://api.automationmacro.com`. Bodies and responses are JSON. Create keys on the **API Keys** page in the app; the [API keys](/docs/api-keys) page explains expiry, the 25 active key limit, the rate limit of 120 requests per minute per key and 300 per user, and the `402` you get without an active subscription. For an AI client, the [MCP server](/mcp) is the better door and takes the same key.

```bash
curl -H "X-API-KEY: mas_your_key" https://api.automationmacro.com/api/executions
```

A key acts as the person who created it and ignores the scopes chosen at creation; scopes matter only to the MCP server. Auth failures use one envelope, `{"message": "...", "details": {"code": "..."}}`, with status `401`, `402` or `429`. Validation failures return `422` with a message that says what was wrong. A resource that belongs to someone else answers `404`, never `403`, so ids cannot be probed.

Paged lists share one shape: `page`, `page_size`, `total_rows`, `total_pages` and `rows`. Pages start at 1 and `page_size` defaults to 50 with a maximum of 200.

## OpenAPI document

The backend publishes its own OpenAPI document at `https://api.automationmacro.com/openapi.json`, with `/openapi.yaml` next to it and an interactive viewer at `/docs` on the same host. No auth is needed to read it. It describes every route, including the app-only ones; the routes on this page are the ones whose security lists `apiKey`.

## Runs and macro run history

Every macro run, on a local device or a cloud device, is recorded on the server: the app reports local runs and the cloud runner reports its own. These routes read that history. Reporting itself is app-only.

### GET /api/executions

Lists your runs, newest first.

| Query field | Meaning |
|---|---|
| `status` | `running`, `completed`, `failed` or `stopped` |
| `device_id`, `macro_id` | Filter by device or macro id |
| `since`, `until` | RFC 3339 timestamps; runs started at or after `since` and at or before `until` |
| `page`, `page_size` | Paging; `page_size` defaults to 50, maximum 200 |

```bash
curl -H "X-API-KEY: mas_your_key" \
  "https://api.automationmacro.com/api/executions?status=failed&since=2026-09-01T00:00:00Z"
```

Each row carries `id`, `execution_id`, `status`, `macro_id`, `macro_name`, `device_id`, `device_name`, `device_port`, `group_id`, `execution_type` (`block` or `python`), `started_at`, `ended_at`, `duration_ms`, `exit_code`, `error_message`, `proxy_exit_ip` and `proxy_country`. Optional fields are omitted when empty. `duration_ms` is computed on the server from the start and end times.

### GET /api/executions/{id}

Returns one run by its numeric `id` from the list. The response is a single row with the fields above.

```bash
curl -H "X-API-KEY: mas_your_key" https://api.automationmacro.com/api/executions/1842
```

### GET /api/executions/{execution_id}/logs

Returns the output of one of your runs by its string `execution_id`. Logs are shipped from cloud runs; local runs keep their output in the app.

| Query field | Meaning |
|---|---|
| `max_lines` | Newest lines to return, 1 to 1000, default 200 |
| `since_seq` | Cursor: only lines from batches after this sequence number; 0 or absent returns the newest window |

```bash
curl -H "X-API-KEY: mas_your_key" \
  "https://api.automationmacro.com/api/executions/hosted-12-abc/logs?max_lines=100"
```

The response holds `execution_id`, `lines` (oldest first) and `last_seq`. Pass `last_seq` back as `since_seq` to read only what is new.

## Cloud runs: run a macro through the API

A cloud device runs its macros server-side, so you can start and stop them without the app. The device must be one of your cloud devices; a local device answers `404` with a message that it is not a cloud device. Creating, starting, stopping and deleting cloud devices is app-only; see [Cloud devices](/docs/cloud-devices).

### POST /api/hosted/devices/{id}/run

Starts a macro on cloud device `{id}`. The device boots first if it is stopped. The call returns as soon as the run is launching; poll it for state.

| Body field | Meaning |
|---|---|
| `macro_id` | The macro to run |
| `args` | Optional. Overrides the device's stored arguments for this run only, keyed by argument name |

```bash
curl -X POST -H "X-API-KEY: mas_your_key" -H "Content-Type: application/json" \
  -d '{"macro_id": 123, "args": {"rounds": 5}}' \
  https://api.automationmacro.com/api/hosted/devices/45/run
```

The response is the run: `id`, `execution_id`, `device_id`, `macro_id`, `state`, `state_message`, `started_at`, `finished_at`, `bundle_sha` and `code_ref`. `state` moves through `launching`, `running`, then `finished` or `failed`, with `stopping` while a stop is in flight. A device that already has an active run answers `409`. When server-side runs are not available, the route answers `503`.

### POST /api/hosted/runs/{id}/stop

Stops run `{id}`. The device stays on. Returns the run with its new state.

```bash
curl -X POST -H "X-API-KEY: mas_your_key" https://api.automationmacro.com/api/hosted/runs/981/stop
```

### GET /api/hosted/runs/{id}

Returns the orchestration state of run `{id}`, the same fields as the start response. The business outcome, exit code and duration live in the runs history under the same `execution_id`.

```bash
curl -H "X-API-KEY: mas_your_key" https://api.automationmacro.com/api/hosted/runs/981
```

## Code sync

A cloud run executes a snapshot of the macro's working tree, not the git repository. The app uploads a snapshot before each cloud run when the code changed, so most integrations never call these routes; they exist so your own tooling can check what the server has and refresh it. See [Git and code sync](/docs/git-and-code-sync).

### GET /api/macros/{id}/code-state

Returns the current snapshot of macro `{id}`: `worktree_hash`, `entry_path`, `size_bytes`, `file_count` and `created_at`. A `404` means the macro has never been synced.

```bash
curl -H "X-API-KEY: mas_your_key" https://api.automationmacro.com/api/macros/123/code-state
```

### POST /api/macros/{id}/code-snapshot

Uploads a working tree as a snapshot.

| Body field | Meaning |
|---|---|
| `zip_file_base64` | Required. The project tree as a zip, base64-encoded |
| `worktree_hash` | Required. The content hash the server recomputes from the zip; a mismatch is rejected |
| `entry_path` | Optional, for example `src/app.py`. When empty the server derives it: `src/app.py`, then any `main.py`, then the first `.py` file |

The request body is limited to 16 MiB, which carries a 10 MiB zip. The response carries `worktree_hash`, `entry_path`, `entry_kind`, `image_ids` (the image library ids the code references), `warnings`, `size_bytes`, `file_count` and `created`, which is false when the same hash already existed. The hash is a digest over the per-file manifest the app computes; the simplest way to refresh a snapshot is to open the macro in the app and run it on the cloud device once.

## Devices and groups

Two read-only lists so an integration can discover the ids that other routes need. Creating, editing and deleting devices and groups, and starting a local device, are app-only.

### GET /api/emulator/devices

Lists your devices.

| Query field | Meaning |
|---|---|
| `macro_id`, `group_id` | Filter by the assigned macro or the group |
| `name` | Case-insensitive substring match on the device name |
| `sort`, `page`, `page_size` | Paging; `page_size` up to 200 |

```bash
curl -H "X-API-KEY: mas_your_key" "https://api.automationmacro.com/api/emulator/devices?group_id=7"
```

Each row carries `id`, `name`, `port`, `macro_id`, `group_id`, `run_count`, `is_hosted` and, for a cloud device, `hosted_state` (`stopped`, `placing`, `starting`, `ready`, `stopping` or `error`).

### GET /api/emulator/groups

Lists your device groups. Query fields are `name`, `description`, `sort`, `page` and `page_size`.

```bash
curl -H "X-API-KEY: mas_your_key" https://api.automationmacro.com/api/emulator/groups
```

Each row carries `id`, `name`, `description`, `kind` (`byod` for devices on your own machine, `cloud` for cloud devices), `no_of_devices`, `created_at` and `updated_at`.

## Settings profiles

A settings profile is a named bundle of arguments for one code macro that devices follow. Every macro has a `Default` profile that cannot be renamed or deleted. Editing a profile changes the next run of every device that follows it. See [Settings profiles](/docs/settings-profiles).

A profile carries `id`, `macro_id`, `name`, `args`, `is_default`, `devices_using` and `updated_at`. `args` has the same shape as a device's own script arguments: values keyed by argument name, plus the pre-rendered `__cli_args__` command line where the app stored one. `args` may not exceed 64 KB.

### GET /api/macro-setting-profiles

Lists the profiles of one macro. `macro_id` is required. The list always includes `Default`, which is created on first read.

```bash
curl -H "X-API-KEY: mas_your_key" "https://api.automationmacro.com/api/macro-setting-profiles?macro_id=123"
```

The response is `items`, an array of profiles.

### POST /api/macro-setting-profiles

Creates a profile. Body: `macro_id`, `name` (1 to 100 characters, unique per macro; `Default` is reserved) and optional `args`.

```bash
curl -X POST -H "X-API-KEY: mas_your_key" -H "Content-Type: application/json" \
  -d '{"macro_id": 123, "name": "Night shift", "args": {"rounds": 20, "sleep_s": 3}}' \
  https://api.automationmacro.com/api/macro-setting-profiles
```

Returns the profile. A name already in use answers `409`; a macro that is not yours or not a code macro answers `404`.

### PUT /api/macro-setting-profiles/{id}

Renames a profile, replaces its `args`, or both. Body: `name` and/or `args`; `args` replaces the whole set. Renaming `Default` is refused.

```bash
curl -X PUT -H "X-API-KEY: mas_your_key" -H "Content-Type: application/json" \
  -d '{"args": {"rounds": 30}}' \
  https://api.automationmacro.com/api/macro-setting-profiles/58
```

### DELETE /api/macro-setting-profiles/{id}

Deletes a profile. Devices that followed it are repointed to `Default`. The response carries `deleted` and `repointed_to`, the id of that `Default` profile. Deleting `Default` is refused.

```bash
curl -X DELETE -H "X-API-KEY: mas_your_key" https://api.automationmacro.com/api/macro-setting-profiles/58
```

## Proxies

Proxies give each device its own exit IP. The API manages the proxy records, attaches them to devices and reads traffic totals; the app applies them at run time. See [Proxies](/docs/proxies). Passwords are accepted on create and never returned by any route except `connection-info` with `include_secrets`.

A proxy record carries `id`, `mode` (`byop` for your own proxy or `managed`), `label`, `scheme`, `host`, `port`, `username`, `session_type`, `gateway`, a `targeting` object (`countries`, `exclude_countries`, `state`, `city`, `zip`, `asn`, `exclude_asn`), and the results of the last check: `status` (`unchecked`, `live` or `dead`), `last_check_at`, `last_exit_ip`, `last_exit_country`, `last_latency_ms`, `last_error` and `created_at`.

### POST /api/proxy

Adds a proxy. Body: `host` and `port` (1 to 65535) are required; `scheme` is `http`, `https` or `socks5` and defaults to `http`; `label`, `username`, `password` and `gateway` are optional. For a gateway proxy you can also send `target_countries`, `exclude_countries`, `target_state`, `target_city`, `target_zip`, `target_asn` and `exclude_asn`; a plain proxy ignores them.

```bash
curl -X POST -H "X-API-KEY: mas_your_key" -H "Content-Type: application/json" \
  -d '{"label": "farm-1", "scheme": "socks5", "host": "proxy.example.net", "port": 1080, "username": "user", "password": "secret"}' \
  https://api.automationmacro.com/api/proxy
```

### GET /api/proxy

Lists your proxies. Query: `mode`, `status`, `page`, `page_size`. Paged.

### POST /api/proxy/import

Bulk-adds proxies. Body: `proxies`, one per line, as `host:port`, `host:port:user:pass` or `scheme://user:pass@host:port`. The response carries `created_count`, `error_count`, `created` and `errors`, each error with `line`, `raw` and `reason`.

```bash
curl -X POST -H "X-API-KEY: mas_your_key" -H "Content-Type: application/json" \
  -d '{"proxies": "10.0.0.1:8080:alice:pw\nsocks5://bob:pw@10.0.0.2:1080"}' \
  https://api.automationmacro.com/api/proxy/import
```

### GET, PUT and DELETE /api/proxy/{id}

`GET` returns one proxy. `PUT` updates `label` and, when present, replaces the whole `targeting` object. `DELETE` removes the proxy.

```bash
curl -X PUT -H "X-API-KEY: mas_your_key" -H "Content-Type: application/json" \
  -d '{"label": "farm-1-eu", "targeting": {"countries": "de,nl"}}' \
  https://api.automationmacro.com/api/proxy/17
```

### POST /api/proxy/{id}/validate

Tests the proxy from the server and stores the result. Returns `proxy_id`, `status`, `reachable`, `latency_ms`, `exit_ip`, `country`, `city`, `asn` and `error`.

```bash
curl -X POST -H "X-API-KEY: mas_your_key" https://api.automationmacro.com/api/proxy/17/validate
```

### POST /api/proxy/assignments

Attaches a proxy to a device. Body: `proxy_id`, `emulator_device_id` (from the devices list) and optional `session_type`, `sticky` or `rotating`. Returns the assignment: `id`, `proxy_id`, `proxy`, `emulator_device_id`, `device_name`, `device_port`, `session_id`, `session_type`, `active` and `created_at`.

```bash
curl -X POST -H "X-API-KEY: mas_your_key" -H "Content-Type: application/json" \
  -d '{"proxy_id": 17, "emulator_device_id": 45, "session_type": "sticky"}' \
  https://api.automationmacro.com/api/proxy/assignments
```

### GET /api/proxy/assignments

Lists active assignments as `items` and `total`.

### GET /api/proxy/devices/{device_id}

Returns the assignment attached to one device, in the same `items` shape. A device with no proxy returns an empty list, not `404`.

### DELETE /api/proxy/assignments/{id}

Removes an assignment and returns the remaining list.

### GET /api/proxy/assignments/{id}/connection-info

Returns what a device needs to use the proxy: `scheme`, `host`, `port`, `username`, `password`, `session_id`, `session_type`, `secrets_revealed` and a `relay` recipe. The password is masked unless you pass `include_secrets=true`, and every reveal is written to the audit log with the key that asked.

```bash
curl -H "X-API-KEY: mas_your_key" \
  "https://api.automationmacro.com/api/proxy/assignments/9/connection-info?include_secrets=true"
```

### GET /api/proxy/usage

Returns traffic routed through your proxies. Query: `period`, one of `today` (default), `week`, `month` or `all`. The response carries `period`, `since`, `items` per proxy, `devices` per device, `groups` per group, and the totals `bytes_up`, `bytes_down` and `bytes_total`. Reporting usage (`POST /api/proxy/usage`) is app-only.

```bash
curl -H "X-API-KEY: mas_your_key" "https://api.automationmacro.com/api/proxy/usage?period=week"
```

## Webhooks

Webhook endpoints receive a signed POST when a macro starts, completes, fails or stops, and when a script emits a custom event. The [Webhooks](/docs/webhooks) page covers payloads, signatures and retries; these routes manage the endpoints and read the delivery log.

An endpoint carries `id`, `url`, `description`, `event_types`, `secret_prefix`, `scope_mode` (`all`, `groups` or `devices`), `scope_group_ids`, `scope_device_ids`, `scope_device_count`, `enabled`, `consecutive_failures`, `disabled_reason`, `last_success_at` and `created_at`. An account can hold up to 10 endpoints.

### GET /api/webhooks

Lists your endpoints as `items` and `total`.

### POST /api/webhooks

Registers an endpoint. Body: `url` (HTTPS; private and internal addresses are refused), optional `description` (up to 255 characters), `event_types` (defaults to `["*"]`), and the scope fields `scope_mode`, `scope_group_ids` and `scope_device_ids`. A narrowed scope must name at least one group or device.

```bash
curl -X POST -H "X-API-KEY: mas_your_key" -H "Content-Type: application/json" \
  -d '{"url": "https://example.com/hooks/mas", "description": "Alerts", "event_types": ["macro.failed", "custom.*"], "scope_mode": "groups", "scope_group_ids": [7]}' \
  https://api.automationmacro.com/api/webhooks
```

The response carries `secret`, the signing secret shown once, plus `endpoint` and `warning`.

### GET, PUT and DELETE /api/webhooks/{id}

`GET` returns one endpoint. `PUT` updates any of `url`, `description`, `event_types`, `enabled`, and the scope fields; omit `scope_mode` to leave the scope unchanged. Setting `enabled` to true clears the failure streak of an auto-disabled endpoint. `DELETE` removes the endpoint and returns the remaining list; its delivery history stays readable.

```bash
curl -X PUT -H "X-API-KEY: mas_your_key" -H "Content-Type: application/json" \
  -d '{"enabled": true}' https://api.automationmacro.com/api/webhooks/4
```

### POST /api/webhooks/{id}/regenerate-secret

Issues a new signing secret and returns it once. Deliveries are signed with the new secret at once, so update your receiver first.

### POST /api/webhooks/{id}/test

Queues a `webhook.test` event to the endpoint, regardless of its subscriptions. Returns `queued`, `event_id` and `message`.

```bash
curl -X POST -H "X-API-KEY: mas_your_key" https://api.automationmacro.com/api/webhooks/4/test
```

### GET /api/webhooks/deliveries

Lists delivery attempts, newest first. Query: `endpoint_id`, `status` (`pending`, `succeeded`, `failed` or `exhausted`), `page`, `page_size`. Each row carries `id`, `endpoint_id`, `event_id`, `event_type`, `status`, `attempts`, `next_attempt_at` (only while pending), `last_status_code`, `last_error`, `last_response_body`, `delivered_at` and `created_at`.

```bash
curl -H "X-API-KEY: mas_your_key" \
  "https://api.automationmacro.com/api/webhooks/deliveries?endpoint_id=4&status=exhausted"
```

### GET /api/webhooks/deliveries/{id}

Returns one delivery with the same fields plus `payload`, the exact body that was sent.

### POST /api/webhooks/deliveries/{id}/redeliver

Queues the delivery again and returns it. Use it after fixing your receiver.

```bash
curl -X POST -H "X-API-KEY: mas_your_key" https://api.automationmacro.com/api/webhooks/deliveries/512/redeliver
```

Emitting a custom event (`POST /api/webhooks/events`) is app-only; scripts emit events with `mas.webhook()` and the app relays them.

## App-only

The following never accept a key and are reached only from the signed-in app: macro creation, editing and deletion; the scheduler; the marketplace; the cloud device lifecycle (create, start, stop, delete, live view, screenshots, APK installs); billing and subscriptions; MAS Agent sessions; API key management; execution reporting; proxy usage reporting; and custom webhook events. Devices and groups can be listed with a key but not changed.
