---
title: App Automate API | TestingBot API Documentation
description: Upload apps and drive Maestro, Espresso and XCUITest runs on real devices,
  then pull HTML and JUnit reports.
source_url:
  html: https://testingbot.com/support/api/app-automate
  md: https://testingbot.com/support/api/app-automate.md
---

# App Automate

Everything for native mobile test runs: Maestro flows, Espresso and XCUITest suites, and their reports.

- **Endpoint:** api.testingbot.com
- **Version:** v1
- **Format:** JSON
- **Auth:** [HTTP Basic](https://testingbot.com/support/api#authentication)

## App Automate: Espresso

POST `/v1/app-automate/espresso/app`

## Upload an Android app for Espresso
 Uploads the production `.apk` you want to test under Espresso. Returns the `app_url` to reference when starting a run. 

POST `/v1/app-automate/espresso/app`
[cURL](https://testingbot.com#)
Request

```bash
$ curl -X POST "https://api.testingbot.com/v1/app-automate/espresso/app" \
-u key:secret
```

GET `/v1/app-automate/espresso/{id}`

## Get an Espresso project
 Returns the metadata and current state of an Espresso project. 
### Arguments

- **`id` integer required:** —

### Response fields

- **`runs` array of object:** Runs belonging to the project.
- **`success` boolean:** True when every run in the project passed.
- **`completed` boolean:** True when every run has reached a terminal state.

GET `/v1/app-automate/espresso/{id}`
[cURL](https://testingbot.com#)
Request

```bash
$ curl "https://api.testingbot.com/v1/app-automate/espresso/{id}" \
-u key:secret
```

GET `/v1/app-automate/espresso/{id}/report`

## Get the Espresso JUnit report
 Returns the JUnit-format XML report for an Espresso run once it has completed. 
### Arguments

- **`id` integer required:** —

GET `/v1/app-automate/espresso/{id}/report`
[cURL](https://testingbot.com#)
Request

```bash
$ curl "https://api.testingbot.com/v1/app-automate/espresso/{id}/report" \
-u key:secret
```

POST `/v1/app-automate/espresso/{id}/run`

## Run an Espresso test suite
 Triggers an asynchronous Espresso run across the requested device capabilities. Returns run metadata; poll status via the standard tests API. 
### Arguments

- **`id` integer required:** —

POST `/v1/app-automate/espresso/{id}/run`
[cURL](https://testingbot.com#)
Request

```bash
$ curl -X POST "https://api.testingbot.com/v1/app-automate/espresso/{id}/run" \
-u key:secret
```

POST `/v1/app-automate/espresso/{id}/tests`

## Upload Espresso test APK
 Attaches an Espresso test `.apk` (the instrumented test bundle) to a project. Pair with `/app` to provide both halves of the test. 
### Arguments

- **`id` integer required:** —

POST `/v1/app-automate/espresso/{id}/tests`
[cURL](https://testingbot.com#)
Request

```bash
$ curl -X POST "https://api.testingbot.com/v1/app-automate/espresso/{id}/tests" \
-u key:secret
```

GET `/v1/app-automate/espresso/{project_id}/{id}`

## Get a specific Espresso run
 Returns the detail record for a single Espresso run inside a project. 
### Arguments

- **`project_id` integer required:** —
- **`id` integer required:** —

### Response fields

- **`id` integer:** Unique numeric Espresso run ID.
- **`created_at` timestamp:** —
- **`status` string:** WAITING, READY, DONE or FAILED.
- **`capabilities` object:** Capabilities the run was started with.
- **`success` boolean:** Whether the run passed. False until a session has completed.
- **`report` object:** Test counts and per-test results for the run.
- **`test` object:** Session detail. Only present once a session has been allocated.

GET `/v1/app-automate/espresso/{project_id}/{id}`
[cURL](https://testingbot.com#)
Request

```bash
$ curl "https://api.testingbot.com/v1/app-automate/espresso/{project_id}/{id}" \
-u key:secret
```

## App Automate: Maestro

GET `/v1/app-automate/maestro`

## List your Maestro projects
 Paginated list of Maestro app-automate projects (one per uploaded test bundle) on the account, newest first. 
### Response fields

- **`data` array of maestro project objects:** Projects for this page, newest first.
- **`meta` meta object:** —

GET `/v1/app-automate/maestro`
[cURL](https://testingbot.com#)
Request

```bash
$ curl "https://api.testingbot.com/v1/app-automate/maestro" \
-u key:secret
```

POST `/v1/app-automate/maestro/app`

## Upload an Android/iOS app for Maestro
 Uploads an `.apk` / `.ipa` to attach to a Maestro project. Returns the `app_url` (`tb://<appkey>`) you'll pass as a capability when running the project. 
### Arguments

- **`file` string required:** —
- **`checksum` string:** MD5 checksum (base64) of the app file for deduplication

POST `/v1/app-automate/maestro/app`
[cURL](https://testingbot.com#)
Request

```bash
$ curl -X POST "https://api.testingbot.com/v1/app-automate/maestro/app" \
-u key:secret \
-F "file=@/path/to/app/file/Application-debug.apk"
```

POST `/v1/app-automate/maestro/app/checksum`

## Check whether an app needs uploading
 Returns whether a binary identified by checksum already exists in TestingBot Storage. Used by the CLI to skip re-uploading unchanged apps. 
### Arguments

- **`checksum` string required:** MD5 checksum (base64) of the app file

POST `/v1/app-automate/maestro/app/checksum`
[cURL](https://testingbot.com#)
Request

```bash
$ curl -X POST "https://api.testingbot.com/v1/app-automate/maestro/app/checksum" \
-u key:secret \
-d "checksum=$(openssl md5 -binary Application-debug.apk | base64)"
```

POST `/v1/app-automate/maestro/other-apps`

## Upload additional apps for a Maestro session
 Uploads supplementary `.apk`/`.ipa` binaries that should be installed alongside the primary app under test (e.g. dependent apps, test runners). 
### Arguments

- **`file` string required:** —

POST `/v1/app-automate/maestro/other-apps`
[cURL](https://testingbot.com#)
Request

```bash
$ curl -X POST "https://api.testingbot.com/v1/app-automate/maestro/other-apps" \
-u key:secret \
-F "file=@/path/to/app/file/DependencyApp-debug.apk"
```

GET `/v1/app-automate/maestro/runs`

## List your Maestro runs
 Paginated list of Maestro app-automate runs across all projects on the account, newest first. Lightweight: no reports are inflated. Use GET /:project\_id/:id for a single run's full results. 
### Response fields

- **`data` array of maestro run summary objects:** Runs for this page, newest first.
- **`meta` meta object:** —

GET `/v1/app-automate/maestro/runs`
[cURL](https://testingbot.com#)
Request

```bash
$ curl "https://api.testingbot.com/v1/app-automate/maestro/runs" \
-u key:secret
```

GET `/v1/app-automate/maestro/runs/{build_name}`

## Look up Maestro runs by build name
 Resolves a build/run name to every matching run on the account (a build can fan out to multiple device runs). Returns project\_id + run\_id without inflating reports. Use GET /:project\_id/:id for a single run's full results. 
### Arguments

- **`build_name` string required:** —

### Response fields

- **`data` array of maestro run summary objects:** Every run submitted under this build name, newest first.

GET `/v1/app-automate/maestro/runs/{build_name}`
[cURL](https://testingbot.com#)
Request

```bash
$ curl "https://api.testingbot.com/v1/app-automate/maestro/runs/{build_name}" \
-u key:secret
```

GET `/v1/app-automate/maestro/{id}`

## Get a Maestro project
 Returns the metadata and current state of a single Maestro app-automate project. 
### Arguments

- **`id` integer required:** —

### Response fields

- **`runs` array of object:** Runs belonging to the project.
- **`success` boolean:** True when every run in the project passed.
- **`completed` boolean:** True when every run has reached a terminal state.

GET `/v1/app-automate/maestro/{id}`
[cURL](https://testingbot.com#)
Request

```bash
$ curl "https://api.testingbot.com/v1/app-automate/maestro/{id}" \
-u key:secret
```

POST `/v1/app-automate/maestro/{id}/run`

## Run a Maestro app flow
 Triggers an asynchronous Maestro run on the uploaded project against one or more device capabilities. Returns one run entry per capability. 
### Arguments

- **`id` integer required:** —

POST `/v1/app-automate/maestro/{id}/run`
[cURL](https://testingbot.com#)
Request

```bash
$ curl -X POST "https://api.testingbot.com/v1/app-automate/maestro/{id}/run" \
-u key:secret
```

POST `/v1/app-automate/maestro/{id}/step`

## Append a step to a Maestro run
 Logs an individual Maestro step result against a running project — used by the Maestro runner to stream per-step status to the dashboard. 
### Arguments

- **`id` integer required:** —

POST `/v1/app-automate/maestro/{id}/step`
[cURL](https://testingbot.com#)
Request

```bash
$ curl -X POST "https://api.testingbot.com/v1/app-automate/maestro/{id}/step" \
-u key:secret
```

POST `/v1/app-automate/maestro/{id}/tests`

## Upload a Maestro test bundle
 Attaches a Maestro flow archive (`.zip` or `.yaml`) to an existing Maestro project. Replaces any previously uploaded tests for that project. 
### Arguments

- **`id` integer required:** —

POST `/v1/app-automate/maestro/{id}/tests`
[cURL](https://testingbot.com#)
Request

```bash
$ curl -X POST "https://api.testingbot.com/v1/app-automate/maestro/{id}/tests" \
-u key:secret
```

GET `/v1/app-automate/maestro/{project_id}/{id}`

## Get a specific Maestro run
 Returns the detail record for a single Maestro run inside a project, including capability used, status, and per-flow results. 
### Arguments

- **`project_id` integer required:** —
- **`id` integer required:** —

### Response fields

- **`id` integer:** Unique numeric Maestro run ID.
- **`status` string:** WAITING, READY, DONE, FAILED or CANCELLED, computed from the newest attempt of each flow.
- **`success` integer:** 1 when every flow passed, 0 otherwise.
- **`capabilities` object:** Capabilities the run was started with.
- **`options` object:** Maestro options the run was started with; `{}` when none were given.
- **`created_at` timestamp:** —
- **`completed` boolean:** Whether every run in the parent project has reached a terminal state.
- **`assets_synced` boolean:** Whether asset processing has finished for every flow in the run.
- **`meta` object:** Metadata supplied at submission. Only present when the run carries any.
- **`report` string:** JUnit XML for the run. Only present once a report exists.
- **`test` object:** Session detail. Only present once a session has been allocated.
- **`assets` object:** Logs, video and screenshots. Only present once asset processing has finished.
- **`environment` object:** Device the run was allocated. Only present once one has been assigned.
- **`flows` array of maestro flow result objects:** Every flow attempt, including retries. Only present once the run has flows.
- **`error_messages` array of string:** Run-level failures from the newest attempt of each flow, so a failure a retry has since fixed does not linger. Only present when the latest attempts reported any.

GET `/v1/app-automate/maestro/{project_id}/{id}`
[cURL](https://testingbot.com#)
Request

```bash
$ curl "https://api.testingbot.com/v1/app-automate/maestro/{project_id}/{id}" \
-u key:secret
```

POST `/v1/app-automate/maestro/{project_id}/{run_id}/cancel`

## Cancel a Maestro run
 Immediately cancels a queued or running Maestro run. The run is marked CANCELLED so its workers stop requesting devices and stop retrying, every flow that has not finished yet is closed out as cancelled, any device session the run already acquired is released, and any request still queued on the grid is dropped. Flows that already completed keep their results and reports. Cancelling is idempotent only in the sense that it is safe to call once: a run that has already reached a terminal state (DONE, FAILED or CANCELLED) returns 409. 
### Arguments

- **`project_id` integer required:** Numeric Maestro project ID the run belongs to.
- **`run_id` integer required:** Numeric Maestro run ID to cancel.

### Response fields

- **`success` boolean:** Whether the cancel was applied.
- **`run` object:** The run in its post-cancel state.
- **`cancelled_flow_count` integer:** How many flows were still queued or running and got closed out. 0 means the run held no unfinished flows (its device session, if any, is still released).

POST `/v1/app-automate/maestro/{project_id}/{run_id}/cancel`
[cURL](https://testingbot.com#)
Request

```bash
$ curl -X POST "https://api.testingbot.com/v1/app-automate/maestro/{project_id}/{run_id}/cancel" \
-u key:secret
```

GET `/v1/app-automate/maestro/{project_id}/{run_id}/flow/{flow_id}`

## Get a Maestro flow result
 Returns the per-step results, logs, and screenshots for one flow inside a Maestro run. 
### Arguments

- **`project_id` integer required:** —
- **`run_id` integer required:** —
- **`flow_id` integer required:** —

### Response fields

- **`id` integer:** Unique numeric flow-attempt ID.
- **`maestro_flow_id` integer:** The flow this attempt ran.
- **`status` string:** WAITING, READY, DONE, FAILED or CANCELLED.
- **`name` string:** Flow name(s) covered by this attempt.
- **`requested_at` timestamp:** —
- **`test_case_id` integer:** Test session backing this attempt. Only present once one has been allocated.
- **`completed_at` timestamp:** Only present once the attempt has finished.
- **`report` string:** JUnit XML for this attempt. Only present once a report exists.
- **`shard_index` integer:** Shard this attempt covered. Only present on a sharded run.
- **`success` integer:** 1 when the flow passed. Only present on a terminal status.
- **`error_messages` array of string:** Failure detail. Only present on a terminal status.
- **`test` object:** Session detail. Only present once a session has been allocated.
- **`assets` object:** Logs, video and screenshots. Only present once asset processing has finished.
- **`assets_synced` boolean:** Whether this attempt's assets have finished processing.

GET `/v1/app-automate/maestro/{project_id}/{run_id}/flow/{flow_id}`
[cURL](https://testingbot.com#)
Request

```bash
$ curl "https://api.testingbot.com/v1/app-automate/maestro/{project_id}/{run_id}/flow/{flow_id}" \
-u key:secret
```

GET `/v1/app-automate/maestro/{project_id}/{run_id}/html_report`

## Maestro run HTML report
 Returns a hosted HTML report URL summarising the Maestro run results. 
### Arguments

- **`project_id` integer required:** —
- **`run_id` integer required:** —

### Response fields

- **`html_report` string:** Signed URL to the rendered HTML report.

GET `/v1/app-automate/maestro/{project_id}/{run_id}/html_report`
[cURL](https://testingbot.com#)
Request

```bash
$ curl "https://api.testingbot.com/v1/app-automate/maestro/{project_id}/{run_id}/html_report" \
-u key:secret
```

GET `/v1/app-automate/maestro/{project_id}/{run_id}/html_report_detailed`

## Maestro run detailed HTML report
 Returns a detailed per-step HTML report URL for the Maestro run, including screenshots and logs. 
### Arguments

- **`project_id` integer required:** —
- **`run_id` integer required:** —

### Response fields

- **`html_report_detailed` string:** Signed URL to the per-step HTML report.

GET `/v1/app-automate/maestro/{project_id}/{run_id}/html_report_detailed`
[cURL](https://testingbot.com#)
Request

```bash
$ curl "https://api.testingbot.com/v1/app-automate/maestro/{project_id}/{run_id}/html_report_detailed" \
-u key:secret
```

GET `/v1/app-automate/maestro/{project_id}/{run_id}/junit_report`

## Maestro run JUnit report
 Returns a JUnit-format XML report for a completed Maestro run. Suitable for ingestion by CI systems (Jenkins, GitHub Actions, etc.). 
### Arguments

- **`project_id` integer required:** —
- **`run_id` integer required:** —

### Response fields

- **`junit_report` string:** JUnit XML for the run, as a string.

GET `/v1/app-automate/maestro/{project_id}/{run_id}/junit_report`
[cURL](https://testingbot.com#)
Request

```bash
$ curl "https://api.testingbot.com/v1/app-automate/maestro/{project_id}/{run_id}/junit_report" \
-u key:secret
```

POST `/v1/app-automate/maestro/{project_id}/{run_id}/retry`

## Retry a Maestro run
 Re-runs an entire Maestro run. Creates a new run on the same device configuration (capabilities) and options, re-scheduling every flow. Returns the newly created run; poll it for status. 
### Arguments

- **`project_id` integer required:** —
- **`run_id` integer required:** —

POST `/v1/app-automate/maestro/{project_id}/{run_id}/retry`
[cURL](https://testingbot.com#)
Request

```bash
$ curl -X POST "https://api.testingbot.com/v1/app-automate/maestro/{project_id}/{run_id}/retry" \
-u key:secret
```

POST `/v1/app-automate/maestro/{project_id}/{run_id}/stop`

## Stop a Maestro run
 Cancels a running Maestro run before it completes. Already-completed flows are preserved; in-flight flows are marked cancelled. Alias of the cancel endpoint, kept because testingbotctl calls it from its Ctrl-C handler; unlike cancel it always answers 200, so stopping an already-finished run is a no-op rather than a 409. 
### Arguments

- **`project_id` integer required:** —
- **`run_id` integer required:** —

POST `/v1/app-automate/maestro/{project_id}/{run_id}/stop`
[cURL](https://testingbot.com#)
Request

```bash
$ curl -X POST "https://api.testingbot.com/v1/app-automate/maestro/{project_id}/{run_id}/stop" \
-u key:secret
```

POST `/v1/app-automate/maestro/{project_id}/{run_id}/{flow_id}/retry`

## Retry a Maestro flow
 Re-runs a single flow from a Maestro run. Creates a new flow attempt inside the same run and schedules it. Returns the newly created flow record; poll the run for its status. 
### Arguments

- **`project_id` integer required:** —
- **`run_id` integer required:** —
- **`flow_id` integer required:** —

POST `/v1/app-automate/maestro/{project_id}/{run_id}/{flow_id}/retry`
[cURL](https://testingbot.com#)
Request

```bash
$ curl -X POST "https://api.testingbot.com/v1/app-automate/maestro/{project_id}/{run_id}/{flow_id}/retry" \
-u key:secret
```

## App Automate: XCUITest

POST `/v1/app-automate/xcuitest/app`

## Upload an iOS app for XCUITest
 Uploads the production iOS `.ipa` you want to test under XCUITest. 

POST `/v1/app-automate/xcuitest/app`
[cURL](https://testingbot.com#)
Request

```bash
$ curl -X POST "https://api.testingbot.com/v1/app-automate/xcuitest/app" \
-u key:secret
```

GET `/v1/app-automate/xcuitest/{id}`

## Get an XCUITest project
 Returns the metadata and current state of an XCUITest project. 
### Arguments

- **`id` integer required:** —

### Response fields

- **`runs` array of object:** Runs belonging to the project.
- **`success` boolean:** True when every run in the project passed.
- **`completed` boolean:** True when every run has reached a terminal state.

GET `/v1/app-automate/xcuitest/{id}`
[cURL](https://testingbot.com#)
Request

```bash
$ curl "https://api.testingbot.com/v1/app-automate/xcuitest/{id}" \
-u key:secret
```

GET `/v1/app-automate/xcuitest/{id}/report`

## Get the XCUITest JUnit report
 Returns the JUnit-format XML report for a completed XCUITest run. 
### Arguments

- **`id` integer required:** —

GET `/v1/app-automate/xcuitest/{id}/report`
[cURL](https://testingbot.com#)
Request

```bash
$ curl "https://api.testingbot.com/v1/app-automate/xcuitest/{id}/report" \
-u key:secret
```

POST `/v1/app-automate/xcuitest/{id}/run`

## Run an XCUITest suite
 Triggers an asynchronous XCUITest run across the requested device capabilities. 
### Arguments

- **`id` integer required:** —

POST `/v1/app-automate/xcuitest/{id}/run`
[cURL](https://testingbot.com#)
Request

```bash
$ curl -X POST "https://api.testingbot.com/v1/app-automate/xcuitest/{id}/run" \
-u key:secret
```

POST `/v1/app-automate/xcuitest/{id}/tests`

## Upload XCUITest test bundle
 Attaches the XCUITest test bundle (.zip) to a project. Pair with `/app` to provide both halves of the test. 
### Arguments

- **`id` integer required:** —

POST `/v1/app-automate/xcuitest/{id}/tests`
[cURL](https://testingbot.com#)
Request

```bash
$ curl -X POST "https://api.testingbot.com/v1/app-automate/xcuitest/{id}/tests" \
-u key:secret
```

GET `/v1/app-automate/xcuitest/{project_id}/{id}`

## Get a specific XCUITest run
 Returns the detail record for a single XCUITest run inside a project. 
### Arguments

- **`project_id` integer required:** —
- **`id` integer required:** —

### Response fields

- **`id` integer:** Unique numeric XCUITest run ID.
- **`created_at` timestamp:** —
- **`status` string:** WAITING, READY, DONE or FAILED.
- **`capabilities` object:** Capabilities the run was started with.
- **`success` boolean:** Whether the run passed. False until a session has completed.
- **`test` object:** Session detail. Only present once a session has been allocated.

GET `/v1/app-automate/xcuitest/{project_id}/{id}`
[cURL](https://testingbot.com#)
Request

```bash
$ curl "https://api.testingbot.com/v1/app-automate/xcuitest/{project_id}/{id}" \
-u key:secret
```

GET `/v1/app-automate/xcuitest/{project_id}/{run_id}/html_report`

## XCUITest run HTML report
 Returns a self-contained HTML report for a single XCUITest run. 
### Arguments

- **`project_id` integer required:** —
- **`run_id` integer required:** —

### Response fields

- **`html_report` string:** Signed URL to the rendered HTML report.

GET `/v1/app-automate/xcuitest/{project_id}/{run_id}/html_report`
[cURL](https://testingbot.com#)
Request

```bash
$ curl "https://api.testingbot.com/v1/app-automate/xcuitest/{project_id}/{run_id}/html_report" \
-u key:secret
```

GET `/v1/app-automate/xcuitest/{project_id}/{run_id}/junit_report`

## XCUITest run JUnit report
 Returns a JUnit-format XML report for a single completed XCUITest run. Suitable for ingestion by CI systems (Jenkins, GitHub Actions, etc.). 
### Arguments

- **`project_id` integer required:** —
- **`run_id` integer required:** —

### Response fields

- **`junit_report` string:** JUnit XML for the run, as a string.

GET `/v1/app-automate/xcuitest/{project_id}/{run_id}/junit_report`
[cURL](https://testingbot.com#)
Request

```bash
$ curl "https://api.testingbot.com/v1/app-automate/xcuitest/{project_id}/{run_id}/junit_report" \
-u key:secret
```

[Previous Webhooks](https://testingbot.com/support/api/webhooks)
