Retry & Delivery
VelaFlows makes best-effort delivery of webhook events with automatic retries and exponential backoff. Understanding the delivery model helps you build a reliable webhook consumer.
Delivery Flow
Event occurs
--> Queue delivery
--> Attempt 1: POST to your URL
--> 2xx response? Done.
--> Non-2xx or timeout? Schedule retry.
--> Attempt 2 (after 1s backoff)
--> 2xx response? Done.
--> Fail? Schedule retry.
--> Attempt 3 (after 2s backoff)
--> Attempt 4 (after 4s backoff)
--> Attempt 5 (after 8s backoff)
--> All retries exhausted: Mark as failed.Retry Schedule
| Attempt | Delay After Previous | Cumulative Time |
|---|---|---|
| 1 (initial) | Immediate | 0s |
| 2 | 1 second | 1s |
| 3 | 2 seconds | 3s |
| 4 | 4 seconds | 7s |
| 5 | 8 seconds | 15s |
Total attempts: 5 (1 initial + 4 retries) Maximum delivery window: ~15 seconds from the first attempt
For events that continue to fail, the system will attempt redelivery with longer backoff periods over a 24-hour window before permanently marking the delivery as failed.
Success and Failure Criteria
Successful Delivery
A delivery is considered successful when your server returns:
- Any 2xx status code (200, 201, 202, 204, etc.)
- Within 10 seconds of receiving the request
The response body is ignored — VelaFlows only checks the status code.
Failed Delivery
A delivery attempt is considered failed when:
| Failure | Description |
|---|---|
| Timeout | No response within 10 seconds |
| Connection error | DNS resolution failure, connection refused, TLS error |
| Non-2xx status | Your server returned 3xx, 4xx, or 5xx |
| HTTP 410 Gone | Special case: subscription is automatically deactivated |
Special Status Codes
| Status | Behavior |
|---|---|
200-299 | Success — no retry |
301, 302 | Redirects are not followed — treated as failure |
410 Gone | Subscription is automatically deactivated (no retries) |
429 Too Many Requests | Retried with backoff |
500-599 | Retried with backoff |
Automatic Deactivation
To protect against abandoned endpoints, VelaFlows automatically deactivates a subscription after consecutive failures over 24 hours. Specifically:
- After 100 consecutive failed deliveries, the subscription is marked as
inactive - No further events are delivered until you reactivate it
- Events that occurred during the inactive period are not retroactively delivered
To reactivate a subscription:
curl -X PATCH https://api.velaflows.com/api/v1/integrations/webhooks/SUBSCRIPTION_ID \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "x-workspace-id: YOUR_WORKSPACE_ID" \
-H "Content-Type: application/json" \
-d '{"active": true}'Manual Retry
You can manually retry a failed delivery via the API:
curl -X POST https://api.velaflows.com/api/v1/integrations/webhooks/SUBSCRIPTION_ID/retry/DELIVERY_ID \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "x-workspace-id: YOUR_WORKSPACE_ID"Delivery Status
Each delivery attempt is logged with:
| Field | Description |
|---|---|
deliveryId | Unique identifier for this delivery attempt |
eventId | The event being delivered |
status | pending, success, failed, retrying |
httpStatus | The HTTP status code returned by your server |
latency | Response time in milliseconds |
attemptNumber | Which attempt this is (1-5) |
nextRetryAt | When the next retry will occur (if applicable) |
failureReason | Description of the failure (timeout, connection error, etc.) |
Deduplication
VelaFlows may deliver the same event more than once in rare circumstances (e.g., network partitions, worker restarts). Always use the eventId field to deduplicate:
// Example: Track processed event IDs
const processedEvents = new Set();
app.post('/webhooks/velaflows', (req, res) => {
const { eventId } = req.body;
if (processedEvents.has(eventId)) {
// Already processed, acknowledge and skip
return res.status(200).json({ received: true, duplicate: true });
}
processedEvents.add(eventId);
// Process the event...
res.status(200).json({ received: true });
});For production use, store processed event IDs in a database with a TTL (e.g., 48 hours) instead of an in-memory Set.
Best Practices
- Return 200 quickly. Process events asynchronously — acknowledge receipt immediately, then handle the event in a background job.
- Implement idempotency. Use
eventIdto ensure the same event is not processed twice. - Log everything. Store raw webhook payloads for at least 7 days to aid debugging.
- Monitor failures. Set up alerts for consecutive delivery failures before automatic deactivation kicks in.
- Use a queue. Put incoming events on a message queue (e.g., SQS, RabbitMQ) so your webhook endpoint always responds within 10 seconds.