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

# Using Flows

> Trigger a Flow run, poll for completion, and read the report and structured output

A **Flow** is a named automation that Diligent runs for you — for example KYC enrichment or adverse-media review. You start a run with an API call; the work happens in the background. This guide covers the public run API: how to trigger a run, how to read the result, and how the async lifecycle works.

<Note>
  You need a published Flow and its `flowId`. Find both in the Diligent dashboard, or ask your Diligent contact.
</Note>

## How a run works

Starting a run is **asynchronous**. `POST /flows/{flowId}/runs` validates the input, queues the run, and returns immediately with a run `id`. It does not wait for the Flow to finish.

**The only way to read the result today is to poll `GET /flow-runs/{runId}`** until `status` is terminal. There is no result in the trigger response, and no push of the finished report on the run API.

```mermaid theme={null}
sequenceDiagram
    participant Client
    participant API
    participant Worker

    Client->>API: POST /flows/{flowId}/runs
    API-->>Client: 202 Accepted { id }
    Note over Client: Trigger returns immediately
    API->>Worker: Queue the run
    Worker->>Worker: QUEUED → RUNNING → COMPLETED or FAILED

    loop Poll until status is terminal
        Client->>API: GET /flow-runs/{runId}
        API-->>Client: QUEUED or RUNNING
    end

    Client->>API: GET /flow-runs/{runId}
    API-->>Client: COMPLETED + report
    opt output_ref is set
        Client->>API: GET /flow-runs/{runId}/output-url
        API-->>Client: Presigned URL (expires in 300s)
        Client->>Client: Download structured output
    end
```

Typical statuses:

| Status      | Meaning                                               |
| ----------- | ----------------------------------------------------- |
| `QUEUED`    | Accepted, waiting for a worker                        |
| `RUNNING`   | The Flow is executing                                 |
| `COMPLETED` | Finished successfully — `report` is available         |
| `FAILED`    | Execution failed — see `error`                        |
| `CANCELLED` | Stopped before finishing                              |
| `SKIPPED`   | Not executed (for example a trigger guard skipped it) |

`COMPLETED`, `FAILED`, `CANCELLED`, and `SKIPPED` are **terminal**. Once a run reaches one of these, `status` does not change again.

## Quick start

### 1. Trigger a run

`input` must match the Flow's active version input schema. The fields below are an example — use the schema for your Flow.

```bash theme={null}
curl -X POST https://api.godiligent.ai/flows/flow-abc123/runs \
  -H "X-API-KEY: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "input": {
      "fullName": "Sossio Sorrentino",
      "email": "sossio.sorrentino@example.com",
      "phone": "+393510000000",
      "country": "Italy",
      "city": "Napoli"
    }
  }'
```

**Response (`202 Accepted`):**

```json theme={null}
{
  "id": "8f14e45f-ceea-4c6b-a1b2-3c4d5e6f7890"
}
```

Save `id`. That is the only handle you have for polling.

If `input` does not match the schema, the call fails immediately with `400` and does not queue a run:

```json theme={null}
{
  "code": "VALIDATION_ERROR",
  "message": "Input does not match the flow's input schema",
  "details": [
    { "field": "/fullName", "message": "must have required property 'fullName'" }
  ]
}
```

A `404` with `FLOW_NOT_FOUND` means the `flowId` does not exist or belongs to another customer.

### 2. Poll the run

Call [Get Run](/api-reference/flows/get-run) with the `id` from step 1. Repeat until `status` is terminal.

```bash theme={null}
curl -X GET https://api.godiligent.ai/flow-runs/8f14e45f-ceea-4c6b-a1b2-3c4d5e6f7890 \
  -H "X-API-KEY: your-api-key"
```

**While the run is in progress:**

```json theme={null}
{
  "id": "8f14e45f-ceea-4c6b-a1b2-3c4d5e6f7890",
  "flow_id": "flow-abc123",
  "status": "RUNNING",
  "triggered_at": "2026-09-08T10:00:00.000Z",
  "started_at": "2026-09-08T10:00:01.000Z",
  "completed_at": null,
  "input": {
    "fullName": "Sossio Sorrentino",
    "email": "sossio.sorrentino@example.com",
    "phone": "+393510000000",
    "country": "Italy",
    "city": "Napoli"
  },
  "report": null,
  "output_ref": null,
  "error": null,
  "created_at": "2026-09-08T10:00:00.000Z",
  "updated_at": "2026-09-08T10:00:01.000Z"
}
```

**When the run completes:**

```json theme={null}
{
  "id": "8f14e45f-ceea-4c6b-a1b2-3c4d5e6f7890",
  "flow_id": "flow-abc123",
  "status": "COMPLETED",
  "triggered_at": "2026-09-08T10:00:00.000Z",
  "started_at": "2026-09-08T10:00:01.000Z",
  "completed_at": "2026-09-08T10:02:14.000Z",
  "input": {
    "fullName": "Sossio Sorrentino",
    "email": "sossio.sorrentino@example.com",
    "phone": "+393510000000",
    "country": "Italy",
    "city": "Napoli"
  },
  "report": "# Screening report\n\n**Subject:** Sossio Sorrentino\n\nNo confirmed adverse media.\n",
  "output_ref": "runs/8f14e45f/output.json",
  "error": null,
  "created_at": "2026-09-08T10:00:00.000Z",
  "updated_at": "2026-09-08T10:02:14.000Z"
}
```

What to read from the completed run:

| Field        | When it is set                 | What it is                                                                                                |
| ------------ | ------------------------------ | --------------------------------------------------------------------------------------------------------- |
| `report`     | `status` is `COMPLETED`        | Full analyst-facing result as Markdown                                                                    |
| `output_ref` | Structured output was captured | Presence (not the value) means you can call [Get Run Output URL](/api-reference/flows/get-run-output-url) |
| `error`      | `status` is `FAILED`           | `{ code, message }` describing the failure                                                                |

`report` is `null` until the run completes. Do not treat a missing report as a finished empty result.

### 3. Download structured output (optional)

Some Flows also write a structured output file (JSON). If Get Run shows `output_ref` set, request a short-lived download URL:

```bash theme={null}
curl -X GET https://api.godiligent.ai/flow-runs/8f14e45f-ceea-4c6b-a1b2-3c4d5e6f7890/output-url \
  -H "X-API-KEY: your-api-key"
```

**Response:**

```json theme={null}
{
  "url": "https://s3.eu-west-1.amazonaws.com/...",
  "expires_in": 300
}
```

Download `url` before it expires (currently 300 seconds). If the run has no structured output, this endpoint returns `404` with `OUTPUT_NOT_AVAILABLE`.

## Examples

### Poll until the run finishes (bash)

Poll every 3 seconds and stop when the status is terminal. Then print the report.

```bash theme={null}
API="https://api.godiligent.ai"
KEY="your-api-key"
FLOW_ID="flow-abc123"

RUN_ID=$(curl -s -X POST "$API/flows/$FLOW_ID/runs" \
  -H "X-API-KEY: $KEY" \
  -H "Content-Type: application/json" \
  -d '{"input":{"fullName":"Sossio Sorrentino","country":"Italy"}}' \
  | jq -r '.id')

echo "Queued run $RUN_ID"

while true; do
  BODY=$(curl -s "$API/flow-runs/$RUN_ID" -H "X-API-KEY: $KEY")
  STATUS=$(echo "$BODY" | jq -r '.status')
  echo "status=$STATUS"

  case "$STATUS" in
    COMPLETED|FAILED|CANCELLED|SKIPPED) break ;;
  esac

  sleep 3
done

if [ "$STATUS" = "COMPLETED" ]; then
  echo "$BODY" | jq -r '.report'
else
  echo "$BODY" | jq '.error'
  exit 1
fi
```

### Poll until the run finishes (Python)

```python theme={null}
import time
import requests

API = "https://api.godiligent.ai"
HEADERS = {"X-API-KEY": "your-api-key"}
TERMINAL = {"COMPLETED", "FAILED", "CANCELLED", "SKIPPED"}

created = requests.post(
    f"{API}/flows/flow-abc123/runs",
    headers={**HEADERS, "Content-Type": "application/json"},
    json={"input": {"fullName": "Sossio Sorrentino", "country": "Italy"}},
)
created.raise_for_status()
run_id = created.json()["id"]

while True:
    run = requests.get(f"{API}/flow-runs/{run_id}", headers=HEADERS)
    run.raise_for_status()
    body = run.json()
    status = body["status"]

    if status in TERMINAL:
        break
    time.sleep(3)

if body["status"] != "COMPLETED":
    raise RuntimeError(body.get("error") or body)

print(body["report"])

if body.get("output_ref"):
    output = requests.get(f"{API}/flow-runs/{run_id}/output-url", headers=HEADERS)
    output.raise_for_status()
    file_url = output.json()["url"]
    data = requests.get(file_url)
    data.raise_for_status()
    print(data.json())
```

### Failed run

When execution fails, polling still ends on a terminal status. Read `error` instead of `report`.

```json theme={null}
{
  "id": "8f14e45f-ceea-4c6b-a1b2-3c4d5e6f7890",
  "flow_id": "flow-abc123",
  "status": "FAILED",
  "triggered_at": "2026-09-08T10:00:00.000Z",
  "started_at": "2026-09-08T10:00:01.000Z",
  "completed_at": "2026-09-08T10:00:12.000Z",
  "input": { "fullName": "Sossio Sorrentino" },
  "report": null,
  "output_ref": null,
  "error": {
    "code": "EXECUTION_ERROR",
    "message": "entityName is required"
  },
  "created_at": "2026-09-08T10:00:00.000Z",
  "updated_at": "2026-09-08T10:00:12.000Z"
}
```

## Polling guidance

* **Poll `GET /flow-runs/{runId}`** — that is the current way to learn status and read `report`.
* Start with a 2–5 second interval. Runs can take seconds to minutes depending on the Flow.
* Stop when `status` is `COMPLETED`, `FAILED`, `CANCELLED`, or `SKIPPED`.
* Persist the run `id` in your system so you can resume polling after a restart.
* Treat `report` as ready only when `status` is `COMPLETED`.
* Call the output-url endpoint only after `output_ref` is set. The download URL expires quickly; fetch the file right away.

## Next steps

* [Run](/api-reference/flows/trigger-run)
* [Get Run](/api-reference/flows/get-run)
* [Get Run Output URL](/api-reference/flows/get-run-output-url)
