Skip to content

Action Delivery Lifecycle

When a write matches a subscription’s filter, WarmHub starts a delivery to the subscription’s webhook target. Each delivery is a run, and a run can make several attempts before it succeeds or gives up.

This page is the conceptual reference for that lifecycle — the statuses a run moves through, when WarmHub retries, the error codes you see on a failed attempt, and what happens when a delivery can no longer recover. It applies to any WarmHub action delivery; today that means subscription webhooks.

To inspect the deliveries for a specific subscription, see Managing Subscriptions → Delivery Feed. For the HTTP endpoints that return runs and attempts, see HTTP API → Actions.

A run carries one status at a time. It starts at pending and moves toward one of three terminal states — succeeded, failed_terminal, or dead_letter.

StatusMeaning
pendingRun created, not yet executed
runningCurrently executing
processingAccepted by the handler and continuing asynchronously while WarmHub waits for a callback
retry_waitAn attempt failed; waiting for the next retry
succeededCompleted successfully
failed_terminalNon-retryable error — no further attempts scheduled
dead_letterNo recovery path remains

A run in processing is waiting for your handler to report back through the callback endpoint: a success callback moves it to succeeded, and a failure callback to failed_terminal. Separately, a run that has reached failed_terminal can be moved back to retry_wait for another attempt with a retry_requested callback.

Each run makes up to 5 attempts. WarmHub retries an attempt only when its failure is retryable (see Error Codes below) and the attempt budget is not yet spent; otherwise the run goes terminal.

Between retries the run sits in retry_wait. The wait grows exponentially — about one second before the second attempt, doubling for each attempt after that — so a flapping target gets progressively more time to recover. When the attempts are exhausted without a success, the run becomes dead_letter.

A single attempt that returns a non-retryable error (for example, an HTTP_400) skips the remaining budget and goes straight to failed_terminal.

These codes appear on a failed attempt, in both wh sub attempts and the run’s lastErrorCode field.

CodeRetryableDescription
WEBHOOK_NETWORK_ERRORYesNetwork or transport error while connecting to the webhook target
WEBHOOK_TARGET_REJECTEDNoWarmHub rejected the target URL at dispatch time because it was not reachable or not allowed. See Webhook URL Requirements for the allowed scheme, ports, and public-network rules.
WEBHOOK_REDIRECT_LIMITNoThe webhook target exceeded WarmHub’s redirect-follow limit. This is usually a redirect loop or an overly long redirect chain at the partner endpoint. Fix the destination so it returns a terminal response directly or within a small number of redirects.
HTTP_<status>DependsRemote returned an HTTP response. HTTP_429 and HTTP_5xx are retryable; other HTTP_4xx responses are not
WEBHOOK_INPUT_NOT_FOUNDNoCould not load execution input for delivery

A failed run ends in one of two states. A non-retryable error — an HTTP_400, or a callback that reports failure — sends the run straight to failed_terminal. A retryable error that never succeeds — repeated HTTP_503s, for instance — keeps retrying until the attempt budget is spent, then lands in dead_letter.

A subscription can name a fallback webhook URL. When a run fails terminally, WarmHub posts a notification to that URL describing the failed delivery — the run, the error, and how many attempts were made. This is an out-of-band alert, not a retry: the original webhook is not re-sent, and the run stays in its failure state regardless of how the fallback responds.

If WarmHub can’t use the fallback URL at all — it’s rejected by webhook URL validation, or it exceeds the redirect-follow cap — a failed_terminal run is promoted to dead_letter. (A fallback that is only momentarily unreachable, such as a 5xx, is retried rather than promoted.) In full, a run reaches dead_letter when:

  • a retryable delivery exhausted its attempts without succeeding,
  • a failed_terminal run’s fallback URL was rejected by webhook URL validation, or
  • a failed_terminal run’s fallback URL exceeded the redirect-follow cap.

Set a fallback URL when you create or update the subscription: wh sub create / wh sub update --fallback-webhook-url, the SDK client.subscription surface, or the warmhub_subscription_create / warmhub_subscription_update MCP tools.

WarmHub records a repo-scoped action notification once a delivery’s failure is final — once no retry or fallback can still change the outcome. Subscriptions with notifyOnSuccess enabled also record successful deliveries. See Managing Subscriptions → Notifications to read these records.