Dedicated SMPP
Home Dedicated SMPP Software
Use own personal SMPP server/client
for receiving and sending SMS via SMPP v.3.3, v3.4, v5.0 protocols
Software Installation
First, in your account, go to the
«Dedicated SMPP»
section and add New IP Address of the VPS/VDS/Cloud server where you will be installing the software. To do this, click the «GET A FREE TRIAL VERSION» or «ADDITIONAL IP» button.
Then install the following required dependencies on your VPS/VDS/Cloud server: MySQL (or Percona, MariaDB) and NSQ. Please install these dependencies before proceeding to the next steps.
Download the software from the link «SMPP Software (Latest Version)» and unzip it.
In the archive, you will find 4 files:
smpp-linux-amd64 - software for Linux OS with AMD architecture;
smpp-linux-arm64 - software for Linux OS with ARM architecture;
smpp-macos-arm64 - software for Mac OS with ARM architecture (Apple silicon);
smpp.conf - configuration file for the SMPP software must be located in the same directory as selected executable binary file.
In the «smpp.conf» file, be sure to specify the following settings:
«MYSQL_DBNAME», «MYSQL_USERNAME», «MYSQL_PASSWORD» and «HTTP_ADMIN_PASSWORD». Other settings can also be specified as needed. For more details, refer to the section: Description of the «smpp.conf»
In the console, make the file executable with "chmod +x /your-path/dedicated-smpp/smpp-linux-amd64" and run with "./your-path/dedicated-smpp/smpp-linux-amd64".
*Depending on your VPS/VDS/Cloud server's/local computer operating system, run the file «smpp-linux-amd64», «smpp-linux-arm64» or «smpp-macos-arm64».
*We recommend running the executable file using systemd. For detailed instructions, see the «Running the SMPP Server/Client with Systemd».
**Interaction with the SMPP server/client occurs through NSQ channels. See the diagram below for more details.
Diagram of your application's interaction with the SMPP software

Description of the «smpp.conf»
In the «smpp.conf» file, the configuration settings for SMPP server/client software. This file must be located in the same directory as the software itself (the executable file).
MYSQL_HOST - (required) Hostname or IP of MySQL server;
MYSQL_PORT - (required) Port number for MySQL server (default 3306);
MYSQL_DBNAME - (required) Name of the MySQL database;
MYSQL_USERNAME - (required) Username for MySQL authentication;
MYSQL_PASSWORD - (optional) Password for MySQL authentication;
SMPP_PORT - (required) Port number for SMPP Server;
SMPP_SSL_PORT - (required) Port number for SMPP Server over SSL (Used only if SMPP_SSL_CERT_PATH and SMPP_SSL_KEY_PATH are specified);
SMPP_SSL_CERT_PATH - (optional) Full path to the SSL certificate file (e.g., .pem, .crt);
SMPP_SSL_KEY_PATH - (optional) Full path to the SSL key file (e.g., .key);
SMPP_SERVER_LOGS_PATH - (required) Path to the folder where SMPP-server PDU logs will be stored;
SMPP_CLIENT_LOGS_PATH - (required) Path to the folder where SMPP-client PDU logs will be stored;
HTTP_PORT - (required) Port number for HTTP server. For example, if the port is 8010, the admin access will be at http://your_server_IP:8010;
HTTP_ADMIN_USERNAME - (required) Username for HTTP admin access;
HTTP_ADMIN_PASSWORD - (required) Password for HTTP admin access (Create your own strong password);
HTTP_API_TOKEN - (required) Randomly generated string, an additional security token for calling the HTTP API (passed in the «X-Api-Token» header for all requests). It is recommended to change this token periodically;
NSQ_SMS_OUT_HOST - (required) Host or IP address of the VPS/VDS/Cloud server where NSQ is installed (default: localhost) for the queue used before sending SMS through the SMPP-client;
NSQ_SMS_OUT_PORT - (required) Port for outbound SMS NSQ messages;
NSQ_SMS_OUT_TOPIC - (required) Topic for outbound SMS NSQ messages;
NSQ_DLR_IN_HOST - (required) Host or IP address of the VPS/VDS/Cloud server where NSQ is installed (default: localhost) for queue of inbound DLR through the SMPP-client;
NSQ_DLR_IN_PORT - (required) Port for inbound DLR NSQ messages;
NSQ_DLR_IN_TOPIC - (required) Topic for inbound DLR NSQ messages.
NSQ_SMS_IN_HOST - (required) Host or IP address of the VPS/VDS/Cloud server where NSQ is installed (default: localhost) for queue of inbound SMS through the SMPP-server;
NSQ_SMS_IN_PORT - (required) Port for inbound SMS NSQ messages;
NSQ_SMS_IN_TOPIC - (required) Topic for inbound SMS NSQ messages;
NSQ_DLR_OUT_HOST - (required) Host or IP address of the VPS/VDS/Cloud server where NSQ is installed (default: localhost) for the queue used before sending DLR through the SMPP-server;
NSQ_DLR_OUT_PORT - (required) Port for outbound DLR NSQ messages;
NSQ_DLR_OUT_TOPIC - (required) Topic for outbound DLR NSQ messages.
*To understand how the software interacts with the NSQ queue and your application, see the Diagram of your application's interaction with the SMPP software
[Unit]
Description="SMPP Service"
[Service]
EnvironmentFile=/home/dedicated-smpp/smpp.conf
ExecStart=/home/dedicated-smpp/smpp-linux-amd64
Restart=on-failure
KillSignal=SIGTERM
Type=simple
[Install]
WantedBy=multi-user.target
Running the Software with Systemd
Create the file "/etc/systemd/system/smpp.service" on your VPS/VDS/Cloud server and copy the example content into it. Specify the full path to your «smpp.conf» file for the EnvironmentFile parameter, and for ExecStart, provide the full path to the executable file that corresponds to your OS and server architecture.
After creating the "/etc/systemd/system/smpp.service" file, you need to enable it by running the command systemctl enable smpp once.
Now the software will automatically start when the system boots up and restart in case of an error.
Use the following commands to manage the software:
service smpp start - Starts the SMPP server/client (if it is stopped);
service smpp restart - Restarts the SMPP server/client. According to the SMPP protocol «unbind» command are sent to all active SMSC/ESME. The restart may take up to 10 seconds;
service smpp stop - Stops the SMPP server/client;
journalctl -u smpp.service -f - View system logs in real time;
journalctl -u smpp.service --since "2024-09-17" --until "2024-09-18" - Display system logs for the period from 2024-09-17 to 2024-09-18;
journalctl -u smpp.service | grep "11.22.33.44" - Search for the string «11.22.33.44» in the system logs.
import requests
response = requests.post('http://your-server-ip:HTTP_PORT/api/gateways',
headers={
'X-API-Key': 'HTTP_API_TOKEN'
},
json={
'smsc_id': 'mysmsc-id-example',
'host': 'smpp.mygateway.com',
'port': 2775,
'username': 'myusername',
'password': 'mypassw',
'enq_interval': 30,
'src_autodetect': True,
'dst_autodetect': True,
'tx_binds': 0,
'rx_binds': 0,
'trx_binds': 1,
'smpp_version': '3.4',
'is_gsm7': True,
'is_secure': False,
'enabled': True,
})
print(response.json())
$ch = curl_init('http://your-server-ip:HTTP_PORT/api/gateways');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',
'X-API-Key: HTTP_API_TOKEN'
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
'smsc_id' => 'mysmsc-id-example',
'host' => 'smpp.mygateway.com',
'port' => 2775,
'username' => 'myusername',
'password' => 'mypassw',
'enq_interval' => 30,
'src_autodetect' => true,
'dst_autodetect' => true,
'tx_binds' => 0,
'rx_binds' => 0,
'trx_binds' => 1,
'smpp_version' => '3.4',
'is_gsm7' => true,
'is_secure' => false,
'enabled' => true,
]));
$response = curl_exec($ch);
curl_close($ch);
echo $response;
fetch('http://your-server-ip:HTTP_PORT/api/gateways', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': 'HTTP_API_TOKEN'
},
body: JSON.stringify({
smsc_id: 'mysmsc-id-example',
host: 'smpp.mygateway.com',
port: 2775,
username: 'myusername',
password: 'mypassw',
enq_interval: 30,
src_autodetect: true,
dst_autodetect: true,
tx_binds: 0,
rx_binds: 0,
trx_binds: 1,
smpp_version: '3.4',
is_gsm7: true,
is_secure: false,
enabled: true
})
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
)
func main() {
url := "http://your-server-ip:HTTP_PORT/api/gateways"
payload := map[string]interface{}{
"smsc_id": "mysmsc-id-example",
"host": "smpp.mygateway.com",
"port": 2775,
"username": "myusername",
"password": "mypassw",
"enq_interval": 30,
"src_autodetect": true,
"dst_autodetect": true,
"tx_binds": 0,
"rx_binds": 0,
"trx_binds": 1,
"smpp_version": "3.4",
"is_gsm7": true,
"is_secure": false,
"enabled": true,
}
jsonData, err := json.Marshal(payload)
if err != nil {
fmt.Println("Error marshaling JSON:", err)
return
}
req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData))
if err != nil {
fmt.Println("Error creating request:", err)
return
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-API-Key", "HTTP_API_TOKEN")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
fmt.Println("Error making request:", err)
return
}
defer resp.Body.Close()
var result map[string]interface{}
json.NewDecoder(resp.Body).Decode(&result)
fmt.Println(result)
}
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
public class Main {
public static void main(String[] args) throws Exception {
HttpClient client = HttpClient.newHttpClient();
JsonObject json = new JsonObject();
json.addProperty("smsc_id", "mysmsc-id-example");
json.addProperty("host", "smpp.mygateway.com");
json.addProperty("port", 2775);
json.addProperty("username", "myusername");
json.addProperty("password", "mypassw");
json.addProperty("enq_interval", 30);
json.addProperty("src_autodetect", true);
json.addProperty("dst_autodetect", true);
json.addProperty("tx_binds", 0);
json.addProperty("rx_binds", 0);
json.addProperty("trx_binds", 1);
json.addProperty("smpp_version", "3.4");
json.addProperty("is_gsm7", true);
json.addProperty("is_secure", false);
json.addProperty("enabled", true);
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("http://your-server-ip:HTTP_PORT/api/gateways"))
.header("Content-Type", "application/json")
.header("X-API-Key", "HTTP_API_TOKEN")
.POST(HttpRequest.BodyPublishers.ofString(json.toString()))
.build();
HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
}
}
OK
Manage SMSC

You can manage your SMS providers (SMSC) through your admin panel (http://your-server-ip:HTTP_PORT) or via the HTTP REST API.
To add an SMSC, perform a POST request to endpoint http://your-server-ip:HTTP_PORT/api/gateways with the following JSON parameters:
smsc_id - required, string, max. 100 characters. A unique string ID for the SMS provider. Example: «infobip-direct»;
host - required, string, max. 100 characters. IP address or domain of the SMSC for SMPP connection;
port - required, number, max. 65536. Port for SMPP connection;
username - required, string, max. 16 characters. Username for connecting to the SMSC;
password - optional, string, max. 9 characters. Password for connecting to the SMSC;
system_type - optional, string, max. 13 characters. System type used in some SMSCs for classifying the type of binding;
enq_interval - required, number, max. 120. Interval for sending the enquire_link command, recommended value is 30;
src_autodetect - required, boolean value. Indicates to the SMPP client that source_addr_ton and source_addr_npi should be determined automatically;
source_ton - optional, number, max. 6. If src_autodetect is turned off, this value will be used for source_addr_ton for all SMS;
source_npi - optional, number, max. 18. If src_autodetect is turned off, this value will be used for source_addr_npi for all SMS;
dst_autodetect - required, boolean value. Indicates to the SMPP client that dest_addr_ton and dest_addr_npi should be determined automatically;
dest_ton - optional, number, max. 6. If dst_autodetect is turned off, this value will be used for dest_addr_ton for all SMS;
dest_npi - optional, number, max. 18. If dst_autodetect is turned off, this value will be used for dest_addr_npi for all SMS;
tx_binds - required, number, max. 10. Indicates to the SMPP client how many concurrent connections (binds) to open in Transmitter mode (for sending submit_sm only);
rx_binds - required, number, max. 10. Indicates to the SMPP client how many concurrent connections (binds) to open in Receiver mode (for receiving deliver_sm only);
trx_binds - required, number, max. 10. Indicates to the SMPP client how many concurrent connections (binds) to open in Transceiver mode (for sending submit_sm and receiving deliver_sm);
smpp_version - required, string, max. 3 characters. The version of the SMPP protocol supported by the SMSC. Can be «3.3», «3,4», «5.0»;
is_gsm7 - required, boolean value. Indicates to the SMPP client that the SMSC supports 7-bit encoding and a maximum SMS length of 160 characters instead of 140;
is_secure - required, boolean value. Indicates to the SMPP client that the connection to the SMSC should be made using a secure SSL/TLS protocol;
enabled - required, boolean value. Active or inactive connection. When deactivated, an «unbind» command will be sent immediately to the SMSC, and the connection will be closed.
To edit an SMSC, perform a PUT request to the endpoint http://your-server-ip:HTTP_PORT/api/gateways with JSON parameters similar to those used for creation.
When editing, the SMPP client will automatically perform a secure reconnection with the new parameters.
To delete an SMSC, perform a DELETE request to the endpoint http://your-server-ip:HTTP_PORT/api/gateways/:id
import requests
response = requests.post('http://your-server-ip:HTTP_PORT/api/clients',
headers={
'X-API-Key': 'HTTP_API_TOKEN'
},
json={
'username': 'myclient1',
'password': 'qwerty123',
'allow_ip': '11.22.33.44,55.66.77.88',
'throughput': 0,
'binds': 3,
'enabled': True,
})
print(response.json())
$ch = curl_init('http://your-server-ip:HTTP_PORT/api/clients');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',
'X-API-Key: HTTP_API_TOKEN'
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
'username' => 'myclient1',
'password' => 'qwerty123',
'allow_ip' => '11.22.33.44,55.66.77.88',
'throughput' => 0,
'binds' => 3,
'enabled' => true,
]));
$response = curl_exec($ch);
curl_close($ch);
echo $response;
fetch('http://your-server-ip:HTTP_PORT/api/clients', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': 'HTTP_API_TOKEN'
},
body: JSON.stringify({
username: 'myclient1',
password: 'qwerty123',
allow_ip: '11.22.33.44,55.66.77.88',
throughput: 0,
binds: 3,
enabled: true
})
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
)
func main() {
url := "http://your-server-ip:HTTP_PORT/api/clients"
payload := map[string]interface{}{
"username": "myclient1",
"password": "qwerty123",
"allow_ip": "11.22.33.44,55.66.77.88",
"throughput": 0,
"binds": 3,
"enabled": true,
}
jsonData, err := json.Marshal(payload)
if err != nil {
fmt.Println("Error marshaling JSON:", err)
return
}
req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData))
if err != nil {
fmt.Println("Error creating request:", err)
return
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-API-Key", "HTTP_API_TOKEN")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
fmt.Println("Error making request:", err)
return
}
defer resp.Body.Close()
var result map[string]interface{}
json.NewDecoder(resp.Body).Decode(&result)
fmt.Println(result)
}
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
public class Main {
public static void main(String[] args) throws Exception {
HttpClient client = HttpClient.newHttpClient();
JsonObject json = new JsonObject();
json.addProperty("username", "myclient1");
json.addProperty("password", "qwerty123");
json.addProperty("allow_ip", "11.22.33.44,55.66.77.88");
json.addProperty("throughput", 0);
json.addProperty("binds", 3);
json.addProperty("enabled", true);
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("http://your-server-ip:HTTP_PORT/api/clients"))
.header("Content-Type", "application/json")
.header("X-API-Key", "HTTP_API_TOKEN")
.POST(HttpRequest.BodyPublishers.ofString(json.toString()))
.build();
HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
}
}
OK
Manage ESME

You can manage your SMPP profiles (ESME) through your admin panel (http://your-server-ip:HTTP_PORT) or via the HTTP REST API.
To add an ESME, perform a POST request to endpoint http://your-server-ip:HTTP_PORT/api/clients with the following JSON parameters:
username - required, string, max. 16 characters. Client login for SMPP connection;
password - optional, string, max. 9 characters. Client password for SMPP connection;
allow_ip - optional, string, max. 255 characters. Comma-separated IPv4 addresses from which the client can connect to the SMPP server;
throughput - optional, number, max. 3000. SMS throughput per minute for a single connection. For example, 60 means the client will not be able to send more than 1 SMS per second. By default, 0 means no limits (~50 SMS/sec.);
binds - required, number, max. 30. Specifies to the SMPP Server how many simultaneous connections (binds) the client can open;
enabled - required, boolean value. Active or inactive connection. When deactivated, an «unbind» command will be sent immediately to the ESME, and the connection will be closed.
To edit an ESME, perform a PUT request to the endpoint http://your-server-ip:HTTP_PORT/api/clients with JSON parameters similar to those used for creation.
When editing, the SMPP server will automatically perform a secure reconnection with the new parameters.
To delete an ESME, perform a DELETE request to the endpoint http://your-server-ip:HTTP_PORT/api/clients/:id
import json
import ansq
messages = []
messages.append(json.dumps({
'smsc_id': 'mysmsc-id-example',
'msg_id': '074b8a96-f983-4b0b-8079-4d4c8502ff02',
'sender': 'Mysender',
'receiver': '390991234567',
'message': 'I am test SMS message',
}).encode())
producer = await ansq.create_writer([f'{NSQ_SMS_OUT_HOST}:{NSQ_SMS_OUT_PORT}'])
producer.mpub(NSQ_SMS_OUT_TOPIC, nsq_messages)
$message = json_encode([
'smsc_id' => 'mysmsc-id-example',
'msg_id' => '074b8a96-f983-4b0b-8079-4d4c8502ff02',
'sender' => 'Mysender',
'receiver' => '390991234567',
'message' => 'I am test SMS message',
]);
$nsq = fsockopen(NSQ_SMS_OUT_HOST, NSQ_SMS_OUT_PORT, $errno, $errstr, 5);
if (!$nsq) {
exit("Connection error: $errstr ($errno)\n");
}
$body = implode("\n", [$message]);
$payload = sprintf("MPUB %s\n%s%s", NSQ_SMS_OUT_TOPIC, strlen($body), "\n" . $body);
fwrite($nsq, $payload);
fclose($nsq);
const nsq = require('nsqjs');
const message = JSON.stringify({
'smsc_id': 'mysmsc-id-example',
'msg_id': '074b8a96-f983-4b0b-8079-4d4c8502ff02',
'sender': 'Mysender',
'receiver': '390991234567',
'message': 'I am test SMS message'
});
const writer = new nsq.Writer(NSQ_SMS_OUT_HOST, NSQ_SMS_OUT_PORT);
writer.connect();
writer.on('ready', () => {
writer.publish(NSQ_SMS_OUT_TOPIC, message, err => {
if (err) {
console.error('Submit error: ', err);
} else {
console.log('Sent to NSQ!');
}
writer.close();
});
});
writer.on('closed', () => {
console.log('Closed!');
});
package main
import (
"encoding/json"
"fmt"
"log"
"github.com/nsqio/go-nsq"
)
type SMSMessage struct {
SMSCID string `json:"smsc_id"`
MsgID string `json:"msg_id"`
Sender string `json:"sender"`
Receiver string `json:"receiver"`
Message string `json:"message"`
}
func main() {
message := SMSMessage{
SMSCID: "mysmsc-id-example",
MsgID: "074b8a96-f983-4b0b-8079-4d4c8502ff02",
Sender: "Mysender",
Receiver: "390991234567",
Message: "I am test SMS message",
}
messageJSON, err := json.Marshal(message)
if err != nil {
log.Fatalf("JSON Encoding error: %v", err)
}
config := nsq.NewConfig()
nsqAddress := fmt.Sprintf("%s:%s", NSQ_SMS_OUT_HOST, NSQ_SMS_OUT_PORT)
producer, err := nsq.NewProducer(nsqAddress, config)
if err != nil {
log.Fatalf("Can't init producer: %v", err)
}
err = producer.Publish(NSQ_SMS_OUT_TOPIC, messageJSON)
if err != nil {
log.Fatalf("Can't submit the message: %v", err)
}
fmt.Println("Sent to NSQ!")
producer.Stop()
}
import com.github.brainlag.nsq.NSQConfig;
import com.github.brainlag.nsq.NSQProducer;
import java.nio.charset.StandardCharsets;
public class SendSMS {
public static void main(String[] args) {
String message = "{\n" +
" \"smsc_id\": \"mysmsc-id-example\",\n" +
" \"msg_id\": \"074b8a96-f983-4b0b-8079-4d4c8502ff02\",\n" +
" \"sender\": \"Mysender\",\n" +
" \"receiver\": \"390991234567\",\n" +
" \"message\": \"I am test SMS message\"\n" +
"}";
try {
sendToNSQ(message);
} catch (Exception e) {
e.printStackTrace();
}
}
private static void sendToNSQ(String message) throws Exception {
NSQConfig config = new NSQConfig();
NSQProducer producer = new NSQProducer();
producer.addAddress(NSQ_SMS_OUT_HOST, NSQ_SMS_OUT_PORT);
producer.start();
try {
producer.produce(NSQ_SMS_OUT_TOPIC, message.getBytes(StandardCharsets.UTF_8));
System.out.println("Sent to NSQ!");
} finally {
producer.shutdown();
}
}
}
{
"smsc_id":"mysmsc-id-example",
"msg_id":"074b8a96-f983-4b0b-8079-4d4c8502ff02",
"sender":"Mysender",
"receiver":"390991234567",
"message":"I am test SMS message"
}
Example JSON for sending Multipart SMS:
[{
"smsc_id":"mysmsc-id-example",
"msg_id":"074b8a96-f983-4b0b-8079-4d4c8502ff02",
"sender":"Mysender",
"receiver":"390991234567",
"message":"Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 ",
"data_coding": 0,
"part_number": 1,
"total_parts": 2,
"reference": 123
},
{
"smsc_id":"mysmsc-id-example",
"msg_id":"3c02d014-512a-40ff-ad10-525308235cd7",
"sender":"Mysender",
"receiver":"390991234567",
"message":"years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words",
"data_coding": 0,
"part_number": 2,
"total_parts": 2,
"reference": 123
}]
Sending SMS
To send SMS through the SMPP Client - use the "NSQ" channel (topic) «NSQ_SMS_OUT_TOPIC».
Diagram 1.1: Submit the SMS to NSQ
Add a record to your NSQ topic «NSQ_SMS_OUT_TOPIC» with the JSON object containing the following fields:
smsc_id - (required, string, max. 100 characters) is a unique string identifier for the SMSC through which the SMS should be sent;
msg_id - (required, string, max. 255 characters) is a unique string identifier for the SMS in your application. When receiving a Delivery Receipt (DLR), this msg_id will also be sent to NSQ channel «NSQ_DLR_IN» for SMS identification and status updating in your DB;
sender - (required, string, max. 25 characters) sender name for the SMS;
receiver - (required, string, max. 15 characters) recipient's phone number;
message - (required, string, max. 140 bytes or 160 characters for 7-bit encoding, 140 for 8-bit encoding or 70 for 16-bit encoding) text of the SMS message. If you are forwarding only a part of the SMS with the specified «part_number», «total_parts» and «reference», the maximum is 134 bytes (153 characters for 7-bit encoding, 134 for 8-bit encoding, or 67 for 16-bit encoding).
*The following fields are mandatory if you need to send a multipart SMS message:
data_coding - (optional, numeric, max. 8) text encoding for SMS and it can range from 0 to 8:
· 0 - The "default" encoding on the provider's side can be «GSM-7» if the SMSC parameters indicate «Is GSM7 (7-bit) encoding supported (is_gsm7=true)» which allows sending a maximum of 160 characters. Alternatively, 8-bit encoding permits sending a maximum of 140 characters in the SMS.;
· 1 - ASCII (ISO-8859-9) (8 bit);
· 2 - Octet unspecified (8-bit);
· 3 - Latin-1 (ISO-8859-1) (8 bit);
· 4 - Octet unspecified (8-bit);
· 5 - JIS (X 0208-1990) (16 bit);
· 6 - Cyrillic (ISO-8859-5) (8 bit);
· 7 - Latin/Hebrew (ISO-8859-8) (8 bit);
· 8 - UCS2/UTF-16 (ISO/IEC-10646) (16 bit);
part_number - (optional, numeric, max. 255) the sequence number of the segment in a multipart SMS message.;
total_parts - (optional, numeric, max. 255) the total number of segments in a multipart SMS message;
reference - (optional, numeric, max. 65535) the reference parameter for a multipart SMS is a unique identifier assigned to the entire message. It links all segments together, allowing the recipient's device to accurately reassemble the complete message from its parts. Each segment will carry the same reference number to ensure proper identification and processing.
import json
import ansq
messages = []
messages.append(json.dumps({
'client_id': 1234,
'msg_id': '074b8a96-f983-4b0b-8079-4d4c8502ff02',
'status': 'delivered',
'err_code': 0,
}).encode())
producer = await ansq.create_writer([f'{NSQ_DLR_OUT_HOST}:{NSQ_DLR_OUT_PORT}'])
producer.mpub(NSQ_DLR_OUT_TOPIC, nsq_messages)
$message = json_encode([
'client_id' => 1234,
'msg_id' => '074b8a96-f983-4b0b-8079-4d4c8502ff02',
'status' => 'delivered',
'err_code' => 0,
]);
$nsq = fsockopen(NSQ_DLR_OUT_HOST, NSQ_DLR_OUT_PORT, $errno, $errstr, 5);
if (!$nsq) {
exit("Connection error: $errstr ($errno)\n");
}
$body = implode("\n", [$message]);
$payload = sprintf("MPUB %s\n%s%s", NSQ_DLR_OUT_TOPIC, strlen($body), "\n" . $body);
fwrite($nsq, $payload);
fclose($nsq);
const nsq = require('nsqjs');
const message = JSON.stringify({
'client_id': 1234,
'msg_id': '074b8a96-f983-4b0b-8079-4d4c8502ff02',
'status': 'delivered',
'err_code': 0
});
const writer = new nsq.Writer(NSQ_DLR_OUT_HOST, NSQ_DLR_OUT_PORT);
writer.connect();
writer.on('ready', () => {
writer.publish(NSQ_DLR_OUT_TOPIC, message, err => {
if (err) {
console.error('Submit error: ', err);
} else {
console.log('Sent to NSQ!');
}
writer.close();
});
});
writer.on('closed', () => {
console.log('Closed!');
});
package main
import (
"encoding/json"
"fmt"
"log"
"github.com/nsqio/go-nsq"
)
type DLRMessage struct {
ClientID string `json:"client_id"`
MsgID string `json:"msg_id"`
Status string `json:"status"`
ErrCode string `json:"err_code"`
}
func main() {
message := DLRMessage{
ClientID: 1234,
MsgID: "074b8a96-f983-4b0b-8079-4d4c8502ff02",
Status: "delivered",
ErrCode: 0,
}
messageJSON, err := json.Marshal(message)
if err != nil {
log.Fatalf("JSON Encoding error: %v", err)
}
config := nsq.NewConfig()
nsqAddress := fmt.Sprintf("%s:%s", NSQ_DLR_OUT_HOST, NSQ_DLR_OUT_PORT)
producer, err := nsq.NewProducer(nsqAddress, config)
if err != nil {
log.Fatalf("Can't init producer: %v", err)
}
err = producer.Publish(NSQ_DLR_OUT_TOPIC, messageJSON)
if err != nil {
log.Fatalf("Can't submit the message: %v", err)
}
fmt.Println("Sent to NSQ!")
producer.Stop()
}
import com.github.brainlag.nsq.NSQConfig;
import com.github.brainlag.nsq.NSQProducer;
import java.nio.charset.StandardCharsets;
public class SendDLR {
public static void main(String[] args) {
String message = "{\n" +
" \"client_id\": 1234,\n" +
" \"msg_id\": \"074b8a96-f983-4b0b-8079-4d4c8502ff02\",\n" +
" \"status\": \"delivered\",\n" +
" \"err_code\": 0,\n" +
"}";
try {
sendToNSQ(message);
} catch (Exception e) {
e.printStackTrace();
}
}
private static void sendToNSQ(String message) throws Exception {
NSQConfig config = new NSQConfig();
NSQProducer producer = new NSQProducer();
producer.addAddress(NSQ_DLR_OUT_HOST, NSQ_DLR_OUT_PORT);
producer.start();
try {
producer.produce(NSQ_DLR_OUT_TOPIC, message.getBytes(StandardCharsets.UTF_8));
System.out.println("Sent to NSQ!");
} finally {
producer.shutdown();
}
}
}
{
"client_id":1234,
"msg_id":"074b8a96-f983-4b0b-8079-4d4c8502ff02",
"status":"delivered",
"err_code":0
}
Sending DLR
To send DLR through the SMPP Server, use the "NSQ" channel (topic) «NSQ_DLR_OUT_TOPIC».
Diagram 2.4: Submit the DLR to NSQ
Add a record to your NSQ topic «NSQ_SMS_OUT_TOPIC» with the JSON object containing the following fields:
client_id - (required, numeric) unique identifier for the SMPP-profile (ESME). It is used to identify the ESME through which the DLR (delivery_sm) needs to be sent;
msg_id - (required, string, max. 255 characters) unique ID for the SMS/segment in response to which a delivery report (DLR) needs to be sent;
status - (required, string, max. 11 characters) final status of the SMS/segment. Possible values: «delivered, rejected, undelivered, expired, unknown»;
err_code - (optional, number, max. 255) the SMPP error code describes the reason for status «rejected, undelivered, expired, unknown». The full list is available at the link SMPP error codes
import ansq
import json
reader = await ansq.create_reader(
topic=NSQ_SMS_IN_TOPIC,
channel='data',
nsqd_tcp_addresses=[f'{NSQ_SMS_IN_HOST}:{NSQ_SMS_IN_PORT}'],
)
async for msg in reader.messages():
sms = json.loads(msg.body)
print(sms)
$readerUrl = "http://NSQ_SMS_IN_HOST:NSQ_SMS_IN_PORT/channel/lookup?topic=NSQ_SMS_IN_TOPIC&channel=data";
while (true) {
$response = file_get_contents($readerUrl);
$messages = json_decode($response, true);
foreach ($messages as $msg) {
$sms = json_decode($msg['body'], true);
print_r($sms);
}
sleep(1);
}
const nsq = require('nsqjs');
const JSONStream = require('JSONStream');
const reader = new nsq.Reader(NSQ_SMS_IN_TOPIC, 'data', {
nsqdTCPAddresses: `${NSQ_SMS_IN_HOST}:${NSQ_SMS_IN_PORT}`,
});
reader.connect();
reader.on('message', (msg) => {
const messageBody = msg.body.toString();
const sms = JSON.parse(messageBody);
console.log(sms);
msg.finish();
});
reader.on('error', (err) => {
console.error('Error:', err);
});
reader.on('nsqd_connected', (host, port) => {
console.log(`Connected to NSQD at ${host}:${port}`);
});
package main
import (
"encoding/json"
"fmt"
"log"
"os"
"os/signal"
"syscall"
"github.com/nsqio/go-nsq"
)
type MessageHandler struct{}
func (h *MessageHandler) HandleMessage(m *nsq.Message) error {
var sms map[string]interface{}
if err := json.Unmarshal(m.Body, &sms); err != nil {
log.Printf("Error unmarshalling message: %v", err)
return err
}
fmt.Printf("Received SMS: %v\n", sms)
return nil
}
func main() {
config := nsq.NewConfig()
address := fmt.Sprintf("%s:%d", NSQ_SMS_IN_HOST, NSQ_SMS_IN_PORT)
consumer, err := nsq.NewConsumer(NSQ_SMS_IN_TOPIC, "data", config)
if err != nil {
log.Fatal(err)
}
consumer.AddHandler(&MessageHandler{})
err = consumer.ConnectToNSQD(address)
if err != nil {
log.Fatal(err)
}
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
<-sigChan
consumer.Stop()
}
import com.github.brainlag.nsq.NSQConsumer;
import com.github.brainlag.nsq.NSQMessage;
import com.github.brainlag.nsq.NSQConfig;
import com.github.brainlag.nsq.lookup.DefaultNSQLookup;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.Executors;
import org.json.JSONObject;
public class Main {
public static void main(String[] args) {
DefaultNSQLookup lookup = new DefaultNSQLookup();
lookup.addLookupAddress(NSQ_SMS_IN_HOST, NSQ_SMS_IN_PORT);
NSQConfig config = new NSQConfig();
NSQConsumer consumer = new NSQConsumer(
lookup, NSQ_SMS_IN_TOPIC, "data",
(NSQMessage message) -> {
String messageBody = new String(message.getMessage(), StandardCharsets.UTF_8);
JSONObject sms = new JSONObject(messageBody);
System.out.println("Received SMS: " + sms.toString());
message.finished();
});
consumer.setExecutor(Executors.newFixedThreadPool(2));
consumer.start();
}
}
{
"client_id":1234,
"msg_id":"074b8a96-f983-4b0b-8079-4d4c8502ff02",
"sender":"Mysender",
"receiver":"390991234567",
"service_type":"",
"src_ton":5,
"src_npi":0,
"dst_ton":1,
"dst_npi":1,
"message":"I am test SMS message",
"data_coding":0,
"reference":0,
"total_parts":1,
"part_number":1,
"smsc_time":1721980552913
}
Receiving SMS
To receive SMS via the SMPP Server, use the "NSQ" channel (topic) «NSQ_SMS_IN_TOPIC».
Diagram 2.3: Receiving the SMS from NSQ
client_id - unique numerical identifier for the SMPP profile (ESME). It is used to identify the user who sent the SMS for further message processing in your system;
msg_id - unique string identifier for the SMS assigned by the SMPP Server, which was provided to the SMS provider (SMSC) in the «submit_sm_resp»;
sender - sender's name in the SMS;
receiver - recipient's address of the SMS;
service_type - is used by some ESME to specify the SMS application service;
src_ton - type of number for sender name;
src_npi - numbering plan indicator for sender name;
dst_ton - type of number for receiver;
dst_npi - numbering plan indicator for receiver;
message - the text of the sent SMS. A maximum of 70, 140, or 160 characters, depending on the encoding;
data_coding - numeric value of the SMS text encoding;
· 0 - The "default" encoding on the provider's side can be «GSM-7» if the SMSC parameters indicate «Is GSM7 (7-bit) encoding supported (is_gsm7=true)» which allows sending a maximum of 160 characters. Alternatively, 8-bit encoding permits sending a maximum of 140 characters in the SMS.;
· 1 - ASCII (ISO-8859-9) (8 bit);
· 2 - Octet unspecified (8-bit);
· 3 - Latin-1 (ISO-8859-1) (8 bit);
· 4 - Octet unspecified (8-bit);
· 5 - JIS (X 0208-1990) (16 bit);
· 6 - Cyrillic (ISO-8859-5) (8 bit);
· 7 - Latin/Hebrew (ISO-8859-8) (8 bit);
· 8 - UCS2/UTF-16 (ISO/IEC-10646) (16 bit);
reference - unique number for the multipart SMS used for all parts of the SMS that form the combined message;
total_parts - numeric value of the number of segments in the sent SMS. If the message is split into 3 parts, then «total_parts=3»;
part_number - numeric value of the segment in the sent SMS (from 1 to 255). When splitting the message into segments, «part_number» is unique for each segment within a single SMS (reference);
smsc_time - Unix Timestamp (with milliseconds) when the SMPP Server received the SMS.
import ansq
import json
reader = await ansq.create_reader(
topic=NSQ_DLR_IN_TOPIC,
channel='data',
nsqd_tcp_addresses=[f'{NSQ_DLR_IN_HOST}:{NSQ_DLR_IN_PORT}'],
)
async for msg in reader.messages():
dlr = json.loads(msg.body)
print(dlr)
$readerUrl = "http://NSQ_DLR_IN_HOST:NSQ_DLR_IN_PORT/channel/lookup?topic=NSQ_DLR_IN_TOPIC&channel=data";
while (true) {
$response = file_get_contents($readerUrl);
$messages = json_decode($response, true);
foreach ($messages as $msg) {
$dlr = json_decode($msg['body'], true);
print_r($dlr);
}
sleep(1);
}
const nsq = require('nsqjs');
const JSONStream = require('JSONStream');
const reader = new nsq.Reader(NSQ_DLR_IN_TOPIC, 'data', {
nsqdTCPAddresses: `${NSQ_DLR_IN_HOST}:${NSQ_DLR_IN_PORT}`,
});
reader.connect();
reader.on('message', (msg) => {
const messageBody = msg.body.toString();
const dlr = JSON.parse(messageBody);
console.log(dlr);
msg.finish();
});
reader.on('error', (err) => {
console.error('Error:', err);
});
reader.on('nsqd_connected', (host, port) => {
console.log(`Connected to NSQD at ${host}:${port}`);
});
package main
import (
"encoding/json"
"fmt"
"log"
"os"
"os/signal"
"syscall"
"github.com/nsqio/go-nsq"
)
type MessageHandler struct{}
func (h *MessageHandler) HandleMessage(m *nsq.Message) error {
var dlr map[string]interface{}
if err := json.Unmarshal(m.Body, &dlr); err != nil {
log.Printf("Error unmarshalling message: %v", err)
return err
}
fmt.Printf("Received DLR: %v\n", sms)
return nil
}
func main() {
config := nsq.NewConfig()
address := fmt.Sprintf("%s:%d", NSQ_DLR_IN_HOST, NSQ_DLR_IN_PORT)
consumer, err := nsq.NewConsumer(NSQ_DLR_IN_TOPIC, "data", config)
if err != nil {
log.Fatal(err)
}
consumer.AddHandler(&MessageHandler{})
err = consumer.ConnectToNSQD(address)
if err != nil {
log.Fatal(err)
}
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
<-sigChan
consumer.Stop()
}
import com.github.brainlag.nsq.NSQConsumer;
import com.github.brainlag.nsq.NSQMessage;
import com.github.brainlag.nsq.NSQConfig;
import com.github.brainlag.nsq.lookup.DefaultNSQLookup;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.Executors;
import org.json.JSONObject;
public class Main {
public static void main(String[] args) {
DefaultNSQLookup lookup = new DefaultNSQLookup();
lookup.addLookupAddress(NSQ_DLR_IN_HOST, NSQ_DLR_IN_PORT);
NSQConfig config = new NSQConfig();
NSQConsumer consumer = new NSQConsumer(
lookup, NSQ_DLR_IN_TOPIC, "data",
(NSQMessage message) -> {
String messageBody = new String(message.getMessage(), StandardCharsets.UTF_8);
JSONObject dlr = new JSONObject(messageBody);
System.out.println("Received DLR: " + dlr.toString());
message.finished();
});
consumer.setExecutor(Executors.newFixedThreadPool(2));
consumer.start();
}
}
{
"smsc_id":"mysmsc-id-example",
"msg_id":"074b8a96-f983-4b0b-8079-4d4c8502ff02",
"gateway_msg_id":"655a1b96a6c90b528d62397caec3c15d5d7dddccacaf9aae6feed22c0b9f9b15",
"total_parts":1,
"part_number":1,
"data_coding":0,
"status":"sent",
"acpt_time":1721980552913,
"sent_time":1721980552933,
"smsc_time":1721980552953
}
Example JSON for receive DLR with final status «delivered/undelivered/rejected/expired/unknown»:
{
"smsc_id":"mysmsc-id-example",
"msg_id":"074b8a96-f983-4b0b-8079-4d4c8502ff02",
"gateway_msg_id":"655a1b96a6c90b528d62397caec3c15d5d7dddccacaf9aae6feed22c0b9f9b15",
"total_parts":1,
"part_number":1,
"data_coding":0,
"status":"delivered",
"acpt_time":1721980552913,
"sent_time":1721980552933,
"smsc_time":1721980552953,
"dlr_time":1721980562327
}
Receiving DLR
To receive DLR via the SMPP Client, use the "NSQ" channel (topic) «NSQ_DLR_IN_TOPIC».
Diagram 1.6: Receiving the DLR from NSQ
smsc_id - unique string identifier of the SMSC that sent the DLR (delivery_sm);
msg_id - a unique string identifier for the SMS in your application that you specified when sending the SMS;
gateway_msg_id - string ID of the SMS/segment in the SMSC. We receive this parameter from the SMS provider after they have accepted the SMS, i.e., for the status «sent» and subsequent ones;
total_parts - numeric value of the number of segments in the sent SMS. If the message is split into 3 parts, then «total_parts=3»;
part_number - numeric value of the segment in the sent SMS (from 1 to 255). When splitting the message into segments, «part_number» is unique for each segment within a single SMS;
data_coding - numeric value of the SMS text encoding. 0 - if 7-bit, 2 - if 8-bit, and 8 - if 16-bit etc.;
status - string value, up to 11 characters. It can be:
· «sent» - SMS/segment has been sent to the SMSC and gateway_msg_id has been received from the SMSC;
· «delivered» - this is the final status, indicating that the SMS/segment has been delivered to the recipient's phone;
· «undelivered» - this is the final status, meaning that the SMS/segment has not been delivered. The possible error code will be in «err_code»;
· «rejected» - this is the final status, meaning that the SMS/segment has been rejected by the SMPP client or the SMS provider. The error code will be in «err_code»;
· «expired» - this is the final status, meaning that a delivery report was not received within 24 hours. If «err_code» is greater than 0, it is likely that the message was not forwarded beyond the SMSC;
· «unknown» - this is the final status, meaning that there was a break in the SMSC chain and it is unknown whether the SMS was delivered to the recipient.
err_code - numeric value of the SMPP error code. The full list is available at the link SMPP error codes;
acpt_time - Unix Timestamp (with milliseconds) when the SMPP client received the SMS/segment for further sending;
sent_time - Unix Timestamp (with milliseconds) when the SMPP client sent the SMS/segment to the provider;
smsc_time - Unix Timestamp (with milliseconds) when the SMPP client received confirmation from the SMS provider and accepted the SMS/segment ID («gateway_msg_id»);
dlr_time - Unix Timestamp (with milliseconds) when the SMPP client received the delivery report (deliver_sm or data_sm) from the provider.
import requests
resp = requests.get('http://your-server-ip:HTTP_PORT/api/gateways',
headers={
'X-API-Key': 'HTTP_API_TOKEN',
})
print(resp.json())
$ch = curl_init('http://your-server-ip:HTTP_PORT/api/gateways');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['X-API-Key: HTTP_API_TOKEN']);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
fetch('http://your-server-ip:HTTP_PORT/api/gateways', {
headers: {
'X-API-Key': 'HTTP_API_TOKEN'
}
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
package main
import (
"fmt"
"io/ioutil"
"net/http"
)
func main() {
req, _ := http.NewRequest("GET", "http://your-server-ip:HTTP_PORT/api/gateways", nil)
req.Header.Set("X-API-Key", "HTTP_API_TOKEN")
client := &http.Client{}
resp, _ := client.Do(req)
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
fmt.Println(string(body))
}
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class Main {
public static void main(String[] args) throws Exception {
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("http://your-server-ip:HTTP_PORT/api/gateways"))
.header("X-API-Key", "HTTP_API_TOKEN")
.build();
HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
}
}
{
"gateways": [
{
"id": 1234,
"smsc_id": "mysmsc-id-example",
"host": "smpp.mygateway.com",
"port": 2775,
"username": "myusername",
"system_type": "",
"enq_interval": 30,
"src_autodetect": true,
"source_ton": 5,
"source_npi": 0,
"dst_autodetect": true,
"dest_ton": 1,
"dest_npi": 1,
"tx_binds": 3,
"rx_binds": 3,
"trx_binds": 3,
"window_size": 1,
"smpp_version": "3.4",
"is_gsm7": true,
"is_secure": false,
"enabled": true,
"updated": 1721751278,
"created": 1721748953,
"queue_count": 0
},
...
],
"connections": {
"BIND_TX": [
{
"gateway_id": 1234,
"bindType": "BIND_TX",
"status": "BOUND_TX",
"connectionId": "06631ea8-ca27-4b30-a66d-53c888f8a0ea",
"localAddr": "192.168.1.2:53992",
"remoteAddr": "12.34.56.78:2775",
"inflightCount": 0,
"queueCount": 0,
"connected": 1721935169
},
...
],
"BIND_RX": [
{
"gateway_id": 1234,
"bindType": "BIND_RX",
"status": "BOUND_RX",
"connectionId": "289556f0-2ba8-410b-84ba-7bfc1ed20ed6",
"localAddr": "192.168.1.2:53989",
"remoteAddr": "12.34.56.78:2775",
"inflightCount": 0,
"queueCount": 0,
"connected": 1721935169
},
...
],
"BIND_TRX": [
{
"gateway_id": 1234,
"bindType": "BIND_TRX",
"status": "BOUND_TRX",
"connectionId": "d41d1f18-f22d-4a29-9403-f064871ad8c0",
"localAddr": "192.168.1.2:54128",
"remoteAddr": "12.34.56.78:29900",
"inflightCount": 0,
"queueCount": 0,
"connected": 1721935159
},
...
]
}
}
Retrieving the list of SMSC
The list of all SMSCs can be obtained in the admin panel (http://your-server-ip:HTTP_PORT) or via the HTTP REST API.
To retrieve the list of SMSC, make a GET request to the endpoint http://your-server-ip:HTTP_PORT/api/gateways
The response will contain a two-dimensional array:
gateways - a list of SMSC;
connections - active connections (binds).
The list of «gateways»:
id - unique numerical ID of the provider in the our database;
smsc_id - unique string ID of the SMS provider;
host - IP address or domain for SMPP connection to the SMSC;
port - port for SMPP connection to the SMSC;
username - username for connecting to the SMSC;
system_type - system type for classifying the type of bind to the SMSC;
enq_interval - interval for sending the «enquire_link» (ping) command;
src_autodetect - whether auto-detection of «source_addr_ton» and «source_addr_npi» is enabled or disabled;
source_ton - «source_addr_ton» by default if «src_autodetect» is disabled;
source_npi - «source_addr_npi» by default if «src_autodetect» is disabled;
dst_autodetect - whether auto-detection of «dest_addr_ton» and «dest_addr_npi» is enabled or disabled;
dest_ton - «dest_addr_ton» by default if «dst_autodetect» is disabled;
dest_npi - «dest_addr_npi» by default if «dst_autodetect» is disabled;
tx_binds - the number of simultaneous connections as «Transmitter»;
rx_binds - the number of simultaneous connections as «Receiver»;
trx_binds - the number of simultaneous connections as «Transceiver»;
window_size - the window for transmitting PDU packets. By default, it is 1, which helps minimize losses in case of connection interruptions;
smpp_version - version of the SMPP protocol;
is_gsm7 - does the SMSC support 7-bit GSM7 encoding (increases SMS length to 160 characters);
is_secure - is the SSL/TLS protocol used;
enabled - whether the connection to the SMSC is enabled or disabled;
updated - Unix Timestamp of the last change to the SMS provider's parameters;
created - Unix Timestamp of the SMS provider's addition;
queue_count - the number of SMS messages in the queue. This may occur if the SMSC is unavailable or has very low throughput. If the value is high, it is recommended to increase the number of connections in Transmitter or Transceiver mode, provided that the SMSC has not set its own sending rate limit.
The list of «connections» can be of 3 types: BIND_TX - SMS sending only mode, BIND_RX - receiving delivery reports (DLR) mode or BIND_TRX - sending SMS and receiving DLR:
gateway_id - numeric ID of the SMS provider in the our database;
bindType - connection mode to the SMSC. It can be «BIND_TX» - Transmitter, «BIND_RX» - Receiver, or «BIND_TRX» - Transceiver;
status - connection status (bind status). It can be «DIAL» - establishing connection, «OPEN» - authorization, «BOUND_TX» - connected as Transmitter, «BOUND_RX» - connected as Receiver, «BOUND_TRX» - connected as Transceiver, «UNBINDING» - disconnecting, or «CLOSED» - connection closed. If the SMS provider is enabled, a reconnection attempt will be made;
connectionId - unique string ID of the connection;
localAddr - connection address on the SMPP client side;
remoteAddr - connection address on the SMSC side;
inflightCount - number of SMS messages in the process of being sent;
queueCount - SMS queue waiting for sending. This may occur if the connection is interrupted or if the SMSC limits throughput;
connected - Unix Timestamp of the last status change of the connection.
import requests
resp = requests.get('http://your-server-ip:HTTP_PORT/api/clients',
headers={
'X-API-Key': 'HTTP_API_TOKEN',
})
print(resp.json())
$ch = curl_init('http://your-server-ip:HTTP_PORT/api/clients');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['X-API-Key: HTTP_API_TOKEN']);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
fetch('http://your-server-ip:HTTP_PORT/api/clients', {
headers: {
'X-API-Key': 'HTTP_API_TOKEN'
}
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
package main
import (
"fmt"
"io/ioutil"
"net/http"
)
func main() {
req, _ := http.NewRequest("GET", "http://your-server-ip:HTTP_PORT/api/clients", nil)
req.Header.Set("X-API-Key", "HTTP_API_TOKEN")
client := &http.Client{}
resp, _ := client.Do(req)
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
fmt.Println(string(body))
}
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class Main {
public static void main(String[] args) throws Exception {
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("http://your-server-ip:HTTP_PORT/api/clients"))
.header("X-API-Key", "HTTP_API_TOKEN")
.build();
HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
}
}
{
"clients": [
{
"id": 1234,
"username": "myclient1",
"password": "qwerty123",
"allow_ip": "11.22.33.44,55.66.77.88",
"throughput": 0,
"binds": 3,
"enabled": true,
"updated": 1721751278,
"created": 1721748953,
},
...
],
"connections": {
"BIND_TX": [
{
"client_id": 1234,
"bindType": "BIND_TX",
"systemType": "",
"status": "BOUND_TX",
"connectionId": "bea1d715-e8d6-4625-b141-aaedc3e20d75",
"localAddr": "188.245.90.194:2775",
"remoteAddr": "12.34.56.78:12345",
"inflightCount": 0,
"queueCount": 0,
"connected": 1721935169
},
...
],
"BIND_RX": [
{
"client_id": 1234,
"bindType": "BIND_RX",
"status": "BOUND_RX",
"connectionId": "e813626e-8490-42a6-abae-f3b8bd1a1616",
"localAddr": "188.245.90.194:2775",
"remoteAddr": "12.34.56.78:23456",
"inflightCount": 0,
"queueCount": 0,
"connected": 1721935169
},
...
],
"BIND_TRX": [
{
"client_id": 1234,
"bindType": "BIND_TRX",
"status": "BOUND_TRX",
"connectionId": "d41d1f18-f22d-4a29-9403-f064871ad8c0",
"localAddr": "188.245.90.194:2775",
"remoteAddr": "12.34.56.78:34567",
"inflightCount": 0,
"queueCount": 0,
"connected": 1721935159
},
...
]
}
}
Retrieving the list of ESME
The list of all ESMEs can be obtained in the admin panel (http://your-server-ip:HTTP_PORT) or via the HTTP REST API.
To retrieve the list of ESMEs, make a GET request to the endpoint http://your-server-ip:HTTP_PORT/api/clients
The response will contain a two-dimensional array:
clients - a list of ESMEs;
connections - active connections (binds).
The list of «clients»:
id - unique numerical ID of the SMPP-profile (ESME) in the our database;
username - username of the ESME;
password - password of the ESME;
allow_ip - сomma-separated list of IPv4 addresses from which the client can connect;
throughput - SMS throughput per minute for a single connection;
binds - how many simultaneous connections (binds) the ESME can open;
enabled - whether the connection to the ESME is enabled or disabled;
updated - Unix Timestamp of the last change to the ESME parameters;
created - Unix Timestamp of the ESME addition;
The list of «connections» can be of 3 types: BIND_TX - SMS sending only mode, BIND_RX - receiving delivery reports (DLR) mode or BIND_TRX - sending SMS and receiving DLR:
client_id - numeric ID of the SMPP profile in the our database;
bindType - connection mode to the SMPP Server. It can be «BIND_TX» - Transmitter, «BIND_RX» - Receiver, or «BIND_TRX» - Transceiver;
status - connection status (bind status). It can be «DIAL» - establishing connection, «OPEN» - authorization, «BOUND_TX» - connected as Transmitter, «BOUND_RX» - connected as Receiver, «BOUND_TRX» - connected as Transceiver, «UNBINDING» - disconnecting, or «CLOSED» - connection closed;
connectionId - unique string ID of the connection;
localAddr - connection address on the SMPP server side;
remoteAddr - connection address on the ESME side;
inflightCount - number of DLR in the process of being sent;
queueCount - DLR queue awaiting submit. This may occur if the client is not have connections as a «Receiver» or «Transceiver»;
connected - Unix Timestamp of the last status change of the connection.
resp = requests.get('http://your-server-ip:HTTP_PORT/api/gateways/logs',
headers={
'X-API-Key': 'HTTP_API_TOKEN',
},
params={
'dateFrom': '2024-08-25',
'dateTo': '2024-08-26',
'smscIds[]': ['mysmsc-id-example'],
})
print(resp.json())
$params = [
'dateFrom' => '2024-08-25',
'dateTo' => '2024-08-26',
'smscIds' => ['mysmsc-id-example'],
];
$url = "http://your-server-ip:HTTP_PORT/api/gateways/logs?" . http_build_query($params);
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['X-API-Key: HTTP_API_TOKEN']);
$response = curl_exec($ch);
curl_close($ch);
print_r(json_decode($response, true));
const params = new URLSearchParams({
dateFrom: '2024-08-25',
dateTo: '2024-08-26',
'smscIds[]': 'mysmsc-id-example'
});
fetch(`http://your-server-ip:HTTP_PORT/api/gateways/logs?${params.toString()}`, {
headers: {
'X-API-Key': 'HTTP_API_TOKEN'
}
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
package main
import (
"encoding/json"
"fmt"
"net/http"
"net/url"
)
func main() {
params := url.Values{
"dateFrom": {"2024-08-25"},
"dateTo": {"2024-08-26"},
"smscIds[]": {"mysmsc-id-example"},
}
reqURL := fmt.Sprintf("%s?%s", "http://your-server-ip:HTTP_PORT/api/gateways/logs", params.Encode())
req, _ := http.NewRequest("GET", reqURL, nil)
req.Header.Set("X-API-Key", "HTTP_API_TOKEN")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
var result map[string]interface{}
json.NewDecoder(resp.Body).Decode(&result)
fmt.Println(result)
}
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.StringJoiner;
public class Main {
public static void main(String[] args) throws Exception {
Map params = Map.of(
"dateFrom", "2024-08-25",
"dateTo", "2024-08-26",
"smscIds[]", "mysmsc-id-example"
);
StringJoiner sj = new StringJoiner("&");
for (Map.Entry entry : params.entrySet()) {
sj.add(URLEncoder.encode(entry.getKey(), StandardCharsets.UTF_8) + "="
+ URLEncoder.encode(entry.getValue(), StandardCharsets.UTF_8));
}
String url = "http://your-server-ip:HTTP_PORT/api/gateways/logs?" + sj.toString();
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.header("X-API-Key", "HTTP_API_TOKEN")
.build();
HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
}
}
[
{
"date": "2024-08-25",
"name": "mysmsc-id-example.log",
"size": 3539546,
"modTime": "2024-08-25T23:59:55.816298164"
},
{
"date": "2024-08-26",
"name": "mysmsc-id-example.log",
"size": 2382360,
"modTime": "2024-08-26T12:04:11.817023138"
},
...
]
Retrieving the List of SMSC/ESME Logs

You can retrieve a list of logs for the SMPP client/server in the admin panel (http://your-server-ip:HTTP_PORT) or via the HTTP REST API.
To retrieve PDU log files, perform a GET request to the endpoint http://your-server-ip:HTTP_PORT/api/gateways/logs for the SMPP-client, or http://your-server-ip:HTTP_PORT/api/clients/logs for the SMPP-server log list.
Parameters to be passed:
dateFrom - required string parameter defining the start date of the search. Format: 2006-01-02;
dateTo - required string parameter defining the end date of the search. Format: 2006-01-02;
smscIds - (used for filtering the list of files for the SMPP-client) string IDs of SMS-providers (SMSCs). Example: «smscIds[]=mysmsc-id-example&smscIds[]=mysmsc-id-2».
clientIds - (used for filtering the list of files for the SMPP-server) number IDs of SMPP-profiles (ESMEs). Example: clientIds[]=1234&clientIds[]=1235».
The response will include a list of log files and their properties. Each object consists of:
date - date in the format «2006-01-02». A new SMPP log file is created daily for each provider, and logs are written to it until a new file is created;
name - filename. Required for detailed viewing or downloading the logs;
size - the file size in bytes;
modTime - date/time in ISO 8601 format of the last modification.
CONCAT(date, name) forms a unique file identifier. To subsequently download or view its contents, you need to provide this concatenated params (date + name) to identify the log file on the server.
import requests
resp = requests.get('http://your-server-ip:HTTP_PORT/api/gateways/logs/download',
headers={
'X-API-Key': 'HTTP_API_TOKEN',
},
params={'files[]': ['2024-08-251234.log']})
print(resp.text)
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => 'http://your-server-ip:HTTP_PORT/api/gateways/logs/download?files[]=2024-08-251234.log',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => array(
'X-API-Key: HTTP_API_TOKEN'
),
));
$response = curl_exec($curl);
curl_close($curl);
echo $response;
fetch('http://your-server-ip:HTTP_PORT/api/gateways/logs/download?files[]=2024-08-251234.log', {
method: 'GET',
headers: {
'X-API-Key': 'HTTP_API_TOKEN'
}
})
.then(response => response.text())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
package main
import (
"fmt"
"io/ioutil"
"net/http"
)
func main() {
client := &http.Client{}
req, err := http.NewRequest("GET", "http://your-server-ip:HTTP_PORT/api/gateways/logs/download?files[]=2024-08-251234.log", nil)
if err != nil {
fmt.Println("Error creating request:", err)
return
}
req.Header.Set("X-API-Key", "HTTP_API_TOKEN")
resp, err := client.Do(req)
if err != nil {
fmt.Println("Error making request:", err)
return
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println("Error reading response body:", err)
return
}
fmt.Println(string(body))
}
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class Main {
public static void main(String[] args) {
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("http://your-server-ip:HTTP_PORT/api/gateways/logs/download?files[]=2024-08-251234.log"))
.header("X-API-Key", "HTTP_API_TOKEN")
.build();
client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.thenApply(HttpResponse::body)
.thenAccept(System.out::println)
.join();
}
}
...
INFO[2024-07-29 21:02:05][1beb339d-24dd-4abc-a1c3-9e77d88856ab]: <<<<< 000000450000000400000000000000080005004d7953656e6465720001013338303939313233343536370000000000000100000110486176652061206e6963652064617921
SUBMIT_SM:
{
"Header": {
"command_length": 69,
"command_id": 4,
"command_status": 0,
"sequence_number": 8
},
"service_type": "",
"source_addr_ton": 5,
"source_addr_npi": 0,
"source_addr": "MySender",
"dest_addr_ton": 1,
"dest_addr_npi": 1,
"destination_addr": "380991234567",
"esm_class": 0,
"protocol_id": 0,
"priority_flag": 0,
"schedule_delivery_time": "",
"validity_period": "",
"registered_delivery": 1,
"replace_if_present_flag": false,
"message": {
"data_coding": 0,
"sm_default_msg_id": 1,
"short_message": "Have a nice day!"
}
}
INFO[2024-07-29 21:02:06][1beb339d-24dd-4abc-a1c3-9e77d88856ab]: >>>>> 000000518000000400000000000000083635356131623936613663393062353238643632333937636165633363313564356437646464636361636166396161653666656564323263306239663962313500
SUBMIT_SM_RESP:
{
"Header": {
"command_length": 81,
"command_id": 2147483652,
"command_status": 0,
"sequence_number": 8
},
"message_id": "074b8a96-f983-4b0b-8079-4d4c8502ff02"
}
...
Viewing/Exporting of SMSC/ESME Logs

You can view or download SMPP logs for SMSC/ESME in the admin panel (http://your-server-ip:HTTP_PORT) or via the HTTP REST API.
To view an SMPP log file or export an archive, perform a GET request to the endpoint http://your-server-ip:HTTP_PORT/api/gateways/logs/download for the SMPP-client or http://your-server-ip:HTTP_PORT/api/clients/logs/download for the SMPP-server.
Parameters to be passed:
files - CONCAT(date, client_id, ".log") is the ID of SMPP files for SMPP Server. Example: «files[]=2024-08-251234.log» or CONCAT(date, smsc_id, ".log") is the ID of SMPP files of SMPP Client. Example: «files[]=2024-08-25mysmsc-id-example.log»;
If the number of «files[]» is greater than 1, the server response will be a ZIP archive containing the files of the requested SMPP logs.
If the number of «files[]» is equal to 1, the server response will contain the contents of the SMPP log file with GZIP compression.