Skip to main content

Connect via SMPP

Most Orbit customers send SMS through the REST Messaging API. If you already run SMPP-based sending infrastructure — a legacy platform migration, an aggregator integration, or a high-throughput application built around an SMPP client library — Orbit also exposes a direct SMPP bind, so you don’t have to rewrite that integration to switch providers.

Wire protocol: PDUs, not JSON

Your client speaks SMPP 3.4 binary PDUs to Orbit’s edge — there is no HTTP JSON layer on the bind. The frames you exchange: A minimal submit_sm for one recipient — the envelope the runnable client examples below put on the wire when they send a single message:
Batch the way SMPP batches: pipeline many submit_sm PDUs in flight over one bind rather than asking for a batch endpoint — there is none on the wire. Respect the remote window your client negotiates and the credential’s TPS cap; data_coding=8 on submit_sm is how you send UCS-2 (Unicode) text, and esm_class=0x40 (UDHI) marks concatenated segments when a message is longer than one segment.

How it works

Orbit issues you a system_id and password. Your SMPP client binds to smpp.orbit.devotel.io:2775 with those credentials and submits submit_sm PDUs exactly as it would against any other SMPP provider. Traffic sent this way flows through the same Orbit sending infrastructure as a REST-submitted message — the same routing, delivery tracking, and compliance gates apply either way.

A runnable client: bind, send one message, read the receipt

Orbit issues the system_id and password below when you create a credential — use the create curl later in this section. Set the four environment variables, then run either example end to end: it opens a transceiver bind, sends one submit_sm, prints the Orbit message id from submit_sm_resp, and (if the credential’s dlrMode is bind or both) reads the deliver_sm receipt before unbind.

Python with smpplib

The logging.DEBUG output is the expected PDU trace — on a healthy run, bind_transceiver_resp (command_status=0), then submit_sm followed immediately by submit_sm_resp:

Node.js with the smpp package

The pdu event observes every inbound frame — the expected PDU trace on a healthy run is bind_transceiver_resp command_status=0 first, then submit_sm → submit_sm_resp, then (when dlrMode is bind or both) a deliver_sm receipt such as id:msg_01H4XK6D7Y stat:DELIVRD. Keep one long-lived bind per process and add an enquire_link keepalive every 30–60 seconds before productionizing either snippet — the discipline below and the linked receipts guide both apply. To send through the snippet, point SMPP_SYSTEM_ID and SMPP_PASSWORD at a credential from the create curl; if the password has aged out of its reveal window, rotate it and use the fresh one.

Ports and connectivity

Orbit’s SMPP edge accepts binds on two ports, and only these two: Both ports speak the same protocol and take the same credentials, so switching between them is a client-side setting, not a new credential. Every other port on smpp.orbit.devotel.io is closed to the internet. Two things follow from that, and they matter when you are debugging a bind:
  • A blocked port drops your connection instead of refusing it. You will not get a fast connection refused. The connection attempt sits open until your own client gives up, so a typo in the port looks exactly like a slow network. Configure an explicit connect timeout — 5 to 10 seconds is plenty — so a wrong port fails in seconds instead of hanging.
  • Your own network has to allow the outbound side. Permit egress from the machine running your SMPP client to smpp.orbit.devotel.io on 2775 and 3550. Corporate firewalls that silently discard traffic to non-web ports produce the identical hang.

The dashboard console

Developer → SMPP is the console for everything on this page. It is organized in three tabs:
  • Credentials — the default tab. Before it lists your bind credentials it shows a Connection details card with the copyable host (smpp.orbit.devotel.io) and both ports, so you can hand the endpoint to a client config without leaving the dashboard. Create, update, reveal, and rotate credentials from the same tab.
  • Carriers — where enterprise tenants register their own upstream carrier connections, and where you fire a bind test against a carrier row (see the testing section below).
  • Compliance — a read-only view of how Orbit’s compliance gates (opt-out suppression, quiet hours, consent tracking) apply to the traffic on your SMPP binds. Every submit_sm passes these gates regardless of the wire path it took.

Reachability and bind testing

The console’s Carriers tab has a Test bind action per carrier row. It exists for upstream carriers, not for verifying your own client; for carrier rows it runs the edge-side checks that fail fast — wrong carrier type, missing SMPP fields, an undecryptable stored password — and returns a structured result:
POST /api/v1/messaging/smpp/carriers/{id}/test exposes the same check over the API, rate-limited so a stuck endpoint can’t be hammered. For your own bind credential, use the diagnostic ladder below — the reachability question (bind_transceiver_resp vs. no response at all) is answered from your own client, not the dashboard.

Is it the network or the credential?

Run bind_transceiver and read what comes back:
  • You get a bind_transceiver_resp — including one carrying an error status such as ESME_RINVPASWD (wrong password) or ESME_RINVSYSID (unknown system_id) — then the edge is reachable and the problem is the credential. Check it hasn’t been rotated or revoked in the dashboard under Developer → SMPP.
  • The TCP connection never completes — nothing reached Orbit. Confirm you are dialling 2775 or 3550, that smpp.orbit.devotel.io resolves for you, and that your egress firewall allows the port.
  • The connection opens on 3550 but the TLS handshake fails — your client is either verifying against a different hostname or is missing the public CA chain in its trust store. Verify against smpp.orbit.devotel.io.

Long-lived bind vs. transient sessions

Run one long-lived bind per sending process and keep it open. Enquire every 30–60 seconds, rebind after any connection drop with exponential backoff, and drain in-flight submit_sm before you unbind on shutdown. Opening a fresh bind per batch or per message is the one usage pattern SMPP punishes and HTTP doesn’t — each bind costs a full handshake, and the edge treats a rapid rebind loop the same way every SMPP server does: throttled and flagged. If your architecture genuinely re-connects per batch, switch that path to the REST API instead.

Troubleshooting bind rejects and throughput

Work this list top to bottom — the first row that matches is the fix. A binding reject comes back in the bind_*_resp as a non-zero command_status; read the status from that PDU before assuming the network ate it.

When to use SMPP vs. the REST API

Both paths send through the same underlying platform — pick whichever fits the integration you already have, or the one your team is more comfortable operating. Delivered status goes wherever you point it: dlrMode = "webhook" posts receipt JSON to your endpoint, dlrMode = "bind" streams deliver_sm frames back on the session, dlrMode = "both" does the two. Everything you would get over the REST path is available no matter which sending wire you chose.

Creating a credential

The response includes the generated system_id and a one-time password. The password is exactly 8 alphanumeric characters — that is the maximum the SMPP 3.4 bind PDU password field can carry (a 9-octet value including its trailing NUL), so paste it into your client configuration verbatim; your client does not need to truncate it. The password is only ever shown in full at creation or rotation — it can be re-fetched once within a 60-second reveal window (GET /smpp/credentials/{id}/reveal) in case your session closed before you copied it, and after that window it’s gone. If you lose it, rotate the credential to issue a new one:
Rotating invalidates the old password immediately, so schedule it for a maintenance window if the bind is live in production.

Throughput limits

Every credential has a TPS (transactions-per-second) cap — 50 by default, adjustable per organization up to a platform ceiling. A single SMPP bind on Orbit’s infrastructure sustainably tops out well below that ceiling, so if you need very high throughput, open several binds (several credentials) rather than pushing one bind past its practical limit.

Bring-your-own upstream carriers (advanced)

Enterprise tenants with their own direct carrier relationships can additionally register upstream SMPP (or HTTP-API) carrier connections that Orbit’s routing layer uses when picking the best egress path for that tenant’s traffic — alongside Orbit’s own carrier network, not instead of it. This is a separate, more advanced surface from the bind credentials above; see the SMPP API reference for the carrier endpoints if your organization has a direct carrier agreement you want Orbit to route through. Test a saved carrier from the console’s Carriers → Test bind action before enabling it for routing.

Rebinding across regions

Multi-region failover is client-side. A healthy implementation caches a connect latency per region endpoint, rebinds to the lowest-latency healthy region after a drop, and never rotates through endpoints per message — only on failure. Keep the same discipline for regions as for binds: rebind on drop with backoff, drain in-flight work before unbind, and let a sticky session do the work of a fleet of ephemeral ones.

Endpoints

See also