Webhook Reliability
Retry logic, dead-letter queue, health monitoring, and best practices for reliable webhook handling.
Delivery guarantees
VantageKit delivers webhooks with at-least-once semantics. Events may be delivered more than once during retries — your endpoint should be idempotent.
Retry schedule
If your endpoint returns a non-2xx status or times out, VantageKit retries with exponential backoff:
| Attempt | Delay | Total elapsed |
|---|---|---|
| 1st retry | 1 second | ~1 second |
| 2nd retry | 2 seconds | ~3 seconds |
| 3rd retry | 4 seconds | ~7 seconds |
| 4th retry | 8 seconds | ~15 seconds |
| 5th retry | 16 seconds | ~31 seconds |
After 5 failed attempts, the delivery is moved to the dead-letter queue.
What counts as a failure
| Response | Result |
|---|---|
| HTTP 2xx | Success — delivery complete |
| HTTP 4xx (except 429) | Permanent failure — no retry, moved to dead-letter |
| HTTP 429 | Retryable — follows the retry schedule |
| HTTP 5xx | Retryable — follows the retry schedule |
| Timeout (>10 seconds) | Retryable — your endpoint must respond within 10 seconds |
| Network error | Retryable — DNS failure, connection refused, etc. |
Dead-letter queue
Deliveries that exhaust all retry attempts or receive a non-retryable error are moved to the dead-letter queue. You can view dead-lettered deliveries in Settings > Webhooks > [endpoint] > Delivery Log with status dead_letter.
Endpoint health status
VantageKit monitors your endpoint's health:
| Status | Meaning |
|---|---|
| Healthy | Deliveries are succeeding |
| Unhealthy | 5 consecutive delivery failures |
| Disabled | Endpoint manually disabled |
When an endpoint becomes unhealthy, deliveries continue to be attempted. A single successful delivery resets the status to healthy.
Delivery timeout
Your endpoint must return a response within 10 seconds. If you need to do heavy processing:
app.post('/webhooks/vantagekit', async (req, res) => {
// Verify signature first (fast)
if (!verifySignature(req)) {
return res.status(401).send('Invalid signature')
}
// Acknowledge receipt immediately
res.status(200).send('OK')
// Process asynchronously (queue, background job, etc.)
const event = JSON.parse(req.body.toString())
await queue.add('process-webhook', event)
})Response handling
- VantageKit reads up to 16 KB of your response body
- Up to 1,000 characters of the response are stored in the delivery log for debugging
- Only the HTTP status code determines success (2xx) or failure — the body is for logging only
Stuck delivery protection
If a delivery stays in processing status for more than 5 minutes (e.g., due to a crash during processing), it's automatically reset to pending and retried.
Best practices
- Return 200 quickly — Acknowledge receipt within 10 seconds, process asynchronously
- Be idempotent — Use
X-VantageKit-DeliveryID to deduplicate - Don't return 4xx for transient errors — Return 5xx or 429 so VantageKit retries
- Monitor your endpoint — Check the delivery log in Settings for failures
- Handle all event types gracefully — If you receive an event type you don't handle, return 200 (not 400)