Skip to main content

Best Practices

Follow these guidelines to build a reliable webhook integration.

Respond Quickly

Your endpoint must respond within 30 seconds. If processing takes longer, accept the webhook immediately and process the event asynchronously:

app.post('/webhooks/zyphr', async (req, res) => {
// Respond immediately
res.status(200).json({ received: true });

// Process asynchronously
await processWebhookEvent(req.body);
});

Always Verify Signatures

Never process a webhook payload without verifying the signature first. Use timing-safe comparison functions to prevent timing attacks.

See Security & Signatures for verification examples in TypeScript, Python, Go, and PHP.

Handle Duplicates

The same event may be delivered more than once (e.g., if your endpoint returns a timeout after processing). Use the webhook-id header (Standard Webhooks format) or the event's id field to deduplicate:

app.post('/webhooks/zyphr', async (req, res) => {
const msgId = req.headers['webhook-id'];

// Check if we've already processed this message
const alreadyProcessed = await cache.exists(`webhook:${msgId}`);
if (alreadyProcessed) {
return res.status(200).json({ received: true });
}

// Process and mark as handled
await processEvent(req.body);
await cache.set(`webhook:${msgId}`, '1', 'EX', 86400); // 24h TTL

res.status(200).json({ received: true });
});

The msg_id is deterministic — the same event delivered to the same webhook always produces the same msg_id, making it safe to use as an idempotency key.

Return Appropriate Status Codes

Your ResponseWhat Zyphr Does
2xxMarks delivery as success, resets failure counter
4xxTreats as a permanent failure — still retried (fix your endpoint)
5xxTreats as a transient failure — retried per retry policy
Timeout (>30s)Treats as a failure — retried per retry policy
tip

Return 200 as quickly as possible. Don't return 4xx to signal "I don't want this event" — instead, update your webhook's event subscriptions.

Use HTTPS

Always use HTTPS endpoints. Keep your SSL/TLS certificates valid — expired certificates will cause delivery failures.

Monitor Your Endpoints

  • Check your webhook's consecutive_failures count regularly
  • Watch for disabled status — auto-disable triggers after 10 consecutive failures
  • Review delivery logs to identify patterns in failures
  • Set up alerting on your side when webhook processing fails

Handle Out-of-Order Events

Events may arrive out of order. For example, you might receive email.opened before email.delivered. Design your event handlers to be order-independent:

  • Use timestamps within the event data to determine event chronology
  • Don't assume events arrive in the order they occurred
  • Use the event id for correlation, not delivery order

Plan Limits

Webhook endpoint limits vary by plan:

PlanMax Webhooks
Free2
Starter10
Pro50
ScaleUnlimited
EnterpriseUnlimited

Webhook delivery attempts are included in all plans at no additional cost. If you hit your endpoint limit, delete unused webhooks or upgrade your plan.

Summary

  1. Respond within 30 seconds — process events asynchronously
  2. Always verify signatures with timing-safe comparison
  3. Deduplicate using webhook-id or event id
  4. Return 2xx immediately, handle errors on your side
  5. Use HTTPS with valid certificates
  6. Monitor endpoint health and watch for auto-disable
  7. Design for out-of-order delivery