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

# Async Polling

> How to trigger a workflow asynchronously and poll for results

## Async Polling

When your workflow takes more than a few seconds to complete, use the async pattern: fire the trigger, then poll for status and retrieve results when done.

This is the recommended approach for integrations that need to programmatically retrieve workflow results.

## How It Works

<Steps>
  <Step title="Trigger the workflow asynchronously">
    Call the trigger endpoint with `x-api-wait: false` (or omit the header entirely). The API returns immediately with an empty body. Two response headers contain the IDs you need for polling:

    * `X-Pf-Run-Id`: The unique identifier for this workflow run
    * `X-Pf-Automation-Id`: The automation/workflow ID

    ```bash theme={null}
    curl -s -i --location 'https://triggers.app.pinkfish.ai/ext/triggers/YOUR_TRIGGER_ID' \
      --header 'Content-Type: application/json' \
      --header 'X-API-KEY: YOUR_API_KEY' \
      --header 'x-api-wait: false' \
      --data '{"user_request": "process this data"}'
    ```

    **Response headers:**

    ```
    X-Pf-Run-Id: 1778786207851669420
    X-Pf-Automation-Id: d831ue4rccbc73knbi80
    ```
  </Step>

  <Step title="Poll for status">
    Check the status endpoint until the workflow completes. Poll every 2-5 seconds for typical workflows.

    <Note>
      Polling endpoints always use the webhook-style URL format (`/ext/webhook/{apiKey}/...`) — even if you triggered the workflow using the API endpoint with `X-API-KEY` header authentication.
    </Note>

    ```bash theme={null}
    curl -s 'https://triggers.app.pinkfish.ai/ext/webhook/YOUR_API_KEY/runs/AUTOMATION_ID/RUN_ID/status'
    ```

    **Response:**

    ```json theme={null}
    {
      "automationId": "d831ue4rccbc73knbi80",
      "runId": "1778786207851669420",
      "status": "COMPLETE",
      "stepStatus": {
        "1": "COMPLETE"
      },
      "stepInfo": {
        "1": {
          "status": "COMPLETE",
          "name": "Step 1",
          "executionOrder": 1
        }
      }
    }
    ```

    **Status values:**

    | Status     | Meaning                                |
    | ---------- | -------------------------------------- |
    | `PENDING`  | Workflow is queued but not yet started |
    | `RUNNING`  | Workflow is currently executing        |
    | `COMPLETE` | Workflow completed successfully        |
    | `FAILED`   | Workflow failed with an error          |
    | `TIMEOUT`  | Workflow exceeded execution time limit |
  </Step>

  <Step title="Fetch the results">
    Once the status is `COMPLETE`, retrieve the full results:

    ```bash theme={null}
    curl -s 'https://triggers.app.pinkfish.ai/ext/webhook/YOUR_API_KEY/runs/AUTOMATION_ID/RUN_ID/results'
    ```

    **Response:**

    ```json theme={null}
    {
      "automationId": "d831ue4rccbc73knbi80",
      "automationVersion": 3,
      "automationName": "Hello World",
      "id": "1778786207851669420",
      "type": "TRIGGER",
      "results": [
        {
          "stepIndex": 1,
          "stepVersion": 3,
          "resultUrls": {
            "output.txt": {
              "url": "https://run-files.app.pinkfish.ai/...",
              "mimeType": "text/plain",
              "size": 13
            },
            "stdout.txt": {
              "url": "https://run-files.app.pinkfish.ai/...",
              "mimeType": "text/plain",
              "size": 419
            }
          },
          "exitCode": 0,
          "duration": 2264
        }
      ],
      "started": "2026-05-14T19:16:47Z",
      "status": "COMPLETE"
    }
    ```
  </Step>
</Steps>

## Complete Examples

### Bash

```bash theme={null}
#!/bin/bash

API_KEY="YOUR_API_KEY"
TRIGGER_ID="YOUR_TRIGGER_ID"

# Step 1: Fire the trigger
RESPONSE=$(curl -s -i --location "https://triggers.app.pinkfish.ai/ext/triggers/$TRIGGER_ID" \
  --header 'Content-Type: application/json' \
  --header "X-API-KEY: $API_KEY" \
  --header 'x-api-wait: false' \
  --data '{"user_request": "process this data"}')

# Step 2: Extract IDs from response headers
RUN_ID=$(echo "$RESPONSE" | grep -i "X-Pf-Run-Id" | cut -d' ' -f2 | tr -d '\r')
AUTOMATION_ID=$(echo "$RESPONSE" | grep -i "X-Pf-Automation-Id" | cut -d' ' -f2 | tr -d '\r')

echo "Run ID: $RUN_ID"
echo "Automation ID: $AUTOMATION_ID"

# Step 3: Poll for status
while true; do
  STATUS_RESPONSE=$(curl -s "https://triggers.app.pinkfish.ai/ext/webhook/$API_KEY/runs/$AUTOMATION_ID/$RUN_ID/status")
  STATUS=$(echo "$STATUS_RESPONSE" | jq -r '.status')

  if [ "$STATUS" = "COMPLETE" ]; then
    echo "Workflow completed!"
    break
  elif [ "$STATUS" = "FAILED" ] || [ "$STATUS" = "TIMEOUT" ]; then
    echo "Workflow ended with status: $STATUS"
    exit 1
  fi

  echo "Status: $STATUS, waiting..."
  sleep 3
done

# Step 4: Fetch results
RESULTS=$(curl -s "https://triggers.app.pinkfish.ai/ext/webhook/$API_KEY/runs/$AUTOMATION_ID/$RUN_ID/results")
echo "$RESULTS" | jq '.'
```

### JavaScript / TypeScript

```javascript theme={null}
const API_KEY = "YOUR_API_KEY";
const TRIGGER_ID = "YOUR_TRIGGER_ID";
const BASE = "https://triggers.app.pinkfish.ai";

async function triggerAndPoll(payload) {
  // Step 1: Fire the trigger
  const res = await fetch(`${BASE}/ext/triggers/${TRIGGER_ID}`, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "X-API-KEY": API_KEY,
      "x-api-wait": "false",
    },
    body: JSON.stringify(payload),
  });

  const runId = res.headers.get("X-Pf-Run-Id");
  const automationId = res.headers.get("X-Pf-Automation-Id");

  // Step 2: Poll for status
  while (true) {
    const statusRes = await fetch(
      `${BASE}/ext/webhook/${API_KEY}/runs/${automationId}/${runId}/status`
    );
    const { status } = await statusRes.json();

    if (status === "COMPLETE") break;
    if (status === "FAILED" || status === "TIMEOUT") {
      throw new Error(`Workflow ended with status: ${status}`);
    }

    await new Promise((r) => setTimeout(r, 3000));
  }

  // Step 3: Fetch results
  const resultsRes = await fetch(
    `${BASE}/ext/webhook/${API_KEY}/runs/${automationId}/${runId}/results`
  );
  return resultsRes.json();
}

const results = await triggerAndPoll({ user_request: "process this data" });
console.log(results);
```

### Python

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

API_KEY = "YOUR_API_KEY"
TRIGGER_ID = "YOUR_TRIGGER_ID"
BASE = "https://triggers.app.pinkfish.ai"

def trigger_and_poll(payload):
    # Step 1: Fire the trigger
    res = requests.post(
        f"{BASE}/ext/triggers/{TRIGGER_ID}",
        headers={
            "Content-Type": "application/json",
            "X-API-KEY": API_KEY,
            "x-api-wait": "false",
        },
        json=payload,
    )

    run_id = res.headers["X-Pf-Run-Id"]
    automation_id = res.headers["X-Pf-Automation-Id"]

    # Step 2: Poll for status
    while True:
        status_res = requests.get(
            f"{BASE}/ext/webhook/{API_KEY}/runs/{automation_id}/{run_id}/status"
        )
        status = status_res.json()["status"]

        if status == "COMPLETE":
            break
        if status in ("FAILED", "TIMEOUT"):
            raise Exception(f"Workflow ended with status: {status}")

        time.sleep(3)

    # Step 3: Fetch results
    results_res = requests.get(
        f"{BASE}/ext/webhook/{API_KEY}/runs/{automation_id}/{run_id}/results"
    )
    return results_res.json()

results = trigger_and_poll({"user_request": "process this data"})
print(results)
```

## Designated Output

If your trigger has a designated output configured, the results response includes a top-level `designatedOutput` field with a direct link to the output file:

```json theme={null}
{
  "id": "1773184744204578408",
  "status": "COMPLETE",
  "results": [ ... ],
  "designatedOutput": {
    "url": "https://run-files.app.pinkfish.ai/...",
    "fileName": "retrieved-poem.txt",
    "mimeType": "text/plain",
    "size": 117
  }
}
```

To download just the designated output:

```bash theme={null}
RESULTS=$(curl -s "https://triggers.app.pinkfish.ai/ext/webhook/YOUR_API_KEY/runs/$AUTOMATION_ID/$RUN_ID/results")

DESIGNATED_URL=$(echo "$RESULTS" | jq -r '.designatedOutput.url // empty')
if [ -n "$DESIGNATED_URL" ]; then
  curl -s "$DESIGNATED_URL"
fi
```

## Best Practices

* **Poll interval**: Every 2-5 seconds for most workflows. For long-running workflows, poll every 10-30 seconds.
* **Set a timeout**: Add a maximum polling duration to avoid infinite loops in production.
* **Handle errors**: Always check for `FAILED` and `TIMEOUT` statuses in your polling loop.
* **Use step status**: The `stepStatus` field in the status response lets you track progress of multi-step workflows while they run.
