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.
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}'{
"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"
}
}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"
}'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"{
"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.
Receive signed run events
whsec_...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.
{
"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);{
"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"
}
}
}
}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"{
"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
}