A2P SMS Message Correlation: Traceability Across APIs, SMPP, Providers, and DLRs
Design a chain of identifiers and events to investigate A2P SMS messages across HTTP APIs, SMPP, providers, retries, segments, and delivery receipts, without confusing acceptance with delivery.

Why the Provider Identifier Is Not Enough
An identifier issued by a provider is necessary, but it is rarely enough to explain the full lifecycle of an A2P SMS. It normally identifies a specific acceptance within that provider's domain. On its own, it does not represent the original customer request, the routing decision, a later retry, the segments of a concatenated message, or events that may arrive afterward.
The issue arises in real operations: a request may remain queued, generate more than one attempt, change routes according to an internal policy, or receive a delayed callback. If the system stores only the external identifier, it will be difficult to answer precisely what was sent, which attempt produced a DLR, and which part of the evidence comes from each system.
The operational rule is to retain a stable internal identifier for the logical message and record, rather than replace, the identifiers assigned at each technical boundary. The provider identifier should be treated as a correlation key within a broader relationship, not as the message's global identity.
- Do not equate provider acceptance with delivery to the handset.
- Do not reuse a provider ID as an internal business ID.
- Do not assume that two providers will use the same format, scope, or retention period for their identifiers.
- Keep the original responses and events alongside their normalized interpretation.

Map of the Traceability Chain
Traceability should model states and events, not just a final status table. A minimum chain starts with the customer request, continues through validation and queue entry, and records every sending attempt created by the system. For each attempt accepted by a provider, add the identifier returned by that provider. Subsequent DLRs and callbacks are stored as separate events.
This approach prevents a common loss of context: overwriting the previous state with the latest received event. A DLR is evidence of a later event; it does not replace evidence that a request was received, an attempt was built, or a provider responded to the submission.
A practical map can follow this sequence: customer request, internal logical message, routing decision, sending attempt, HTTP or SMPP submission, provider acceptance or error, and later status events. When segmentation exists, each segment should be linked to the logical message and, where applicable, to the attempt that generated it.
- Customer request: records the customer reference, where available, and the receipt time.
- Logical message: represents the intent to send content to a destination.
- Attempt: represents a specific submission through a given route or provider.
- Provider acceptance: records the external ID and the response received.
- DLR or callback event: retains the original payload, receipt time, and resulting association.
- Derived status: must be reconstructable from events rather than replacing them.

Recommended Identifier Model
A robust scheme separates identifiers by function and scope. The correlation ID links the initial request to internal processing. The idempotency key protects against duplicates of the same customer request. The internal message ID identifies the logical message. The attempt ID identifies each specific submission. The provider message ID records the identifier returned by a provider for a specific acceptance.
These keys are not interchangeable. In particular, a retry must not receive the same attempt ID, even when it represents the same logical message. Likewise, two repeated requests should not create different logical messages if the same valid idempotency key is recognized within the policy defined by the platform.
Generate internal IDs with sufficient uniqueness and keep them opaque to external customers unless the integration contract states otherwise. The ID structure must not disclose phone numbers, content, provider, route, or sensitive operational information.
- correlation ID: created when the operation or business context is received; links internal systems and related records.
- idempotency key: supplied by the customer or defined by the integration; detects repeats of the same operation.
- internal message ID: created when the logical message is accepted into the platform; remains stable throughout its lifecycle.
- attempt ID: created for each submission attempt; changes with every retry or route change.
- provider message ID: received after a provider accepts an attempt; may be opaque and provider-specific.
- event ID: created when each callback, DLR, or query result is ingested; enables event deduplication and auditing.
What Each Party Should Create and What Must Not Be Reused
The customer may provide an idempotency key or a business reference. The platform should create its own correlation, message, attempt, and event IDs. Each provider may return its own message identifier. This allocation keeps responsibilities clear and prevents a short-scope key from improperly becoming a universal key.
Do not reuse an idempotency key as a message ID, or an internal message ID as an attempt ID. Likewise, do not turn a provider message ID into a customer reference. These reuses may appear to simplify the model at first, but they prevent the proper representation of retries, route migrations, and discrepancies between systems.
If an HTTP integration allows custom parameters to be added to the callback URL, they can serve as an additional association clue. Even so, they do not replace the provider's native ID or justify skipping origin validation, complete event logging, and later reconciliation.
- A repeated request may share an idempotency key, but it must not produce two logical messages if policy recognizes it as a duplicate.
- A logical message can have many attempts.
- Each attempt may receive zero, one, or more external identifiers, depending on the interface and available events.
- A received callback must not automatically create a new message when its reference is unknown.
- The association must record its method and confidence level: exact, probable, or unresolved.
One-to-One and One-to-Many Relationships: Messages, Segments, Retries, Routes, and DLRs
The logical message is the central entity, but it does not always correspond to a single submission or a single technical SMS unit. Concatenated content may be split into multiple segments. One message may generate several attempts. Each attempt may travel through a different route and produce separate subsequent events.
Model these relationships explicitly. A logical message can have one or more segments; each segment may require its own technical outcome. A logical message can have one or more attempts, while an attempt must belong to one logical message. An attempt can have a recorded routing decision and one or more associated provider or DLR events.
In SMPP, concatenated messages can be linked through sar_msg_ref_num, sar_total_segments, and sar_segment_seqnum. The SAR reference is generated by the originator to allow reassembly. It is useful to retain it as a technical segmentation attribute, but it must not replace the platform's internal message ID.
- Logical message to segments: one to many.
- Logical message to attempts: one to many.
- Attempt to provider or route: usually one to one per submission, although the history of decisions must be retained.
- Attempt to events: one to many.
- Segment to DLRs: may be one to many when there are repeated events, status changes, or evidence received through more than one channel.
HTTP and SMPP: Practical Correlation Differences
In an HTTP API, the provider usually responds with a resource or message identifier. Store it together with the initial response, timestamp, and initial status. Subsequent callbacks should first be associated through that native identifier when present. If the provider exposes a resource query, it can be used to reconcile missing callbacks or non-final statuses.
In SMPP, sequence_number correlates a request PDU with its associated response within an asynchronous session. It is assigned by the PDU originator, increases monotonically, and the associated response retains that value. It is therefore a short-lived transport reference, not a permanent message identifier across systems or providers.
The submit_sm_resp response may return a message_id assigned by the SMSC. This ID is opaque and belongs to the SMSC domain. In a DLR, the receipted_message_id TLV identifies the message covered by the receipt through the same opaque ID returned when the original submission was accepted. Store both the original value and the complete DLR to demonstrate how the association was made.
To request DLRs in SMPP, use registered_delivery in submit_sm or data_sm. The standard provides for requesting a final success or failure result, or final failure only. Requesting a receipt does not guarantee that an event will arrive, that it will have a uniform format, or that it proves independent receipt on the handset.
- HTTP: link the creation response, native ID, and every callback to the corresponding attempt.
- SMPP sequence_number: use it for request-response correlation in the session, never as a persistent business identifier.
- SMPP message_id: store it as an external ID assigned to an accepted submission.
- SMPP receipted_message_id: use it as the primary DLR association key when present and matching.
- SMPP user_message_reference: can help if propagated, but it is an optional TLV; do not design a guarantee around its presence.
- DLR inside short_message: do not assume a universal format; the SMPP specification indicates that it may be provider-specific.
Operational Fields Worth Recording
Identifiers explain which objects are related; metadata explains what happened and under which conditions. Record separate timestamps for request receipt, message creation, attempt creation, sending to the provider, provider response, callback receipt, and derived update. Avoid using one date field for all these stages.
Keep the destination in a normalized representation separate from operational references. For SMS, E.164 is a useful reference for the international normalization of numbers. Even so, a normalized number must not be used as the sole correlation key: multiple messages can target the same destination, and destination data is potentially personal.
Record the sender, encoding, length and number of segments, requested DLR configuration, interface used, validation result, and the version of the applied routing policy. The policy version makes it possible to explain a historical decision without inferring it from the current configuration.
- Timestamps with clearly defined time zone and clock source.
- Normalized destination, protected as potentially personal data.
- Sender used in the submission, without assuming it identifies the actual sender.
- Encoding, size, and effective segmentation.
- Channel and interface: HTTP, SMPP, or another internal adapter.
- Selected route or provider and routing policy version.
- DLR request, acceptance response, error code, and original event payload.
- Association method and confidence level.
Duplicate, Out-of-Order, or Unrecognized Callbacks
Callbacks are asynchronous events. They may arrive duplicated, out of order, with additional fields, or without the expected reference. The correct design is not to trust arrival order, but to preserve every event and calculate a derived status through explicit rules.
For deduplication, calculate a fingerprint of the original payload and combine, where possible, the provider, external identifier, event type, reported status, and receipt timestamp. Deduplication should mark equivalent events without deleting the evidence received. If two events have the same reference but different data, retain them as separate records and record the discrepancy.
When a reference is not recognized, store the event in a quarantine or unassociated area. Do not create a relationship based only on matches in destination, time, or content: those attributes can produce false positives. Apply probabilistic associations only if operational policy permits it, label them accordingly, and keep the original event unmodified.
Callback parameters may vary by channel and event type, and may even be extended. Receivers should tolerate new fields and retain the payload without requiring every provider to use the same schema.
- Persist the original payload and relevant headers first.
- Validate authenticity and origin using the mechanism documented by each provider.
- Deduplicate without deleting original evidence.
- Do not assume chronological order from receipt order.
- Keep unassociated events for investigation and reconciliation.
- Do not elevate a probable association to certainty without a verifiable reference.
Frequently asked questions
Does the SMPP message_id identify a message globally?
No. The message_id returned in submit_sm_resp is an opaque identifier assigned by the SMSC. It is useful for correlating an accepted submission with a DLR when that DLR includes receipted_message_id, but it must not be treated as a global reference across providers or platforms.
What is sequence_number used for in SMPP?
It is used to correlate a request PDU with its associated response within an asynchronous SMPP session. It is assigned by the PDU originator and must not be used as a persistent message identifier.
Does a DLR confirm that the recipient read the SMS?
No. A DLR reports a delivery status communicated by the messaging chain. Provider acceptance does not equal delivery to the handset either. You should document which event was received, where it came from, and its scope.
Should I rely on user_message_reference to correlate SMPP DLRs?
Not as the sole basis of the design. SMPP defines user_message_reference as an optional parameter, and its propagation must not be assumed. Always retain the mapping between the internal attempt ID and the message_id returned by the SMSC.
What should I do if a callback arrives without a known identifier?
Keep the original event as unassociated, record its origin, and reconcile with the provider when possible. Do not create a definitive association based only on number, time, or content, because it could link different messages.
How long should I retain traceability records?
Define the retention period according to operational purpose, applicable obligations, and security policies. Apply data minimization, storage limitation, access controls, and integrity measures. Separate operational identifiers from personal data such as destination, sender, or content.
Sources consulted
- SMPP Protocol Specification v3.4, Issue 1.2SMPP Developers Forum
- Messages resourceTwilio
- Best Practices for Messaging Delivery Status LoggingTwilio
- Outbound Message Status in Status CallbacksTwilio
- E.164: The international public telecommunication numbering planInternational Telecommunication Union
- A guide to the data protection principlesInformation Commissioner's Office