Skip to main content

Delivery & Retry Logic

Zyphr delivers webhook events reliably with configurable retry policies and automatic circuit breaker protection.

How Delivery Works

Event Produced → RabbitMQ Queue → Webhook Worker → HTTP POST → Customer Endpoint
↓ ↓
30s timeout 2xx = success
non-2xx = schedule retry
  1. When an event occurs (email delivered, user created, etc.), Zyphr finds all active webhooks subscribed to that event type.
  2. A delivery record is created for each matching webhook and published to the message queue.
  3. The webhook worker picks up the delivery job and sends an HTTP POST to your endpoint.
  4. Your endpoint has 30 seconds to respond with a 2xx status code.
  5. On failure, the delivery is retried according to the webhook's retry policy.

Default Retry Policy

New webhooks use the following default retry schedule:

AttemptDelay After FailureCumulative Wait
1Immediate
21 minute1 minute
35 minutes6 minutes
430 minutes36 minutes
52 hours2 hours 36 minutes
612 hours14 hours 36 minutes

After 6 attempts (default), the delivery status is set to exhausted.

Custom Retry Policies

Configure a custom retry policy when creating or updating a webhook:

curl -X POST https://api.zyphr.dev/v1/webhooks \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://yourapp.com/webhooks",
"events": ["email.delivered"],
"retry_policy": {
"max_attempts": 3,
"intervals": [60, 300, 900]
}
}'
ParameterTypeConstraintsDefault
max_attemptsinteger1–106
intervalsinteger[]Each value in seconds, max 10 entries[60, 300, 1800, 7200, 43200, 86400]

If the number of retries exceeds the intervals array length, the last interval is repeated.

Delivery Statuses

StatusDescription
pendingDelivery is queued and waiting to be sent
successEndpoint returned a 2xx response
failedDelivery failed, retry scheduled
exhaustedAll retry attempts exhausted

Delivery Headers

Every webhook delivery includes the following HTTP headers. The exact headers depend on the webhook's header_format setting.

Standard Webhooks Format (header_format: "standard")

HeaderDescriptionExample
Content-TypeAlways application/jsonapplication/json
User-AgentZyphr user agentZyphr-Webhook/1.0
webhook-idStable message ID for deduplicationmsg_a1b2c3d4e5f6...
webhook-timestampUnix timestamp (seconds)1707307200
webhook-signatureHMAC-SHA256 signaturev1,K5oZfzN...

Legacy Format (header_format: "legacy")

HeaderDescriptionExample
Content-TypeAlways application/jsonapplication/json
User-AgentZyphr user agentZyphr-Webhook/1.0
X-Zyphr-SignatureHMAC-SHA256 hex signaturesha256=a1b2c3...
X-Zyphr-TimestampUnix timestamp (seconds)1707307200
X-Zyphr-Delivery-IdDelivery record IDdel_uuid
X-Zyphr-Event-TypeEvent typeemail.delivered

Set header_format to "both" to receive both sets of headers during a migration period.

Circuit Breaker (Auto-Disable)

Zyphr automatically disables webhooks that consistently fail:

  • Threshold: 10 consecutive delivery failures across any deliveries to that webhook
  • Action: Webhook status changes to disabled
  • Reason: Recorded in disabled_reason field
  • Recovery: Manually re-enable by setting status to "active" via the API — this resets the failure counter
# Re-enable a disabled webhook
curl -X PUT https://api.zyphr.dev/v1/webhooks/YOUR_WEBHOOK_ID \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "status": "active" }'

The consecutive failure counter resets to zero on any successful delivery.

Manual Retry

Retry a failed or exhausted delivery manually:

curl -X POST https://api.zyphr.dev/v1/webhooks/YOUR_WEBHOOK_ID/deliveries/DELIVERY_ID/retry \
-H "Authorization: Bearer YOUR_API_KEY"

This resets the delivery to pending with zero attempts and queues it for immediate delivery. Only deliveries with status failed or exhausted can be retried.