Accepted Does Not Mean Delivered: How to Reconcile Sending Statuses and DLRs in A2P SMS
An operational framework for separating API and SMPP acknowledgements, provider acceptance, network statuses, and final DLRs without turning transport evidence into a claim of handset receipt.

The operational question: What evidence actually exists after sending an SMS?
In A2P SMS, the word “sent” can describe very different facts. It may mean that an application created an HTTP request, that an ESME successfully received submit_sm_resp, that a provider accepted the traffic, that an upstream carrier accepted the message, or that a delivery receipt was received. These signals do not have the same evidentiary scope.
The practical rule is simple: each status must express the available evidence, not the most favorable conclusion. A successful API response or a correct command_status in submit_sm_resp confirms the outcome of that technical interaction. It does not, by itself, confirm that the message reached the destination network or that the subscriber received it on their handset.
This distinction is particularly important in OTP and transactional flows. A system may decide to retry, show a screen to the user, or open an operational investigation based on the wrong status if it turns “accepted” into “delivered.” In legitimate marketing, the same mistake can distort metrics and route-quality decisions.
A DLR should not automatically be presented as independent verification of physical receipt on the handset either. It is evidence of a status communicated through the messaging chain. Its specific meaning depends on the received event, the fields available, and the guarantees offered by each participant in the route.
- Request acknowledgement: evidence that an entity processed a technical request.
- Provider or carrier acceptance: evidence of admission for subsequent processing.
- Intermediate status: evidence of queuing, sending, or transit, but not necessarily a final outcome.
- Final DLR: reported evidence of delivery or non-delivery; it must be retained along with its source and original payload.
- Silence: the absence of an observed event, not proof of delivery or failure.

The four layers that should not be mixed
A robust reconciliation architecture separates at least four layers. The first is local acceptance: the application has validated and sent a request, or placed it in its own queue. This is an internal fact and does not indicate the remote outcome.
The second is remote acceptance. In SMPP, every operation except alert_notification has an associated request and response. If the originator does not receive the response, it must assume that the PDU was not received by the remote entity. When it receives submit_sm_resp, command_status communicates the success or failure of the submit_sm request. This transport acknowledgement is not a DLR.
The third layer is provider or network processing. In HTTP APIs, some providers expose statuses such as accepted, queued, sending, or sent. These can be valuable for identifying a queue, a dispatch phase, or acceptance by an upstream carrier, but they remain distinct from delivery confirmation.
The fourth layer is the reported outcome. SMPP defines statuses such as ENROUTE, DELIVERED, EXPIRED, UNDELIVERABLE, UNKNOWN, ACCEPTED, and REJECTED. This variety shows why a single Boolean success field often loses critical information for operations, customer support, and quality analysis.
- Layer 1: request created or accepted by the originating system.
- Layer 2: request accepted or rejected by an API, SMSC, or provider.
- Layer 3: processing, queuing, dispatch, or transit reported by the delivery chain.
- Layer 4: final outcome or still-unresolved status reported through a DLR, query, or reconciliation.

Why a message_id is not proof of delivery
In SMPP, message_id is a unique reference assigned by the SMSC. The specification describes it as an opaque, implementation-dependent value. It can be used as a handle in subsequent operations, such as query_sm, cancel_sm, or replace_sm. Its existence enables operation correlation; it does not prove that the recipient received the SMS.
An integration should not rely on a single identifier. The provider may generate its own identifier, an SMSC may return another, and a DLR may include receipted_message_id. In addition, a business message may require its own stable identifier to link the sending attempt to a transaction, an OTP session, or a legitimate transactional notification.
The recommended practice is to create an immutable internal identifier before sending. This identifier should link to every technical attempt, its external references, received events, and derived status. If retries are performed, each attempt should have its own identity and retain an explicit relationship with the original logical message.
Do not use the destination number, the full message text, or an approximate timestamp as the primary correlation key. These fields may repeat, change because of segmentation, or create privacy issues. Retain only the data required for operations and apply the relevant data-protection controls.
- message_logical_id: identifies the business message or action.
- attempt_id: identifies each technical sending attempt.
- provider_message_id: identifier returned by the API or provider.
- smpp_message_id: identifier returned by submit_sm_resp where applicable.
- receipted_message_id: reference included in an SMPP DLR where applicable.
- event_id or event fingerprint: enables duplicate detection without deleting evidence.
- correlation_version: documents correlation rules when they change over time.
State machine model for an A2P SMS
The state machine should distinguish observed events from derived conclusions. An event is immutable: for example, an accepted HTTP response, a successful submit_sm_resp, a deliver_sm with message_state, or a later query. The current status is a projection calculated from all correlated events and explicit rules.
A minimum model can maintain internal statuses for creation, local sending, and response waiting; remote acceptance statuses; transitional processing statuses; reported final statuses; and a reconciliation status indicating whether the case is closed, awaiting observation, or requires review. There is no need to force every provider into a taxonomy more precise than the available evidence.
For SMPP, treat successful submit_sm_resp as “accepted by the SMPP entity,” not as “delivered.” When receipts arrive through deliver_sm, record the available values, including receipted_message_id, message_state, and, where present, network_error_code. Intermediate notifications and DLRs can share this transport mechanism.
A cautious projection can show “delivery reported” when normalized evidence is DELIVERED or an equivalent status communicated by the provider. It can show “non-delivery reported” for final negative statuses such as UNDELIVERABLE, EXPIRED, or REJECTED, without removing the original code. When only a transit status exists, the outcome should remain pending or in progress.
- CREATED: the intent to send was created, with no remote evidence.
- SUBMITTED_LOCAL: the system attempted to transmit the request.
- REMOTE_ACCEPTED: an API, SMSC, or provider accepted the technical request.
- IN_PROGRESS: evidence exists of queuing, dispatch, transit, or ENROUTE.
- DELIVERED_REPORTED: a reported positive final status was received through the messaging chain.
- FAILED_REPORTED: a reported negative final status was received through the messaging chain.
- UNKNOWN_OR_UNRESOLVED: insufficient final evidence exists, or UNKNOWN was received.
- RECONCILIATION_PENDING: an operational window has expired and the case requires querying or closure classification.
How to normalize heterogeneous HTTP, SMPP, and DLR events without losing original data
Normalization supports consistent operations; it does not replace source semantics. Always retain the original event alongside a normalized representation. The original payload is necessary for technical auditing, mapping debugging, and adaptation when a provider adds or changes callback fields.
For each event, record at least the source, interface type, receipt time at your platform, available external identifier, original status, normalized status, error codes, and protected original payload. If the provider reports a network-originated timestamp, store it separately from the receipt time: they do not necessarily represent the same instant.
In SMPP, a deliver_sm can carry delivery receipts. For these events, receipted_message_id and message_state are relevant parameters; network_error_code may be present. Do not discard a receipt because it lacks a field that your integration does not guarantee: classify it using the available evidence and flag it for review if it cannot be correlated safely.
In HTTP, do not assume that all callbacks contain the same fields or that all statuses exist for every channel or configuration. Maintain a versioned mapping table by provider and interface. The mapping should transform external statuses into broad operational categories while preserving the literal external value.
- Do not replace the original status with the normalized one; retain both.
- Differentiate event_received_at from event_reported_at when the source provides its own timestamp.
- Record error codes and texts without turning them into unconfirmed diagnoses.
- Version mapping and precedence rules.
- Keep uncorrelated events in an investigation queue instead of associating them through weak similarity.
Transient, final, and unknown statuses: explicit rules
Classify statuses by their operational function. Transient statuses indicate that additional information may arrive; final statuses communicate an outcome and, according to your rules, close the attempt; unknown statuses express a lack of resolution and must not be converted into success or failure for analytical convenience.
SMPP lists ENROUTE as a transit status and DELIVERED, EXPIRED, DELETED, UNDELIVERABLE, and REJECTED among statuses that can be final depending on the integration context. UNKNOWN requires particular caution: it indicates that the status cannot be determined, not that the message failed or was delivered.
Define in advance which statuses close an attempt and which statuses allow further events. The rule should not exist only as implicit code. It should be documented, testable, and modifiable in a controlled way if an interface or operational relationship changes.
Do not turn the passage of time into proof of delivery. Time can only trigger a reconciliation action or an administrative closure with declared uncertainty. If no DLR exists, the correct outcome may be “no final status observed within the window,” not “delivered.”
- Transient: queued, sending, sent, ENROUTE, or equivalents reported by the source.
- Reported positive final: DELIVERED or an explicit provider equivalent.
- Reported negative final: EXPIRED, UNDELIVERABLE, REJECTED, or explicit provider equivalents.
- Indeterminate: UNKNOWN, correlation errors, missing DLRs, and incomplete events.
- Administrative: closed by operational policy, always separate from the reported delivery outcome.
Duplicate, out-of-order, or contradictory DLRs
Messaging systems must be designed for repeated events and imperfect ordering. SMPP states that an SMSC should respond in the same order in which it receives requests, but is not required to do so, and the ESME must be able to handle out-of-sequence responses. This precaution should extend to HTTP callbacks, internal queues, and retry processes.
The answer is not to overwrite the latest received data. Store every event as an immutable fact and derive the current status through a precedence policy. The policy should consider at least correlation, status category, the time reported by the source when reliable, receipt time, source, and the version of the applied rule.
A duplicate can be detected using an event identifier where one exists, or through a fingerprint calculated from stable event fields. Marking it as a duplicate does not mean deleting it: it retains diagnostic value and demonstrates that the system received more than one notification.
When contradictions arise, do not invent a resolution. For example, if a positive final signal appears and is later followed by a negative event that cannot be explained by a different attempt, retain both, mark the case as contradictory, and apply a cautious presentation rule. The operational view may require review, while the audit trail should show the full sequence.
- Do not use “the latest event wins” as the only rule.
- Derive status per attempt, not only per logical message.
- Apply idempotent deduplication before updating projections.
- Separate late events from invalid events: a late event may be legitimate.
- Escalate unresolvable contradictions for operational review.
- Record the reason for every derived status change.
What to do when no DLR arrives
The absence of a DLR does not prove that the message was not processed or that it was not delivered. It may indicate that a callback was not received, that the integration did not request or support it in that case, that the event could not be correlated, or that the chain has not yet produced an observable outcome.
Set waiting windows by use case, provider, and interface only when you have a justified operational policy. Avoid turning one provider’s recommendation into a universal rule. As an example of documented practice, Twilio recommends querying the resource if delivered or undelivered has not been received within 12 hours and reconciling statuses at least daily; this guidance must be adapted and validated for each integration, not assumed to reflect industry-wide behavior.
For legitimate OTPs, the product objective often requires a quick decision, but delivery status must not be replaced with an assumption. Design appropriate authentication alternatives, control retries to avoid duplication, and record that the outcome remains pending when final evidence does not arrive. For transactional messages, a longer window may be appropriate depending on process criticality and expectations. For consent-based marketing, separate reported-delivery measurement from engagement metrics and do not infer reading from a DLR.
Operational closure should distinguish among “final outcome reported,” “outcome queried,” “no DLR observed,” and “not correlatable.” This precision reduces disputes between systems and prevents quality reports from turning uncertainty into an artificial delivery rate.
- Verify that the DLR or callback request is configured where the interface permits it.
- Check receiver endpoint availability, authentication, and callback error logs.
- Schedule reconciliation queries when the provider offers a queryable status source.
- Close with an uncertainty label when no final evidence exists.
- Do not attribute silence to a network failure, provider failure, or delivery without supporting data.
- Measure requested DLRs, received DLRs, correlated DLRs, and cases without final resolution separately.
Frequently asked questions
Does a submit_sm_resp with a successful command_status confirm SMS delivery?
No. It confirms the outcome of the submit_sm request with the remote SMPP entity. It is technical acceptance of the request, not confirmation of delivery to the subscriber.
What does an SMPP message_id prove?
It proves that the SMSC assigned a reference for subsequent operations, such as querying, cancelling, or replacing. It is useful for correlation, but it is not proof of delivery.
What status should be shown if no DLR arrives?
An uncertainty status, such as “no final status observed” or “reconciliation pending,” according to your policy. The absence of a DLR must not be labeled as delivery or failure without additional evidence.
How should duplicate DLRs be handled?
Record every event immutably, detect duplicates using an identifier or stable fingerprint, and make derived-status updates idempotent. Do not delete the original evidence.
Can a DLR arrive out of order?
Yes. SMPP requires an implementation to be able to handle out-of-sequence responses. Therefore, arrival order should not be the sole basis for deciding the final status.
Does a delivered DLR independently prove that the user saw or read the message?
No. A delivered DLR is delivery evidence communicated through the messaging chain. By itself, it does not prove reading, user attention, or consent.
Sources consulted
- SMPP Protocol Specification v3.4, Issue 1.2SMPP Developers Forum
- Outbound Message Status in Status CallbacksTwilio Documentation
- Messages resourceTwilio Documentation
- Best Practices for Messaging Delivery Status LoggingTwilio Documentation