SMPP Protocol
SMPP (Short Message Peer-to-Peer Protocol) is a binary protocol designed 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, or integration with existing telecom infrastructure.
Connection Modes
Agile Telecom's SMPP server supports three connection modes. Choose the one that fits your use case:
- Transmitter (TX) – Send messages only. Use this if you only originate SMS and don't need delivery reports.
- Receiver (RX) – Receive delivery notifications only. Use this for dedicated notification monitoring from TX connections elsewhere.
- Transceiver (TRX) – Send messages and receive notifications over the same connection. Ideal for bi-directional messaging with fewer connection slots.
By default, your account allows up to 4 active connections—combine modes as needed (e.g., 4 TX + 4 RX, or 4 TRX). Contact support if you need additional capacity.
Connection Details
| Parameter | Value |
|---|---|
| HOST | smpp.agiletelecom.com |
| PORT | 2776 (TLS) |
| SYSTEM ID | Your account username |
| PASSWORD | Your account password |
| MODE | TX, RX, or TRX |
| Login-AddrRange | Leave empty |
| Login-SystemType | Leave empty |
| Login-TON | 1 |
| Login-NPI | 1 |
| Login-DCS | 0 |
Send an ENQUIRE_LINK packet at least every 30 seconds. If no response is received within 5 minutes, the connection will be closed. Most SMPP libraries handle this automatically.
Code Examples
- Python
- Node.js
- Java
- PHP
- Go
- C#
- Ruby
Install the smpplib library:
pip install smpplib
Connect and send an SMS:
```python
import smpplib
import smpplib.gsm
import smpplib.consts
# Create a client
client = smpplib.client.Client("smpp.agiletelecom.com", 2776, allow_unknown_opt_params=True)
# Connect with TLS
client.connect()
# Bind as Transceiver (TRX mode)
client.bind(
smpplib.consts.SMPP_BIND_TX,
system_id="YOUR_USERNAME",
password="YOUR_PASSWORD",
)
# Send SMS
parts, encoding_flag, msg_type_flag = smpplib.gsm.make_parts("Hello World!")
for part in parts:
pdu = client.send_message(
source_addr_ton=smpplib.consts.SMPP_TON_INTL,
source_addr="12345",
dest_addr_ton=smpplib.consts.SMPP_TON_INTL,
destination_addr="+391234567890",
short_message=part,
)
print(f"Message ID: \{pdu.message_id\}")
# Keep connection alive
import time
while True:
client.listen()
time.sleep(1)
client.unbind()
client.disconnect()
Install the smpp library:
npm install smpp
Connect and send an SMS:
const smpp = require("smpp");
const client = new smpp.Session(\{
host: "smpp.agiletelecom.com",
port: 2776,
tls: true,
logger: "bunyan",
\});
client.on("error", (err) => \{
console.error("Client error:", err);
\});
client.on("connect", () => \{
console.log("Connected to SMPP server");
// Bind as Transceiver
client.bind_transceiver(
\{
system_id: "YOUR_USERNAME",
password: "YOUR_PASSWORD",
\},
(pdu) => \{
if (pdu.command_status === 0) \{
console.log("Successfully bound");
// Send SMS
client.submit_sm(
\{
source_addr_ton: 1,
source_addr: "12345",
dest_addr_ton: 1,
destination_addr: "+391234567890",
short_message: "Hello World!",
\},
(pdu) => \{
console.log(`Message ID: $\{pdu.message_id\}`);
client.unbind();
client.close();
\}
);
\}
\}
);
\});
client.connect();
Add dependency (using Maven):
<dependency>
<groupId>com.cloudhopper</groupId>
<artifactId>ch-smpp</artifactId>
<version>6.0.0</version>
</dependency>
Connect and send:
import com.cloudhopper.smpp.SmppSession;
import com.cloudhopper.smpp.SmppSessionConfiguration;
import com.cloudhopper.smpp.impl.DefaultSmppSessionFactory;
import com.cloudhopper.smpp.pdu.PduAsyncResponse;
import com.cloudhopper.smpp.pdu.SubmitSm;
import com.cloudhopper.smpp.pdu.SubmitSmResp;
SmppSessionConfiguration config = new SmppSessionConfiguration();
config.setWindowSize(1);
config.setName("Agile Telecom");
config.setType(SmppSessionConfiguration.TYPE_TRANSCEIVER);
config.setHost("smpp.agiletelecom.com");
config.setPort(2776);
config.setConnectTimeout(5000);
config.setBindTimeout(5000);
config.setSystemId("YOUR_USERNAME");
config.setPassword("YOUR_PASSWORD");
config.setUsingSSL(true);
DefaultSmppSessionFactory factory = new DefaultSmppSessionFactory();
SmppSession session = factory.createSmppSession(config);
session.rebind();
SubmitSm pdu = new SubmitSm();
pdu.setSourceAddress("12345");
pdu.setDestinationAddress("+391234567890");
pdu.setShortMessage("Hello World!".getBytes());
PduAsyncResponse asyncResponse = session.submitSmAsync(pdu);
SubmitSmResp response = (SubmitSmResp) asyncResponse.getResponse();
System.out.println("Message ID: " + response.getMessageId());
session.unbindAndClose();
Install the php-smpp library:
composer require onurb/php-smpp
Connect and send:
<?php
require 'vendor/autoload.php';
use SMPPClient\SMPP;
use SMPPClient\SMPPClient;
use SMPPClient\PDU\SubmitSM;
$client = new SMPPClient([
'host' => 'smpp.agiletelecom.com',
'port' => 2776,
'use_ssl' => true,
'system_id' => 'YOUR_USERNAME',
'password' => 'YOUR_PASSWORD',
]);
// Connect and bind
$client->connect();
$client->bindTransmitter();
// Send SMS
$pdu = new SubmitSM();
$pdu->source_addr = '12345';
$pdu->destination_addr = '+391234567890';
$pdu->short_message = 'Hello World!';
$response = $client->submit($pdu);
echo "Message ID: " . $response->message_id . "\n";
$client->close();
?>
Install the SMPP library:
go get github.com/fiorix/go-smpp/smpp
Connect and send:
package main
import (
"fmt"
"log"
"github.com/fiorix/go-smpp/smpp"
)
func main() \{
conn := &smpp.Conn\{
Addr: "smpp.agiletelecom.com:2776",
User: "YOUR_USERNAME",
Passwd: "YOUR_PASSWORD",
TLS: true,
\}
session, err := conn.Session()
if err != nil \{
log.Fatal(err)
\}
defer session.Close()
// Send SMS
pdu, err := session.Submit(&smpp.SubmitSM\{
SourceAddr: "12345",
DestinationAddr: "+391234567890",
ShortMessage: []byte("Hello World!"),
\})
if err != nil \{
log.Fatal(err)
\}
fmt.Println("Message ID:", pdu.(*smpp.SubmitSMResp).MessageID)
\}
Install NuGet package:
dotnet add package AleaIOT.SMPP
Connect and send:
using SMPP;
using SMPP.PDU;
class Program \{
static async Task Main() \{
var client = new SMPPClient();
await client.ConnectAsync(
"smpp.agiletelecom.com",
2776,
useTLS: true
);
var bindResp = await client.BindAsync(
systemId: "YOUR_USERNAME",
password: "YOUR_PASSWORD",
bindMode: BindMode.Transceiver
);
if (bindResp.Status == CommandStatus.ESME_ROK) \{
var submitResp = await client.SubmitSmAsync(
sourceAddr: "12345",
destAddr: "+391234567890",
message: "Hello World!"
);
Console.WriteLine($"Message ID: \{submitResp.MessageID\}");
\}
await client.DisconnectAsync();
\}
\}
Install the gem:
gem install ruby-smpp
Connect and send:
require 'ruby-smpp'
# Create connection
gateway = SMPP.connect(
host: 'smpp.agiletelecom.com',
port: 2776,
ssl: true
)
# Bind as Transceiver
gateway.bind_transceiver(
system_id: 'YOUR_USERNAME',
password: 'YOUR_PASSWORD'
) do |pdu|
if pdu.command_status == 0
puts "Bound successfully"
# Send SMS
gateway.send_message(
source_addr: '12345',
destination_addr: '+391234567890',
short_message: 'Hello World!'
) do |resp|
puts "Message ID: #\{resp.message_id\}"
end
end
end
gateway.close
## Handling Errors
When your connection fails or a message doesn't submit, check these error codes to understand what happened and how to fix it.
### Bind & Authentication Errors
| Error Code | Hex | Description |
|-----------|-----|-------------|
| 4 | 0x04 | **Bind not yet completed** — Send bind request first before submitting messages |
| 5 | 0x05 | **User already bound** — You've reached your connection limit (default: 4). Close unused connections or request higher limits |
| 13 | 0x0E | **Invalid credentials** — Check your SYSTEM ID and password |
| 8 | 0x08 | **System error** — Try again after a few seconds |
| 10 | 0x0A | **Invalid source address** — The sender ID must be numeric (max 16 chars) or alphanumeric (max 11 chars). See sender ID rules below |
| 11 | 0x0B | **Invalid destination address** — Use a valid international phone number (e.g., +391234567890) |
| 21 | 0x15 | **Invalid SYSTEM_TYPE** — Leave this field empty |
| 88 | 0x58 | **Throughput limit exceeded** — You're sending too fast. Slow down and implement backoff retry logic |
| 259 | 0x103 | **Insufficient credit** — Your account balance is too low. Top up your credit |
| 1026 | 0x402 | **Sender ID not allowed** — The sender ID is blocked or not approved. Check your registered aliases or request approval |
| 1 | 0x01 | **Invalid message length** — Message is too long. SMS limited to 160 characters (or 70 for unicode) |
| 69 | 0x45 | **Text decoding failed** — Message encoding issue. Check character set and try again |
| 97 | 0x61 | **Invalid scheduled delivery time** — Format must be YYYYMMDDHHMMSS |
| 98 | 0x62 | **Invalid validity period** — Expiry time must be after current time |
## Delivery Status Error Codes
When a message is delivered or fails, you'll receive a DELIVER_SM notification with a status code. These codes indicate what happened on the carrier's network after you submitted your message.
### Routing & ESME Errors
| Code | Category | Description |
|------|----------|-------------|
| 101 | ROUTING ERROR | No route found |
| 102 | ESME ERROR | ESME error |
### HLR Errors
| Code | Category | Description |
|------|----------|-------------|
| 201 | HLR ERROR | Absent subscriber |
| 202 | HLR ERROR | Facility not supported |
| 203 | HLR ERROR | System failure |
| 204 | HLR ERROR | Unexpected data value |
| 205 | HLR ERROR | Data missing |
| 206 | HLR ERROR | Memory capacity exceeded |
| 207 | HLR ERROR | Mobile subscriber not reachable |
| 208 | HLR ERROR | Reject |
| 209 | HLR ERROR | Local Cancel |
| 210 | HLR ERROR | Abort |
| 211 | HLR ERROR | Exception (internal) |
| 212 | HLR ERROR | Unknown subscriber |
| 213 | HLR ERROR | Teleservice not provisioned |
| 214 | HLR ERROR | Call barred |
| 215 | HLR ERROR | Roaming not allowed |
| 216 | HLR ERROR | Bearer service not provisioned |
| 350 | HLR ERROR | Unknown error |
### MSC Errors
| Code | Category | Description |
|------|----------|-------------|
| 401 | MSC ERROR | Unidentified subscriber |
| 402 | MSC ERROR | Absent subscriber, IMSI detached |
| 403 | MSC ERROR | Absent subscriber, no page response |
| 404 | MSC ERROR | Subscriber busy for MT SMS |
| 405 | MSC ERROR | Facility not supported |
| 406 | MSC ERROR | Illegal subscriber |
| 407 | MSC ERROR | Illegal equipment |
| 408 | MSC ERROR | System failure |
| 409 | MSC ERROR | Unexpected data value |
| 410 | MSC ERROR | Data missing |
| 411 | MSC ERROR | Memory capacity exceeded |
| 412 | MSC ERROR | Equipment protocol error |
| 413 | MSC ERROR | Device not short message equipped |
| 414 | MSC ERROR | Reject |
| 415 | MSC ERROR | Local Cancel |
| 416 | MSC ERROR | Abort |
| 417 | MSC ERROR | Exception (internal) |
| 418 | MSC ERROR | SM delivery failure |
| 419 | MSC ERROR | Temporary mobile error |
| 550 | MSC ERROR | Unknown error |
### SMSC Errors
| Code | Category | Description |
|------|----------|-------------|
| 615 | SMSC ERROR | Network Timeout |
| 616 | SMSC ERROR | Routing error |
| 617 | SMSC ERROR | Mobile device error |
| 618 | SMSC ERROR | Invalid text or charset |
### Other Errors
| Code | Category | Description |
|------|----------|-------------|
| 806 | SCREENING ERROR | Destination blocked |
| 901 | ESME EXTERNAL ERROR | Invalid destination address |
| 902 | ESME EXTERNAL ERROR | Invalid source address |
| 903 | ESME EXTERNAL ERROR | Message queue full |
| 904 | ESME EXTERNAL ERROR | Invalid parameter |
## Sender ID Requirements
Choose your sender ID based on the message type and recipient expectation.
| Type | Max Length | Example | When to Use |
|------|-----------|---------|------------|
| **Numeric (International)** | 16 characters | +391234567890123 | Transactional SMS from services (receipts, alerts) |
| **Alphanumeric** | 11 characters | COMPANY ABC | Marketing, branding (must be pre-approved) |
Numeric sender IDs are displayed as-is. Alphanumeric IDs require approval—contact support with your desired sender name.
*Limits comply with AGCOM regulation no. 42/13/CIR (effective October 16, 2013).*
## Message Tracking
### Message IDs in Notifications
The message_id returned in SUBMIT_SM_RESP (your receipt of the submit) and used in DELIVER_SM notifications uses a UUID format:
AGILE-XXXXXXXX-XXXXXX-XXXXXX-XXXXXX
Store this ID to match delivery reports back to your outbound messages. If your system can't handle UUIDs, contact support to request an alternative format.
### Delivery Timestamps
Delivery notifications include the timestamp when the message was delivered to the handset, in this format:
yyMMddHHmmss
For example: `260408143052` = April 8, 2026 at 14:30:52 UTC. Note that the year is 2-digit.
## Security: IP Binding
By default, the SMPP server accepts connections from any IP address. For higher security, you can restrict access to specific IPs or subnets—useful for production environments where you control the originating server.
:::warning[Request IP Binding]
Contact support to whitelist your IP addresses. You'll need to provide exact IPs (e.g., 203.0.113.42) or CIDR ranges (e.g., 203.0.113.0/24).
:::
## Common Setup Questions
**Q: Should I use TX, RX, or TRX?**
Use TRX unless you have a reason to separate send and receive traffic. If you need high throughput, consider multiple TRX connections instead of separate TX/RX pairs.
**Q: How do I handle delivery reports?**
Listen on your RX or TRX connection for DELIVER_SM PDUs. Each contains the message_id you sent, the destination number, and a final delivery status.
**Q: What's the throughput limit?**
Standard accounts allow up to 100 messages per second per connection. If you exceed this, you'll receive error code 88. Contact support for higher limits.
## What's next?
- [Inbound SMS](inbound-sms) – Receive SMS on your dedicated number
- [Credit Check](credit-check) – Monitor your account balance
- [MNP Lookup](mnp) – Query mobile number information