Skip to Content
Webhooks & EventsRetry & Delivery

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

AttemptDelay After PreviousCumulative Time
1 (initial)Immediate0s
21 second1s
32 seconds3s
44 seconds7s
58 seconds15s

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:

FailureDescription
TimeoutNo response within 10 seconds
Connection errorDNS resolution failure, connection refused, TLS error
Non-2xx statusYour server returned 3xx, 4xx, or 5xx
HTTP 410 GoneSpecial case: subscription is automatically deactivated

Special Status Codes

StatusBehavior
200-299Success — no retry
301, 302Redirects are not followed — treated as failure
410 GoneSubscription is automatically deactivated (no retries)
429 Too Many RequestsRetried with backoff
500-599Retried with backoff

Automatic Deactivation

To protect against abandoned endpoints, VelaFlows automatically deactivates a subscription after consecutive failures over 24 hours. Specifically:

  1. After 100 consecutive failed deliveries, the subscription is marked as inactive
  2. No further events are delivered until you reactivate it
  3. 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:

FieldDescription
deliveryIdUnique identifier for this delivery attempt
eventIdThe event being delivered
statuspending, success, failed, retrying
httpStatusThe HTTP status code returned by your server
latencyResponse time in milliseconds
attemptNumberWhich attempt this is (1-5)
nextRetryAtWhen the next retry will occur (if applicable)
failureReasonDescription 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

  1. Return 200 quickly. Process events asynchronously — acknowledge receipt immediately, then handle the event in a background job.
  2. Implement idempotency. Use eventId to ensure the same event is not processed twice.
  3. Log everything. Store raw webhook payloads for at least 7 days to aid debugging.
  4. Monitor failures. Set up alerts for consecutive delivery failures before automatic deactivation kicks in.
  5. Use a queue. Put incoming events on a message queue (e.g., SQS, RabbitMQ) so your webhook endpoint always responds within 10 seconds.