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

# Automate research with workflows

> Run Metal AI workflows from the API, track runs, and trigger them from events.

Workflows are Metal's automation engine: they chain data lookups, AI reasoning, and document generation into repeatable research. You author workflows visually in the Metal app, then use the API to run and monitor them. Each execution is a **run**. For the underlying model, see the [workflows concept](/concepts/workflows).

## Find the workflow to run

List your workflows to get the `id` of the one you want to execute.

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl https://api.metal.ai/v1/workflows \
  -H "x-metal-client-id: $METAL_CLIENT_ID" \
  -H "x-metal-api-key: $METAL_API_KEY"
```

## Create a run

Trigger a run with the inputs the workflow expects. Runs are asynchronous: this returns immediately with a run record.

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl -X POST https://api.metal.ai/v1/workflows/665f1c2a9b1e4a0012a3b4c5/runs \
  -H "x-metal-client-id: $METAL_CLIENT_ID" \
  -H "x-metal-api-key: $METAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "input": { "companyId": "665f1c2a9b1e4a0012a3b4c6" } }'
```

## Track a run to completion

Poll the run until it finishes, then read its output.

```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
import os
import time
import requests

BASE = "https://api.metal.ai/v1"
headers = {
    "x-metal-client-id": os.environ["METAL_CLIENT_ID"],
    "x-metal-api-key": os.environ["METAL_API_KEY"],
    "Content-Type": "application/json",
}

def run_workflow(workflow_id, input_payload, interval=10, timeout=600):
    run = requests.post(
        f"{BASE}/workflows/{workflow_id}/runs",
        headers=headers,
        json={"input": input_payload},
    ).json()["data"]

    terminal = {"completed", "failed", "cancelled"}
    deadline = time.time() + timeout
    while time.time() < deadline:
        run = requests.get(f"{BASE}/workflow-runs/{run['id']}", headers=headers).json()["data"]
        if run["status"].lower() in terminal:
            return run
        time.sleep(interval)
    raise TimeoutError(f"Run {run['id']} did not finish in {timeout}s")
```

<Info>
  Prefer events over polling for long-running workflows. A [webhook](/guides/webhooks) trigger lets an external system both start a run and be notified when it finishes.
</Info>

## Manage runs

| Action                      | Endpoint                             |
| --------------------------- | ------------------------------------ |
| List runs for a workflow    | `GET /v1/workflows/{id}/runs`        |
| Get a run                   | `GET /v1/workflow-runs/{id}`         |
| Retry a failed run          | `POST /v1/workflow-runs/{id}/retry`  |
| Re-run with the same inputs | `POST /v1/workflow-runs/{id}/rerun`  |
| Cancel an in-progress run   | `POST /v1/workflow-runs/{id}/cancel` |

## Trigger from external events

Instead of calling the API yourself, you can expose a webhook trigger on a workflow so an external system starts a run when something happens on its side — a new deal in your CRM, a file landing in storage, or a form submission.

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl -X POST https://api.metal.ai/webhooks/workflows/your-workflow-slug \
  -H "Content-Type: application/json" \
  -d '{ "companyId": "665f1c2a9b1e4a0012a3b4c6" }'
```

See [webhooks](/guides/webhooks) for configuring the trigger and securing its secret.

## Next steps

<CardGroup cols={2}>
  <Card title="Run over a list" icon="list" href="/guides/manage-lists">
    Screen an entire set of targets in one pass.
  </Card>

  <Card title="Trigger from events" icon="bolt" href="/guides/webhooks">
    Start runs from your own systems without managing API keys.
  </Card>
</CardGroup>
