---
title: "Start a workflow run"
method: POST
path: "/v3/workflow-runs"
tags: ["Durable Workflows"]
---

# Start a workflow run

`POST /v3/workflow-runs`

## Request

- Base URL: `https://api.heygen.com`
- URL: `https://api.heygen.com/v3/workflow-runs`
- Auth: one of:
  - API key in header `x-api-key`
  - HTTP bearer

Starts a run of a workflow and submits the run for execution. No video is created and nothing is charged by this call. Omit `version_number` to use the latest published version, resolved exactly once per `Idempotency-Key`. The key is required: repeating it returns that original run, reusing it with different inputs returns 409 `conflict`, and it is how to recover from a retryable 503.

## Headers

- `Idempotency-Key` string, required

## Request body

- CreateWorkflowRunRequest: Request body for POST /v3/workflow-runs.
  - `workflow_id` string, required: Workflow to run.
  - `version_number` integer, nullable: Published version to run. Omit to run the latest published version, resolved once when the request's Idempotency-Key is first accepted; the response reports the exact version_number the run is pinned to.
  - `inputs` object: Values for the version's declared run inputs. Validated against the frozen plan. Media inputs accept an asset id (a bare string, or {"type": "asset_id", "asset_id": ...}), a publicly accessible HTTPS source ({"type": "url", "url": ...}), or bounded inline content ({"type": "base64", "media_type": ..., "data": ...}); URL and inline sources are imported into your asset library before the run executes. URL sources are limited to 32 MB for images and 32 MB for audio; inline content to 5 MB for images and 16 MB for audio, and it must also fit this endpoint's 1 MiB request-body limit.

## Response `200`

Existing run returned for a repeated Idempotency-Key.

- object
  - `data` WorkflowRunResponse: Response body for a single workflow run: self-contained at terminal state.
    - `id` string, required: Run id (dwr_…).
    - `workflow` WorkflowRunWorkflowRef, required: The workflow and exact immutable version this run executes against.
      - `id` string, required: Workflow this run belongs to.
      - `version_id` string, required: Immutable version this run is pinned to.
      - `version_number` integer, required: Version number this run is pinned to.
    - `status` 'pending' | 'processing' | 'succeeded' | 'partial' | 'failed' | 'canceled', required: Run lifecycle as published to callers.
    - `created_at` string, date-time, required: When the run was created (UTC).
    - `started_at` string, date-time, nullable: When the run started executing (UTC), or null if it has not started.
    - `completed_at` string, date-time, nullable: When the run reached a terminal status (UTC), or null if still in progress.
    - `progress` WorkflowRunProgress, required: Aggregate node counts. Counts, never node summaries — the body must not grow with the graph.
      - `total` integer, required: Total nodes in this run's frozen plan.
      - `pending` integer: Nodes not yet started.
      - `processing` integer: Nodes currently running.
      - `succeeded` integer: Nodes that completed successfully.
      - `failed` integer: Nodes that failed.
      - `skipped` integer: Nodes that were skipped.
      - `canceled` integer: Nodes that were canceled.
    - `error` WorkflowRunError: Sanitized terminal error. Codes come from the closed product vocabulary; operator detail never crosses this boundary.
      - `code` string, required: Stable public error code.
      - `message` string, required: Caller-safe explanation of the error.
      - `node_id` string, nullable: The failing node, when the failure is attributable to exactly one node.
    - `outputs` WorkflowRunOutput[]: Declared top-level outputs of the run's frozen plan.
      - `name` string, required: Output name declared by the workflow version.
      - `type` string, nullable: Output value type (e.g. video, text), once produced.
      - `status` 'pending' | 'ready' | 'failed', required: Per-output resolution status, derived from the persisted artifact status.
      - `delivery` 'inline' | 'reference': Which shape a READY output takes in this response. ``inline`` carries the value in ``value``; ``reference`` carries a read-time URL. An output whose stored value exceeds the inline byte bounds is delivered as ``reference`` even when it was persisted as inline JSON, so this describes the response, not the storage strategy.
      - `artifact_id` string, nullable: Durable identity of the produced output, once produced.
      - `value` unknown
      - `url` string, nullable: Read-time URL for a ready reference delivery. Never persisted; mint a fresh one per read. For an inline JSON output delivered as a reference (its value exceeded the inline byte bounds), the URL serves the value itself as a JSON document.
      - `expires_in` integer, nullable: Seconds until `url` expires. Null when `is_public` is true — a public URL never expires.
      - `is_public` boolean, nullable: True when `url` is served from a public bucket, so it is permanent and unauthenticated.

## Other responses

- `201`: Created.
- `400`: Invalid request parameters
- `401`: Authentication failed
- `404`: Resource not found
- `409`: Conflict — `error.code` is one of: `conflict`, `workflow_archived`, `workflow_run_admission_refused`.
- `429`: Rate limit exceeded
- `503`: Temporarily unavailable — the request did not complete and is safe to retry.

## Changes

- **2026-11-03** `7dfa5e59b4f5`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/heygen/apis/heygen-external-api/changes/v3/workflow-runs/post.md)

---

[API](https://skmtc.dev/heygen/apis/heygen-external-api.md) - [All operations](https://skmtc.dev/heygen/apis/heygen-external-api/llms.txt) - [OpenAPI document](https://skmtc.dev/heygen/apis/heygen-external-api/revisions/7dfa5e59b4f5?raw)
