Webhooks are at-least-once: plan for it
What does at-least-once delivery mean for SMS webhooks?
At-least-once delivery means SMSRoute may send the same webhook event multiple times. Your endpoint must be idempotent: use a unique event ID to detect and ignore duplicates. This ensures no message status is lost, even during transient network issues.
Webhook delivery — the delivery receipts and inbound messages your provider POSTs to you — follows an at-least-once guarantee, not exactly-once. If your endpoint is slow or returns an error, the provider retries; so the same event can arrive twice, three times, out of order. This is a *feature* — it's how a webhook survives a transient failure on your end — but only if you handle the duplicates it implies. SMS webhook retry handling is really two disciplines: absorbing the provider's retries to you without double-processing, and doing your own retries to the provider correctly. Get both right and the at-least-once guarantee becomes reliability; ignore either and it becomes duplicate charges, double opt-outs, and corrupted state.
| Direction | Who retries | Your defense | Failure backstop |
|---|---|---|---|
| Inbound (provider → you) | The provider, on your non-2xx or timeout | Dedupe on event id; ack fast; verify signature | Ordered state machine |
| Outbound (you → provider) | You, on transient send failure | Backoff + idempotency key | Dead-letter queue |
Absorbing the provider's retries (inbound to you)
How do I handle inbound SMS webhook retries from SMSRoute?
SMSRoute automatically retries inbound webhooks if your endpoint doesn't respond with HTTP 200 within 5 seconds. Implement idempotency by storing processed event IDs. This guarantees you never miss a delivery receipt or incoming message, even during temporary downtime.
When a provider retries a webhook to you, the defense is idempotency: process each unique event exactly once no matter how many times it arrives. The mechanism is the same one that makes DLR handling safe.
- Dedupe on the event id. Every webhook carries a unique event or message id. Track processed ids and ignore repeats. This turns at-least-once into effectively exactly-once on your side.
- Acknowledge fast, process async. Return a 200 immediately so the provider knows you got it and stops retrying. Do the actual work afterward. A slow handler causes needless retries.
- Respect ordering. A retried older event can arrive after a newer one. Use a state machine so a late 'sent' can't overwrite a 'delivered'.
- Verify before processing. Check the signature first, so a forged or replayed event never reaches your logic.
The 200-fast rule matters more than it looks: providers retry on non-2xx responses AND on timeouts.
Your own retries (outbound to the provider)
How should I retry outbound SMS API calls to SMSRoute?
For outbound API calls, implement exponential backoff with jitter. SMSRoute's API returns a unique message ID on success; retry only on network errors or 5xx responses. This pairs with our adaptive multi-route delivery to maximize message reach without overloading your system.
The other direction: when *your* send to the provider fails transiently, you retry (the standard retry pattern). The discipline here mirrors the inbound side.
- Retry only transient failures: timeouts and 5xx get retried with exponential backoff; 4xx (bad request) does not. Fix it instead. Retrying a permanent failure just wastes attempts.
- Use idempotency keys: every retry carries the same idempotency key so a retry of a send that actually succeeded doesn't duplicate the message. At-least-once retries plus idempotency equals exactly-once effect.
- Bound the retries: cap attempts and total time, especially for OTP where a message retried for minutes is useless. Give up gracefully rather than retrying forever.
- Dead-letter the failures: after the retry budget is exhausted, move the message to a dead-letter queue instead of dropping it silently. That's where you inspect, alert, and decide on manual intervention.
Without it, a message that fails all retries disappears silently.
The complete picture
How do inbound and outbound retries work together for reliable SMS delivery?
SMSRoute's retry system works both ways: we retry webhooks to your endpoint, and you retry API calls to us. Combined with our automatic failover across 149 countries and real-time DLR webhooks, this creates end-to-end at-least-once delivery. That ensures every message reaches its destination.
Put both directions together and you have a webhook integration that's reliable under real network conditions: inbound events deduplicated and processed once, verified and acknowledged fast; outbound sends retried on transient failure with idempotency keys, bounded, and dead-lettered when exhausted. The unifying principle is that at-least-once delivery is a guarantee you build *on top of*, not something that just works — the provider promises to keep trying, and you promise to handle the duplicates and failures that promise creates.
SMSRoute is a no-KYC SMS API with crypto billing (BTC, ETH, USDT, XMR, LTC, and SOL) whose webhooks follow the same at-least-once model as the industry — because that's what survives real networks. Build the two-way discipline once: idempotent inbound handling with fast acks and signature checks, idempotent outbound retries with backoff and dead-lettering. Then a dropped connection or a slow endpoint becomes a non-event instead of a duplicate-message incident or a silently-lost send. of failures; it's handling the retries that failures cause, in both directions.
For more on the at-least-once delivery model, see the IETF RFC 7230 on HTTP retries and the Twilio webhook retry documentation. SMSRoute's published route pages list delivery from $0.004/message (premium direct-carrier corridors up to $0.035) with sub-100ms median submission and ~98.6% delivered success (smsroute.cc route pages, 2026).
Related reading
FAQ
Why do SMS webhooks get delivered more than once?
How do I handle duplicate webhook events?
How should I retry failed SMS sends to a provider?
What is a dead-letter queue for SMS?
Send your first SMS in 5 minutes
No KYC. Pay with BTC, ETH, USDT, XMR, LTC, and SOL. Live routes to 149 countries.
Get an API key →