DocsWorkflowsMonitor, cancel, retry
Workflows

Monitor, cancel, and retry workflow runs

Follow a workflow run to terminal status, cancel active work, retry eligible failures, and reconcile final usage.

On this page

Use the public wrun_... identifier for every lifecycle request. Poll while a run is queued or running; stop automatic polling at succeeded, failed, cancelled, or manual_review.

Webhooks can replace frequent polling for state changes, but the run resource remains the current source of truth. Final usage and billing ledger entries show the settled or released amount for the run.

Read Errors and review before automating retry behavior.

01

Poll to a terminal state

Poll links.self while status is queued or running. Stop at succeeded, failed, cancelled, manual_review.

set -euo pipefail

API_URL="https://api.entirefeed.com/v1"
INPUTS='{"video_idea":"A slow product reveal with soft daylight"}'

STARTERS=$(curl -fsS "$API_URL/workflow-starters" \
  -H "Authorization: Bearer $ENTIREFEED_API_KEY")
STARTER_VERSION=$(printf '%s' "$STARTERS" | jq -r   '.data[] | select(.slug == "text-to-video-basic") | .version')

ESTIMATE=$(curl -fsS "$API_URL/workflow-starters/text-to-video-basic/estimate" \
  -H "Authorization: Bearer $ENTIREFEED_API_KEY" \
  -H "Content-Type: application/json" \
  -d "$(jq -n --argjson inputs "$INPUTS" --arg version "$STARTER_VERSION"     '{inputs: $inputs, starter_version: $version}')")
if [ "$(printf '%s' "$ESTIMATE" | jq '.unknown_costs | length')" -ne 0 ]; then
  printf '%s\n' "Estimate contains unknown costs; run creation stopped." >&2
  exit 1
fi
ESTIMATE_VERSION=$(printf '%s' "$ESTIMATE" | jq -r '.estimate_version')

RUN=$(curl -fsS "$API_URL/workflow-starters/text-to-video-basic/runs" \
  -H "Authorization: Bearer $ENTIREFEED_API_KEY" \
  -H "Idempotency-Key: launch-video-042" \
  -H "Content-Type: application/json" \
  -d "$(jq -n --argjson inputs "$INPUTS" --arg starter "$STARTER_VERSION"     --arg estimate "$ESTIMATE_VERSION"     '{inputs: $inputs, starter_version: $starter, estimate_version: $estimate}')")
RUN_ID=$(printf '%s' "$RUN" | jq -r '.id')

while :; do
  RUN=$(curl -fsS "$API_URL/workflow-runs/$RUN_ID" \
    -H "Authorization: Bearer $ENTIREFEED_API_KEY")
  STATUS=$(printf '%s' "$RUN" | jq -r '.status')
  case "$STATUS" in succeeded|failed|cancelled|manual_review) break ;; esac
  sleep 2
done

printf '%s' "$RUN" | jq '{status, output, usage}'
200 Succeeded workflow run
{
  "id": "wrun_0123456789abcdef0123456789abcdef",
  "object": "workflow_run",
  "status": "succeeded",
  "starter_slug": "text-to-video-basic",
  "starter_version": "st_0123456789abcdef01234567",
  "created_at": "2026-07-14T08:30:00Z",
  "started_at": "2026-07-14T08:30:03Z",
  "completed_at": "2026-07-14T08:31:20Z",
  "output": {
    "video": {
      "id": "asset_0123456789abcdef0123456789abcdef",
      "type": "video",
      "url": "https://cdn.entirefeed.com/examples/launch-video.mp4",
      "mime_type": "video/mp4",
      "expires_at": null
    }
  },
  "usage": {
    "amount_usd": "1.47",
    "line_items": [
      {
        "label": "Video generation",
        "amount_usd": "1.47",
        "confidence": "exact",
        "reason": "Final cost confirmed after completion."
      }
    ],
    "billing_status": "settled",
    "billing_reason": "final_cost_confirmed"
  },
  "error": null,
  "metadata": {
    "customer_job_id": "launch-video-042"
  },
  "links": {
    "self": "https://api.entirefeed.com/v1/workflow-runs/wrun_0123456789abcdef0123456789abcdef"
  }
}
02

Cancel active work

Cancel queued or running work. Read the returned run and continue polling if cancellation has not reached a terminal state.

curl https://api.entirefeed.com/v1/workflow-runs/wrun_0123456789abcdef0123456789abcdef/cancel \
  -X POST \
  -H "Authorization: Bearer $ENTIREFEED_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "reason": "Campaign was paused"
}'
03

Retry eligible terminal work

Retry the latest failed or cancelled run only after work started and when its retry lineage has no active run. The retry is new work with a new public ID and its own Idempotency-Key.

curl https://api.entirefeed.com/v1/workflow-runs/wrun_0123456789abcdef0123456789abcdef/retry \
  -X POST \
  -H "Authorization: Bearer $ENTIREFEED_API_KEY" \
  -H "Idempotency-Key: launch-video-042-retry-1"
201 Queued retry
{
  "id": "wrun_fedcba9876543210fedcba9876543210",
  "object": "workflow_run",
  "status": "queued",
  "starter_slug": "text-to-video-basic",
  "starter_version": "st_0123456789abcdef01234567",
  "created_at": "2026-07-14T08:35:00Z",
  "estimated_cost": {
    "amount_usd": "1.53"
  },
  "metadata": {
    "customer_job_id": "launch-video-042"
  },
  "links": {
    "self": "https://api.entirefeed.com/v1/workflow-runs/wrun_fedcba9876543210fedcba9876543210"
  }
}

After an uncertain response, replay the same source run and key to recover the original retry. Reusing that key for another source returns idempotency_conflict. If a newer retry exists, operate on the latest run instead.

04

Receive signed run events

Signing secret returned oncewhsec_...
curl https://api.entirefeed.com/v1/webhooks/endpoints \
  -X POST \
  -H "Authorization: Bearer $ENTIREFEED_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "url": "https://example.com/webhooks/entirefeed",
  "events": [
    "workflow_run.succeeded",
    "workflow_run.failed",
    "workflow_run.cancelled",
    "workflow_run.manual_review"
  ]
}'

Supported run events are workflow_run.queued, workflow_run.running, workflow_run.succeeded, workflow_run.failed, workflow_run.cancelled, workflow_run.manual_review. Omitting events uses generation.succeeded, generation.failed, workflow_run.succeeded, workflow_run.failed.

200 Endpoint and secret
{
  "id": "f419d0cf-5d8e-4b4c-82bb-6042b17e68f8",
  "url": "https://example.com/webhooks/entirefeed",
  "events": [
    "workflow_run.succeeded",
    "workflow_run.failed",
    "workflow_run.cancelled",
    "workflow_run.manual_review"
  ],
  "secret": "whsec_store_this_value_once"
}

Verify EntireFeed-Signature against the exact raw request body before parsing. Store EntireFeed-Event-ID to make delivery handling idempotent.

import { createHmac, timingSafeEqual } from "node:crypto";

const parts = Object.fromEntries(
  signatureHeader.split(",").map((part) => part.split("=", 2)),
);
const timestamp = Number(parts.t);
const received = Buffer.from(parts.v1 ?? "", "hex");
const expected = Buffer.from(
  createHmac("sha256", process.env.ENTIREFEED_WEBHOOK_SECRET!)
    .update(Buffer.concat([Buffer.from(String(timestamp) + "."), rawBody]))
    .digest("hex"),
  "hex",
);

const fresh = Math.abs(Date.now() / 1000 - timestamp) <= 300;
const valid =
  fresh &&
  received.length === expected.length &&
  timingSafeEqual(received, expected);
Signed workflow run event
{
  "id": "evt_0123456789abcdef0123456789abcdef",
  "type": "workflow_run.succeeded",
  "created_at": "2026-07-14T08:31:20Z",
  "data": {
    "object": {
      "id": "wrun_0123456789abcdef0123456789abcdef",
      "object": "workflow_run",
      "status": "succeeded",
      "starter_slug": "text-to-video-basic",
      "starter_version": "st_0123456789abcdef01234567",
      "created_at": "2026-07-14T08:30:00Z",
      "started_at": "2026-07-14T08:30:03Z",
      "completed_at": "2026-07-14T08:31:20Z",
      "output": {
        "video": {
          "id": "asset_0123456789abcdef0123456789abcdef",
          "type": "video",
          "url": "https://cdn.entirefeed.com/examples/launch-video.mp4",
          "mime_type": "video/mp4",
          "expires_at": null
        }
      },
      "usage": {
        "amount_usd": "1.47",
        "line_items": [
          {
            "label": "Video generation",
            "amount_usd": "1.47",
            "confidence": "exact",
            "reason": "Final cost confirmed after completion."
          }
        ],
        "billing_status": "settled",
        "billing_reason": "final_cost_confirmed"
      },
      "error": null,
      "metadata": {
        "customer_job_id": "launch-video-042"
      },
      "links": {
        "self": "https://api.entirefeed.com/v1/workflow-runs/wrun_0123456789abcdef0123456789abcdef"
      }
    }
  }
}
Acknowledge after durable receipt.Store EntireFeed-Event-ID before returning 2xx and ignore repeated delivery of the same event ID.
05

Reconcile final usage

Terminal usage is the run-level billing outcome. Filter the ledger by workflow_run_id to reconcile reservation, settlement, and release entries.

curl https://api.entirefeed.com/v1/billing/ledger?workflow_run_id=wrun_0123456789abcdef0123456789abcdef \
  -X GET \
  -H "Authorization: Bearer $ENTIREFEED_API_KEY"
200 Workflow run ledger
{
  "data": [
    {
      "id": "2f55ef4d-1f48-45db-9406-23754a3287ba",
      "type": "RESERVE",
      "amount_usd": "-1.53",
      "available_after_usd": "8.47",
      "reserved_after_usd": "1.53",
      "workflow_run_id": "wrun_0123456789abcdef0123456789abcdef",
      "description": "Reserved estimated operation cost.",
      "created_at": "2026-07-14T08:30:00Z"
    },
    {
      "id": "923fce22-42b1-4c74-8dc7-fafaf8fd64c4",
      "type": "SETTLE",
      "amount_usd": "-1.47",
      "available_after_usd": "8.47",
      "reserved_after_usd": "0.06",
      "workflow_run_id": "wrun_0123456789abcdef0123456789abcdef",
      "description": "Settled final operation cost.",
      "created_at": "2026-07-14T08:31:20Z"
    },
    {
      "id": "f0d251ef-a44f-41c3-b2f8-9d8db0ac9505",
      "type": "RELEASE",
      "amount_usd": "0.06",
      "available_after_usd": "8.53",
      "reserved_after_usd": "0.00",
      "workflow_run_id": "wrun_0123456789abcdef0123456789abcdef",
      "description": "Released unused reservation.",
      "created_at": "2026-07-14T08:31:20Z"
    }
  ],
  "next_cursor": null
}
Reconcile by public run ID.Each related entry exposes the same wrun_... value used by the lifecycle API.