> ## Documentation Index
> Fetch the complete documentation index at: https://support.myapps.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Running a workflow

> Trigger a run, poll it, and read the output

Running is two calls: start it, then poll it. There's no synchronous "run and return the image" — execution is always asynchronous.

## Start a run

`POST /run` with the deployment and its inputs:

```json theme={null}
{
  "deployment_id": "your-deployment-id",
  "inputs": {
    "input_text": "a lighthouse at dusk",
    "input_image": "https://example.com/reference.png"
  }
}
```

Only `deployment_id` is required — `inputs` depends on what your graph expects. The response is immediate:

```json theme={null}
{ "run_id": "..." }
```

That means **queued**, not finished. Store the `run_id`.

## Poll the run

`GET /run` with the `run_id`:

```json theme={null}
{
  "id": "...",
  "workflow_id": "...",
  "workflow_version_id": "...",
  "machine_id": "...",
  "workflow_inputs": {
    "input_text": "a lighthouse at dusk",
    "input_image": "https://somestatic.png"
  },
  "origin": "api",
  "status": "success",
  "created_at": "...",
  "started_at": "...",
  "ended_at": "..."
}
```

The run echoes back the inputs it actually received and the exact version that ran — invaluable when a result is wrong and you need to know whether the input or the graph was at fault.

## Statuses

| Status        | Terminal? | What to do                                         |
| ------------- | --------- | -------------------------------------------------- |
| `not-started` | No        | Queued — keep polling                              |
| `running`     | No        | Executing — keep polling                           |
| `uploading`   | **No**    | Finished executing, storing outputs — keep polling |
| `success`     | Yes       | Read the output                                    |
| `failed`      | Yes       | Stop; inspect the error                            |

<Warning>
  `uploading` is the one that trips people up. It means execution finished but outputs aren't ready. Only `success` and `failed` are terminal — anything else means poll again.
</Warning>

## Polling well

```js theme={null}
async function waitForRun(client, runId, {
  intervalMs = 2000,
  timeoutMs = 600000,
} = {}) {
  const deadline = Date.now() + timeoutMs;

  while (Date.now() < deadline) {
    const run = await client.getWorkflowOutput(runId);

    if (run.status === 'success') return run;
    if (run.status === 'failed') throw new Error(`Run ${runId} failed`);

    await new Promise((resolve) => setTimeout(resolve, intervalMs));
  }

  throw new Error(`Run ${runId} timed out after ${timeoutMs}ms`);
}
```

Guidelines that save trouble:

* **Poll every 1–3 seconds.** Tighter than that is wasted requests; looser makes fast runs feel slow.
* **Always set a timeout.** A run that never reaches a terminal state must not hang your process forever.
* **Treat `failed` as expected**, not exceptional. Graphs fail on bad inputs; handle it.
* **Persist the `run_id`** before you start polling. If your process restarts mid-run, that ID is the only way back to the result.
* **Back off on errors.** A `500` while polling doesn't mean the run failed — retry the poll.

<Tip>
  For long runs, don't hold an HTTP request open while polling. Store the `run_id`, return to your caller, and poll from a background job. Video-length graphs outlast most request timeouts.
</Tip>

## Errors

| Response                       | Meaning                                                      |
| ------------------------------ | ------------------------------------------------------------ |
| `401 Invalid or expired token` | Bad, expired, or missing token — check the Bearer header     |
| `400 Workflow not found`       | The `deployment_id` or `run_id` doesn't exist or isn't yours |
| `500` with an `error` field    | Server-side failure; the message says why                    |

More detail in [Troubleshooting](/comfyui/troubleshooting).

## Concurrency

Nothing forces you to run one at a time — start several and poll each `run_id` independently. Runs execute on machines, so throughput depends on available capacity rather than on your polling loop. If you're firing a large batch, keep a bounded number in flight rather than launching hundreds at once.
