> For the complete documentation index, see [llms.txt](https://docs.drizz.dev/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.drizz.dev/automate-and-integrate/trigger-a-run.md).

# Trigger a run

`POST <DRIZZ_API_BASE_URL>/testplan/run` starts a test plan from outside Drizz. Drizz provisions devices, starts the plan, and returns an `execution_id`.

|                           |                                                                                         |
| ------------------------- | --------------------------------------------------------------------------------------- |
| **Batch**                 | `POST <DRIZZ_API_BASE_URL>/testplan/run/batch`                                          |
| **Headers**               | `x-api-key`, `Content-Type: application/json`                                           |
| **Returns**               | `execution_details.execution_id`                                                        |
| **Batch partial success** | HTTP **207** — check `failed_executions`                                                |
| **Watch out**             | The response confirms the run started. There is no endpoint to poll it or fetch results |

## Prerequisites

* Access token from [Authenticate](/automate-and-integrate/authenticate.md), in `$DRIZZ_API_KEY`
* API base URL, in `$DRIZZ_BASE_URL`
* Test plan ID, from the web app
* A registered app version for every package the plan installs
* `curl`, or an equivalent HTTP client

## Copy this

```bash
# Trigger one test plan against a registered build
curl -X POST "$DRIZZ_BASE_URL/testplan/run" \
  -H "x-api-key: $DRIZZ_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "test_plan_id": "ADuF4ViN",
    "apks": { "com.shopease.android": "1.0.0" }
  }'
```

## Trigger a single test plan

```
POST <DRIZZ_API_BASE_URL>/testplan/run
```

### Request headers

| Header         | Required | Value              |
| -------------- | -------- | ------------------ |
| `x-api-key`    | Yes      | Your access token  |
| `Content-Type` | Yes      | `application/json` |

### Request parameters

| Name           | Type   | Required | Max | Description                                                            |
| -------------- | ------ | -------- | --- | ---------------------------------------------------------------------- |
| `test_plan_id` | string | Yes      | 64  | The test plan to run. Example: `ADuF4ViN`                              |
| `apks`         | object | Yes      | —   | Package-to-version map. Example: `{ "com.shopease.android": "1.0.0" }` |

`apks` names the registered version each package runs against. Each version must already exist in Drizz — uploaded earlier in the pipeline, or registered previously. An unregistered version returns **404**.

### Response

```json
{
  "test_plan_id": "ADuF4ViN",
  "execution_details": {
    "status": "triggered",
    "execution_id": "exec_123456"
  }
}
```

| Field                            | Type   | Description                     | Example       |
| -------------------------------- | ------ | ------------------------------- | ------------- |
| `test_plan_id`                   | string | Echo of the plan triggered      | `ADuF4ViN`    |
| `execution_details.status`       | string | Execution state at trigger time | `triggered`   |
| `execution_details.execution_id` | string | Identifier for this execution   | `exec_123456` |

`status: triggered` means Drizz accepted the request and started provisioning. It carries no information about whether the tests pass.

## Trigger multiple test plans

```
POST <DRIZZ_API_BASE_URL>/testplan/run/batch
```

One batch request replaces a loop of single triggers, stays inside the rate limit, and provisions devices independently for each plan.

```bash
curl -X POST "$DRIZZ_BASE_URL/testplan/run/batch" \
  -H "x-api-key: $DRIZZ_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "test_plans": [
      {
        "test_plan_id": "ADuF4ViN",
        "apks": { "com.shopease.android": "1.0.0" }
      },
      {
        "test_plan_id": "nBZfQL3R",
        "apks": { "com.shopease.android": "1.0.0" }
      }
    ]
  }'
```

### Request parameters

| Name                        | Type   | Required | Max | Description                                                            |
| --------------------------- | ------ | -------- | --- | ---------------------------------------------------------------------- |
| `test_plans`                | array  | Yes      | —   | List of test plan objects                                              |
| `test_plans[].test_plan_id` | string | Yes      | 64  | Test plan ID. Example: `nBZfQL3R`                                      |
| `test_plans[].apks`         | object | Yes      | —   | Package-to-version map. Example: `{ "com.shopease.android": "1.0.0" }` |

### Response

| Field                   | Type   | Description                                         | Example                  |
| ----------------------- | ------ | --------------------------------------------------- | ------------------------ |
| `message`               | string | Batch status                                        | `Triggered successfully` |
| `successful_executions` | array  | Plans that started, with their execution references | `[]`                     |
| `failed_executions`     | array  | Plans that did not start, with the reason           | `[]`                     |

A batch can partially succeed. **HTTP 207** means some plans started and some did not. Read `failed_executions` rather than treating any 2xx as a clean start.

## Errors

| Code    | Meaning           | What to do                                                      |
| ------- | ----------------- | --------------------------------------------------------------- |
| **200** | Success           | Record `execution_id`                                           |
| **207** | Multi-Status      | Batch partially succeeded — inspect `failed_executions`         |
| **400** | Bad Request       | Validate the payload and parameters                             |
| **404** | Not Found         | Verify the test plan ID and that the app version is registered  |
| **429** | Too Many Requests | Over the rate limit. Use the batch endpoint, back off and retry |
| **500** | Server Error      | Contact `support@drizz.dev`                                     |
| **502** | Bad Gateway       | Retry, or check service availability                            |

## Where the API ends

{% hint style="warning" %}
**There is no endpoint to poll a run or fetch its results.** Once you have an `execution_id`, the API has nothing more to give you: no status lookup, no report, no artifacts, no JUnit or JSON export, no webhook on completion.

Results live in the Drizz web app. Read the report there. If you're building a pipeline around this, read [CI/CD](/automate-and-integrate/ci-cd.md) first — it changes what you can sensibly build.
{% endhint %}

## Common mistakes

| What you did                                   | What happens                                          |
| ---------------------------------------------- | ----------------------------------------------------- |
| Treated `status: triggered` as "tests passed"  | It only means the run started                         |
| Polled a status endpoint                       | There isn't one. The call 404s                        |
| Looped single triggers for a big suite         | HTTP 429. Use the batch endpoint                      |
| Named a version you never uploaded             | HTTP 404 — the app version isn't registered           |
| Read a 207 as full success                     | Some plans failed to start. Check `failed_executions` |
| Hard-coded a plan ID from another organization | 404 — plan IDs don't cross organizations              |

## Next

* [Errors & limits](/automate-and-integrate/errors-and-limits.md) — status codes, rate limits, retries
* [CI/CD](/automate-and-integrate/ci-cd.md) — what a pipeline can and can't do today
* [Test plans](/running-tests/test-plans.md) — where plan IDs come from

***

*Last updated: 6 August 2026*
