# SMPP PDU anatomy: header and body

> 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.

- Canonical URL: https://www.broadnet.me/reference/smpp-pdu-anatomy
- Language: en
- Last updated: 2026-07-18
- Publisher: BroadNet (https://www.broadnet.me)

The exact byte layout of an SMPP PDU: the 16-byte header (command_length, command_id, command_status, sequence_number), the body, and how fields are encoded.

## 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.

*SMPP header fields (16 bytes, big-endian)*

| 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.

*Common command_id values*

| 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).

*SMPP field encodings*

| 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.

*submit_sm header + start of body (annotated)*

```hex
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_message
```

## Frequently asked questions

### 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.

## Sources

- [SMPP v3.4 specification — §2, §3 PDU format](https://smpp.org/SMPP_v3_4_Issue1_2.pdf)
- [SMPP v5.0 specification](https://smpp.org/SMPP_v5.pdf)

## Related

- [SMPP protocol overview](https://www.broadnet.me/reference/smpp-protocol-overview)
- [SMPP PDU decoder (free tool)](https://www.broadnet.me/tools/smpp-pdu-decoder)
- [SMPP TLV optional parameters](https://www.broadnet.me/reference/smpp-tlv-parameters)
- [SMPP command_status error codes](https://www.broadnet.me/reference/smpp-command-status-errors)
- [Developer documentation](https://www.broadnet.me/developers)
