Every SMPP message is a PDU (Protocol Data Unit) with a fixed 16-byte header followed by an operation-specific body. The header is four big-endian 4-byte integers: command_length, command_id, command_status and sequence_number. command_length counts the whole PDU including the header, so a receiver reads 4 bytes first, then the rest.
bind_transceiverOpen and authenticate the sessionbind_transceiver_respSession acceptedsubmit_smSend a messagesubmit_sm_respAccepted, message ID returneddeliver_smDelivery receipt or inbound messagedeliver_sm_respAcknowledgedenquire_linkKeepalive while idle
The 16-byte header
Every PDU begins with the same four unsigned 32-bit integers in network byte order (big-endian). command_length is the total octet count of the PDU including these 16 header bytes — a parser reads the first 4 bytes, learns the full length, then reads the remainder. command_id identifies the operation. command_status is 0 (ESME_ROK) in requests and carries an error code in responses. sequence_number correlates a request with its response.
| Offset | Field | Size | Purpose |
|---|---|---|---|
| 0 | command_length | 4 octets | Total PDU length in octets, header included |
| 4 | command_id | 4 octets | Operation identifier (e.g. submit_sm = 0x00000004) |
| 8 | command_status | 4 octets | 0 in a request; error code in a response |
| 12 | sequence_number | 4 octets | Correlates request↔response (0x00000001–0x7FFFFFFF) |
command_id and the response bit
Each operation has a command_id. The corresponding response uses the same value with the most-significant bit set (logical OR with 0x80000000). So submit_sm is 0x00000004 and submit_sm_resp is 0x80000004. generic_nack (0x80000000) is the catch-all error response for an unparseable or invalid PDU.
| Operation | Request | Response |
|---|---|---|
| generic_nack | — | 0x80000000 |
| bind_receiver | 0x00000001 | 0x80000001 |
| bind_transmitter | 0x00000002 | 0x80000002 |
| query_sm | 0x00000003 | 0x80000003 |
| submit_sm | 0x00000004 | 0x80000004 |
| deliver_sm | 0x00000005 | 0x80000005 |
| unbind | 0x00000006 | 0x80000006 |
| replace_sm | 0x00000007 | 0x80000007 |
| cancel_sm | 0x00000008 | 0x80000008 |
| bind_transceiver | 0x00000009 | 0x80000009 |
| enquire_link | 0x00000015 | 0x80000015 |
| data_sm | 0x00000103 | 0x80000103 |
The body and how fields are encoded
After the header comes the operation-specific body of mandatory parameters, optionally followed by TLV optional parameters. SMPP uses three field encodings. Integers are fixed-size, unsigned, big-endian. C-Octet Strings are ASCII text terminated by a NULL (0x00), with a declared maximum length that includes the terminator. Octet Strings are raw byte fields whose length is given by a preceding length field (for example short_message, whose size is set by sm_length).
| Type | Encoding |
|---|---|
| Integer | Fixed 1/2/4-octet unsigned, big-endian (network byte order) |
| C-Octet String | NUL-terminated ASCII; max length includes the 0x00 terminator |
| C-Octet String (Decimal/Hex) | As above but restricted to decimal or hex ASCII digits |
| Octet String | Raw bytes; length supplied by a separate length field, no terminator |
A worked example: submit_sm on the wire
A minimal submit_sm PDU. The header's command_length (0x0000003D = 61) is the total byte count; command_id 0x00000004 is submit_sm; command_status 0 marks a request; sequence_number 0x0000000F correlates the response. The body then follows in the fixed field order defined by the spec.
00 00 00 3D command_length = 61
00 00 00 04 command_id = submit_sm
00 00 00 00 command_status = ESME_ROK (request)
00 00 00 0F sequence_number = 15
-- body --
00 service_type = "" (NULL)
05 source_addr_ton = 5 (alphanumeric)
00 source_addr_npi = 0 (unknown)
42 4E 00 source_addr = "BN" + NULL
01 dest_addr_ton = 1 (international)
01 dest_addr_npi = 1 (E.164/ISDN)
... destination_addr, esm_class, ... short_messageFrequently asked
Does command_length include the header?
Yes. command_length is the total length of the entire PDU in octets, the 16-byte header included. A receiver reads exactly command_length bytes to consume one whole PDU off the TCP stream.
What byte order does SMPP use?
Network byte order — big-endian — for all multi-octet integer fields, including every field in the header. A 4-byte value of 61 is transmitted as 00 00 00 3D.
How are text fields terminated?
Text fields are C-Octet Strings: ASCII characters terminated by a single NULL byte (0x00). An empty C-Octet String is just the NULL byte. The declared maximum length includes that terminator.
What is a generic_nack?
generic_nack (command_id 0x80000000) is the response the receiver returns when a PDU cannot be parsed or is fundamentally invalid — for example a bad command_length or unknown command_id. Its command_status carries the reason.
How does a receiver frame PDUs on the TCP stream?
TCP is a byte stream with no message boundaries, so the receiver reads the first 4 bytes to get command_length, then reads that many total bytes to isolate one complete PDU before parsing the rest of the header and body.
Source specifications
The published standards this page is grounded in. Opens in a new tab.