123eworld Knowledge Hub → Transactional SMS → Page 187

Transactional SMS API Delivery Reports: DLR Mapping, Status Normalization and Message Lifecycle

Developer reference for SMS delivery reports, DLR normalization, provider status mapping, terminal states, delayed receipts and message lifecycle design.

DLR meaning

A delivery report is evidence from a downstream messaging system about the state of an SMS attempt. It should not automatically be treated as proof of handset behaviour beyond what the provider and carrier actually report.

Normalized statuses

Providers use different status names. Normalize them into a stable platform vocabulary such as accepted, submitted, delivered, failed, expired and unknown. Preserve the original provider status internally.

Logical versus attempt state

One logical message may have multiple provider attempts. Store attempt state separately from logical message state so failover and retry do not overwrite history.

Terminal states

Define which statuses are terminal. Delivered and permanent failure may be terminal, while submitted or temporary failure may remain open for later events.

Delayed reports

A delivery report may arrive minutes or hours after submission. The system should allow late events without incorrectly reopening a completed message.

Contradictory events

Provider systems can sometimes send events that appear inconsistent. Use documented precedence rules and preserve raw evidence for investigation rather than blindly overwriting state.

Mapping version

Status mappings should be versioned. If a provider changes terminology, historical messages should remain explainable using the mapping that was active when the event was processed.

Receipts and billing

Delivery status and billing are related but separate. A failed message can still be billable depending on the commercial agreement and provider stage at which billing occurs.

API exposure

Customer-facing status responses should be simple and stable. Provider-specific details can be exposed as optional diagnostic fields or through privileged support views.

Testing

Test every normalized state, duplicate receipt, late receipt, unknown provider status and contradictory event sequence.

Reporting

Dashboards should show delivery rate, failure rate, pending age and provider-specific status distributions.

Developer takeaway

DLR architecture is strongest when raw provider evidence, normalized customer state and logical message history are all retained separately.

Provider status dictionary

Maintain a provider-specific dictionary that maps raw statuses to normalized states. Include the meaning, terminal behaviour and effective mapping revision. When a provider changes terminology, update the mapping through a controlled release.

Current status versus history

The current message status is useful for fast API reads, while the status history provides the evidence needed to understand transitions. Do not force every customer query to reconstruct the entire history.

Receipt deduplication

Provider delivery reports may be repeated. Use provider event ID where available; otherwise derive a safe deduplication identity from provider reference, status and timestamp rules defined for that provider.

Unknown statuses

An unknown provider status should not be silently converted to delivered or failed. Put it into an explicit unknown or pending category and alert operations if it occurs frequently.

Late terminal events

A delivered event received after a temporary failure should normally move the logical message to delivered. A late event after a terminal permanent failure requires provider-specific precedence rules and preserved evidence.

Provider attempts

Each attempt should contain provider name, route, request timestamp, provider reference and final attempt state. This allows support to distinguish a provider failure from a customer-facing logical failure.

Status APIs

Customers generally need a small stable set of statuses. Provider raw codes can be available through a diagnostic endpoint or support interface without becoming part of the primary application contract.

Analytics

Delivery analytics should be based on normalized logical states while provider analytics can use raw categories. Mixing the two creates misleading cross-provider comparisons.

Reconciliation

Run reconciliation jobs that compare accepted messages with provider receipts and identify messages that remain unresolved beyond expected windows.

Final implementation test

Replay duplicate, late and unknown DLRs in different orders and confirm the logical state follows documented rules.

Lifecycle model

A practical logical lifecycle might move from accepted to queued, submitted, delivered or failed. Intermediate states should remain distinguishable from terminal outcomes. The API should not expose every internal provider state as a separate customer status because that makes application logic fragile.

Receipt timing

Providers may return a submission acknowledgment immediately and a delivery report much later. The platform should retain the time of both events. Customers can then distinguish provider submission latency from delivery latency. This is particularly useful when investigating reports that messages were accepted quickly but reached recipients slowly.

Status reconciliation

A reconciliation job can find messages that have remained in an intermediate state beyond the expected reporting window. The job can query provider evidence, reprocess missing events or flag the case for support. Reconciliation should be idempotent so running it twice does not alter correct state.

Final reference

Delivery reporting should preserve raw evidence while presenting a simple customer-facing lifecycle. This balance makes the platform both easy to integrate and possible to audit.

Customer status contract

The public status contract should remain stable even when providers change. A customer should be able to write application logic against delivered, failed, pending or unknown without understanding every provider-specific code. The platform can enrich the status response with a diagnostic category while keeping the core enum small.

Support investigation

When support investigates a message, the useful evidence is the logical message ID, current state, transition history, provider attempt IDs, raw provider statuses and timestamps. This structure allows an engineer to explain the difference between API acceptance, provider submission and actual delivery.

Retention

Status history can grow faster than message content because every attempt and receipt creates another record. Apply retention and archival policies deliberately. If historical status is required for billing or compliance, preserve the necessary evidence separately from high-volume operational history.

Knowledge-base conclusion

DLR handling should be designed as a state machine with evidence, not as a simple field that gets overwritten whenever a provider sends a new status.

End-to-end engineering scenario

A useful delivery-report test is to create a sequence of accepted, submitted, temporary failure, delivered and duplicate events. Replay the sequence in several different orders. The logical message should end in the same documented state regardless of harmless delivery ordering. Then inject an unknown provider status and confirm it becomes an explicit diagnostic condition instead of being silently classified as success. This is the kind of test that protects the platform when a provider changes behaviour without notice.

Advanced reference scenario

Delivery reports should also support aggregate reconciliation. If 100,000 messages were accepted but only 99,500 have reached a terminal state after the expected reporting period, the platform should surface the 500 unresolved messages as an operational queue. The reconciliation process can classify them by provider, destination and age. This is more useful than assuming that all missing reports represent failures. It also gives operations a measurable backlog that can be investigated and cleared.

Engineering decision guide

For developer troubleshooting, the status API should expose enough context to explain a pending message without exposing provider internals unnecessarily. Useful fields include current normalized state, accepted time, last transition time and a safe failure category. If the message remains pending beyond a normal window, the response can indicate that reconciliation is in progress. This gives customers a clear next action: wait, inspect a diagnostic field or contact support with the message ID. It also prevents applications from treating every non-delivered state as a reason to resend.

Final architecture perspective

A final delivery-report implementation should include an explicit state-transition table. For each incoming provider event, define the current state, incoming state, whether the transition is allowed, whether the event is ignored as stale, and whether support evidence is retained. This prevents different workers from implementing slightly different rules. It also makes future provider integrations easier because the adapter translates raw provider statuses into a common event model before the state machine processes them. Once this model is established, the same lifecycle can support status APIs, dashboards, reconciliation and customer webhooks.

Continue through the 123eworld Knowledge Hub

Explore the complete 123eworld Knowledge Hub for practical SMS API, transactional messaging and developer architecture guides.

Visit 123eworld.com for messaging and digital communication services.