SMSwarriors. Global Messaging. Real Connections.
REST API · v4.1.0

Build on the SMSwarriors API.

One HTTPS endpoint to send SMS to 200 countries, with real-time delivery reports, GSM & Unicode support, and automatic long-message concatenation. JSON in, JSON out.

REST over HTTPS, JSON payloads Real-time DLR callbacks GSM + Unicode (UCS) Also available: SMPP 3.4
Quickstart

Send your first message in three steps.

01

Get credentials

Sign up or ask your account manager for an API username, password and your dedicated sending host.

02

POST a JSON payload

Send a single JSON request to the submission endpoint — auth, sender, recipient and message text.

03

Track delivery

We call your dlrUrl in real time as the message moves toward the handset.

Endpoint placeholder. Every example on this page uses <your-api-host> in place of your real submission host. Your account manager will give you the live hostname, username and password — swap them in before you go live.
cURL — send a GSM text message
curl -L "https://<your-api-host>/bulk/sendsms" \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{
    "type": "text",
    "auth": { "username": "your_username", "password": "your_password" },
    "sender": "SMSwarriors",
    "receiver": "4179123456",
    "dcs": "GSM",
    "text": "Hello from SMSwarriors!",
    "dlrMask": 19,
    "dlrUrl": "https://your-app.com/dlr-callback"
  }'
Response — 202 Accepted
{
  "msgId": "9325d0a8-2638-11e6-afe7-bffc7cc8fa4f",
  "numParts": 1
}
Authentication

Username & password, in the request body.

There's no separate API key or OAuth flow. Every request carries an auth object with the username and password issued to your account. Always send requests over HTTPS so credentials stay encrypted in transit.

JSON — auth object
"auth": {
  "username": "your_username",
  "password": "your_password"
}
Sending IP

Must be on your account's IP allowlist, or requests fail with RC_IP_NOT_ALLOWED.

Transport

HTTPS strongly recommended for all production traffic.

Credential rotation

Contact support to rotate a compromised password immediately.

Send SMS

Submit a text message.

POST /bulk/sendsms

Send one JSON object per request. The same endpoint handles single messages and each message in a bulk job — call it once per recipient, or fan requests out from your own queue. There are three message types: GSM-encoded text, Unicode text, and WAP Service Indication (WSI) links — pick the one you need with type and dcs.

Common parameters

ParameterTypeRequiredDescription
typestringRequiredMessage type — "text" or "wsi".
senderstringRequiredAlphanumeric or numeric originator address (your sender ID).
receiverstringRequiredDestination number in E.164 format, without the leading + (e.g. 4179123456).
authobjectRequiredYour username / password credentials.
dlrMaskintegerOptionalBitmask of DLR events to subscribe to. Default 19 (all final statuses). See Delivery reports.
dlrUrlstringOptionalHTTPS callback URL we POST delivery reports to.
flashbooleanOptionalDeliver as a Flash SMS (displays immediately, not stored on the handset).
validityPeriodMinutesintegerOptionalHow long the message stays valid for delivery attempts. Defaults to 24 hours if omitted.
customobjectOptionalFree-form JSON echoed back verbatim in every DLR for this message — handy for correlating with your own IDs.

Text message fields

ParameterTypeRequiredDescription
textstringRequiredUTF-8 encoded message body.
dcsstringRequiredCharacter encoding — "GSM" for standard GSM 03.38, or "UCS" for Unicode (emoji, non-Latin scripts).
bash
CONTENT='{
  "type": "text",
  "auth": {"username": "your_username", "password": "your_password"},
  "sender": "SMSwarriors",
  "receiver": "4179123456",
  "dcs": "GSM",
  "text": "This is a test message",
  "dlrMask": 19,
  "dlrUrl": "https://your-app.com/dlr-callback"
}'

curl -L "https://<your-api-host>/bulk/sendsms" \
  -H "Content-Type: application/json" \
  -X POST -d "$CONTENT"
Node.js — fetch
const res = await fetch("https://<your-api-host>/bulk/sendsms", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    type: "text",
    auth: { username: "your_username", password: "your_password" },
    sender: "SMSwarriors",
    receiver: "4179123456",
    dcs: "GSM",
    text: "This is a test message",
    dlrMask: 19,
    dlrUrl: "https://your-app.com/dlr-callback",
  }),
});

const data = await res.json();
console.log(data.msgId, data.numParts);
PHP — cURL
$payload = json_encode([
    "type" => "text",
    "auth" => ["username" => "your_username", "password" => "your_password"],
    "sender" => "SMSwarriors",
    "receiver" => "4179123456",
    "dcs" => "GSM",
    "text" => "This is a test message",
    "dlrMask" => 19,
    "dlrUrl" => "https://your-app.com/dlr-callback",
]);

$ch = curl_init("https://<your-api-host>/bulk/sendsms");
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_POSTFIELDS => $payload,
    CURLOPT_HTTPHEADER => ["Content-Type: application/json"],
    CURLOPT_RETURNTRANSFER => true,
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
JSON — Unicode (UCS) message
{
  "type": "text",
  "auth": {"username": "your_username", "password": "your_password"},
  "sender": "SMSwarriors",
  "receiver": "4179123456",
  "dcs": "UCS",
  "text": "Message with UTF-8 characters üöä€ 你好",
  "dlrMask": 19,
  "dlrUrl": "https://your-app.com/dlr-callback"
}

Use "dcs": "UCS" for emoji, Arabic, Cyrillic, CJK or any character outside the GSM 03.38 alphabet. We convert UTF-8 input to UTF-16 automatically — no manual encoding needed on your side.

WAP push

Sending a WSI (WAP Service Indication).

A WSI delivers a clickable link with a title, rendered natively by the handset — useful for one-tap links in place of a plain-text URL. Use the same endpoint with type: "wsi".

ParameterTypeRequiredDescription
urlstringRequiredURL displayed and opened by the WSI.
titlestringRequiredTitle text shown alongside the link.
JSON — WSI request
{
  "type": "wsi",
  "auth": {"username": "your_username", "password": "your_password"},
  "sender": "SMSwarriors",
  "receiver": "41787078880",
  "url": "https://smswarriors.com/",
  "title": "SMSwarriors",
  "dlrMask": 19,
  "dlrUrl": "https://your-app.com/dlr-callback"
}
Responses

What comes back from the submission call.

HTTP 202 Accepted
{
  "msgId": "9325d0a8-2638-11e6-afe7-bffc7cc8fa4f",
  "numParts": 2
}

msgId is a UUID you'll see again in every DLR for this message. numParts tells you how many physical SMS segments the message was split into (billing is per part).

HTTP 420 Rejected
{
  "error": {
    "code": "107",
    "message": "Invalid sender"
  }
}

The submission was rejected outright — see the full error code table below for what to fix.

Retry policy: don't retry a 420 rejection except for error 105 (throttling). For a 500 server error, wait at least 1 minute before retrying.
Reference

Submission error codes.

Returned in the error.code field of a 420 response.

CodeNameMeaning
101RC_APPLICATION_ERRORSystem-level application failure.
102RC_ENCODING_ERRORUnsupported encoding, or encoding doesn't match content.
103RC_NO_ACCOUNTInvalid username/password combination.
104RC_IP_NOT_ALLOWEDSending IP address isn't on your account's allowlist.
105RC_THROTTLING_ERRORRate limit exceeded. Safe to retry after 1 second.
106RC_BLACKLISTED_SENDERSender ID is blacklisted at the destination.
107RC_INVALID_SENDERIllegal characters in the sender field.
108RC_MESSAGE_TOO_LONGMessage exceeds the maximum allowed length.
109RC_BAD_CONTENT_FORMATThe text parameter format is invalid.
110RC_MISSING_MANDATORY_PARAMETERA required field was omitted.
111RC_UNKNOWN_MESSAGE_TYPEtype isn't "text" or "wsi".
112RC_BAD_PARAMETER_VALUEA parameter value is malformed.
113RC_NO_CREDITInsufficient account balance.
114RC_NO_ROUTENo delivery route to the destination.
115RC_CONCAT_ERRORConcatenation would exceed the maximum SMS part limit.
116RC_LOOP_DETECTEDA message loop was detected and the send was blocked.
Delivery reports

Real-time DLR callbacks.

POST your dlrUrl

If you set dlrUrl on submission, we POST a JSON delivery report to that URL as the message moves through the network — from acceptance by the carrier through to the handset (or failure).

Message lifecycle

Every message resolves to one of four outcomes. Each has its own reasons and its own timing.

DELIVERED

Delivered to the handset as fast as possible. Once the destination confirms, we send a DELIVERED DLR.

UNDELIVERED

Delivery to the handset failed and no further attempts are made. Common causes: nonexistent number, no route to destination, the validity period expiring (24 hours by default), or a permanent network error.

REJECTED

Can happen two ways: immediately, as an HTTP 420 error response to your submission (invalid credentials, no credit, bad sender, malformed request) — or later, as a REJECTED DLR after we'd already accepted it (restricted destination, unsupported content, disabled account).

BUFFERED

A temporary hold — absent subscriber, full handset buffer, or a network/device issue. If your dlrMask includes it, you get one DLR per retry attempt. Retries stop on success, a permanent error, or when the validity period expires.

DLR events & mask

Choose which events you want with dlrMask — add the values together. Default is 19 (Delivered + Undelivered + Rejected — the three final states).

EventMask valueStatusMeaning
DELIVERED1FinalConfirmed delivery to the handset.
UNDELIVERED2FinalDelivery to the handset failed.
BUFFERED4TemporaryQueued at the SMSC, retry in progress.
SENT_TO_SMSC8TemporaryHanded off to the carrier's SMSC.
REJECTED16FinalRejected at the SMSC layer.

Callback payload

JSON — POST to your dlrUrl
{
  "msgId": "9325d0a8-2638-11e6-afe7-bffc7cc8fa4f",
  "event": "DELIVERED",
  "errorCode": 0,
  "errorMessage": "",
  "partNum": 0,
  "numParts": 1,
  "accountName": "your_username",
  "sendTime": 0,
  "dlrTime": 2,
  "custom": {}
}
FieldTypeDescription
msgIdstringMatches the msgId from the original submission response.
eventstringOne of DELIVERED, UNDELIVERED, BUFFERED, SENT_TO_SMSC, REJECTED.
errorCodeintegerFailure reason code — 0 means no error. See table below.
errorMessagestringHuman-readable description of the error, if any.
partNumintegerZero-based index of this part within a concatenated message.
numPartsintegerTotal parts in the message (1 for a single SMS).
accountNamestringYour account username.
sendTimeintegerSeconds from submission to SMSC delivery.
dlrTimeintegerSeconds from SMSC delivery to this report.
customobjectEcho of the custom object you sent at submission, if any.
mcc / mncstringMobile Country/Network Code — account-dependent, not always present.
countrystringISO2 destination country — account-dependent.
price / currencystringInformational cost of the SMS — account-dependent.
Concatenated messages get one DLR per part, all sharing the same msgId but with increasing partNum (0, 1, …) up to numParts − 1. Wait for every part before deciding a message fully delivered.

DLR error codes

Full list — most of these come straight from the mobile network (HLR/MSC/SGSN-level failures); the ones above 500 are gateway-level.

CodeMeaning
0No error.
1Unknown subscriber.
9Illegal subscriber.
11Teleservice not provisioned.
13Call barred.
15CUG (closed user group) reject.
19No SMS support in MS (handset).
20Error in MS (handset).
21Facility not supported.
22Memory capacity exceeded.
29Absent subscriber — handset offline or unreachable.
30MS busy for MT SMS.
36Network/protocol failure.
44Illegal equipment.
60No paging response.
61GMSC congestion.
63HLR timeout.
64MSC/SGSN timeout.
70SMRSE/TCP error.
72MT congestion.
75GPRS suspended.
80No paging response via MSC.
81IMSI detached.
82Roaming restriction.
83Deregistered in HLR for GSM.
84Purged for GSM.
85No paging response via SGSN.
86GPRS detached.
87Deregistered in HLR for GPRS.
88MS purged for GPRS.
89Unidentified subscriber via MSC.
90Unidentified subscriber via SGSN.
112Originator missing credit on prepaid account.
113Destination missing credit on prepaid account.
114Error in prepaid system.
500Other error.
989Supplier rejected the SMS.
990HLR failure.
991Rejected by message text filter.
992Ported numbers not supported on destination.
993Blacklisted sender.
994Account has no credit.
995Undeliverable number.
996Validity period expired before delivery.
997Blacklisted recipient.
998No route to destination.
999Repeated submission — possible loop detected.
Encoding

Character limits & long-message concatenation.

Messages longer than one segment are split automatically — no extra work required — but part count affects billing, so it's worth knowing the limits.

dcs: "GSM"

GSM 03.38

  • 160 characters per single segment.
  • ~153 characters per segment once concatenated (7-byte UDH header eats into the 140-byte budget).
  • Covers the standard GSM alphabet plus escape sequences (€, brackets, etc. count double).
dcs: "UCS"

Unicode (UCS-2 / UTF-16)

  • 70 characters per single segment.
  • ~67 characters per segment once concatenated.
  • Supports any UTF-8 input — emoji, Arabic, Cyrillic, CJK. We handle the UTF-8 → UTF-16 conversion server-side.
Billing applies per physical SMS part, not per API call — check numParts in the submission response to know exactly what a message will cost.

Sender ID character support (alphanumeric)

Letters and digits (0–9, a–z, A–Z) are always supported. Beyond that, only the special characters below are allowed in an alphanumeric sender ID.

SupportedASCII codeNot supportedASCII code
SPACE0x20$0x24
!0x21@0x40
"0x22[0x5B
#0x23\0x5C
%0x25]0x5D
&0x26^0x5E
'0x27_0x5F
(0x28`0x60
)0x29{0x7B
*0x2A|0x7C
+0x2B}0x7D
,0x2C~0x7E
-0x2D
.0x2E
/0x2F
:0x3A
;0x3B
<0x3C
=0x3D
>0x3E
?0x3F
Full GSM 7-bit default alphabet (advanced reference)

The GSM 03.38 default 7-bit alphabet used for "dcs": "GSM" message bodies. Find a character's row (low nibble) and column (high nibble) to get its code point — e.g. A is row 1, column 0x40 → 0x41.

Dec / Hex0x0_0x1_0x2_0x3_0x4_0x5_0x6_0x7_
0 / 0x0@ΔSP0¡P¿p
1 / 0x1£_!1AQaq
2 / 0x2$Φ"2BRbr
3 / 0x3¥Γ#3CScs
4 / 0x4èΛ¤4DTdt
5 / 0x5éΩ%5EUeu
6 / 0x6ùΠ&6FVfv
7 / 0x7ìΨ'7GWgw
8 / 0x8òΣ(8HXhx
9 / 0x9ÇΘ)9IYiy
10 / 0xALFΞ*:JZjz
11 / 0xBØESC+;KÄkä
12 / 0xCøÆ,<LÖlö
13 / 0xDCRæ-=MÑmñ
14 / 0xEÅß.>NÜnü
15 / 0xFåÉ/?O§oà

A few characters aren't in the base table — they're sent as a 2-character escape sequence (ESC + code below), so each one counts as 2 characters against the GSM segment limit.

CharacterEscape sequence
ESC 0x65
Form feedESC 0x0A
[ESC 0x3C
\ESC 0x2F
]ESC 0x3E
^ESC 0x14
{ESC 0x28
|ESC 0x40
}ESC 0x29
~ESC 0x3D
Rate limits

Throttling & retries.

Submitting faster than your account's allowed rate returns error 105 — RC_THROTTLING_ERROR. This is the one rejection that's safe to retry automatically.

Recommended retry policy
  • Error 105 (throttling): back off ~1 second, then resubmit.
  • HTTP 500: wait at least 1 minute before retrying.
  • Any other 420 rejection: fix the request — don't blind-retry.
  • Need a higher sustained rate for a bulk campaign or wholesale volume? Talk to your account manager — throughput is tunable per account.

Ready to integrate?

Get your API username, password and sending host — most accounts are ready to send test messages the same day.

Need SMPP 3.4 instead?

High-volume and wholesale accounts can bind directly over SMPP for lower latency and tighter throughput control — endpoints, bind credentials and throughput tiers.