BroadNet
HTTP API & webhooksWebhooks

Message status (DLR) and inbound (MO) webhook payloads

Updated 6 min readStandards-based
Direct answer

Two webhook payloads carry messaging events. A delivery-status (DLR) event reports a message's outcome — message_id, a status enum (accepted, delivered, undelivered, expired, rejected), an error reason and timestamps. An inbound (MO) event reports a message a subscriber sent to you. Both are the HTTP equivalents of SMPP deliver_sm receipts and mobile-originated messages.

DiagramHow an A2P message reaches the phone
  1. Your applicationSends over the REST API or SMPP
  2. BroadNet platformValidates, routes and bills each message
  3. Operator SMSCDirect operator connection
  4. Subscriber handsetThe message is delivered
The delivery receipt (DLR) travels back along the same path to your webhook
§01

Two event families: status and inbound

A messaging platform emits two fundamentally different callbacks. A delivery-status event (DLR, delivery receipt) is the asynchronous follow-up to a message you sent: it tells you what finally happened to it in the operator network. An inbound event (MO, mobile-originated) is unsolicited — a message a subscriber sent to one of your numbers or a reply to a campaign. They share the webhook transport and signing, but their schemas and their meaning differ, so a robust consumer branches on an event-type discriminator before parsing the rest. Both are the HTTP-layer expression of things SMPP delivers as deliver_sm PDUs: a receipt flag distinguishes a DLR from an MO on the wire, and the same distinction is an explicit event field over HTTP.

§02

Delivery-status (DLR) payload

A delivery-status payload correlates to the original send by the server-assigned message_id and reports a status drawn from a small enumeration, plus the reason and the timestamps that let you measure latency and reconcile billing. The status enum is the heart of it: it distinguishes an intermediate accepted/enroute state from the terminal states, and separates a positive delivered from the several ways a message can fail. Terminal negative states carry an error code and human-readable reason so a consumer can decide whether the destination is permanently bad (suppress it) or the failure was transient (safe to resend). Timestamps typically include when the platform accepted the message and when the terminal status occurred.

Delivery-status (DLR) webhook bodyjson
{
  "event": "message.delivered",
  "message_id": "01HZY8Q3M6",
  "to": "+15551234567",
  "status": "delivered",
  "error": null,
  "reason": null,
  "submitted_at": "2026-07-19T09:15:22Z",
  "done_at": "2026-07-19T09:15:25Z",
  "sequence": 3
}
§03

The delivery-status enumeration

Standardising the status values is what lets one consumer handle every operator, because raw network responses vary wildly. A canonical enum maps them to a handful of states with clear semantics: whether the state is terminal, and whether it is a success. Intermediate states (accepted, enroute) may be superseded by a later event for the same message id, which is exactly why payloads carry a sequence or timestamp and why consumers apply the self-ordering rule from webhook reliability — a late accepted must never overwrite an earlier delivered.

Canonical delivery-status values
statusTerminalMeaning
acceptedNoPlatform accepted the message for delivery
enrouteNoHanded to the operator, awaiting a receipt
deliveredYesConfirmed delivered to the handset
undeliveredYesOperator could not deliver (see error/reason)
expiredYesValidity period elapsed before delivery
rejectedYesRefused (invalid number, blocked sender, spam filter)
unknownYesOperator returned no definitive final state
§04

Inbound (MO) message payload

An inbound event carries the message content plus the routing needed to reply and to correlate a conversation. The critical fields are from (the subscriber's MSISDN in E.164), to (the number or short/long code they messaged), and the text; alongside them a message id for the inbound message itself, a received timestamp, and — because inbound SMS is subject to the same encoding and segmentation rules as outbound — the encoding and, for a multipart message, the concatenation reference and part indices so the consumer can reassemble the full text. Inbound events are how keyword campaigns, two-way conversations and STOP/HELP compliance flows are driven.

Inbound (MO) webhook bodyjson
{
  "event": "message.inbound",
  "message_id": "01HZY8R7T0",
  "from": "+15551234567",
  "to": "44160",
  "text": "STOP",
  "encoding": "GSM7",
  "received_at": "2026-07-19T09:20:11Z",
  "concat": { "ref": null, "part": 1, "total": 1 }
}
§05

Event-type taxonomy

Beyond DLR and MO, a platform emits lifecycle and account events, and a consistent naming scheme — a dotted resource.action string — lets a consumer subscribe to and route them cleanly. Delivery-status events are variations on message.<status>; the inbound event is message.inbound. Grouping by the resource prefix means a consumer can match message.* for everything about a message and reserve other prefixes for account or number events. Every event should also carry a stable event id (used as the signature nonce and the dedup key) and the type string, so the discriminator and the idempotency key are always present.

Common messaging event types
Event typeFamilyFired when
message.acceptedStatus (DLR)Message accepted for delivery
message.deliveredStatus (DLR)Delivery to the handset confirmed
message.undeliveredStatus (DLR)Operator failed to deliver
message.expiredStatus (DLR)Validity period elapsed
message.rejectedStatus (DLR)Message refused upstream
message.inboundInbound (MO)A subscriber sent you a message
§06

Mapping HTTP DLR to SMPP deliver_sm

The HTTP delivery-status callback and the SMPP delivery receipt are the same information in two encodings, and a platform that bridges REST and SMPP translates between them. On SMPP a receipt arrives as a deliver_sm PDU whose esm_class marks it as a receipt; its short-message field carries a text receipt with id, stat and err fields, and the message_state TLV carries the numeric state. The HTTP DLR lifts those into JSON: the SMPP stat token maps to the canonical status enum, the err value maps to the error field, and the receipt's submit/done times map to submitted_at/done_at. Understanding this mapping is what lets one delivery-handling code path serve both transports; the SMPP receipt format and the deliver_sm PDU are documented in detail in the SMPP reference.

SMPP delivery receipt to HTTP DLR field mapping
SMPP receipt fieldHTTP DLR fieldNote
id (receipt)message_idCorrelates to the original submit_sm / POST
stat (DELIVRD/UNDELIV/EXPIRED/REJECTD)statusToken maps to the canonical enum
errerrorOperator/network error code
message_state TLVstatus (numeric backing)SMPP numeric state behind the token
submit date / done datesubmitted_at / done_atLatency and reconciliation timestamps
FAQ

Frequently asked

What is in a delivery-status (DLR) webhook payload?

The server-assigned message_id that correlates it to your send, a status drawn from a canonical enum (accepted, enroute, delivered, undelivered, expired, rejected, unknown), an error code and human-readable reason for failures, and timestamps for when the message was submitted and when the terminal status occurred.

How is an inbound (MO) event different from a DLR?

A DLR is the asynchronous outcome of a message you sent. An inbound (MO) event is unsolicited — a message a subscriber sent to your number or a reply to a campaign — carrying from, to, the text, encoding and, for multipart messages, concatenation fields. They share transport and signing but have different schemas, so consumers branch on the event type first.

Why standardise the delivery-status enum?

Because raw operator responses vary enormously. Mapping them to a small canonical enum with clear terminal-and-success semantics lets one consumer handle every operator uniformly, and lets you decide from the status alone whether to suppress a bad destination, resend a transient failure, or record a confirmed delivery.

How do I stop a late 'accepted' overwriting an earlier 'delivered'?

Use the self-ordering fields. DLR payloads carry a timestamp and often a sequence number; the consumer compares them to the last state it recorded for that message id and discards any event that is older than what it already has, so an out-of-order intermediate state never clobbers a terminal one.

How does an HTTP DLR map to an SMPP deliver_sm receipt?

They carry the same information. On SMPP the receipt is a deliver_sm PDU marked in esm_class, with id, stat and err fields and a message_state TLV. The HTTP DLR maps stat to the status enum, err to error, and the receipt timestamps to submitted_at/done_at — so one delivery-handling path can serve both transports.

What fields let me reassemble a multipart inbound SMS?

The concatenation fields: a reference that ties the parts together, the part index and the total count. Because inbound SMS obeys the same encoding and segmentation rules as outbound, a long inbound message arrives as several MO events that the consumer stitches together in order using those fields.

References

Source specifications

The published standards this page is grounded in. Opens in a new tab.

BNMaintained by the BroadNet engineering team·Carrier-grade messaging since 2003
Put it on the wire

Build with a real SMPP bind and REST API.

Request test access with real test keys: send to your own verified number first, then move to production across 200+ countries once your sender identity and routes are approved.