Connect your ERP or system to SMSDESK via SMPP protocol
{host}:2775C1409138) — recommended — or your account email address
rqym36t4)264811234567)
The SMSDESK SMPP Server allows external systems to connect and submit SMS messages (SUBMIT_SM) using the industry-standard SMPP v3.4 protocol. This is ideal for:
Submit SMS messages via SUBMIT_SM PDU
Not available - incoming SMS (MO) is not forwarded over SMPP
Delivery report (DELIVER_SM) confirms your message was accepted into the send queue - stat:ACCEPTD. Handset delivery status via HTTP API
API key based (first 8 chars). Plain TCP — use VPN for encrypted transport
| Protocol Version | SMPP v3.4 |
| Server Address | {host} (your SMSDESK deployment domain) |
| Port | 2775 (standard SMPP port) |
| Supported Bind Types | TRANSMITTER, TRANSCEIVER (BIND_RECEIVER is refused with ESME_RBINDFAIL). Use TRANSCEIVER if you want queue-acceptance receipts (DELIVER_SM) |
| Max Sessions | 100 concurrent sessions total, maximum 10 sessions per IP address |
| Session Timeout | 300 seconds (5 minutes) of inactivity - send ENQUIRE_LINK every 30-60s |
| Bind Deadline | 30 seconds - connections that do not bind within 30s are closed |
| Bind Throttle | 5 failed binds per IP within 5 minutes → ESME_RTHROTTLED until the window expires |
| Max PDU Size | 70,000 bytes (accommodates message_payload TLV for long messages) |
| Throughput | 300 messages per minute per session (ESME_RTHROTTLED when exceeded) |
| Destination TON | 1 (International) |
| Destination NPI | 1 (ISDN/Telephone) |
| Source TON | 0 (Unknown) or 5 (Alphanumeric) |
| Source NPI | 0 (Unknown) |
| Data Coding | 8 = UCS2/Unicode; all other values (including 0, 1, 3) are
decoded as ISO-8859-1 Latin-1 |
| Character Set | ISO-8859-1 (Latin-1) or UCS-2 (data_coding=8). GSM 7-bit packed encoding
(data_coding=0) is not unpacked — send Latin-1 encoded bytes |
SMPP authentication uses your SMSDESK API credentials:
System ID: C1409138 (recommended - shown in portal as "SMPP System ID")
OR your-email@example.com (also accepted)
Password: your-api-key (first 8 characters only)
system_id to 16 octets (15 characters
+ terminator), and most client libraries enforce this limit by truncating longer values. If your
email address is longer than 15 characters it will be truncated on the wire and authentication will
fail — always use the C<id> form shown in the portal (e.g.,
C1409138). Both forms are accepted by the server; the C<id> form is
guaranteed to fit the protocol limit.
rqym••••••••).
C<id>, recommended) or email as
system_id
passwordsystem_id=SMSDESK)
0x00000000 |
ESME_ROK | Bind successful |
0x00000005 |
ESME_RALYBND | Session is already bound |
0x0000000E |
ESME_RINVPASWD | Invalid credentials - wrong system_id or password (check System ID format and the first 8 chars of your API key) |
0x0000000F |
ESME_RINVSYSID | Missing system_id |
0x0000000D |
ESME_RBINDFAIL | Credentials valid but account not permitted to bind - user or account inactive, SMPP not enabled, no default channel, or client account not ACTIVE |
0x00000008 |
ESME_RSYSERR | Internal server error - retry or contact support |
0x00000058 |
ESME_RTHROTTLED | Too many failed binds from your IP - wait 5 minutes |
Submit an SMS message for delivery to a mobile number.
source_addr field in the PDU is
ignored. Messages are routed using your account's default channel configured in the web
portal. This ensures proper billing and routing through your assigned SMS channels.
SUBMIT_SM PDU:
service_type: ""
source_addr_ton: 0 or 5 (alphanumeric)
source_addr_npi: 0
source_addr: "Any" (ignored for routing - default channel used; echoed back in DELIVER_SM receipts)
dest_addr_ton: 1 (international)
dest_addr_npi: 1 (ISDN)
dest_addr: "264811234567" (7-15 digits, optional leading +)
esm_class: 0 (or 0x40 for UDH concatenation)
protocol_id: 0
priority_flag: 0 (stored but ignored - priority comes from your account settings)
schedule_delivery_time: "" (scheduled delivery NOT supported - rejected with 0x00000061)
validity_period: ""
registered_delivery: 0 or 1 (set 1 to receive a DELIVER_SM queue-acceptance receipt - transceiver binds only)
replace_if_present_flag: 0
data_coding: 8 (UCS2) or anything else = Latin-1
sm_default_msg_id: 0
sm_length: [message length, max 254]
short_message: "Your message text"
SUBMIT_SM_RESP PDU:
command_status: 0x00000000 (success)
message_id: "26986088" (sms_outgoing row ID - use with /sms/2/reports)
When you set registered_delivery in SUBMIT_SM and are bound as
TRANSCEIVER, the server pushes a DELIVER_SM receipt after the message is
accepted and persisted to the outbound queue. This confirms SMSDESK's own processing —
receipt and database insertion for onward network delivery. It is not a handset delivery
report.
stat:ACCEPTD means "received and queued". For final
network/handset delivery status, poll the HTTP API (/sms/2/reports,
/sms/2/logs) with the message_id from SUBMIT_SM_RESP, or use
webhooks.
DELIVER_SM PDU (server → client):
esm_class: 0x04 (SMSC delivery receipt)
source_addr: the message destination number
destination_addr: your original source_addr
data_coding: 3 (Latin-1)
short_message: "id:26986088 sub:001 dlvrd:000 submit date:2510151130
done date:2510151130 stat:ACCEPTD err:000 text:Your message"
TLVs:
0x001E receipted_message_id = "26986088" (matches SUBMIT_SM_RESP message_id)
0x0427 message_state = 6 (ACCEPTED)
Your client must respond to DELIVER_SM with DELIVER_SM_RESP (standard - most libraries do
this automatically). A failed SUBMIT_SM returns a non-zero command_status in
SUBMIT_SM_RESP and no receipt is sent.
Incoming SMS is not forwarded over SMPP. BIND_RECEIVER is refused with
ESME_RBINDFAIL. Use the HTTP API or webhooks for inbound messages.
Keepalive mechanism to maintain connection.
ENQUIRE_LINK PDU → ENQUIRE_LINK_RESP
Gracefully close the SMPP session.
UNBIND PDU → UNBIND_RESP → Connection closed
Messages longer than 160 GSM characters (or 70 UCS2 characters) are supported via concatenation. The server reassembles the parts and queues a single message. Each part is billed separately by the network.
Per-part acknowledgment: every part is acknowledged with a SUBMIT_SM_RESP.
Intermediate parts return a temporary message_id of the form PART-{ref}-{seq};
the final part returns the real queued message ID. The queue-acceptance receipt
(DELIVER_SM, if requested) is sent once, after the last part is assembled. If a client disconnects
mid-sequence, the already-acknowledged parts are queued as one partial message — they are never
dropped.
Set esm_class to 0x40 (UDH indicator) and include a UDH with concatenation
information in the short_message field:
UDH for a 2-part message with an 8-bit reference (6 bytes, prepended to each part):
05 00 03 A5 02 01 (UDHL=5, IEI=00 concatenation, IEDL=3, ref=0xA5, total=2, seq=1)
05 00 03 A5 02 02 (same ref and total, seq=2)
Per part: up to 153 GSM characters, or 67 UCS2 characters (134 bytes).
Alternatively, use the SAR (Segmentation and Reassembly) TLVs in the PDU:
sar_msg_ref_num: 12345 (reference number, same for all parts)
sar_total_segments: 2 (total number of parts)
sar_segment_seqnum: 1 (sequence number, 1-based)
For messages longer than 254 bytes, use the message_payload TLV (tag 0x0424) instead of
short_message. The server reads the full message from this TLV.
| Encoding | data_coding | Single SMS | Per concatenated part |
|---|---|---|---|
| GSM 7-bit (Latin) | 0 | 160 chars | 153 chars |
| UCS2 (Unicode) | 8 | 70 chars | 67 chars |
Every queued message carries a priority that decides the order in which it is sent to the mobile
networks: the highest priority is dispatched first, and messages of equal priority are sent in the
order they were received (FIFO). The priority_flag field in SUBMIT_SM is
stored but does not influence queue order. Priority is resolved as follows:
CRITICAL, HIGH, NORMAL, LOW,
BULK). When set, it applies to everything that user submits.
| Submission method | Default priority |
|---|---|
| REST API, other API integrations, automatic replies | HIGH |
| SMPP | NORMAL |
| Portal (single send), Email-to-SMS | LOW |
| Portal CSV upload, contact-group sends | BULK |
Tip: create a dedicated API-only user for OTP traffic and give it
CRITICAL priority so one-time passwords are never held up behind campaigns, and give your
bulk-campaign user BULK. Priority only changes the order within the queue; it does not
throttle or reject any traffic.
Maximum message size: 255 parts, the GSM limit for a concatenated SMS (39,015 GSM-7
characters or
17,085 UCS2 characters; GSM extension characters count double). Larger submissions are rejected with
ESME_RINVMSGLEN and nothing is queued. Send long messages as concatenated parts (UDH or
sar_* TLVs) or in a single message_payload TLV (up to 65,535 octets). Each
part is billed as one SMS,
and mobile networks may accept fewer parts than the GSM maximum.
import smpplib.client
import smpplib.consts
# Connect to SMPP server
client = smpplib.client.Client('{host}', 2775)
# Handle queue-acceptance receipts (DELIVER_SM)
def on_deliver(pdu):
# short_message contains: "id:<id> sub:001 dlvrd:000 ... stat:ACCEPTD ..."
print(f'Receipt: {pdu.short_message!r}')
client.set_message_received_handler(on_deliver)
# Bind as transceiver (required to receive DELIVER_SM receipts)
# System ID = C<client_user_id> from the portal (spec-safe, max 15 chars)
# Password = first 8 characters of your API key
client.connect()
client.bind_transceiver(
system_id='C1409138', # SMPP System ID from portal (or your email, ≤15 chars)
password='rqym36t4' # First 8 chars of API key
)
# Send SMS - registered_delivery=1 requests the DELIVER_SM receipt
resp = client.send_message(
source_addr='Any', # Ignored for routing - default channel used
destination_addr='264811234567', # No + prefix
short_message=b'Hello from SMPP!',
data_coding=3, # Latin-1 (8 = UCS2)
registered_delivery=1,
)
print(f'Accepted, message_id={resp.message_id}')
# Listen for the DELIVER_SM receipt
client.listen(timeout=10)
# Unbind and disconnect
client.unbind()
client.disconnect()
<?php
require_once 'vendor/autoload.php';
use PhpSmpp\Transport\Socket;
use PhpSmpp\Client;
use PhpSmpp\SMPP;
// Create transport
$transport = new Socket(['sms.com.na'], 2775);
$transport->open();
// Create client
$smpp = new Client($transport);
// Bind with SMPP System ID (recommended) or email
$smpp->bindTransceiver(
'C1409138', // SMPP System ID from portal (or email if ≤15 chars)
'rqym36t4' // First 8 chars of API key
);
// Send SMS (source is ignored, default channel used)
$smpp->sendSMS(
'Any', // Ignored - default channel used
'264811234567', // No + prefix
'Hello from SMPP!',
null,
SMPP::DATA_CODING_DEFAULT
);
echo "✓ Message sent successfully!\n";
// Unbind
$smpp->close();
?>
import org.jsmpp.bean.*;
import org.jsmpp.session.SMPPSession;
public class SmppExample {
public static void main(String[] args) throws Exception {
SMPPSession session = new SMPPSession();
// Connect and bind with API key (first 8 characters)
session.connectAndBind(
"{host}",
2775,
new BindParameter(
BindType.BIND_TRX,
"C1409138", // SMPP System ID from portal (or email if ≤15 chars)
"rqym36t4", // First 8 chars of API key
"SMSDESK",
TypeOfNumber.UNKNOWN,
NumberingPlanIndicator.UNKNOWN,
null
)
);
// Send SMS (source is ignored, default channel used)
String messageId = session.submitShortMessage(
"CMT",
TypeOfNumber.UNKNOWN,
NumberingPlanIndicator.UNKNOWN,
"Any", // Ignored - default channel used
TypeOfNumber.INTERNATIONAL,
NumberingPlanIndicator.ISDN,
"264811234567", // No + prefix
new ESMClass(),
(byte) 0,
(byte) 1,
null,
null,
new RegisteredDelivery(SMSCDeliveryReceipt.DEFAULT),
(byte) 0,
new GeneralDataCoding(Alphabet.ALPHA_DEFAULT),
(byte) 0,
"Hello from SMPP!".getBytes()
);
System.out.println("✓ Message sent! ID: " + messageId);
// Unbind
session.unbindAndClose();
}
}
using JamaaTech.Smpp.Net.Client;
using JamaaTech.Smpp.Net.Lib;
class Program
{
static void Main()
{
// Create client
var client = new SmppClient();
client.Name = "SMSDESK-Client";
// Connect
client.Start();
// Bind with SMPP System ID (recommended) or email
client.Bind(
"{host}",
2775,
"C1409138", // SMPP System ID from portal (or email if ≤15 chars)
"rqym36t4" // First 8 chars of API key
);
// Send SMS (source is ignored, default channel used)
var message = new TextMessage
{
DestinationAddress = "264811234567", // No + prefix
SourceAddress = "Any", // Ignored - default channel used
Text = "Hello from SMPP!",
RegisterDeliveryNotification = false
};
client.SendMessage(message);
Console.WriteLine("✓ Message sent successfully!");
// Unbind
client.Shutdown();
}
}
The SMPP server returns standard SMPP v3.4 error codes. Common errors:
| Code | Hex | Name | Description | Action |
|---|---|---|---|---|
| 0 | 0x00000000 | ESME_ROK | Success - message accepted and queued | Continue |
| 1 | 0x00000001 | ESME_RINVMSGLEN | Empty message, or >255 parts after assembly | Fix message length; do not retry unchanged |
| 2 | 0x00000002 | ESME_RINVCMDLEN | Malformed/truncated PDU | Fix client encoding |
| 3 | 0x00000003 | ESME_RINVCMDID | Unsupported command | Check command ID |
| 4 | 0x00000004 | ESME_RINVBNDSTS | SUBMIT_SM before bind | Bind first |
| 5 | 0x00000005 | ESME_RALYBND | Session already bound | Continue; bind once |
| 8 | 0x00000008 | ESME_RSYSERR | Internal server error | Retry; contact support if persistent |
| 11 | 0x0000000B | ESME_RINVDSTADR | Invalid destination (must be 7-15 digits) | Fix number format; do not retry unchanged |
| 13 | 0x0000000D | ESME_RBINDFAIL | Account not bind-eligible (inactive/SMPP off/no channel) | Contact administrator |
| 14 | 0x0000000E | ESME_RINVPASWD | Invalid system_id or password | Use C<id> System ID + first 8 chars of API key |
| 15 | 0x0000000F | ESME_RINVSYSID | Missing system_id | Supply System ID |
| 69 | 0x00000045 | ESME_RSUBMITFAIL | Message could not be queued (server-side failure) | Retry; contact support if persistent |
| 88 | 0x00000058 | ESME_RTHROTTLED | Rate limit - 300 msg/min per session or failed-bind lockout | Retry with delay |
| 97 | 0x00000061 | ESME_RINVSCHED | Scheduled delivery not supported | Send immediately; omit schedule_delivery_time |
stat:ACCEPTD receipt means "queued", not
delivered - poll /sms/2/reports for final statusschedule_delivery_time is rejected (0x61) - send at
the time you want delivery// Pseudo-code for error handling
if (response.command_status == ESME_RTHROTTLED) {
// Wait and retry
sleep(1000);
retry_message();
} else if (response.command_status == ESME_RINVDSTADR) {
// Invalid number - don't retry
log_error("Invalid destination number");
mark_as_failed();
} else if (response.command_status == ESME_ROK) {
// Success
save_message_id(response.message_id);
}