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. Read cost on API and MCP workflow-run responses to reconcile billing. cost.estimated and cost.reserved describe the original estimate and hold; cost.settled is the actual wallet charge, and cost.released is the unused credit returned. USD strings preserve sub-cent precision.

Execution completion and billing finalization are separate. A builder run may already be complete while cost.billing_status is reserved and cost.finalized_at is null. Read the same operation again for the finalized charge. manual_review means billing remains unresolved; released closes billing with a zero charge. Failed and cancelled runs may still settle paid work.

estimated_line_items on public workflow runs remain the original estimate, not a final charge breakdown. Completion webhooks carry the same cost contract. Builder reads include operation_id; retain it to read the same billed attempt, since retries have their own settlements. A builder run without a Console billing operation has cost: null.

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, cost}'
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
    }
  },
  "cost": {
    "estimated": {
      "amount_usd": "1.53"
    },
    "reserved": {
      "amount_usd": "1.53"
    },
    "settled": {
      "amount_usd": "1.20"
    },
    "released": {
      "amount_usd": "0.33"
    },
    "adjustment": null,
    "billing_status": "settled",
    "billing_reason": "final_cost_confirmed",
    "finalized_at": "2026-07-14T08:31:22Z"
  },
  "estimated_line_items": [
    {
      "label": "Video generation",
      "amount_usd": "1.53",
      "confidence": "exact",
      "reason": "Calculated from the current starter inputs."
    }
  ],
  "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
        }
      },
      "cost": {
        "estimated": {
          "amount_usd": "1.53"
        },
        "reserved": {
          "amount_usd": "1.53"
        },
        "settled": {
          "amount_usd": "1.20"
        },
        "released": {
          "amount_usd": "0.33"
        },
        "adjustment": null,
        "billing_status": "settled",
        "billing_reason": "final_cost_confirmed",
        "finalized_at": "2026-07-14T08:31:22Z"
      },
      "estimated_line_items": [
        {
          "label": "Video generation",
          "amount_usd": "1.53",
          "confidence": "exact",
          "reason": "Calculated from the current starter inputs."
        }
      ],
      "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.