Skip to main content

Webhook Retries

Lettr automatically retries failed webhook deliveries to ensure you receive all events.

Retry Schedule

When a webhook delivery fails, Lettr retries with exponential backoff:
AttemptDelayTime After Failure
1Immediate0 minutes
21 minute1 minute
35 minutes6 minutes
430 minutes36 minutes
52 hours2.6 hours
68 hours10.6 hours
724 hours34.6 hours
After 7 failed attempts, the webhook is marked as failed and no further retries occur.

Success Response

Your endpoint must return a 2xx status code within 30 seconds:
app.post('/webhooks/lettr', (req, res) => {
  // Process the event
  
  // Return success
  res.sendStatus(200);
});

Failure Conditions

Webhooks are retried when:
  • Response status is 4xx or 5xx (except 410)
  • Connection timeout (30 seconds)
  • DNS resolution failure
  • SSL/TLS errors
Webhooks are not retried when:
  • Response status is 2xx (success)
  • Response status is 410 (Gone) - webhook is disabled

Idempotency

Your webhook handler should be idempotent. Use the event ID to prevent duplicate processing:
app.post('/webhooks/lettr', async (req, res) => {
  const event = req.body;
  
  // Check if already processed
  const processed = await db.webhookEvents.findOne({ eventId: event.id });
  if (processed) {
    return res.sendStatus(200);
  }
  
  // Process the event
  await handleEvent(event);
  
  // Mark as processed
  await db.webhookEvents.create({ eventId: event.id });
  
  res.sendStatus(200);
});

Monitoring Webhook Health

View webhook delivery status in the dashboard or via API:
const webhook = await lettr.webhooks.get('wh_123');

console.log({
  status: webhook.status,           // 'active', 'failing', 'disabled'
  lastDeliveryAt: webhook.lastDeliveryAt,
  lastDeliveryStatus: webhook.lastDeliveryStatus,
  failureCount: webhook.failureCount
});

View Delivery History

const deliveries = await lettr.webhooks.listDeliveries('wh_123', {
  limit: 10
});

deliveries.forEach(delivery => {
  console.log({
    eventId: delivery.eventId,
    eventType: delivery.eventType,
    status: delivery.status,
    attempts: delivery.attempts,
    responseCode: delivery.responseCode
  });
});

Manual Retry

Retry a specific failed delivery:
await lettr.webhooks.retryDelivery('wh_123', 'delivery_abc');

Disabling Webhooks

Webhooks are automatically disabled after repeated failures. Re-enable in the dashboard or via API:
await lettr.webhooks.update('wh_123', {
  status: 'active'
});
If your endpoint consistently fails, fix the underlying issue before re-enabling the webhook to avoid missing events.