# SMPP TLV optional parameters

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

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

SMPP's optional parameters use a Tag-Length-Value format. The TLV byte layout plus the common tags: message_payload, receipted_message_id, message_state, user_message_reference and the sar_* concatenation set.

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

*message_payload TLV carrying a 5-byte value*

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

*Frequently used optional parameters*

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

*message_state values*

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

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

## Sources

- [SMPP v3.4 specification — §5.3 optional parameters](https://smpp.org/SMPP_v3_4_Issue1_2.pdf)
- [SMPP v5.0 specification](https://smpp.org/SMPP_v5.pdf)

## Related

- [SMPP delivery receipts (DLR format)](https://www.broadnet.me/reference/smpp-delivery-receipts-dlr)
- [SMS segmentation & UDH](https://www.broadnet.me/reference/sms-segmentation-concatenation-udh)
- [SMPP submit_sm reference](https://www.broadnet.me/reference/smpp-submit-sm)
- [SMPP PDU decoder (free tool)](https://www.broadnet.me/tools/smpp-pdu-decoder)
- [SMPP PDU anatomy](https://www.broadnet.me/reference/smpp-pdu-anatomy)
