Blog

Webhooks vs Polling: Syncing SMS Delivery Status the Right Way

3 November 2026

Sending an SMS is the easy part. The harder engineering problem is knowing what happened to it afterwards: was it accepted by the operator, delivered to the handset, rejected, or silently expired? Your application needs that state to retry an OTP, mark a notification as failed, or stop charging a customer for a message that never arrived.

There are two mainstream ways to keep your system in sync with an SMS platform: polling the API for status, or receiving webhooks (HTTP callbacks) when the status changes. Both work. They fail in different ways, and the right choice depends on your traffic pattern and your infrastructure.

How SMS delivery status actually flows

A delivery report (DLR) is not generated by the SMS provider alone. The message travels from your API call to the provider, then to a carrier or aggregator, then to the destination network, and finally to the handset. Each hop can report back a status, and the final confirmation comes from the mobile network.

Two consequences matter for your design:

  • Status is asynchronous and multi-stage. A message can move from queued to sent to delivered over seconds — or, if the handset is off, over a much longer window.
  • Final status is not guaranteed everywhere. Some routes and some countries return limited or no handset-level confirmation. Timing and granularity vary by operator and destination, so build for "unknown" as a legitimate state rather than an error.

Polling: simple, predictable, wasteful

With polling, your service periodically asks the API: "what is the status of these message IDs?" It is the easiest model to implement because it requires no public endpoint, no inbound firewall rule, and no TLS certificate on your side.

Where polling makes sense:

  • Low or batch-oriented volume — a few campaigns a day, reconciled afterwards.
  • Environments where you cannot expose an inbound HTTP endpoint (internal networks, strict compliance zones).
  • Early prototypes where you want a working integration in an afternoon.

Where polling hurts:

  • Latency vs. cost trade-off. Poll every 30 seconds and your OTP flow feels sluggish; poll every second and you burn requests, most of which return nothing new.
  • Scaling. Tracking tens of thousands of in-flight messages means either large batch status queries or a lot of noise.
  • Rate limits. Aggressive polling is the fastest way to get throttled.

If you poll, poll smartly: query in batches by message ID, use exponential backoff (fast for the first minute, slower after), and stop polling messages that reached a terminal state or passed their validity period.

Webhooks: near real-time, but you own the endpoint

With webhooks, you register a callback URL and the platform posts a payload to it whenever a message status changes. UIPAPP supports webhooks alongside instant delivery reports in the panel, so your database can reflect the same state the dashboard shows, without constant querying.

Advantages:

  • Status updates arrive close to real time, which matters for OTP retry logic and fraud rules.
  • No wasted requests — you only process events that exist.
  • Scales naturally with volume instead of with the number of in-flight messages.

The responsibilities that come with it:

  • Availability. If your endpoint is down or slow, events have to be retried. Treat the webhook receiver as a critical service.
  • Verification. A public URL can be called by anyone. Validate a shared secret or signature, restrict to HTTPS, and ignore anything that fails the check.
  • Idempotency. Retries mean you will see the same event twice. Key on the message ID plus status, and make writes safe to repeat.
  • Ordering. Events can arrive out of order. Never let a sent event overwrite a delivered one — compare timestamps or use a state machine that only moves forward.

A webhook receiver that does not fall over

The most common mistake is doing real work inside the HTTP handler. A better pattern:

  1. Validate the signature and the payload shape.
  2. Write the raw event to a queue or append-only table.
  3. Return 200 OK immediately — ideally in well under a second.
  4. Process asynchronously: update the message record, trigger business logic, emit metrics.

This keeps your response time stable under bursts, which is exactly when campaigns generate thousands of DLRs at once. Log every event you reject and why; silent drops are painful to debug weeks later.

The hybrid approach most teams end up with

In production, webhooks and polling are complements rather than competitors. Use webhooks as the primary channel for low-latency updates, and run a reconciliation job that periodically polls for messages still sitting in a non-terminal state after a reasonable window — say, anything still sent after an hour.

That safety net catches the edge cases webhooks cannot: a deployment that took your endpoint offline, a DNS hiccup, a payload rejected by a bad validation rule. It also gives you a clean source of truth when finance or support asks why a specific message was billed.

Choosing for your use case

  • OTP and transactional flows: webhooks, with polling as a fallback. Latency is part of the user experience here.
  • Bulk campaigns: webhooks for live dashboards, plus a batch reconciliation pass once the send window closes.
  • Internal tools with no public endpoint: polling with backoff is perfectly acceptable.
  • Very high throughput: if you are already on an SMPP connection, DLRs arrive on the same session — but you still need the same idempotency and state-machine discipline.

Whichever model you pick, the durable part of the design is not the transport. It is a clear status state machine, safe repeated writes, and a reconciliation path for the messages that fall through the cracks. Get those right and switching between webhooks and polling becomes a configuration detail rather than a rewrite.

Create your free account today

Start sending within minutes. Reach us on WhatsApp or Telegram — real humans answer.