---
title: "Trigger a plan run"
method: POST
path: "/api/triggers/plans/{planId}/run"
tags: ["Triggers – Plans"]
---

# Trigger a plan run

`POST /api/triggers/plans/{planId}/run`

## Request

- Base URL: `https://aximo.autify.com`
- URL: `https://aximo.autify.com/api/triggers/plans/{planId}/run`
- Auth: API key in header `x-api-key`
- TypeScript: <https://skmtc.dev/autify/apis/aximo-api/docs/api/triggers/plans/:planId/run/post.ts> (a function with typed inputs; its response is checked against the schema)

Starts a new run for the specified test plan. The plan must have **API access enabled**
in its settings.

Cases run in parallel or one at a time according to the plan's Execution setting;
the optional `executionMode` body field overrides that setting for this run only,
and the response echoes the effective mode.

The optional `variables` body field supplies per-run values for `{{NAME}}`
placeholders, overriding the plan's stored variables for this run only.

The optional `tags` body field labels the run and every session it creates,
so the results can be found again by tag.

Returns a `planRunId` — poll `GET /api/triggers/plans/{planId}/runs/{planRunId}`
to track overall progress.

## Path parameters

- `planId` string, uuid, required

## Request body

- object
  - `model` 'default' | 'haiku' | 'sonnet' | 'kimi' | 'gpt-5.4' | 'gpt-5.4-mini' | 'gpt-5.5' | 'auto': Model to use for all cases in this plan run. Call `GET /api/models` for the values your organization can run — anything else is rejected with `400`, and the error carries the usable list in `availableModels`. When omitted, the `defaultModel` from that endpoint applies.
  - `executionMode` 'parallel' | 'sequential': Override the plan's stored Execution setting for this run only. `parallel` runs cases concurrently up to the organization's concurrency limit; `sequential` runs one case at a time in plan order. When omitted, the plan's setting applies (plans default to `parallel`). The plan's stop-on-failure option takes effect only when the effective mode is `sequential`.
  - `mobileConfig` object: Fallback device and app for this run's mobile cases. Used only for a mobile case with no device saved on the plan or on the case; a saved device always wins, and keeps its own app. To run on a different device, save it on the case (`PATCH /api/cases/{caseId}` with `apiMobileConfig`) or on the plan. Unlike `desktopConfig`, this does not replace the plan's devices. Same shape and gates as a case's `apiMobileConfig`: either a Device Farm device (`deviceArn` + `deviceName`) or a registered local phone (`userDeviceId`, with `platform` resolved from the device), never both.
    - `deviceArn` string: Encrypted device token from `GET /api/devices`.
    - `deviceName` string
    - `userDeviceId` string, uuid: A registered local phone from `GET /api/devices/user`. Requires BYOD.
    - `platform` 'ANDROID' | 'IOS'
    - `bundleId` string: App to launch on a local phone when `apps` is not used.
    - `uploadArn` string: Encrypted upload token from `GET /api/uploads/{id}`.
    - `uploadType` 'own_app' | 'existing' | 'sample'
    - `appName` string
    - `apps` object[]: Up to 3 apps to install for mobile cases. Takes precedence over the single-app fields (`uploadArn`/`uploadType`/`appName`); the first entry is the primary app.
      - `uploadArn` string, required: Encrypted upload token from `GET /api/uploads/{id}`.
      - `uploadType` 'own_app' | 'existing' | 'sample', required
      - `appName` string, nullable, required
      - `bundleId` string
  - `desktopConfig` object: BYOD desktop devices to run this plan's desktop cases on; overrides the plan's stored desktop configuration for this run. Device metadata (name, platform, OS) is re-resolved server-side from the device registry, and each device's availability is checked before the run starts. With `deviceMode: pool` the run starts as long as one device is available. Requires the BYOD feature to be enabled for your organization. All devices must be desktop devices (`windows`, `macos`, or `linux`) registered via `POST /api/devices/register`.
    - `devices` object[], required
      - `userDeviceId` string, uuid, required: ID of a registered device from `GET /api/devices/user`.
      - `name` string
      - `platform` string
      - `os` string, nullable
  - `variables` object[]: Variable values for this run only — never persisted on the plan. Each entry is merged over the plan's stored variables by name and fills the matching `{{NAME}}` placeholder in case scenarios; names not referenced by any case in the plan are ignored. See `GET /api/plans/{planId}` for the plan's stored variables. If a name is registered as a **secret** in the workspace (or is a stored secret plan variable), the supplied value is encrypted at rest, handled as a secret during the run, and never echoed back. Sending the literal mask `***` for such a name keeps the current secret value (the plan's stored override, or the workspace secret when no override is stored).
    - `name` string, required: Variable name — uppercase, starts with a letter, letters/ digits/underscores only. Duplicate names are rejected.
    - `value` string, required
  - `tags` string[]: Caller-supplied labels, for example the commit and pull request a run was testing. At most 10, each 1-64 characters after trimming; duplicates are collapsed. A request exceeding a bound is refused with 400, never truncated. Tags are returned exactly as stored and are visible to everyone in your organization, so never put secrets in them.

## Response `201`

Plan run triggered.

- object
  - `success` boolean, required
  - `planRunId` string, uuid, required
  - `totalCases` integer, required: Number of test cases in this plan run.
  - `executionMode` 'parallel' | 'sequential', required: The effective mode this run executes with (the request override if given, otherwise the plan's stored setting).
  - `tags` string[], required: Caller-supplied labels, for example the commit and pull request a run was testing. At most 10, each 1-64 characters after trimming; duplicates are collapsed. A request exceeding a bound is refused with 400, never truncated. Tags are returned exactly as stored and are visible to everyone in your organization, so never put secrets in them.

## Other responses

- `400`: Invalid request body — including a `model` this organization cannot run (the response then carries `availableModels`), `desktopConfig` devices that do not belong to the organization or are not desktop devices (the device checks also apply to the plan's stored desktop configuration when `desktopConfig` is omitted, e.g. a bound device that was since unregistered), an undecryptable `mobileConfig` token (`details.path` names the slot, e.g. `mobileConfig.apps[0].uploadArn`), or a schema failure (`details` carries the zod error tree).
- `401`: Missing or invalid API key.
- `403`: API access is not enabled for this plan; BYOD desktop devices were requested (in the body or via the plan's stored desktop configuration) while BYOD is not enabled for the organization; or the plan contains multi-device test cases while that feature is not enabled.
- `404`: Test plan or upload not found. For an upload, `details.path` names the slot (e.g. `mobileConfig.apps[0].uploadArn`).
- `412`: One or more requested BYOD desktop devices are not currently available. `details.unavailableDevices` lists each one with a `reason` of `offline` (device agent not connected) or `unknown` (device status could not be determined). Also applies when the plan's stored desktop configuration references unavailable devices. With `deviceMode: pool` the run is refused only when none of the devices is available.
- `429`: Rate limit exceeded — e.g. the daily session limit was reached for the organization, or too many API requests were sent in a short window (retry after the number of seconds in the `Retry-After` header).
- `500`: Unexpected server error.

---

[API](https://skmtc.dev/autify/apis/aximo-api.md) - [All operations](https://skmtc.dev/autify/apis/aximo-api/llms.txt) - [OpenAPI document](https://skmtc.dev/autify/apis/aximo-api/revisions/590722011284?raw)
