_Docs/
Get StartedModulesPlatformDeployCookbookChangelogReference
_Stack
_Modules
  • Ledger
  • Numscript
  • Payments
  • WalletsEE
  • FlowsEE
  • ReconciliationEE
  • WebhooksEE
    • Receiving Webhooks
    • Delivery Lifecycle and Guarantees
    • Event Reference
  1. Modules
  2. Webhooks
  3. Delivery Lifecycle and Guarantees
Webhooks

Delivery Lifecycle and Guarantees

Webhooks 2.5.0 uses one durable delivery model that separates broker ingestion from outbound HTTP attempts. This page defines what the pipeline guarantees, where duplicates can occur, how retries are scheduled, and how operators inspect and replay deliveries.

The worker does not expose a runtime pipeline selector. Delivery inspection and replay APIs use the same persisted model as automatic dispatch.

Upgrade from the attempts model#

New installations start directly with durable deliveries. Existing installations that still have outstanding retries in the former attempts table require a coordinated upgrade:

  1. Stop every old Webhooks worker and wait for it to terminate.
  2. Apply the new database migrations.
  3. Run webhooks backfill-deliveries until it completes.
  4. Deploy the new Webhooks version and recreate its workers.

The backfill command is resumable and idempotent. It exists only as an upgrade adapter: the runtime does not read or write the old attempts queue, and running old and new workers together is not supported. Operator-managed installations coordinate this sequence during the upgrade.

Delivery pipeline#

For each broker event, Webhooks selects active configurations that subscribe to its exact event type. In one database transaction, it creates one pending delivery per matching configuration. The pair (event ID, configuration ID) is unique, so broker redelivery cannot enqueue the same delivery twice. Webhooks acknowledges the broker message only after this transaction commits.

The dispatcher then claims due rows with database locks, marks them delivering, and performs outbound requests independently of broker consumption. Multiple workers can claim work without intentionally selecting the same row.

Durable guarantee summary#

PropertyContract
AcceptanceA matching delivery is stored before the broker event is acknowledged. If persistence fails, the broker can redeliver the event.
Broker deduplicationRepeated broker delivery of the same event does not create another delivery for the same configuration.
HTTP deliveryAt-least-once attempts until the endpoint returns 2xx or the delivery reaches a terminal condition. Successful processing by the receiver is not guaranteed.
DuplicatesPossible. A request can reach the endpoint before Webhooks loses the response or fails to commit the attempt result. Receivers must deduplicate.
OrderingNot guaranteed across events or configurations. The dispatcher is concurrent and retries can overtake earlier deliveries.
TimeoutEach HTTP attempt has a 30-second timeout.
Retry budgetAt most 15 attempts and at most 10 hours per retry generation with default settings. The first limit reached terminates the generation.
Manual recoveryFailed deliveries can receive a fresh retry generation through the replay API. Pending deliveries can be expedited.

This is not an exactly-once protocol. Exactly-once business effects require idempotency in the receiving application.

Durable delivery states#

StatusMeaning
pendingStored and waiting for its first or next attempt. nextAttemptAt indicates when it becomes eligible.
deliveringClaimed by a dispatcher worker and currently in flight.
succeededThe endpoint returned a 2xx response. This is terminal.
failedThe endpoint returned a permanent error, or the retry count or elapsed retry window was exhausted.
cancelledDelivery stopped because its configuration was deactivated or deleted.

Every HTTP call creates an append-only attempt record with the endpoint, attempt number, replay generation, outcome, status code, duration, response excerpt, error, and timestamp. If a worker stops while a delivery is delivering, claims older than five minutes are recovered: active configurations return to pending; inactive or deleted configurations become cancelled.

Response classification#

Webhooks classifies the final response from the endpoint as follows:

ResultDelivery action
2xxMark succeeded; no more automatic attempts.
408 Request TimeoutRetry.
429 Too Many RequestsRetry and honor a valid Retry-After when it requests a longer delay.
Other 4xxMark failed immediately. These errors normally require a configuration or application change.
5xxRetry. A valid Retry-After can extend the delay.
Network error or 30-second timeoutRetry. The attempt records status code 0.

The response body does not affect classification. Webhooks reads at most 64 KiB of it for diagnostics.

Return 429 with Retry-After when your service is healthy but temporarily rate-limited. Return another 4xx only when retrying the same request cannot succeed without a change, because Webhooks treats it as terminal.

Retry schedule#

The default policy uses exponential backoff without jitter. The first request is immediate, then delays double from one minute until they reach the one-hour cap:

AttemptApproximate time from first attempt
1Immediately
21 minute
33 minutes
47 minutes
515 minutes
631 minutes
71 hour 3 minutes
82 hours 3 minutes
9–15Once per hour, with attempt 15 at approximately 9 hours 3 minutes

Automatic retries stop when either limit is reached:

  • 15 total attempts in the current generation;
  • 10 elapsed hours from the first attempt in the current generation.

For retryable responses, Webhooks uses Retry-After when it is valid and longer than the computed backoff. It accepts delay-seconds or an HTTP date, caps an endpoint-supplied delay at six hours, and never allows it to extend the 10-hour retry window.

Webhooks does not open a per-endpoint circuit breaker or automatically disable a configuration after repeated failures. Attempt and time caps bound each delivery; operators must monitor terminal failures and queue growth.

Configuration changes during delivery#

Deactivating or deleting a configuration cancels its pending deliveries. An in-flight attempt can finish, but any non-successful result becomes cancelled instead of returning to the queue. Reactivating a configuration does not resurrect cancelled deliveries; replay also rejects cancelled deliveries.

Updating an endpoint affects later attempts because the dispatcher reads the current configuration before sending. Rotating the secret similarly causes later attempts to use the new secret.

Inspect deliveries#

List deliveries by configuration, status, or creation window. The list excludes payloads and uses an opaque cursor; request one delivery to retrieve its payload.

bash
curl --get "$STACK_URL/api/webhooks/deliveries" \
  --header "Authorization: Bearer $ACCESS_TOKEN" \
  --data-urlencode "configId=$CONFIG_ID" \
  --data-urlencode "status=failed" \
  --data-urlencode "createdAtFrom=2026-08-01T00:00:00Z" \
  --data-urlencode "pageSize=100"

Valid statuses are pending, delivering, succeeded, failed, and cancelled. pageSize defaults to 100 and cannot exceed 1,000.

Retrieve a delivery and its attempt history:

bash
curl "$STACK_URL/api/webhooks/deliveries/$DELIVERY_ID" \
  --header "Authorization: Bearer $ACCESS_TOKEN"

curl "$STACK_URL/api/webhooks/deliveries/$DELIVERY_ID/attempts?pageSize=100" \
  --header "Authorization: Bearer $ACCESS_TOKEN"

Use lastStatusCode, lastError, nextAttemptAt, and the attempts list to distinguish an endpoint rejection from a timeout, retry backlog, or exhausted retry budget.

Replay one delivery#

Only failed and pending deliveries belonging to an active configuration are eligible for replay. A replay request requires an Idempotency-Key header.

bash
curl --request POST "$STACK_URL/api/webhooks/deliveries/$DELIVERY_ID/replay" \
  --header "Authorization: Bearer $ACCESS_TOKEN" \
  --header "Idempotency-Key: replay-$DELIVERY_ID-20260805"

For a failed delivery, replay increments replayGeneration, resets its attempt count and 10-hour retry window, and queues it immediately. For a pending delivery, replay only moves the next attempt to now; it does not reset the current budget. succeeded, delivering, and cancelled deliveries are not eligible for replay.

The same idempotency key and request return the original replay result for 24 hours. Reusing the key for a different replay returns a conflict.

Replay a bounded set#

Bulk replay operates synchronously on one page of at most 1,000 deliveries. A request must include createdAtFrom; its creation-time window must be positive and cannot exceed 90 days.

bash
curl --request POST "$STACK_URL/api/webhooks/deliveries/replay" \
  --header "Authorization: Bearer $ACCESS_TOKEN" \
  --header "Content-Type: application/json" \
  --header "Idempotency-Key: incident-20260805-page-1" \
  --data '{
    "createdAtFrom": "2026-08-05T08:00:00Z",
    "createdAtTo": "2026-08-05T10:00:00Z",
    "statuses": ["failed", "pending"],
    "configIds": ["'"$CONFIG_ID"'"],
    "pageSize": 1000
  }'

The response separates replayed failed deliveries, expedited pending deliveries, and skipped rows. When hasMore is true, submit another request with nextCursor and a new Idempotency-Key. Keep the original filters unchanged; the cursor binds to the time window, statuses, and configuration IDs.

Retention and observability#

With default settings, Webhooks retains succeeded deliveries for 30 days and failed or cancelled deliveries for 90 days. Attempt history is deleted with its delivery. Retention runs hourly. These values are runtime settings for self-hosted deployments and can be changed or disabled.

Webhooks exports OpenTelemetry traces and the following delivery metrics when metrics export is configured:

MetricPurpose
webhooks_delivery_attempts_totalAttempts by outcome and HTTP status class.
webhooks_delivery_duration_secondsOutbound request duration.
webhooks_retry_queue_depthPending delivery count, capped at 1,000,000.
webhooks_replayed_deliveries_totalDeliveries replayed or expedited manually.
webhooks_delivery_transitions_totalDurable delivery state transitions.
webhooks_delivery_claims_recovered_totalStale in-flight claims recovered after worker interruption.

Alert on a growing retry queue, sustained 5xx or timeout rates, terminal failures, and old pending deliveries. A delivery can be safely considered complete only when it is succeeded, or when your operational process has accepted its terminal failure.

Receiving WebhooksEvent Reference
On This Page
  • Upgrade from the attempts model
  • Delivery pipeline
  • Durable guarantee summary
  • Durable delivery states
  • Response classification
  • Retry schedule
  • Configuration changes during delivery
  • Inspect deliveries
  • Replay one delivery
  • Replay a bounded set
  • Retention and observability