Developer docs · Updated

SMS HTTP(S) API reference

The Techalpha SMS platform HTTP(S) API lets external systems submit SMS messages and request their delivery status.

Methods
GET or POST
Auth
Username and password
Default throughput
20 messages per second
Status window
48 hours
Request · submit an SMS
curl --get "https://<host>:<port>/api" \
  --data-urlencode "username=<username>" \
  --data-urlencode "password=<password>" \
  --data-urlencode "ani=<ani>" \
  --data-urlencode "dnis=<dnis>" \
  --data-urlencode "message=<message>" \
  --data-urlencode "command=submit" \
  --data-urlencode "serviceType=" \
  --data-urlencode "longMessageMode=2"
Response · 200 OK
HTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8

{"message_id":"alss-a1b2c3d4-e5f67890"}

The body is JSON even though the header says text/html. Keep the message ID to query delivery status for up to 48 hours.

Authentication

Each interconnection has its own username and password, issued by Techalpha. Request credentials from Techalpha for every new interconnection. Credentials must be sent with every API request; requests without valid credentials are rejected as unauthorized.

Endpoint

Requests can be sent using GET or POST to the URL issued for your interconnection, in this format:

Base URL
https://<host>:<port>/api?

For POST requests, the URL must not include "api?". HTTPS is recommended; plain HTTP is supported but sends credentials unencrypted.

SMS submission

Request format

GET
https://<host>:<port>/api?username=<username>&password=<password>&ani=<ani>&dnis=<dnis>&message=<message>&command=submit&serviceType=<serviceType>&longMessageMode=<longMessageMode>

Parameters

ParameterRequiredDescription
usernameYesLogin issued for your interconnection.
passwordYesPassword issued for your interconnection.
aniYesSender ID (caller ID). Alphanumeric, up to 32 characters. Destination routes may apply stricter limits; many carriers cap alphanumeric sender IDs at 11 characters.
dnisYesDestination number in international E.164 format, up to 15 digits.
messageYesMessage text. URL-encode the value when sending via GET.
commandYesRequest type. Must be set to submit.
serviceTypeYesService type issued for your interconnection. The parameter must be present but may be left blank.
longMessageModeNoHow long messages are handled. Default: 1 (cut). See below.

Long message handling

ValueModeBehavior
1cut (default)Trims the message to the first 140 bytes and sends only that. The rest of the text is dropped.
2splitSplits the message into segments using a UDH header.
3split_sarSplits the message into segments using SAR parameters.
4payloadSends the full text in the message_payload field via SMPP.

Important: the default (1, cut) silently truncates long messages. Set longMessageMode to 2 or 3 explicitly if your messages may exceed one segment.

When splitting (modes 2 and 3), segment size depends on the encoding:

dataCodingSingle message limitPer segment when split
0 (GSM 7-bit)160 characters152 characters
1 to 7140 characters134 characters
8 (UCS-2 / Unicode)70 characters (140 bytes)67 characters (134 bytes)

Segment count = message length divided by the per-segment size, rounded up.

Responses

HTTP statusMeaningResponse body
200 OKMessage acceptedJSON with the message ID, e.g. {"message_id":"alss-a1b2c3d4-e5f67890"}
400 Bad RequestRejected: no compatible route, or unknown usernamePlain-text reason, e.g. NO ROUTES or Unknown username
401 UnauthorizedIncorrect passwordPlain-text reason, e.g. Incorrect password

Successful responses are returned with Content-Type: text/html; charset=UTF-8 even though the body is JSON. Parse the body as JSON regardless of the header.

Sample response
HTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8

{"message_id":"alss-a1b2c3d4-e5f67890"}

Code examples

These examples use GET with URL-encoded values. The POST body format is not yet part of this specification; confirm it with your account team before you switch to POST.

cURL
curl --get "https://<host>:<port>/api" \
  --data-urlencode "username=<username>" \
  --data-urlencode "password=<password>" \
  --data-urlencode "ani=<ani>" \
  --data-urlencode "dnis=<dnis>" \
  --data-urlencode "message=<message>" \
  --data-urlencode "command=submit" \
  --data-urlencode "serviceType=" \
  --data-urlencode "longMessageMode=2"
Python
import json
import requests

params = {
    "username": "<username>",
    "password": "<password>",
    "ani": "<ani>",
    "dnis": "<dnis>",
    "message": "<message>",
    "command": "submit",
    "serviceType": "",     # must be present, may be blank
    "longMessageMode": 2,  # split instead of the default cut
}

# requests URL-encodes every parameter value
resp = requests.get("https://<host>:<port>/api", params=params, timeout=30)

if resp.status_code == 200:
    # Body is JSON even though Content-Type is text/html
    message_id = json.loads(resp.text)["message_id"]
    print("Accepted:", message_id)
else:
    # 400: NO ROUTES or Unknown username. 401: Incorrect password.
    print("Rejected:", resp.status_code, resp.text)
Node.js
const params = new URLSearchParams({
  username: "<username>",
  password: "<password>",
  ani: "<ani>",
  dnis: "<dnis>",
  message: "<message>",
  command: "submit",
  serviceType: "",      // must be present, may be blank
  longMessageMode: "2", // split instead of the default cut
});

// URLSearchParams URL-encodes every parameter value
const res = await fetch(`https://<host>:<port>/api?${params}`);
const body = await res.text();

if (res.ok) {
  // Body is JSON even though Content-Type is text/html
  const { message_id } = JSON.parse(body);
  console.log("Accepted:", message_id);
} else {
  // 400: NO ROUTES or Unknown username. 401: Incorrect password.
  console.error("Rejected:", res.status, body);
}

Delivery status request

Request format

GET
https://<host>:<port>/api?username=<username>&password=<password>&messageId=<messageId>&command=query

Parameters

All parameters are mandatory.

ParameterDescription
usernameLogin issued for your interconnection.
passwordPassword issued for your interconnection.
messageIdMessage ID returned in the submit response.
commandRequest type. Must be set to query.

Status can be requested within 48 hours of submission.

Status values

StatusMeaning
ENROUTEMessage is in the routing stage.
SENTMessage was delivered to the SMSC.
ACCEPTDMessage was accepted by the SMSC.
DELIVRDMessage was delivered to the subscriber.
UNDELIVMessage could not be delivered.
EXPIREDMessage storage period expired.
DELETEDMessage was deleted.
REJECTDMessage was rejected by the SMSC.
UNKNOWNMessage status is unknown.

Responses

HTTP statusMeaningResponse body
200 OKStatus returnedJSON, e.g. {"status":"DELIVRD"}
200 OKMessage ID not foundJSON with an error in the status field, e.g. {"status":"message ID alss-some-message-id not found"}
400 Bad RequestInvalid request, e.g. unknown usernamePlain-text reason, e.g. Unknown username
401 UnauthorizedIncorrect passwordPlain-text reason, e.g. Incorrect password

A "not found" result returns 200 OK, not 404. Check the value of status against the list above before treating a response as a valid delivery status.

Sample response
HTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8

{"status":"DELIVRD"}

Code examples

cURL
curl --get "https://<host>:<port>/api" \
  --data-urlencode "username=<username>" \
  --data-urlencode "password=<password>" \
  --data-urlencode "messageId=<messageId>" \
  --data-urlencode "command=query"
Python
import json
import requests

STATUSES = {"ENROUTE", "SENT", "ACCEPTD", "DELIVRD", "UNDELIV",
            "EXPIRED", "DELETED", "REJECTD", "UNKNOWN"}

params = {
    "username": "<username>",
    "password": "<password>",
    "messageId": "<messageId>",
    "command": "query",
}

resp = requests.get("https://<host>:<port>/api", params=params, timeout=30)

if resp.status_code == 200:
    status = json.loads(resp.text)["status"]
    if status in STATUSES:
        print("Status:", status)
    else:
        # A missing message ID also returns 200, with an error string
        print("Lookup failed:", status)
else:
    print("Rejected:", resp.status_code, resp.text)
Node.js
const STATUSES = new Set([
  "ENROUTE", "SENT", "ACCEPTD", "DELIVRD", "UNDELIV",
  "EXPIRED", "DELETED", "REJECTD", "UNKNOWN",
]);

const params = new URLSearchParams({
  username: "<username>",
  password: "<password>",
  messageId: "<messageId>",
  command: "query",
});

const res = await fetch(`https://<host>:<port>/api?${params}`);
const body = await res.text();

if (res.ok) {
  const { status } = JSON.parse(body);
  if (STATUSES.has(status)) {
    console.log("Status:", status);
  } else {
    // A missing message ID also returns 200, with an error string
    console.error("Lookup failed:", status);
  }
} else {
  console.error("Rejected:", res.status, body);
}

Delivery reports and throughput

Delivery reports (DLR)

Delivery report settings are configured per customer, per connection. Contact your Techalpha account team to set up DLR for your interconnection. Delivery status can also be polled at any time within 48 hours using command=query.

Throughput

Each connection has a default limit of 20 messages per second (TPS). Higher throughput is available on request and is subject to commercial discussion with your Techalpha account team.

Integration notes and best practices

  1. Use HTTPS; prefer POST once the body format is confirmed with your account team. With GET, credentials and message text appear in the URL and can be stored in proxy and server logs.
  2. Set longMessageMode explicitly. The default (1, cut) truncates long messages without warning. Use 2 or 3 for messages that may exceed one segment.
  3. Parse response bodies as JSON. Successful responses carry Content-Type: text/html but the body is JSON.
  4. Read the body on 400 errors. Both routing failures (NO ROUTES) and authentication failures (Unknown username) return 400. Use the body text to tell them apart.
  5. Validate status values. A message ID that is not found returns 200 OK with an error string in status. Treat any value outside the status list as an error.
  6. Query status within 48 hours. Status is not available after 48 hours from submission.
  7. Check sender ID length per destination. The platform accepts up to 32 characters, but many destinations limit alphanumeric sender IDs to 11 characters.
  8. URL-encode parameter values on GET requests, especially message and ani.

Get credentials for your interconnection

Talk to the Techalpha team about API access, delivery reports and higher throughput.

Talk to an expert