DLR Callbacks in A2P SMS: Idempotent Design for Duplicates and Out-of-Order Events
Design DLR callback handling as an auditable history and an idempotent state projection that can handle retries, duplicates, late events, and out-of-order sequences.

The operational question: what happens if a callback arrives twice, late, or out of order?
A delivery receipt (DLR) callback should not be treated as a single, ordered, definitive update. In an HTTP integration, communication can fail before the sender receives a response, and the request may be repeated. RFC 9110 defines idempotency as the property whereby multiple identical requests have the same intended effect as a single request.
In practice, the receiver must be able to accept the same notification more than once without counting it multiple times or improperly changing the message status. It must also be able to record a receipt that arrives after another previously observed event, without deleting evidence or assuming that reception order is the same as the actual processing order in the messaging chain.
The operating rule is simple: store every reception, derive the current status through explicit rules, and keep those two things separate. The history answers what was received and when; the state projection answers what operational outcome the system calculates from the available evidence.
- Do not assume delivery just because a callback reached your endpoint.
- Do not assume that the most recently received callback necessarily represents the latest event that occurred on the network.
- Do not apply an irreversible update based only on destination, content, or an approximate match.
- Respond safely to repetitions: a retry must not create duplicate accounting, analytics, or operational effects.

What a DLR represents, and what it does not represent
In SMPP, a DLR is requested in submit_sm through registered_delivery. The receipt may be returned to the ESME through deliver_sm or data_sm. Therefore, receipt availability depends on whether receipts were requested and on the configuration or behavior of the messaging platform.
SMPP distinguishes an SMSC Delivery Receipt from an intermediate notification through esm_class encoding. Intermediate notifications are a separate type, and their support depends on the SMSC implementation; they should not automatically be interpreted as final outcomes.
A DLR reports the status reported by the messaging chain within that flow. It is not equivalent to independent verification that a person has seen, read, or understood the content on their device. In SMPP semantics, DELIVERED indicates delivery to the destination; the cited standard does not define a human-read or content-viewed status.
- Differentiate submit_sm acceptance, which confirms submission to the responding system, from a later DLR.
- Store whether a DLR was requested and under which registered_delivery mode when that information is available.
- Label the outcome as a messaging-reported status, not as proof that the recipient read it.
- Treat intermediate notifications as separate events from final statuses.

Minimum data model for useful traceability
The design must preserve the data needed to reconstruct a decision. For each sent message, retain an immutable internal identifier and the external identifier returned by the messaging system. In SMPP, receipted_message_id identifies the message that is the subject of the receipt and corresponds to the opaque message_id returned when the original submission is confirmed.
Store the destination in a normalized representation and also retain the addressing context that was received or sent. E.164 defines the international public telecommunication numbering plan; normalization helps prevent formatting variations, but it should not replace the original values needed for troubleshooting.
The historical DLR format in short_message may include an identifier, submission and completion dates, status, and error. However, its specifics may be gateway- or SMSC-specific. Therefore, extract normalized fields for operations, but always retain the original payload.
- Message: internal ID, external ID or message_id, origin, normalized destination, original addressing values, route or sending context, and submission date.
- Received event: provider event ID if available, reception timestamp, sender-reported timestamp when available, event type, raw status, raw error code, and original payload.
- Projection: calculated status, rationale for the decision, event or events supporting that decision, and update timestamp.
- Audit: parser or rule version applied, correlation result, and any detected exception.
Correlation: prioritize stable keys and reject weak matches
Correlation between the original message and the DLR should first rely on the external identifier assigned by the SMSC. SMPP defines receipted_message_id as the identifier of the message to which the receipt applies. In addition, query_sm uses the SMSC-assigned message_id together with the source address as a matching mechanism.
Do not use destination, text, time window, or sender as automatic substitutes for the external ID. These attributes can recur across legitimate messages and lead to incorrect attribution. A wrong correlation is more harmful than a pending event: it can turn another message's status into a delivery or failure that never belonged to it.
When correlation is not conclusive, record the callback without losing it and route it to an exception queue or register. This makes it possible to investigate format changes, truncated IDs, encoding variations, or other integration-specific behavior without contaminating the projection of known messages.
- First option: match receipted_message_id to the message_id stored when the submission was accepted.
- Retain the external value exactly as received, in addition to any normalized form required by the integration.
- Use origin, destination, timestamps, and route as supporting validations, not as the sole assignment key.
- If there is more than one candidate or none, mark correlation as inconclusive and do not materialize the status onto a specific message.
An idempotent pattern for receiving and processing callbacks
Idempotency does not require ignoring every repetition. It allows you to preserve each received request as evidence while preventing the repetition from changing the operational outcome more than once. RFC 9110 clarifies that a server may log each individual request even when the intended effect of the operation is idempotent.
Implement two layers. The first is a reception log, preferably immutable, that stores the payload, available relevant headers, reception time, and parser result. The second is effect application: deduplication, correlation, and state calculation. Only this second layer must be protected against applying the same logical event twice.
If the sender provides a stable event ID, use it as the deduplication key within the correct scope of the integration. If no such ID exists, create a fingerprint from stable attributes present in the callback, retain the components used, and keep the original payload. Do not base the fingerprint on fields that may change because of local transformation or on ambiguous attributes without documenting the risk.
- 1. Receive the callback and persist the reception before applying business effects.
- 2. Validate and extract available fields without discarding the original payload.
- 3. Determine whether a stable event ID exists; otherwise, calculate a documented fingerprint for the logical event.
- 4. Insert or detect the event atomically in the logical event register.
- 5. Correlate by external ID and apply transition rules only once for each logical event.
- 6. Return a consistent HTTP response after persisting the result needed to make a retry safe, within the sender's webhook contract and applicable latency and availability limits.
State machine: make permitted transitions explicit
A state machine prevents logic from depending on the accidental order in which events arrive. SMPP formatting guidance classifies ENROUTE as an intermediate status and DELIVERED, EXPIRED, DELETED, and UNDELIVERABLE as final statuses. It also indicates that a message being retried may remain ENROUTE and later end as EXPIRED or DELIVERED.
Represent received statuses without overwriting them and define a calculated operational status layer. In the SMPP receipt model, final statuses do not progress to other statuses. Therefore, a conservative policy is to retain a later event that appears to contradict an already calculated final status and trigger an exception for reconciliation or investigation rather than silently replacing the status.
Do not generalize error codes from one platform to another. Network or SMSC codes may be gateway- or platform-specific. Store them as original evidence and create internal classifications only when their rules are documented for the specific integration.
- Raw status: the received value, without destructive reinterpretation.
- Normalized status: a documented internal category, if the integration allows reliable mapping.
- Calculated status: the result of applying precedence and transition rules to correlated events.
- Exception: conflicting events, a contradictory final event, missing correlation, or an unrecognized format.
- Conservative rule in the SMPP receipt model: an already materialized final status should not become another final status merely because a later callback arrives.
Late and out-of-order events: preserve evidence and calculate cautiously
Store at least two separate timestamps: when your system received the callback and the time reported by the callback, if it provides one. You may also need to retain submission and completion dates included in receipt formats. These timestamps are not interchangeable: one describes local observation and the other is information communicated by the messaging platform.
To determine the calculated status, do not blindly sort by arrival time. Apply a documented policy that considers the status type, whether it is intermediate or final, and the quality of correlation. If there is no reliable basis for ordering two events, do not invent a sequence: retain both and flag the conflict.
The operational output should distinguish between the complete history and the current summary. A dashboard can show a calculated final status while also indicating that repeated, late, or conflicting events occurred. This approach reduces the temptation to hide signals that later prove essential for support, reconciliation, or quality analysis.
- Retain local reception order, reported timestamps, and the original payload.
- Preserve every reception in the immutable log. When appropriate, link a repeated reception to the same logical event rather than creating a second identical logical event.
- Do not replace one final status with another incompatible final status without a verifiable contractual or technical rule for that integration.
- Use an investigation queue for conflicts and uncorrelated messages.
- Expose in the audit trail the event supporting the calculated status and the events that could not be applied.
Production checklist
Before connecting DLR callbacks to metrics, billing, alerts, or campaign decisions, test the integration as an event system rather than only as a successful HTTP call. The priority is ensuring that a retry, unexpected payload, or uncorrelated receipt does not become an incorrect delivery claim.
Where a platform provides HTTP or SMPP connectivity, confirm the documented semantics of fields, formats, and receipt rules with the other party before turning them into critical automations.
- Test the same repeated callback and confirm that the calculated status and counters do not change because of the repetition.
- Test an intermediate event that arrives after a final status and confirm that it does not degrade or overwrite the final outcome.
- Test two incompatible final statuses and verify that the second is retained and marked for investigation.
- Test callbacks with no event ID, with an unknown external ID, and with unrecognized date formats or payloads.
- Confirm that uncorrelated messages are not automatically assigned based on destination or content.
- Review that reports distinguish acceptance, messaging-reported DLR status, and any independent verification that may exist outside the DLR.
Frequently asked questions
Does a duplicate DLR callback mean the SMS was delivered twice?
No. It may be a repeated notification. Each reception can be retained in the reception log, while the repetition is linked to the same logical event so that the calculated status and counters are applied only once.
Can I correlate a DLR using only the destination number?
This is not recommended. A destination can recur across multiple messages. The primary basis for correlation should be the external ID assigned to the message, such as the SMPP message_id associated with receipted_message_id. The destination is useful as a supporting validation.
Does DELIVERED confirm that the recipient read the message?
No. DELIVERED is a delivery status reported within the messaging flow. It is not independent confirmation of human reading or that the content was viewed on the device.
What should I do if a final status arrives after a different final status?
As a conservative policy, retain both events and flag the conflict for investigation. In the SMPP receipt model, do not silently replace one final status with another incompatible final status solely because of callback reception order.
Is it necessary to store the original DLR payload?
Yes. Formats and codes may vary across platforms. The original payload makes it possible to audit the parser, review non-normalized fields, and investigate discrepancies without losing the received evidence.
Sources consulted
- SMPP Protocol Specification v3.4, Issue 1.2SMPP Developers Forum
- SMPP Delivery Receipt FormatSMPP Developers Forum
- RFC 9110: HTTP SemanticsIETF / RFC Editor
- Recommendation ITU-T E.164: The international public telecommunication numbering planInternational Telecommunication Union