Skip to content
OmniChannels.ai

SMPP v3.4 connectivity

When to use SMPP instead of REST, how binds are provisioned over TLS, submit_sm essentials, encoding, concatenation, delivery receipts and error codes.

Base URL https://api.broadnet.meUpdated

SMPP (Short Message Peer-to-Peer) is the telecom protocol for exchanging SMS over a persistent TCP connection. We accept SMPP v3.4 binds over TLS for high-volume SMS, with the same routes as the REST API.

SMPP or REST?#

REST API SMPP v3.4
Channels SMS plus WhatsApp, RCS, Messenger, Instagram, Telegram, Email and Push SMS only
Connection One HTTPS request per call One long-lived TLS session (a bind)
Throughput Suits most application traffic Built for sustained high volume, with many messages in flight
Delivery receipts Webhook to your HTTPS endpoint deliver_sm on the same session
Effort Any HTTP library An SMPP client library and connection management
Best for Applications, OTP, multichannel use cases Aggregators, resellers and enterprises that already run SMPP clients

You can use both on one account: REST for multichannel and application traffic, SMPP for high-volume SMS.

Provisioning a bind#

SMPP access is set up by our team rather than self-served. When you request access, tell us your destination countries, sender IDs, expected peak throughput and the public IP addresses you will connect from. You receive:

Item Detail
Host and TLS port Provided with your credentials. Use the TLS port for production traffic.
system_id and password Your bind credentials. Store them like any other secret.
system_type Leave empty unless we give you a value.
Throughput Messages per second per bind, agreed for your account.
Number of binds How many concurrent sessions you may open.
IP allowlist Only the source IPs you register can connect.

Resellers and wholesale partners can have dedicated binds.

Bind types#

Bind PDU Direction
Transceiver (TRX) bind_transceiver Submit messages and receive receipts on one session. Recommended.
Transmitter (TX) bind_transmitter Submit only. Receipts need a separate receiver bind.
Receiver (RX) bind_receiver Receive receipts and inbound messages only.

Use a single transceiver bind per connection unless your SMPP client requires separate transmitter and receiver sessions.

Session lifecycle#

  1. Open a TLS connection to the host and port you were given.
  2. Send bind_transceiver with your system_id and password, and wait for bind_transceiver_resp with command_status 0.
  3. Submit messages with submit_sm. Each submit_sm_resp returns the message_id.
  4. Receive delivery receipts as deliver_sm and answer every one with deliver_sm_resp.
  5. Send enquire_link about every 30 seconds while idle, and reconnect if responses stop.
  6. Close with unbind and wait for unbind_resp.

submit_sm_resp only means the message was accepted for delivery. The final outcome arrives later in the delivery receipt.

submit_sm essentials#

Field Value Notes
source_addr_ton / source_addr_npi 5 / 0 for an alphanumeric sender ID; 1 / 1 for an international number Must match the sender type.
source_addr Your approved sender ID, up to 11 characters if alphanumeric Unregistered sender IDs may be rejected or replaced in some countries.
dest_addr_ton / dest_addr_npi 1 / 1 International format.
destination_addr Country code plus number, no +, for example 447700900123 Same format as the REST API.
registered_delivery 1 Request a delivery receipt.
data_coding 0 (GSM-7) or 8 (UCS-2) See encoding below.
short_message The text, encoded per data_coding Up to 254 octets.
message_payload (TLV 0x0424) Alternative to short_message for long content Use one or the other, never both; set sm_length to 0 when using it.
esm_class 0x40 when short_message starts with a UDH Needed when you segment messages yourself.
validity_period Optional How long delivery attempts continue.

Encoding#

data_coding Encoding Single message Per part when concatenated
0 GSM-7 default alphabet 160 characters 153 characters
8 UCS-2 (Unicode: Arabic, emoji, other scripts) 70 characters 67 characters

One character outside GSM-7 means the whole message must be sent as UCS-2.

Concatenation (long messages)#

Messages longer than one segment are split into parts that the handset reassembles. You can do this in one of two ways:

  • Segment it yourself. Send each part as its own submit_sm whose short_message starts with a 6-octet User Data Header (reference number, total parts, part number), and set esm_class to 0x40. The header is why parts hold 153 or 67 characters.
  • Send it whole in the message_payload TLV and let the SMSC segment it. Confirm with our team that this is enabled on your bind before relying on it.

Use the same reference number for all parts of a message and a new one for each new message.

Delivery receipts#

When you set registered_delivery to 1, the receipt arrives as a deliver_sm with the receipt bit (0x04) set in esm_class. The text follows the common SMPP receipt format:

deliver_sm short_message

id:2603121407537573731 sub:001 dlvrd:001 submit date:2610100914 done date:2610100915 stat:DELIVRD err:000 text:Your OmniDemo code

id matches the message_id from submit_sm_resp. Where present, the receipted_message_id TLV (0x001E) carries the same ID and is easier to parse than the text.

stat Meaning
DELIVRD Delivered to the handset.
UNDELIV Undeliverable.
EXPIRED The validity period ended before delivery.
REJECTD Rejected by the network, for example a blocked sender.
DELETED Deleted before delivery.
ACCEPTD Accepted; an intermediate state.
UNKNOWN No state information available.

Always answer deliver_sm with deliver_sm_resp. Unacknowledged receipts are re-sent and can lead to throttling of the bind.

Throughput and windowing#

SMPP is asynchronous: you can send several submit_sm PDUs before the responses arrive. The number of unacknowledged PDUs in flight is your window.

  • A window of 1 (waiting for each response) is the most common cause of low throughput.
  • The documented default is 1,000 messages per second per bind with a window of 100 outstanding submit_sm. Your account's limits are confirmed with your credentials.
  • If you receive ESME_RTHROTTLED, slow down and retry with exponential backoff.
  • On disconnect, reconnect with backoff and jitter, not in a tight loop, and track outstanding sequence numbers so you can reconcile receipts.
  • Higher aggregate throughput comes from more binds, up to the number agreed for your account.

Common command_status codes#

Code Name Meaning What to do
0x00000000 ESME_ROK Success. Nothing.
0x00000001 ESME_RINVMSGLEN Invalid message length. Check sm_length, or that you did not send both short_message and message_payload.
0x00000004 ESME_RINVBNDSTS Command not allowed in the current bind state. For example, submitting on a receiver bind.
0x00000005 ESME_RALYBND Already bound. Do not bind twice on one session.
0x00000008 ESME_RSYSERR System error. Retry with backoff.
0x0000000A ESME_RINVSRCADR Invalid source address. Check the sender ID and its TON/NPI.
0x0000000B ESME_RINVDSTADR Invalid destination address. Check the number format.
0x0000000D ESME_RBINDFAIL Bind failed. Check system_id, password and your source IP.
0x0000000E ESME_RINVPASWD Invalid password. Check credentials.
0x00000014 ESME_RMSGQFUL Message queue full. Back off and retry.
0x00000045 ESME_RSUBMITFAIL Submit failed. Check the message, then retry later.
0x00000058 ESME_RTHROTTLED Throughput limit exceeded. Slow down; retry with backoff.

Example session#

This sketch uses the open-source smpp package for Node.js. Any SMPP v3.4 client library (for example jSMPP for Java or python-smpplib) follows the same steps.

Node.js

import smpp from "smpp";

// Host, TLS port and credentials come with your bind.
const session = smpp.connect({
  url: `ssmpp://${process.env.SMPP_HOST}:${process.env.SMPP_TLS_PORT}`,
  auto_enquire_link_period: 30000,
});

session.bind_transceiver(
  { system_id: process.env.SMPP_SYSTEM_ID, password: process.env.SMPP_PASSWORD },
  (bindResp) => {
    if (bindResp.command_status !== 0) throw new Error(`bind failed: ${bindResp.command_status}`);

    session.submit_sm(
      {
        source_addr_ton: 5,
        source_addr_npi: 0,
        source_addr: "OmniDemo",
        dest_addr_ton: 1,
        dest_addr_npi: 1,
        destination_addr: "447700900123",
        registered_delivery: 1,
        short_message: "Your OmniDemo code is 482913",
      },
      (resp) => console.log("accepted", resp.message_id),
    );
  },
);

session.on("deliver_sm", (pdu) => {
  if (pdu.esm_class & 0x04) console.log("receipt", pdu.short_message.message);
  session.send(pdu.response()); // always acknowledge
});

Check your library's documentation for its TLS URL scheme and encoding options; option names differ between libraries.

Next steps#

Ready to send your first message?

Request test credentials. An engineer sets up your account, and test sends go to your own verified numbers.

Request test access