Receiving
Reassembled messages, structured delivery receipts, and manual acknowledgement.
Bind as a receiver or transceiver and the SMSC pushes deliver_sm PDUs at
you. The client routes them into two high-level events, acknowledging each PDU
automatically.
The message event
Ordinary mobile-originated messages arrive as AssembledMessage objects. Multipart
messages (concat UDH or SAR TLVs) are reassembled — segments may arrive out of
order or interleaved with other messages — and delivered once, complete:
client.on("message", (message) => {
message.sourceAddr; // "46709771337"
message.destinationAddr; // your address
message.message; // full decoded text (or Buffer for binary payloads)
message.parts; // segments it arrived in
message.pdus; // the underlying PDUs, in sequence order
});Incomplete multipart messages whose remaining segments never arrive are evicted after 60 seconds.
The receipt event
deliver_sm PDUs flagged as delivery receipts (esm_class receipt bits, or the
receiptedMessageId/messageState TLVs) are parsed into typed objects instead:
client.on("receipt", (receipt, pdu) => {
receipt.messageId; // TLV preferred over the text `id:` field
receipt.state; // "DELIVRD" | "UNDELIV" | "EXPIRED" | …
receipt.messageState; // numeric MESSAGE_STATE, when known
receipt.submitted; // sub:001 → 1
receipt.delivered; // dlvrd:001 → 1
receipt.submitDate; // Date (UTC)
receipt.doneDate; // Date (UTC)
receipt.error; // err:000
receipt.text; // first characters of the original message
receipt.raw; // the unparsed short-message text
});Both the appendix-B text format (id:… sub:… dlvrd:… stat:… err:… text:…) and the
receipt TLVs are understood; TLVs win when both are present.
Acknowledgement
By default every deliver_sm is answered with a success deliver_sm_resp after your
event handlers run. To control the response yourself — for example to reject
deliveries you failed to persist — disable auto-ack and respond from the raw event:
import { CommandStatus } from "@better-smpp/core";
const client = new SmppClient({ autoAckDeliverSm: false, /* … */ });
client.on("deliver_sm", async (pdu) => {
try {
await store(pdu);
await client.respond(pdu, { messageId: "" });
} catch {
await client.respond(pdu, {
commandStatus: CommandStatus.ESME_RX_T_APPN, // temporary failure — SMSC retries
});
}
});The raw deliver_sm event always fires (before message/receipt), so it also works
for logging alongside auto-ack.
Other SMSC-initiated PDUs
client.on("alert_notification", (pdu) => { /* subscriber became reachable */ });
client.on("outbind", (pdu) => { /* SMSC requests a bind on this connection */ });enquire_link pings from the SMSC are answered automatically by the connection layer.