Skip to main content

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

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

1

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
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
2

Poll for status

Check the status endpoint until the workflow completes. Poll every 2-5 seconds for typical workflows.
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.
curl -s 'https://triggers.app.pinkfish.ai/ext/webhook/YOUR_API_KEY/runs/AUTOMATION_ID/RUN_ID/status'
Response:
{
  "automationId": "d831ue4rccbc73knbi80",
  "runId": "1778786207851669420",
  "status": "COMPLETE",
  "stepStatus": {
    "1": "COMPLETE"
  },
  "stepInfo": {
    "1": {
      "status": "COMPLETE",
      "name": "Step 1",
      "executionOrder": 1
    }
  }
}
Status values:
StatusMeaning
PENDINGWorkflow is queued but not yet started
RUNNINGWorkflow is currently executing
COMPLETEWorkflow completed successfully
FAILEDWorkflow failed with an error
TIMEOUTWorkflow exceeded execution time limit
3

Fetch the results

Once the status is COMPLETE, retrieve the full results:
curl -s 'https://triggers.app.pinkfish.ai/ext/webhook/YOUR_API_KEY/runs/AUTOMATION_ID/RUN_ID/results'
Response:
{
  "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"
}

Complete Examples

Bash

#!/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

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

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:
{
  "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:
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.