---
title: Maestro REST API | TestingBot
description: 'REST API reference for running and managing Maestro mobile tests on
  TestingBot: upload apps and flows, start test runs, cancel runs, list projects,
  fetch results and JUnit reports.'
source_url:
  html: https://testingbot.com/support/app-automate/maestro/api
  md: https://testingbot.com/support/app-automate/maestro/api.md
---

# Maestro API Overview

TestingBot provides a REST API to upload apps, run Maestro flows, and retrieve information about your [Maestro projects and runs](https://testingbot.com/support/app-automate/maestro).

In the documentation below, a Maestro project using a `:project_id` consists of a mobile application that was uploaded together with a zipped set of Maestro flow files.

A Maestro project can have multiple test runs, depending on how many device configurations (capabilities) you specified when you [started the tests](https://testingbot.com/support/app-automate/maestro/api#run).

All API endpoints require authentication using your TestingBot API key and secret, which you can obtain from the Member area.

| Base URL | Authentication |
| --- | --- |
| `https://api.testingbot.com/v1/app-automate/maestro` | HTTP Basic Auth (API key:secret) |

## CLI Alternative

For most use cases, we recommend using the [TestingBot CLI](https://testingbot.com/support/app-automate/maestro#cli) instead of the REST API directly. The CLI handles uploading your app, flows and running tests in a single command:

```bash
# Install the CLI
npm install -g @testingbot/cli

# Authenticate
testingbot login

# Run Maestro tests (handles upload + run automatically)
testingbot maestro app.apk ./flows --device "Pixel 8" --deviceVersion "14"
```

The CLI provides:

- Automatic app and flow uploads
- Real-time progress and streaming output
- Automatic report downloads
- Simpler authentication

Use the REST API directly when you need programmatic access or custom integration workflows.

## Upload App

Upload your Android (.apk/.aab) or iOS (.ipa/.app) application to TestingBot. This creates a new Maestro project and returns a project ID.

| Method | Endpoint |
| --- | --- |
| `POST` | `/app-automate/maestro/app` |

### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `file` | file | Yes | The app file to upload (.apk, .aab, .ipa, .app or .zip) |

[cURL](https://testingbot.com#)

```bash
curl -u api_key:api_secret \
-X POST "https://api.testingbot.com/v1/app-automate/maestro/app" \
-F "file=@/path/to/your/app.apk"
```

### Response

```json
{
  "id": 17876,
  "app": {
    "app_url": "https://...",
    "icon_url": "https://...",
    "app_version": "1.0.0",
    "bundle_id": "com.example.app"
  }
}
```

Save the returned `id` - you'll need it for uploading flows and running tests.

## Upload Maestro Flows

Upload your Maestro flow files (as a .zip archive) to an existing project. This replaces any previously uploaded flows.

| Method | Endpoint |
| --- | --- |
| `POST` | `/app-automate/maestro/:id/tests` |

### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | integer | Yes | The project ID returned from the app upload |
| `file` | file | Yes | A .zip file containing your Maestro flow YAML files |

[cURL](https://testingbot.com#)

```bash
curl -u api_key:api_secret \
-X POST "https://api.testingbot.com/v1/app-automate/maestro/:id/tests" \
-F "file=@/path/to/flows.zip"
```

### Response

```json
{
  "id": 17876,
  "flows": [
    {
      "id": 123,
      "name": "login_flow"
    }
  ]
}
```

## Run Maestro Tests

Start running your Maestro flows on one or more devices. You can specify multiple device configurations to run tests in parallel.

| Method | Endpoint |
| --- | --- |
| `POST` | `/app-automate/maestro/:id/run` |

### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | integer | Yes | The project ID |
| `capabilities` | array | Yes | Array of device configurations to run tests on |
| `maestroOptions` | object | No | Additional Maestro options (version, env, flows, tags). See [Test Options](https://testingbot.com/support/app-automate/maestro/options). |
| `shardSplit` | integer | No | Group flows into N chunks, each chunk running on a separate device. When omitted, each flow runs in its own session on the same device. |
| `metadata` | object | No | CI/CD metadata stored with the run (e.g. `commitSha`, `pullRequestId`, `repoOwner`, `repoName`, `branch`). Displayed in the TestingBot dashboard. |

[cURL](https://testingbot.com#)

```bash
curl -u api_key:api_secret \
-X POST "https://api.testingbot.com/v1/app-automate/maestro/:id/run" \
-d '{"capabilities":[{"platformName":"Android","version":"14","deviceName":"Pixel 8"}]}' \
-H "Content-Type: application/json"
```

### Response

```json
{
  "success": true,
  "id": 17876,
  "runs": [
    {
      "id": 18809,
      "capabilities": {
        "platformName": "Android",
        "version": "14",
        "deviceName": "Pixel 8"
      },
      "flows": [
        {
          "id": 123,
          "name": "login_flow",
          "report": "...",
          "requested_at": "2025-11-28T09:15:00.000Z",
          "completed_at": "2025-11-28T09:16:00.000Z",
          "status": "DONE",
          "test_case_id": 456
        }
      ]
    }
  ]
}
```

### Error Responses

| Status Code | Description |
| --- | --- |
| `402` | Insufficient credits to run this test. Please upgrade your account to continue running Maestro tests. |

## List Maestro Projects

Retrieve a paginated list of all your Maestro projects.

| Method | Endpoint |
| --- | --- |
| `GET` | `/app-automate/maestro` |

### Query Parameters

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `offset` | integer | 0 | Number of records to paginate |
| `count` | integer | 10 | Number of records to return |

[cURL](https://testingbot.com#)

```bash
curl -u api_key:api_secret \
"https://api.testingbot.com/v1/app-automate/maestro"
```

### Response

```json
{
  "data": [
    {
      "id": 17876,
      "name": "Maestro Project 17876",
      "created_at": "2025-11-28T08:12:31.000Z",
      "updated_at": "2025-11-28T08:12:31.000Z",
      "completed": false,
      "app": {
        "app_url": "https://...",
        "icon_url": "https://...",
        "app_version": "2.7.50420",
        "bundle_id": "org.wikipedia"
      },
      "flows_url": "https://...",
      "flows": [
        {
          "id": 123,
          "name": "login_flow",
        }
      ],
      "runs": [18809, 18854, 18863, 18866]
    }
  ],
  "meta": {
    "offset": 0,
    "count": 10,
    "total": 25
  }
}
```

## List Maestro Runs

Retrieve a paginated list of all your Maestro runs across every project, newest first. This is a lightweight endpoint: it returns run metadata only and does not inflate the (potentially large) flow reports, so it is well suited to dashboards and CI lookups. For a single run's full results and reports, use [Get Run Info](https://testingbot.com#runInfo).

| Method | Endpoint |
| --- | --- |
| `GET` | `/app-automate/maestro/runs` |

### Query Parameters

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `offset` | integer | 0 | Number of records to paginate |
| `count` | integer | 10 | Number of records to return |

[cURL](https://testingbot.com#)

```bash
curl -u api_key:api_secret \
"https://api.testingbot.com/v1/app-automate/maestro/runs?count=10&offset=0"
```

### Response

```json
{
  "data": [
    {
      "id": 18809,
      "project_id": 17876,
      "name": "maestro-android-PR12730-26648200708",
      "status": "DONE",
      "success": 1,
      "capabilities": {
        "platformName": "Android",
        "version": "14",
        "deviceName": "Pixel 8"
      },
      "created_at": "2025-11-28T09:15:00.000Z",
      "completed_at": "2025-11-28T09:16:00.000Z"
    }
  ],
  "meta": {
    "offset": 0,
    "count": 10,
    "total": 25
  }
}
```

The `status` and `success` fields reflect the run's last-known state. A run is finished once `status` is `DONE` or `FAILED`.

## Find Run by Build Name

Look up runs by their build name (the `name` capability you set when [starting the tests](https://testingbot.com#run)). This lets you resolve a build name straight to its `project_id` and `run_id` in a single request, instead of listing projects and scanning each one.

A single build can fan out to multiple runs (for example one run per device configuration), so this endpoint always returns an array of every matching run.

| Method | Endpoint |
| --- | --- |
| `GET` | `/app-automate/maestro/runs/:build_name` |

### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `build_name` | string | Yes | The build name to look up, matched against the run's `name`. Remember to URL-encode names that contain spaces or special characters. |

[cURL](https://testingbot.com#)

```bash
curl -u api_key:api_secret \
"https://api.testingbot.com/v1/app-automate/maestro/runs/maestro-android-PR12730-26648200708"
```

### Response

```json
{
  "data": [
    {
      "id": 18809,
      "project_id": 17876,
      "name": "maestro-android-PR12730-26648200708",
      "status": "DONE",
      "success": 1,
      "capabilities": {
        "platformName": "Android",
        "version": "14",
        "deviceName": "Pixel 8"
      },
      "created_at": "2025-11-28T09:15:00.000Z",
      "completed_at": "2025-11-28T09:16:00.000Z"
    }
  ]
}
```

### Error Responses

| Status Code | Description |
| --- | --- |
| `404` | No run matches the supplied build name on your account. |

Use the returned `project_id` and `id` (run ID) to fetch full results from [Get Run Info](https://testingbot.com#runInfo) or download the [JUnit Report](https://testingbot.com#report).

## Get Maestro Project Info

Retrieve details about a specific Maestro project, including all its runs.

| Method | Endpoint |
| --- | --- |
| `GET` | `/app-automate/maestro/:id` |

[cURL](https://testingbot.com#)

```bash
curl -u api_key:api_secret \
"https://api.testingbot.com/v1/app-automate/maestro/:id"
```

### Response

```json
{
  "runs": [
    {
      "id": 18809,
      "status": "DONE",
      "capabilities": {
        "version": "16",
        "deviceName": "Pixel 9",
        "platformName": "Android"
      },
      "success": 1,
      "report": "",
      "options": {},
      "flows": [
        {
          "id": 123,
          "name": "login_flow",
          "report": "...",
          "requested_at": "2025-11-28T09:15:00.000Z",
          "completed_at": "2025-11-28T09:16:00.000Z",
          "status": "DONE",
          "test_case_id": 456
        }
      ]
    }
  ],
  "success": true,
  "completed": true
}
```

### Run Status Values

| Status | Description |
| --- | --- |
| `WAITING` | Run is queued and waiting for a device |
| `READY` | The test is running on the device |
| `DONE` | Run has completed |
| `FAILED` | Run failed to complete |
| `CANCELLED` | Run was [cancelled](https://testingbot.com#cancel) before it finished, either through the API or from the dashboard |

## Get Run Info

Retrieve detailed information about a specific Maestro run. Use this to poll for completion status.

| Method | Endpoint |
| --- | --- |
| `GET` | `/app-automate/maestro/:project_id/:run_id` |

[cURL](https://testingbot.com#)

```bash
curl -u api_key:api_secret \
"https://api.testingbot.com/v1/app-automate/maestro/:project_id/:run_id"
```

Replace `:project_id` with your project ID and `:run_id` with the run ID.

### Response

```json
{
  "id": 18809,
  "status": "DONE",
  "capabilities": {
    "version": "16",
    "deviceName": "Pixel 9",
    "platformName": "Android"
  },
  "flows": [
    {
      "id": 123,
      "name": "login_flow",
      "report": "...",
      "requested_at": "2025-11-28T09:15:00.000Z",
      "completed_at": "2025-11-28T09:16:00.000Z",
      "status": "DONE",
      "test_case_id": 456
    }
  ],
  "success": 1,
  "report": "",
  "options": {},
  "completed": true
}
```

Poll this endpoint to check if the test has finished. The `completed` field will be `true` when the run has completed.

## JUnit Report

Retrieve the JUnit XML report for a completed Maestro run. This is useful for CI/CD integration.

| Method | Endpoint |
| --- | --- |
| `GET` | `/app-automate/maestro/:project_id/:run_id/junit_report` |

[cURL](https://testingbot.com#)

```bash
curl -u api_key:api_secret \
"https://api.testingbot.com/v1/app-automate/maestro/:project_id/:run_id/junit_report"
```

### Response

```json
{
  "junit_report": "<?xml version=\"1.0\"?><testsuites>...</testsuites>"
}
```

The `junit_report` field contains the XML-formatted JUnit report generated by Maestro.

## Cancel Maestro Run

Immediately cancel a queued or running Maestro run. Cancelling stops the run from requesting any more devices, closes out every flow that has not finished yet, releases the device session the run is currently holding, and drops any request the run still has queued on the device grid, so you stop being billed for it right away. Flows that already completed keep their results and reports.

| Method | Endpoint |
| --- | --- |
| `POST` | `/app-automate/maestro/:project_id/:run_id/cancel` |

### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `project_id` | integer | Yes | The project ID |
| `run_id` | integer | Yes | The ID of the run to cancel |

[cURL](https://testingbot.com#)

```bash
curl -u api_key:api_secret \
-X POST "https://api.testingbot.com/v1/app-automate/maestro/:project_id/:run_id/cancel"
```

### Response

```json
{
  "success": true,
  "run": {
    "id": 18809,
    "project_id": 4211,
    "name": "nightly-regression",
    "status": "CANCELLED",
    "success": 0,
    "capabilities": {
      "platformName": "Android",
      "version": "16",
      "deviceName": "Pixel 9"
    },
    "created_at": "2025-11-28T09:15:00.000Z",
    "completed_at": "2025-11-28T09:15:42.000Z"
  },
  "cancelled_flow_count": 3
}
```

`cancelled_flow_count` is how many flows were still queued or running when the cancel landed. A `0` means the run held no unfinished flows; its device session, if it had one, is still released.

### Error Responses

| Status Code | Description |
| --- | --- |
| `403` | Your account is read-only and cannot start or stop tests. |
| `404` | No project or run with those IDs exists on your account. |
| `409` | The run had already reached a terminal state (`DONE`, `FAILED` or `CANCELLED`), so there was nothing to cancel. |

A cancelled run cannot be resumed, and its individual flows cannot be retried with [Retry Maestro Flow](https://testingbot.com#retry). Use [Retry Maestro Run](https://testingbot.com#retryRun) to start the whole run again on the same capabilities.

## Stop Maestro Run

Alias of [Cancel Maestro Run](https://testingbot.com#cancel), kept for existing integrations. It does exactly the same thing, with one difference: it always answers `200`, so stopping a run that has already finished is a no-op rather than a `409`. It returns only a success flag, not the run. New integrations should use the cancel endpoint.

| Method | Endpoint |
| --- | --- |
| `POST` | `/app-automate/maestro/:project_id/:run_id/stop` |

[cURL](https://testingbot.com#)

```bash
curl -u api_key:api_secret \
-X POST "https://api.testingbot.com/v1/app-automate/maestro/:project_id/:run_id/stop"
```

### Response

```json
{
  "success": true
}
```

## Retry Maestro Flow

Re-run a single flow from a Maestro run. This creates a new flow attempt inside the same run and schedules it on the same device configuration, which is useful for retrying a flaky or failed flow without starting a whole new run.

| Method | Endpoint |
| --- | --- |
| `POST` | `/app-automate/maestro/:project_id/:run_id/:flow_id/retry` |

### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `project_id` | integer | Yes | The project ID |
| `run_id` | integer | Yes | The run ID |
| `flow_id` | integer | Yes | The ID of the flow (from the run's `flows` array) to retry |

[cURL](https://testingbot.com#)

```bash
curl -u api_key:api_secret \
-X POST "https://api.testingbot.com/v1/app-automate/maestro/:project_id/:run_id/:flow_id/retry"
```

### Response

```json
{
  "success": true,
  "flow": {
    "id": 124,
    "name": "login_flow",
    "status": "WAITING",
    "requested_at": "2025-11-28T09:20:00.000Z"
  }
}
```

The retry runs as a new flow attempt inside the same run. Poll the [Get Run Info](https://testingbot.com#runInfo) endpoint to track the new flow's status via its returned `id`.

## Retry Maestro Run

Re-run an entire Maestro run. This creates a brand new run on the same device configuration (capabilities) and options as the original, re-scheduling every flow. Use this when you want to repeat a full run rather than a single [flow](https://testingbot.com#retry).

| Method | Endpoint |
| --- | --- |
| `POST` | `/app-automate/maestro/:project_id/:run_id/retry` |

### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `project_id` | integer | Yes | The project ID |
| `run_id` | integer | Yes | The ID of the run to retry |

[cURL](https://testingbot.com#)

```bash
curl -u api_key:api_secret \
-X POST "https://api.testingbot.com/v1/app-automate/maestro/:project_id/:run_id/retry"
```

### Response

```json
{
  "success": true,
  "run": {
    "id": 18920,
    "capabilities": {
      "platformName": "Android",
      "version": "14",
      "deviceName": "Pixel 8"
    },
    "flows": [
      {
        "id": 125,
        "name": "login_flow",
        "status": "WAITING",
        "requested_at": "2025-11-28T09:25:00.000Z"
      }
    ]
  }
}
```

A new run is created with its own `id`. Poll the [Get Run Info](https://testingbot.com#runInfo) endpoint with that ID to track its status.

## Frequently asked questions

Do I have to use the REST API or can I just use the TestingBot CLI?

For most workflows the [TestingBot CLI](https://testingbot.com/support/app-automate/maestro) is simpler since it handles upload, run and result polling in one command. The REST API is the right choice when you want fine-grained control, are integrating from a non-Node environment, or need to fetch artifacts asynchronously.

How do I upload my app and Maestro flows via the API?

POST your binary to the `/maestro/app` endpoint and your flow ZIP to `/maestro/:id/tests` with HTTP basic auth (API key and secret). The response returns IDs you use when starting the run. See the [Upload App](https://testingbot.com#uploadApp) and [Upload Maestro Flows](https://testingbot.com#uploadFlows) sections.

How do I start a Maestro test run via the API?

POST to the run endpoint with the app and flow IDs from the upload step, plus your device target and any options. See the [Run Maestro Tests section](https://testingbot.com#run) for the request body and response shape.

How do I fetch JUnit-style reports for CI?

GET the run's JUnit endpoint to receive a JUnit XML file with per-flow pass and fail results. Most CI runners can consume this format directly. See the [JUnit Report section](https://testingbot.com#report).

How do I find a run by its build name?

GET `/maestro/runs/:build_name` to resolve a build name straight to its project ID and run ID(s) in one request, instead of listing projects and scanning each one. A build can map to multiple runs (one per device), so the response is always an array. See the [Find Run by Build Name section](https://testingbot.com#findRun).

How do I cancel a running Maestro test?

POST to the cancel endpoint with the project ID and run ID. The run stops requesting devices, its unfinished flows are closed out, and the device session it holds is released, so billing stops right away. Flows that already finished keep their results. See the [Cancel Maestro Run section](https://testingbot.com#cancel) for details.

Can I retry a single flow from a cancelled run?

No. A cancelled run cannot be reopened, so flow-level retry returns a `409` for it. Use [Retry Maestro Run](https://testingbot.com#retryRun) to run the whole thing again on the same capabilities.

How do I retry a single failed flow?

POST to the retry endpoint with the project ID, run ID and flow ID. This re-runs just that flow inside the same run instead of starting a whole new run. See the [Retry Maestro Flow section](https://testingbot.com#retry).

How do I retry an entire run?

POST to the run-level retry endpoint with the project ID and run ID. This creates a new run on the same capabilities and options, re-scheduling every flow. See the [Retry Maestro Run section](https://testingbot.com#retryRun).

### Looking for more help?

Have questions or need more information? Reach out via email or Slack.

[Email us](https://testingbot.com/contact/new) [Join our Slack](https://join.slack.com/t/testingb0t/shared_invite/zt-3bcw9xch-jk19~6XPs_xBrsAgAedkCw)
