SMPP
Connect your SMPP client to TNZ to send SMS, receive replies and track delivery receipts.
Overview
This guide covers client setup for outbound SMS (MT), inbound SMS (MO) and delivery receipts. A bind authenticates your client and opens an SMPP session. Use TX to send messages, RX to receive messages and receipts, or TRX to do both.
Configure your client with the connection details and account limits TNZ supplies.
Request access
Contact TNZ for SMPP pricing and access credentials. Tell us:
- Your SMPP client or library and its version.
- The countries you intend to send messages to.
- Your expected message volume and peak sending rate.
- Whether you need inbound SMS and delivery receipts.
Your TNZ representative will arrange access and explain how to supply your client IP addresses for allowlisting. Do not include passwords in an initial enquiry.
Setup sequence
- Request access and IP allowlisting through a TNZ representative.
- Configure your connection and bind with the settings TNZ supplies.
- Submit a test SMS to a test number you control.
- Check the receipt and match its message ID to the ID in the submission response.
- Complete compatibility testing before production use.
Connect and bind
Your TNZ representative provides your hostname and credentials privately and arranges allowlisting for your client IP addresses.
Connection settings
| Connection setting | Guidance |
|---|---|
| Standard SMPP | Port 2775, without TLS. |
| TLS SMPP | Port 2776. Use this endpoint for encrypted client connections. |
| TLS validation | Keep certificate-chain and hostname validation enabled. Ask TNZ which TLS versions your connection supports and whether you need a client certificate. |
| Protocol | Use SMPP 3.4 (interface_version=0x34). Use the SMPP 3.4 format for submissions. |
| Bind type | Use TRX to send and receive in one session, or separate TX and RX sessions if your application needs them. An RX session is for receiving only. |
| Credentials | Supply NUL-terminated system_id and password. Enter both values exactly as supplied, including letter case. Check that your client's field lengths can hold the full credentials. |
| Other bind fields | Send a complete bind PDU. Use empty system_type and address_range, and zero bind addr_ton/addr_npi. The connector ignores these values. The address_range field does not restrict which inbound messages your session receives. |
Recommended starting settings
Configure these settings in your client, then test them at your agreed sending rate. The server does not enforce client timeouts or keepalive intervals.
| Setting | Recommendation |
|---|---|
| Request window | Start with one submission at a time, waiting for its response before sending the next. |
| Keepalive | Send enquire_link after 30 seconds of inactivity. |
| Response timeout | Start with 30 seconds. Continue to read incoming PDUs and acknowledge deliveries while waiting. |
| Reconnect | After a failed connection attempt, wait about 1 second before retrying. Increase the delay exponentially, with a maximum of 60 seconds. Add a small random variation to each delay to avoid simultaneous retries. Handle server-initiated unbind. Use only the failover endpoints TNZ supplies privately. |
Limits and session behaviour
Keep your total active binds within the limit agreed with TNZ. The connection limit applies separately to each connector process, rather than across all servers.
Stay within the sending rate agreed with TNZ. The connector does not enforce a submission rate limit. Confirm whether your agreed rate counts submissions or message parts.
Your client must send keepalive requests. The connector does not send them or disconnect sessions solely because they are idle. A timeout while the connector reads an incomplete protocol data unit (PDU) can disrupt subsequent PDU parsing. Include partial-PDU timeouts in your connection recovery tests.
Send SMS
Use submit_sm on a TX or TRX session. Use your account's provisioned sender identities, with the destination number format and TON/NPI settings supplied by TNZ.
Check the address format before sending. The connector passes addresses through unchanged, regardless of the submission TON/NPI values.
Illustrative submission
This example illustrates the main fields in a submission and its response. Replace the address placeholders with your provisioned sender and a test destination. Use the TON/NPI settings supplied by TNZ.
submit_sm
sequence_number: 2
source_addr: <SOURCE>
destination_addr: <DESTINATION>
esm_class: 0
registered_delivery: 1
data_coding: 0
sm_length: 5
short_message hex: 48 65 6C 6C 6F
submit_sm_resp
sequence_number: 2
command_status: 0
message_id: 01900000-0000-7000-8000-000000000001
Submission fields
| Field | Settings and behaviour |
|---|---|
| service_type | Send an empty string. The PDU must include this field, although the connector ignores its value. |
| source_addr / destination_addr | Use provisioned addresses and TNZ-approved number formatting. |
| esm_class | Use 0 for ordinary text or set 0x40 for supported multipart UDH. |
| protocol_id / priority_flag | Use 0. These fields do not change message delivery behaviour. |
| schedule_delivery_time / validity_period | Use empty strings. These fields do not schedule messages or set a message expiry time. |
| registered_delivery | Use 1 when requesting receipts. This setting does not restrict which receipts your client receives. See Delivery receipts for details. |
| replace_if_present_flag / sm_default_msg_id | Use 0. These fields do not replace earlier messages or select stored message text. |
| data_coding | Choose a data_coding value from the encoding table below and encode your message text to match. |
| sm_length / short_message | Set sm_length to the number of bytes in short_message, including any user data header (UDH). Count bytes, not characters. The one-byte length field can represent at most 255 bytes. Confirm message size limits for your route with TNZ. |
| Optional TLVs | The connector ignores optional TLVs. Do not use SAR TLVs, message_payload or other optional TLVs to carry essential data. |
What a successful response means: A successful submit_sm_resp acknowledges the submitted message or multipart segment. For a complete message, the connector sends the response before queuing the message for processing. Incomplete multipart segments remain in memory until all parts arrive or the connector restarts.
This response does not confirm persistent storage, acceptance for delivery or delivery to the handset. Use delivery receipts to track the reported delivery status.
For a complete message, the submission response contains a lowercase, hyphenated UUID. Store this message ID exactly as received, without converting it to a number or changing its case.
Supported operations
The connector handles TX/RX/TRX binds, submit_sm, enquire_link and unbind. It sends deliver_sm and logs client deliver_sm_resp responses.
query_sm, cancel_sm, replace_sm, submit_multi and data_sm are unsupported. The connector sends neither a response nor a generic_nack for unsupported commands. Set a response timeout in your client to handle unanswered requests.
Encoding and multipart messages
Choose an encoding
| data_coding | Submission encoding |
|---|---|
0x00 | Unpacked GSM 7-bit, one byte per base character with ESC sequences for extension characters. Packed septets are not supported. |
0x03 | ISO-8859-1 (Latin-1). Emoji cannot be represented. |
0x08 | UTF-16BE, including valid surrogate pairs for emoji. Odd byte lengths and invalid surrogate sequences are rejected. |
0x01 | Uses UTF-8 decoding. ASCII works, but invalid UTF-8 bytes become replacement characters. Use one of the encodings above for non-ASCII text. |
Use the encodings in the table above. Other coding values fall back to UTF-8 decoding, with replacement characters for invalid bytes. This fallback does not support other encodings or binary SMS. Test Unicode text on the receiving handset and check the inbound Unicode limitations below.
For example, to send Hi 😀, use data_coding=8, sm_length=10 and bytes 00 48 00 69 00 20 D8 3D DE 00.
Multipart submissions
For multipart messages, submit each segment with a UDH containing an 8-bit concatenation reference (information element 0x00) or a 16-bit reference (information element 0x08). Set the UDHI bit (0x40) in esm_class.
Your client supplies the segments, which the connector combines into one message for delivery. The connector does not split messages into SMS parts. Ask TNZ for the SMS part limits and charges that apply to your connection.
- Do not split a UTF-16 surrogate pair or a GSM escape sequence between message parts. The connector decodes each part independently.
- SAR TLVs and
message_payloadare unsupported. Put the message text in the short_message field. A submission that contains text only in an ignored TLV can return success with an empty message. - Use the same concatenation reference for every part of one message. Use a different reference for each concurrent message with the same sender and destination.
- Disconnecting and reconnecting does not clear incomplete multipart messages. They remain in memory without an expiry time, but a connector restart removes them.
Multipart response format: Until all parts arrive, the connector returns success with an empty response body. There is no message ID field or NUL terminator in that response. Only the response to the part that completes the message contains the message UUID.
Configure your client to handle an empty response body for incomplete multipart submissions. Message IDs and receipts apply to the complete message, not individual parts.
Delivery receipts
Receipts arrive on an RX or TRX bind as deliver_sm with esm_class=0x04 and data_coding=0. The source and destination address fields both contain the destination address from the original submission, with TON/NPI 1/1.
Receipt format
The receipt contains text fields only. It has no receipt TLVs, submission or completion dates, or original message text. Match the receipt's id value exactly to the message ID returned in the submission response.
Illustrative receipt text:
id:01900000-0000-7000-8000-000000000001 sub:001 dlvrd:001 stat:DELIVRD err:000
Read the status
| Status received by the connector | Receipt status |
|---|---|
Exactly DELIVRD | stat:DELIVRD dlvrd:001 |
| Any other value | stat:UNDELIV dlvrd:000, including temporary states or differently capitalised values. |
UNDELIV covers both temporary delivery states and final failures. Do not automatically resend based only on this status. The error field can be 000 even on failure. Use the status mapping above when processing receipts.
Receipts always report sub:001. A multipart message has one UUID, so its receipts identify the complete message rather than individual parts.
Receipt options and acknowledgements
The registered_delivery setting does not filter delivery receipts. Your client must handle both success and failure receipts, including when it requests no receipts or failure receipts only.
Acknowledge each receipt promptly with deliver_sm_resp. Use the sequence number from the receipt, status 0 and an empty message ID. Where possible, save the receipt to persistent storage before acknowledging it. The connector logs acknowledgements but does not use them to clear pending receipt records or decide whether to resend receipts.
A delivery receipt does not tell you whether the recipient read the message. Do not rely on receipts being resent after a disconnection. See Acknowledgements and retries under Receive SMS.
Receive SMS
Ask TNZ to arrange a receiving number and route its messages to your connection. Inbound SMS arrive as deliver_sm with esm_class=0, which distinguishes them from delivery receipts. Use an RX or TRX bind.
Inbound encoding and length limits
| Inbound path | Known limitation |
|---|---|
| HTTP callback path | Sends unpacked GSM 7-bit with data_coding=0. Characters the connector cannot encode, including emoji, become ?. |
| File-based path | Sends UTF-8 bytes labelled data_coding=0. Non-ASCII text can decode incorrectly. |
Neither inbound method splits messages into parts or includes multipart headers or SAR TLVs. At 256 encoded bytes or more, the length field overflows and no longer matches the message size. Percent-decoding can also corrupt multibyte UTF-8. Arrange tests with TNZ using the characters and message lengths your application needs to receive on this connection.
Acknowledgements and retries
Promptly acknowledge inbound messages with deliver_sm_resp, the same sequence number, status 0 and an empty message ID.
- The connector does not wait for an acknowledgement or resend a message when an acknowledgement is missing. It considers a message delivered once it writes the message to the client connection.
- The connector rejects HTTP callbacks while your account is disconnected and does not store them. The system sending the callback must handle retries.
- Queued messages and delivery statuses can be lost if no receiving session is available or a socket write fails.
- When your client binds again, the connector loads saved retry messages into memory and deletes their files before your client acknowledges them. These messages can still be lost before acknowledgement. Do not rely on automatic retries for file-based inbound messages.
- Handle duplicate messages in your client and keep your own message records. Confirm retention arrangements with TNZ.
- The connector tries receiving sessions in registration order and sends each message or receipt to the first session it can write to successfully. It does not rotate deliveries evenly between sessions or send a copy to every session.
Test your connection
Before testing, agree on the test account, destination numbers and message volume with TNZ. Check that your client library handles the responses described in this guide.
- Verify TLS certificate and hostname validation on port 2776, then bind using SMPP 3.4 and your provisioned credentials.
- Send GSM extension characters, Latin-1 and UTF-16BE emoji. Check that the text displayed on the handset matches the text your application sent.
- Test both UDH reference formats, out-of-order parts and your library's handling of empty response bodies for incomplete multipart submissions.
- Match complete-message UUIDs to successful and unsuccessful receipts. Check both temporary and final delivery statuses against the receipt mapping above.
- Test inbound text on your configured connection, including Unicode and the message lengths your application uses.
- Agree controlled tests for reconnects, outstanding submissions, missing acknowledgements, process restarts and multiple receivers.
- Test the recommended request window and timeouts at your agreed sending rate.
The connector cannot answer message status queries or detect duplicate submissions. If the connection drops before a response arrives, your client cannot determine whether the submission succeeded. Retrying the submission can send the same message twice.
Errors and troubleshooting
Response codes
| Response | Meaning and action |
|---|---|
0x00000000 | Success. See the response details under Send SMS. Incomplete multipart submissions return an empty response body. |
0x0000000D (ESME_RBINDFAIL) | Check for incorrect credentials, an unavailable account, an IP address that is not allowlisted, too many active connections, or a submission sent before binding. Contact TNZ if the bind still fails. Use the recommended reconnect delay. |
0x00000001 (ESME_RINVMSGLEN) | The connector uses this code for submission parsing errors, including malformed fields, incorrect lengths, invalid Unicode and unsupported UDH. Correct the PDU before retrying. |
| No response | Check for an unsupported command, a response timeout or an incorrectly formed PDU. A missing response does not tell you whether the submission succeeded. |
The connector does not return ESME_RTHROTTLED when your client exceeds its agreed sending rate. Set your client to stay within the rate agreed with TNZ.
Do not rely on automatic retries for unacknowledged deliveries. Retain sequence numbers and complete-message IDs for troubleshooting.
Common questions
Why can my client not connect?
Check the hostname supplied by TNZ, your port and TLS settings, and that TNZ has allowlisted the IP address your client connects from.
Review connection settingsWhy does the message text look wrong?
Check that data_coding matches your message encoding and that sm_length matches the number of message bytes.
Review submission encodings or inbound text limitations.Why am I seeing duplicate messages?
Check whether your client resubmits messages after a timeout or reconnect.
Review timeout and reconnect testingWhy are receipts or replies missing?
Check that your RX or TRX session is connected.
Review acknowledgement and retry limitationsGet help with SMPP
When contacting support, include the UTC time of the issue and your client library name and version. We also need the bind type, sequence numbers, message IDs, SMPP command and response status, text encoding and message byte count. For multipart issues, add the concatenation reference, total number of parts and each part's sequence number. Also provide your request window, sending rate and reconnect history.
Send the steps to reproduce the issue and a PDU trace with sensitive information removed. Check configuration and PDU logs for credentials, authentication keys, private keys, connection addresses, personal telephone numbers and message content before sharing them. Bind PDUs contain credentials even in hexadecimal form, so remove those values from the trace as well.
For questions about connection settings, sending limits, message formatting or delivery behaviour, contact TNZ.
Contact the TNZ team