SMPP Protocol
What is SMPP
SMPP (Short Message Peer-to-Peer) is a binary protocol for reliable, persistent connections to send SMS at scale with delivery tracking. Choose SMPP when you need:
- Persistent connections for high-volume messaging
- Real-time delivery notifications via delivery receipts
- Integration with existing telecom infrastructure
Connection Modes
SMPP supports three bind modes. The default allocation is 4 connections, which can be combined across modes.
| Mode | Direction | Description |
|---|---|---|
| TX (Transmitter) | Send only | Used exclusively for submitting messages. Cannot receive delivery receipts. |
| RX (Receiver) | Receive only | Used exclusively for receiving delivery receipts and MO (Mobile Originated) messages. |
| TRX (Transceiver) | Send & Receive | Bidirectional — can both submit messages and receive delivery receipts on the same connection. Recommended for most integrations. |
:::tip Recommended Setup Use TRX mode for simplicity. A single transceiver connection handles both sending and receiving, reducing complexity and connection overhead. :::
Connection Details
| Parameter | Value |
|---|---|
| HOST | smpp.agiletelecom.com |
| PORT | 2776 (TLS) |
| SYSTEM ID | Account username |
| PASSWORD | Account password |
| MODE | TX, RX, or TRX |
| Login-AddrRange | Empty |
| Login-SystemType | Empty |
| Login-TON | 1 |
| Login-NPI | 1 |
| Login-DCS | 0 |
:::warning Keep-Alive Required
Send an ENQUIRE_LINK packet at least every 30 seconds. If no response is received within 5 minutes, the connection will be closed.
:::
Code Examples
Python (smpplib)
pip install smpplib
import smpplib.client
import smpplib.consts
import smpplib.gsm
client = smpplib.client.Client("smpp.agiletelecom.com", 2776, allow_unknown_opt_params=True)
client.connect()
client.bind_transceiver(system_id="YOUR_SYSTEM_ID", password="YOUR_PASSWORD")
# Register delivery receipt handler
client.set_message_received_handler(lambda pdu: print(f"DLR received: {pdu.short_message}"))
parts, encoding_flag, msg_type_flag = smpplib.gsm.make_parts("Hello from SMPP!")
for part in parts:
client.send_message(
source_addr_ton=smpplib.consts.SMPP_TON_ALNUM,
source_addr="MySender",
dest_addr_ton=smpplib.consts.SMPP_TON_INTL,
destination_addr="393401234567",
short_message=part,
data_coding=encoding_flag,
registered_delivery=True,
)
client.listen() # blocks, waiting for DLRs
Node.js (smpp)
npm install smpp
const smpp = require("smpp");
const session = smpp.connect(
{ url: "smpp://smpp.agiletelecom.com:2776", auto_enquire_link_period: 30000 },
() => {
session.bind_transceiver({ system_id: "YOUR_SYSTEM_ID", password: "YOUR_PASSWORD" }, (pdu) => {
if (pdu.command_status === 0) {
session.submit_sm({
source_addr: "MySender",
destination_addr: "393401234567",
short_message: "Hello from SMPP!",
registered_delivery: 1,
}, (submitPdu) => {
console.log("Message ID:", submitPdu.message_id);
});
}
});
}
);
session.on("deliver_sm", (pdu) => {
console.log("DLR:", pdu.short_message.message);
session.deliver_sm_resp({ sequence_number: pdu.sequence_number });
});
Java (CloudHopper)
<!-- pom.xml -->
<dependency>
<groupId>com.cloudhopper</groupId>
<artifactId>ch-smpp</artifactId>
<version>5.1.0</version>
</dependency>
import com.cloudhopper.smpp.*;
import com.cloudhopper.smpp.impl.DefaultSmppClient;
import com.cloudhopper.smpp.type.*;
DefaultSmppClient client = new DefaultSmppClient();
SmppSessionConfiguration config = new SmppSessionConfiguration();
config.setHost("smpp.agiletelecom.com");
config.setPort(2776);
config.setSystemId("YOUR_SYSTEM_ID");
config.setPassword("YOUR_PASSWORD");
config.setType(SmppBindType.TRANSCEIVER);
SmppSession session = client.bind(config, new DefaultSmppSessionHandler() {
@Override
public PduResponse firePduRequestReceived(PduRequest req) {
if (req instanceof DeliverSm) {
System.out.println("DLR: " + new String(((DeliverSm) req).getShortMessage()));
}
return req.createResponse();
}
});
SubmitSm submit = new SubmitSm();
submit.setSourceAddress(new Address((byte) 0x05, (byte) 0x00, "MySender"));
submit.setDestAddress(new Address((byte) 0x01, (byte) 0x01, "393401234567"));
submit.setShortMessage("Hello from SMPP!".getBytes());
submit.setRegisteredDelivery((byte) 1);
session.submit(submit, 10000);
PHP (php-smpp)
composer require alexandr-mironov/php-smpp
<?php
require 'vendor/autoload.php';
use smpp\{Client, SMPP, transport\Socket};
$transport = new Socket(["smpp.agiletelecom.com"], 2776);
$transport->setRecvTimeout(30000);
$smpp = new Client($transport);
$transport->open();
$smpp->bindTransceiver("YOUR_SYSTEM_ID", "YOUR_PASSWORD");
$message = "Hello from SMPP!";
$from = new \smpp\Address("MySender", SMPP::TON_ALPHANUMERIC);
$to = new \smpp\Address("393401234567", SMPP::TON_INTERNATIONAL, SMPP::NPI_E164);
$messageId = $smpp->sendSMS($from, $to, $message);
echo "Message ID: {$messageId}\n";
$smpp->close();
Go (go-smpp)
go get github.com/fiorix/go-smpp
package main
import (
"fmt"
"github.com/fiorix/go-smpp/smpp"
"github.com/fiorix/go-smpp/smpp/pdu/pdufield"
)
func main() {
tx := &smpp.Transceiver{
Addr: "smpp.agiletelecom.com:2776",
User: "YOUR_SYSTEM_ID",
Passwd: "YOUR_PASSWORD",
Handler: smpp.HandlerFunc(func(p smpp.Pdu) {
fmt.Println("DLR received:", p.FieldList())
}),
}
conn := tx.Bind()
defer tx.Close()
<-conn // wait for connection
sm, err := tx.Submit(&smpp.ShortMessage{
Src: "MySender",
Dst: "393401234567",
Text: pdufield.Raw("Hello from SMPP!"),
Register: pdufield.DeliverySetting(1),
})
if err != nil {
panic(err)
}
fmt.Println("Message ID:", sm.RespID())
}
C# (.NET)
dotnet add package JamaaTech.SMPP.Net.Lib
using JamaaTech.Smpp.Net.Client;
using JamaaTech.Smpp.Net.Lib;
var client = new SmppClient();
client.Properties.Host = "smpp.agiletelecom.com";
client.Properties.Port = 2776;
client.Properties.SystemID = "YOUR_SYSTEM_ID";
client.Properties.Password = "YOUR_PASSWORD";
client.Properties.DefaultEncoding = DataCoding.UCS2;
client.MessageDelivered += (sender, args) =>
Console.WriteLine($"DLR: {args.ShortMessage.MessageId}");
client.Start();
var message = new TextMessage
{
SourceAddress = "MySender",
DestinationAddress = "393401234567",
Text = "Hello from SMPP!",
RegisterDeliveryNotification = true
};
client.SendMessage(message);
Ruby (ruby-smpp)
gem install ruby-smpp
require 'smpp'
config = {
host: "smpp.agiletelecom.com",
port: 2776,
system_id: "YOUR_SYSTEM_ID",
password: "YOUR_PASSWORD",
system_type: "",
source_ton: 5,
source_npi: 0,
}
EventMachine.run do
trx = Smpp::Transceiver.new(config, self)
trx.send_mt("MySender", "393401234567", "Hello from SMPP!")
trx.on_delivery_report do |dr|
puts "DLR received: #{dr}"
end
end
Error Codes
Bind & Authentication Errors
| Code | Name | Description |
|---|---|---|
| 1 | ESME_RINVMSGLEN | Invalid message length |
| 4 | ESME_RINVNUMRANGE | Invalid number range |
| 5 | ESME_RALYBND | Already bound |
| 8 | ESME_RSYSERR | System error |
| 10 | ESME_RINVSRCADR | Invalid source address |
| 11 | ESME_RINVDSTADR | Invalid destination address |
| 13 | ESME_RINVBNDSTS | Invalid bind status |
| 21 | ESME_RINVSCHED | Invalid scheduled delivery time |
| 69 | ESME_RPROHIBITED | ESME prohibited from using specified feature |
| 88 | ESME_RTHROTTLED | Throttling error — too many requests |
| 97 | ESME_RINVDCS | Invalid data coding scheme |
| 98 | ESME_RINVSRCADDRSUBUNIT | Invalid source address subunit |
| 259 | VENDOR_SPECIFIC_259 | Vendor-specific authentication failure |
| 1026 | VENDOR_SPECIFIC_1026 | Vendor-specific bind error |
Delivery Status Errors
HLR Errors
| Code | Description |
|---|---|
| 201 | Unknown subscriber |
| 202 | Teleservice not provisioned |
| 203 | Call barred |
| 204 | Absent subscriber (HLR) |
| 205 | Absent subscriber (MSC) |
| 206 | Unidentified subscriber |
| 207 | Roaming not allowed |
| 208 | Illegal subscriber |
| 209 | Bearer service not provisioned |
| 210 | Not allowed for the subscriber |
| 211 | Illegal equipment (IMEI blacklisted) |
| 212 | Forwarding violation |
| 213 | CUG reject |
| 214 | Illegal SS operation |
| 215 | SS not available |
| 350 | HLR system failure |
MSC Errors
| Code | Description |
|---|---|
| 401 | SM delivery failure |
| 402 | Message waiting list full |
| 403 | System failure (MSC) |
| 404 | Data missing |
| 405 | Unexpected data value |
| 406 | User busy |
| 407 | Subscriber absent |
| 408 | Delivery timeout |
| 409 | SC congestion |
| 410 | Facility not supported |
| 411 | No SME delivery |
| 412 | Message cancelled |
| 413 | SC address invalid |
| 414 | Illegal message |
| 415 | Not SC subscriber |
| 416 | Ussd busy |
| 417 | Data coding error |
| 418 | Message class not supported |
| 419 | Duplicate message |
| 550 | MSC system failure |
SMSC Errors
| Code | Description |
|---|---|
| 615 | Expired (message TTL exceeded) |
| 616 | Deleted by operator |
| 617 | Enqueued (still pending) |
| 618 | Undeliverable |
Other Errors
| Code | Description |
|---|---|
| 806 | Number portability error |
| 901 | Rejected by filter |
| 902 | Blacklisted destination |
| 903 | Content filtered |
| 904 | Loop detected |
Sender ID
| Type | Max Length | Example | Use Case |
|---|---|---|---|
| Numeric | 16 characters | +393401234567 | Two-way messaging, responses expected |
| Alphanumeric | 11 characters | MyBrand | Marketing, notifications, one-way messaging |
:::caution AGCOM Regulation In Italy, alphanumeric sender IDs are subject to AGCOM regulation 42/13/CIR. Your sender ID must be pre-registered and approved before use. Contact support for registration. :::
Message Tracking
Each submitted message receives a unique message ID in UUID format:
AGILE-XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
Delivery receipt timestamps follow the format:
yyMMddHHmmss
Example: 240315143022 = March 15, 2024 at 14:30:22
Security: IP Binding
:::warning IP Whitelisting All SMPP connections require IP whitelisting. Your source IP addresses must be registered with Agile Telecom before you can establish a connection. Contact support to add or update your whitelisted IPs.
Connections from non-whitelisted IPs will be silently dropped. :::
FAQ
TX vs RX vs TRX: which should I choose?
Use TRX (Transceiver) unless you have a specific reason to separate send and receive paths. TRX simplifies your integration by handling both directions on a single connection.
How do I receive delivery reports?
Bind in RX or TRX mode. Delivery reports arrive as DELIVER_SM PDUs with the esm_class field set to indicate a delivery receipt. Parse the short_message field to extract status, message ID, and timestamps.
What is the throughput limit?
The default throughput is 100 messages/second per account. If you need higher throughput, contact your account manager to discuss capacity upgrades.