TLVs (optional parameters, introduced in v3.4) extend a PDU beyond its mandatory fields. Each is encoded as a 2-byte Tag, a 2-byte Length and a Length-byte Value, all big-endian. TLVs appear after the mandatory body, in any order, and let SMPP carry payloads, concatenation metadata and receipt correlation without changing the fixed field layout.
The TLV format
A TLV (also called an optional parameter) is a self-describing field: a 2-octet Tag identifies the parameter, a 2-octet Length gives the size of the Value in octets, and the Value follows. Both Tag and Length are big-endian. Because they are tagged, TLVs can appear in any order after the mandatory parameters and a receiver simply skips tags it does not recognise.
04 24 Tag = 0x0424 (message_payload)
00 05 Length = 5 octets
48 65 6C 6C 6F Value = "Hello"Common TLV tags
A handful of TLVs do most of the work in production. message_payload carries a message too long for short_message. receipted_message_id and message_state make delivery receipts machine-parseable. user_message_reference lets you attach your own correlation id. The sar_* set provides standards-based concatenation without UDH bytes in the body.
| Tag | Name | Value | Use |
|---|---|---|---|
| 0x0424 | message_payload | Octet String | Message body when it exceeds short_message |
| 0x001E | receipted_message_id | C-Octet String (65) | Original message_id in a delivery receipt |
| 0x0427 | message_state | Integer (1) | Final/interim state in a receipt (see below) |
| 0x0204 | user_message_reference | Integer (2) | ESME-assigned correlation reference |
| 0x020C | sar_msg_ref_num | Integer (2) | Concatenation: shared reference across segments |
| 0x020E | sar_total_segments | Integer (1) | Concatenation: total number of segments |
| 0x020F | sar_segment_seqnum | Integer (1) | Concatenation: this segment's sequence |
| 0x0423 | network_error_code | Octet String (3) | Network-specific failure detail in a receipt |
| 0x0426 | more_messages_to_send | Integer (1) | Signals more segments follow |
| 0x020A | source_port | Integer (2) | Application port addressing (source) |
| 0x020B | dest_port | Integer (2) | Application port addressing (destination) |
message_state values
The message_state TLV (0x0427) reports a message's lifecycle state numerically — the machine-readable companion to the DLR text's stat keyword. These values also appear in query_sm_resp.
| Value | State | DLR stat keyword |
|---|---|---|
| 1 | ENROUTE | (in transit) |
| 2 | DELIVERED | DELIVRD |
| 3 | EXPIRED | EXPIRED |
| 4 | DELETED | DELETED |
| 5 | UNDELIVERABLE | UNDELIV |
| 6 | ACCEPTED | ACCEPTD |
| 7 | UNKNOWN | UNKNOWN |
| 8 | REJECTED | REJECTD |
| 9 | SKIPPED (v5.0) | — |
SAR concatenation vs UDH
SMPP offers two ways to concatenate a long message. The UDH method puts a 6-byte User Data Header inside short_message and sets the esm_class UDHI bit. The SAR method keeps the body clean and carries the three sar_* TLVs instead. Pick one and be consistent per route — some networks accept only UDH, others only SAR. Never mix the two on the same message.
Rules and gotchas
Do not populate both short_message and message_payload — providers reject that. Custom TLVs outside the ranges your provider approves are commonly stripped or cause parse errors downstream, so only send metadata in agreed tags. A receiver must tolerate unknown TLVs by skipping Length bytes; a malformed Length is the classic cause of a corrupted parse of everything after it.
Frequently asked
What does TLV stand for in SMPP?
Tag-Length-Value. Each optional parameter is encoded as a 2-byte tag, a 2-byte length, and that many bytes of value. TLVs were introduced in SMPP v3.4 and appear after the mandatory body of a PDU.
When should I use message_payload instead of short_message?
Use the message_payload TLV (0x0424) when the message exceeds the 254-octet short_message field, or when your provider prefers it for long content. Do not set both fields on the same PDU.
How do I correlate a delivery receipt back to my submitted message?
Match the receipted_message_id TLV (0x001E) in the receipt against the message_id you stored from submit_sm_resp. Normalise formats first, since some SMSCs return the id in hex on submit and decimal in the receipt.
Are sar_* TLVs better than UDH for concatenation?
Neither is universally better — they are two encodings of the same idea. SAR keeps the message body free of header bytes; UDH is more widely accepted. Use whichever your route supports and stay consistent.
Can I add my own custom TLVs?
Only within the vendor-specific tag range your provider approves. Unapproved custom TLVs are frequently stripped or rejected in transit, so agree the tags and their meaning with the provider first.
Source specifications
The published standards this page is grounded in. Opens in a new tab.