LOGIC TELECOM
CommunicationsAugust 4, 20266 min read

Reliable SMS status webhooks: signatures, retries and idempotency

How to receive SMS status events without loss or duplication: request signing, idempotency, retries, event ordering, dead-letter queues and reconciliation.

A protected SMS status stream passes signature validation and a retry queue
Contents

A reliable SMS status webhook treats redelivery as a normal condition, not an exception. The receiver verifies the request, durably stores an event under a unique identifier and returns 2xx quickly; business processing happens asynchronously. This design prevents transient failures from becoming data loss and stops duplicates or out-of-order delivery reports from triggering the wrong action.

Why one HTTP handler is not enough

A Delivery Receipt crosses several systems. Any participant may retry after a timeout even when your service has already written the result. Events such as submitted, delivered and failed may also arrive out of order. If the endpoint immediately closes an order, credits a bonus or starts a fallback notification, one retry can repeat the business action.

The webhook is therefore an integration boundary, not the home of all workflow logic. It should do four things:

  1. accept and validate the request format;
  2. authenticate the expected sender;
  3. durably store the original event;
  4. respond before the sender’s timeout expires.

The event contract

Agree the contract before launch. A useful minimum includes:

  • the customer’s message identifier;
  • the platform or route identifier;
  • a unique event identifier;
  • raw and normalized status;
  • source-event and receive timestamps;
  • error code and description where applicable;
  • schema version;
  • webhook delivery-attempt identifier;
  • request timestamp and signature.

The phone number and SMS body are rarely needed by a status consumer. If a number is required, restrict retention and access and mask it in logs. Use an opaque correlation ID to link a DLR to an order or authentication flow.

Signatures and replay protection

An IP allowlist can be an additional control, but it does not prove body integrity. Use a cryptographic signature, such as an HMAC over the unmodified request body, timestamp and event ID. Verify it before interpreting business fields and compare signature values in constant time.

The timestamp limits the replay window. Reject requests outside that window and store the event ID in a deduplication table. Keep signing secrets outside the codebase and support rotation with a short overlap between old and new keys.

Do not write secrets, full phone numbers or signature material to ordinary application logs. A request ID, validation result, key version and payload hash are usually sufficient for investigation.

Idempotency: one fact, one state change

The same event may be delivered more than once. Add a unique constraint on provider + event_id or another stable key defined by the contract. Persisting the event and recording its acceptance should be atomic.

If the provider does not issue an event ID, a composite key can use the message ID, status, source timestamp and error code. This is weaker: timestamp rounding and normalization rules must be explicit or distinct events may be merged.

Business actions also need idempotency. The transition from submitted to delivered may close a workflow once; a duplicate delivered must not close it again. Use an outbox for side effects: store the new state and a command for downstream processing in one transaction.

Handling out-of-order events

Do not use receive time as the only ordering rule. Store an event journal and calculate the current message state separately. Transition rules should consider terminal states and the source timestamp.

A late submitted event should not replace an existing delivered state. A newer terminal result may need special treatment if the provider supports corrections. Do not guess: define a state-transition table with the DLR set described in our guide to SMS delivery metrics.

Retain unknown raw values and route them to an observation category. Silently converting an unknown code to failed corrupts analytics and can trigger a fallback too early.

Retries, backoff and the dead-letter queue

A platform normally retries a webhook after a timeout or non-2xx response. The endpoint should respond after durable storage but before expensive processing. If neither the database nor broker accepted the event, return an error: a false 200 OK turns a temporary outage into permanent loss.

The internal consumer should retry a limited number of times with exponential backoff and jitter. Immediate unbounded retries turn one dependency failure into a request storm. After the budget is exhausted, send the event to a dead-letter queue with the reason, attempt count and safe replay instructions.

Replay must use the same deduplication path. An operator selects an event range, invokes the normal consumer and sees the outcome of every record.

Reconciliation catches silent loss

Even a well-designed webhook does not prove that the history is complete. Run a periodic reconciliation job:

  • find messages without a final status beyond a control interval;
  • query the platform’s status API or export;
  • compare counts and status distributions for the period;
  • restore missing events through the same idempotent path;
  • record the cause of each discrepancy.

Reconciliation is particularly valuable after an incident, signing-key rotation or contract change. It complements rather than replaces webhook delivery.

Metrics and alerts

Observe the entire path, not only the 2xx rate:

  • source-event-to-receive and processing latency;
  • retry and duplicate rate;
  • signature failures and expired requests;
  • queue depth and oldest-event age;
  • dead-letter queue volume;
  • messages without a terminal state;
  • unknown codes and forbidden transitions.

Separate webhook transport faults from SMS non-delivery. The first describes your integration; the second describes the communications route. The wider approach to alerting is covered in SLIs, SLOs and error budgets.

Acceptance checklist

Before production, reproduce:

  1. two identical events in sequence;
  2. delivered arriving before submitted;
  3. an invalid signature and an expired timestamp;
  4. a timeout after a successful write;
  5. an unavailable database or broker;
  6. an unknown status and a new schema version;
  7. exhausted retries and DLQ replay;
  8. reconciliation of a missing webhook.

Measure response time under peak load and verify that one slow consumer cannot block all inbound traffic. The general responsibility boundary can be checked against our guide to external API resilience.

Practical conclusion

An SMS DLR webhook becomes reliable when replay is safe, ordering is not assumed and every accepted event is traceable from the raw request to the business state. Signing protects source trust, idempotency handles duplicates, queues absorb transient failures and reconciliation finds silent gaps.

QuickTel provides an API and status events for enterprise SMS. Agree the DLR format, signature rules, timeouts, retries and reconciliation method before integration, then validate the contract in your own test environment.

SMSAPINotificationsIntegrations

Read also