Skip to content

Webhook Retries

When a webhook delivery fails, inSigner automatically retries using an exponential backoff schedule. This ensures your endpoint receives all events even during temporary outages.

A webhook delivery is considered failed if:

  • Your endpoint returns a non-2xx HTTP status code (e.g. 4xx, 5xx)
  • The connection times out after 10 seconds
  • DNS resolution fails
  • The connection is refused
  • A TLS/SSL error occurs

A delivery is successful when your endpoint returns any 2xx status code (200, 201, 202, 204, etc.).

Failed deliveries are retried up to 8 times with exponential backoff:

AttemptDelay after failure
1st retry~1 minute
2nd retry~5 minutes
3rd retry~30 minutes
4th retry~2 hours
5th retry~5 hours
6th retry~10 hours
7th retry~18 hours
8th retry~24 hours

Total retry window: approximately 2.5 days from the first attempt.

If a webhook endpoint consistently fails, inSigner automatically deactivates it:

  • After 20 consecutive failures, the webhook status changes to "inactive"
  • An email notification is sent to the organization admin
  • The failureCount field in the webhook object tracks consecutive failures
  • The counter resets to 0 on any successful delivery

After fixing your endpoint, reactivate the webhook via the API:

Terminal window
curl -X PATCH https://app.insigner.co/api/v1/webhooks/wh_abc123 \
-H "Authorization: Bearer isk_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"status": "active"}'

inSigner validates all webhook URLs to prevent Server-Side Request Forgery (SSRF) attacks:

  • Blocked: Private IP ranges (10.x.x.x, 172.16-31.x.x, 192.168.x.x)
  • Blocked: Loopback addresses (127.0.0.1, ::1, localhost)
  • Blocked: Link-local addresses (169.254.x.x)
  • Blocked: Metadata service IPs (169.254.169.254)
  • Required in production: HTTPS URLs only

If your webhook URL resolves to a blocked address, the registration is rejected with a validation error.

Because of retries, your endpoint may receive the same event more than once. Design your webhook handler to be idempotent:

  • Use the X-InSigner-Delivery-Id header to deduplicate events
  • Store processed delivery IDs and skip duplicates
const processedDeliveries = new Set();
app.post('/webhooks/insigner', (req, res) => {
const deliveryId = req.headers['x-insigner-delivery-id'];
if (processedDeliveries.has(deliveryId)) {
// Already processed — return 200 to stop retries
return res.status(200).json({ received: true, duplicate: true });
}
processedDeliveries.add(deliveryId);
// Process the event...
res.status(200).json({ received: true });
});