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

# Trigger and poll automations

> Start configured automations and poll their results asynchronously.

Call `POST /ai/trigger` to run automations that match a trigger source. This example assumes the project already has an automation configured with the `endpoint` source. For a named webhook, [configure a webhook automation](/api/automations/webhooks) first and replace `endpoint` below with its exact webhook name.

Use the authentication variables from the [quickstart](/api/quickstart) and set `PROJECT_ID` to the target project. cURL uses Bash and `jq`; Python uses `requests`; JavaScript uses Node.js 22 or later with `.mjs` files. Trigger endpoints are under `/ai`, with `project_id` in the request body.

## Start the runs

Send `wait: false` so the API returns run IDs while execution continues. `context` carries the JSON input your configured automations expect; the example provides a message.

<CodeGroup>
  ```bash cURL theme={null}
  set -euo pipefail

  jq -n --arg project "$PROJECT_ID" '{
    project_id: $project,
    source: "endpoint",
    context: {
      message: "Run the configured automation check"
    },
    wait: false
  }' > trigger.json

  curl --fail-with-body --silent --show-error \
    "$FLOW_API_URL/ai/trigger" \
    -H "X-API-Key: $FLOW_API_KEY" \
    -H "customer: $FLOW_CUSTOMER" \
    -H "Content-Type: application/json" \
    --data-binary @trigger.json \
    -o trigger-response.json

  jq '.results[] | {agent_id, automation_id, automation_name, status}' \
    trigger-response.json
  ```

  ```python Python theme={null}
  import os
  import json
  import requests

  base_url = os.environ["FLOW_API_URL"].rstrip("/")
  headers = {"X-API-Key": os.environ["FLOW_API_KEY"], "customer": os.environ["FLOW_CUSTOMER"]}

  payload = {
      "project_id": os.environ["PROJECT_ID"],
      "source": "endpoint",
      "context": {"message": "Run the configured automation check"},
      "wait": False,
  }

  response = requests.post(
      f"{base_url}/ai/trigger", headers=headers, timeout=30,
      json=payload
  )
  response.raise_for_status()
  data = response.json()
  with open("trigger-response.json", "w") as output:
      json.dump(data, output, indent=2)
  for run in data["results"]:
      print(json.dumps({key: run[key] for key in ("agent_id", "automation_id", "automation_name", "status")}))
  ```

  ```javascript JavaScript theme={null}
  const baseUrl = process.env.FLOW_API_URL.replace(/\/$/, "");
  const headers = {
    "X-API-Key": process.env.FLOW_API_KEY,
    customer: process.env.FLOW_CUSTOMER,
  };

  import { writeFile } from "node:fs/promises";
  const payload = {
    project_id: process.env.PROJECT_ID,
    source: "endpoint",
    context: { message: "Run the configured automation check" },
    wait: false,
  };

  const url = new URL(`${baseUrl}/ai/trigger`);
  const response = await fetch(url, {
    method: "POST",
    headers: { ...headers, "Content-Type": "application/json" },
    body: JSON.stringify(payload),
    signal: AbortSignal.timeout(30_000),
  });
  if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
  const data = await response.json();
  await writeFile("trigger-response.json", JSON.stringify(data, null, 2));
  for (const { agent_id, automation_id, automation_name, status } of data.results) {
    console.log({ agent_id, automation_id, automation_name, status });
  }
  ```
</CodeGroup>

An asynchronous request returns HTTP `202`. `results` contains one entry per matched automation, with its `agent_id` and current status. An empty `results` array means there are no matched runs to poll. Check the project's configured source before sending another trigger.

A trigger can match multiple automations. Always include `project_id` to scope the request to the intended project.

## Poll by run ID

Copy an `agent_id` from the trigger response. Polling accepts up to 50 comma-separated run IDs per request.

```bash theme={null}
export AGENT_IDS="run-id-from-trigger-response"
```

<CodeGroup>
  ```bash cURL theme={null}
  curl --fail-with-body --silent --show-error --get \
    "$FLOW_API_URL/ai/trigger/runs" \
    -H "X-API-Key: $FLOW_API_KEY" \
    -H "customer: $FLOW_CUSTOMER" \
    --data-urlencode "agent_ids=$AGENT_IDS" \
    | jq '.items[] | {
        agent_id, status, integration_response,
        integration_grade, branches, error
      }'
  ```

  ```python Python theme={null}
  import os
  import json
  import requests

  base_url = os.environ["FLOW_API_URL"].rstrip("/")
  headers = {"X-API-Key": os.environ["FLOW_API_KEY"], "customer": os.environ["FLOW_CUSTOMER"]}

  response = requests.get(
      f"{base_url}/ai/trigger/runs", headers=headers, timeout=30,
      params={"agent_ids": os.environ["AGENT_IDS"]}
  )
  response.raise_for_status()
  data = response.json()
  for run in data["items"]:
      print(json.dumps({key: run.get(key) for key in ("agent_id", "status", "integration_response", "integration_grade", "branches", "error")}))
  ```

  ```javascript JavaScript theme={null}
  const baseUrl = process.env.FLOW_API_URL.replace(/\/$/, "");
  const headers = {
    "X-API-Key": process.env.FLOW_API_KEY,
    customer: process.env.FLOW_CUSTOMER,
  };

  const url = new URL(`${baseUrl}/ai/trigger/runs`);
  url.search = new URLSearchParams({ agent_ids: process.env.AGENT_IDS }).toString();
  const response = await fetch(url, {
    method: "GET",
    headers,
    signal: AbortSignal.timeout(30_000),
  });
  if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
  const data = await response.json();
  for (const { agent_id, status, integration_response, integration_grade, branches, error } of data.items) {
    console.log({ agent_id, status, integration_response, integration_grade, branches, error });
  }
  ```
</CodeGroup>

The poll response uses `items`, rather than the trigger response's `results`.

| Status      | Action                                                            |
| ----------- | ----------------------------------------------------------------- |
| `running`   | Poll again after a delay.                                         |
| `completed` | Read `integration_response`, `integration_grade`, and `branches`. |
| `failed`    | Read `error` and handle the failure.                              |

Start with a few seconds between polls, increase the interval for long runs, and set a maximum waiting time for your application. Match returned items to the requested `agent_id` values; an absent result is not evidence of completion. Output fields can be `null` when the automation does not provide that output.

## Preserve run IDs

Persist returned run IDs before processing results. If a poll fails, retry the poll with the same IDs.

Avoid automatically resending the trigger after a timeout: the original runs can still be executing, and another trigger can start additional runs. Prefer polling over `wait: true`, which holds the trigger connection open and can lose its response to a network timeout while execution continues.
