The short version
When you're processing delivery receipts, status updates, and carrier callbacks from an SMS API, network failures and duplicate events are not edge cases—they're operational realities. A carrier may retry a webhook delivery after a timeout, your server may acknowledge receipt while failing to persist the state, or two parallel processes may handle the same event. Without proper retry logic and idempotency handling, you risk duplicate notifications to users, inconsistent delivery states, and support escalations from phantom duplicates. This guide covers the implementation patterns Notilify customers use to build resilient webhook workflows that handle retries, deduplicate events, and maintain consistent state across delivery receipts and status updates.
- Webhooks from SMS carriers are unreliable—implement exponential backoff with a maximum of 5 retries
- Use idempotency keys from Notilify webhook payloads to prevent duplicate processing
- Store delivery state transitions in your own database rather than trusting single webhook events
- Handle concurrent webhook deliveries by implementing idempotent upserts
- Test your retry logic with Notilify's webhook simulation endpoints before production launch
Implementing Exponential Backoff for SMS Webhooks
When Notilify or a carrier retries a webhook delivery, it's typically because the initial request failed to return a 2xx response within the timeout window. The retry schedule varies by provider, but you should design your webhook handler to handle a minimum of 3-5 retry attempts with increasing delays.
A production-ready implementation uses exponential backoff with jitter. Calculate delay as `min(baseDelay * (2 ^ attempt), maxDelay) + random(0, jitter)`. For SMS webhooks, a base delay of 5 seconds with a max delay of 5 minutes and jitter of 1-2 seconds handles most carrier retry patterns without overwhelming your server during incidents.
Your webhook endpoint must return a 2xx status within 30 seconds to prevent unnecessary retries. If your processing involves database writes, external API calls, or queue enqueuing, consider acknowledging the receipt immediately (return 2xx) and processing asynchronously. This reduces the window for timeout-driven retries and improves your delivery success metrics.
Idempotency Patterns for Webhook Processing
- Every Notilify webhook payload includes an `idempotency_key` field that uniquely identifies the event. Store this key in a database table or distributed cache with a TTL matching your retry window (typically 24-48 hours). Before processing any webhook, check if the key exists—skip processing if it does.
- Implement idempotent upserts for delivery state. When handling status updates like `delivered`, `failed`, or `undelivered`, use database operations that update the record only if the new state is more recent than the existing one. Compare timestamps or use explicit state machine transitions to prevent race conditions.
- For OTP and phone verification webhooks, use the idempotency key to prevent duplicate notification triggers. If your system processes a delivery receipt twice, don't re-send the OTP—the original message already delivered or failed. Track message-level state separately from delivery receipt state.
- Handle concurrent deliveries by implementing optimistic locking or distributed locks when processing the same webhook. If two processes receive the same event simultaneously, the database constraint on the idempotency key ensures only one succeeds, and the other can safely skip.
Operational Checklist for Production Webhook Deployments
Before launching SMS webhook handlers to production, verify your implementation handles the scenarios Notilify supports: duplicate deliveries within the retry window, out-of-order status updates, and failed webhook responses. Test with synthetic events using your Notilify dashboard to confirm your handler returns 2xx responses and correctly updates delivery state.
Monitor your webhook processing metrics: success rate, duplicate event rate, and average processing latency. A spike in duplicate events often indicates your idempotency logic is not catching all retry paths. Set up alerts for webhook failures that exceed your retry threshold so your team can investigate before users report issues.
Keep your webhook secret secure and rotate it periodically. Notilify signs each webhook payload—verify the signature in your handler to prevent spoofed delivery events. Document your webhook handler behavior in your support runbook so on-call engineers can debug delivery issues without reverse-engineering production code.
FAQ
What happens if my webhook handler is down for an extended period?
Notilify retries webhooks according to our retry schedule for up to 24 hours. If your endpoint is unavailable beyond the retry window, you may miss events. Implement a reconciliation process that queries Notilify's delivery status API periodically to sync state for any missing webhooks—this is especially important for high-value OTPs and security notifications where missing a delivery status could impact user experience.
Can I rely on webhook delivery order?
No. Carriers and Notilify make no guarantee about webhook delivery order. A 'delivered' webhook may arrive before or after a 'queued' webhook depending on carrier timing and retry behavior. Always implement your state handling based on timestamps or sequence numbers rather than arrival order.
How do I test webhook retry behavior without triggering actual retries?
Use Notilify's developer dashboard to simulate webhook deliveries. You can configure your test endpoint to return 4xx or 5xx responses and observe how the retry logic behaves. For idempotency testing, send the same idempotency key multiple times and verify your handler correctly deduplicates the events.
Should I process webhooks synchronously or asynchronously?
Return a 2xx response synchronously, then process the event asynchronously. This prevents timeout-driven retries from carriers and ensures your webhook handler remains responsive. Use a job queue or background worker to handle the actual processing, with appropriate error handling for failed processing attempts.
Read API docs
Use Notilify to build transactional SMS with clearer delivery state, sender planning, and support visibility.
Read API docs