SMS REST API
The SMS REST API lets you send text messages to any mobile number worldwide through a single HTTPS endpoint. It handles encoding, multipart splitting, and delivery tracking automatically — you provide the destination, sender ID, and message body.
Download the SMS Universal Postman Collection.
Quick Start
The fastest way to send an SMS: make a POST request with your API key, destination number, sender, and message text. The API returns a messageId you can use to track delivery.
Base URL
https://lora-api.agiletelecom.com
Authentication
Every request requires an API key passed in the X-Api-Key header. Generate your key from the Agile Telecom portal under Settings > API Keys.
| Header | Required | Description |
|---|---|---|
Content-Type | Yes | Must be application/json |
X-Api-Key | Yes | Your API key from the portal |
Environment Variables
These variables are used throughout the examples. Replace them with your actual values.
| Variable | Example | Description |
|---|---|---|
BASE_URL | https://lora-api.agiletelecom.com | API base URL |
API_KEY | ak_live_***** | Your API key |
DESTINATION | +393471234567 | Recipient number in international format (with country code) |
SENDER | MyCompany | Alphanumeric (max 11 chars) or numeric (max 16 digits), registered on the portal |
Send an SMS
Send a single text message to a phone number. This is the most common operation — it covers 90% of integration use cases.
Endpoint: POST /api/message-server/sms/send
- cURL
- Python
- Node.js
- PHP
- Java
- C#
- Go
- Ruby
curl -X POST https://lora-api.agiletelecom.com/api/message-server/sms/send \
-H "Content-Type: application/json" \
-H "X-Api-Key: YOUR_API_KEY" \
-d '{
"destination": "+393471234567",
"sender": "MyCompany",
"body": "Hello from Agile Telecom SMS API!"
}'
import requests
response = requests.post(
"https://lora-api.agiletelecom.com/api/message-server/sms/send",
headers={
"Content-Type": "application/json",
"X-Api-Key": "YOUR_API_KEY"
},
json={
"destination": "+393471234567",
"sender": "MyCompany",
"body": "Hello from Agile Telecom SMS API!"
}
)
data = response.json()
print(f"Message ID: {data['messageId']}")
print(f"Parts: {data['results']['sms']['parts']}")
const response = await fetch(
"https://lora-api.agiletelecom.com/api/message-server/sms/send",
{
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Api-Key": "YOUR_API_KEY"
},
body: JSON.stringify({
destination: "+393471234567",
sender: "MyCompany",
body: "Hello from Agile Telecom SMS API!"
})
}
);
const data = await response.json();
console.log(`Message ID: ${data.messageId}`);
console.log(`Parts: ${data.results.sms.parts}`);
$ch = curl_init("https://lora-api.agiletelecom.com/api/message-server/sms/send");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-Api-Key: YOUR_API_KEY"
],
CURLOPT_POSTFIELDS => json_encode([
"destination" => "+393471234567",
"sender" => "MyCompany",
"body" => "Hello from Agile Telecom SMS API!"
])
]);
$response = curl_exec($ch);
curl_close($ch);
$data = json_decode($response, true);
echo "Message ID: " . $data["messageId"] . "\n";
echo "Parts: " . $data["results"]["sms"]["parts"] . "\n";
import java.net.http.*;
import java.net.URI;
HttpClient client = HttpClient.newHttpClient();
String json = """
{
"destination": "+393471234567",
"sender": "MyCompany",
"body": "Hello from Agile Telecom SMS API!"
}
""";
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://lora-api.agiletelecom.com/api/message-server/sms/send"))
.header("Content-Type", "application/json")
.header("X-Api-Key", "YOUR_API_KEY")
.POST(HttpRequest.BodyPublishers.ofString(json))
.build();
HttpResponse<String> response = client.send(request,
HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-Api-Key", "YOUR_API_KEY");
var payload = new
{
destination = "+393471234567",
sender = "MyCompany",
body = "Hello from Agile Telecom SMS API!"
};
var content = new StringContent(
System.Text.Json.JsonSerializer.Serialize(payload),
System.Text.Encoding.UTF8,
"application/json"
);
var response = await client.PostAsync(
"https://lora-api.agiletelecom.com/api/message-server/sms/send",
content
);
var result = await response.Content.ReadAsStringAsync();
Console.WriteLine(result);
package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"io"
)
func main() {
payload, _ := json.Marshal(map[string]string{
"destination": "+393471234567",
"sender": "MyCompany",
"body": "Hello from Agile Telecom SMS API!",
})
req, _ := http.NewRequest("POST",
"https://lora-api.agiletelecom.com/api/message-server/sms/send",
bytes.NewBuffer(payload))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-Api-Key", "YOUR_API_KEY")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))
}
require "net/http"
require "json"
require "uri"
uri = URI("https://lora-api.agiletelecom.com/api/message-server/sms/send")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Post.new(uri)
request["Content-Type"] = "application/json"
request["X-Api-Key"] = "YOUR_API_KEY"
request.body = {
destination: "+393471234567",
sender: "MyCompany",
body: "Hello from Agile Telecom SMS API!"
}.to_json
response = http.request(request)
data = JSON.parse(response.body)
puts "Message ID: #{data['messageId']}"
puts "Parts: #{data['results']['sms']['parts']}"
Response
On success, the API returns HTTP 202 Accepted:
{
"messageId": "9b3dfde1-7ee6-411f-8f56-6cb18106e0cc",
"simulation": false,
"results": {
"sms": {
"accepted": true,
"unicode": false,
"parts": 1
}
}
}
| Field | Description |
|---|---|
messageId | Unique identifier for tracking delivery status via webhooks |
simulation | false = real send, true = dry run (no SMS dispatched) |
results.sms.accepted | true means the message entered the delivery pipeline |
results.sms.unicode | Whether the message required Unicode encoding (emoji, CJK, Arabic, etc.) |
results.sms.parts | Number of SMS parts — messages over 160 chars (GSM) or 70 chars (Unicode) are split automatically |
Simulation Mode
Test your integration without sending a real SMS. The API validates everything — authentication, sender registration, message encoding — and returns a realistic response, but nothing reaches the recipient's phone.
Set "simulation": true in the request body. All other parameters stay the same.
{
"destination": "+393471234567",
"sender": "MyCompany",
"body": "This SMS will not be sent, it's only a simulation.",
"simulation": true
}
Use simulation mode during development and CI/CD pipeline testing. It validates your payload without consuming credits or reaching real phone numbers.
Scheduled Delivery
Queue an SMS for future delivery. The message is stored server-side and dispatched at the specified date and time. Useful for appointment reminders, marketing campaigns, and time-sensitive notifications.
Add scheduledDate in ISO 8601 format to the request body:
{
"destination": "+393471234567",
"sender": "MyCompany",
"body": "Your appointment is tomorrow at 10:00.",
"scheduledDate": "2025-10-30T18:50:00.000+0000",
"enableNotification": true
}
| Field | Description |
|---|---|
scheduledDate | Delivery date/time in ISO 8601 format with timezone offset |
enableNotification | Set to true to receive delivery callbacks for this message |
Always include the timezone offset (e.g., +0000 for UTC, +0100 for CET). Without it, the scheduled time may be interpreted incorrectly.
Personalized Messages with Placeholders
Insert dynamic values into your message text. Define placeholder tokens with curly braces in the body, then provide the replacement values in the placeholders object. Ideal for OTP codes, customer names, order numbers, and personalized notifications.
{
"destination": "+393471234567",
"sender": "MyCompany",
"body": "Hello {name}, your code is {code}.",
"placeholders": {
"name": "Luigi",
"code": "123456"
}
}
The recipient receives: "Hello Luigi, your code is 123456."
Tokens are case-sensitive: \{Name\} and \{name\} are different placeholders. Make sure the keys in placeholders match the tokens in body exactly.
Custom Message ID
By default, the system generates a UUID for each message. If you need to correlate messages with your internal records, pass your own messageId:
{
"destination": "+393471234567",
"sender": "MyCompany",
"body": "Order #12345 has been shipped.",
"messageId": "550e8400-e29b-41d4-a716-446655440000"
}
The same ID will appear in delivery callbacks, allowing you to match delivery status to your records.
UDH (User Data Header)
For advanced use cases like WAP Push, Flash SMS, or custom concatenation, you can include raw UDH data in hexadecimal format:
{
"destination": "+393471234567",
"sender": "MyCompany",
"body": "Message with custom UDH",
"udhData": "050003CC0201"
}
UDH is intended for developers who need low-level control over SMS PDU headers. Most integrations do not need this — the API handles concatenation automatically.
Delivery Callbacks (Webhooks)
When you configure a delivery URL in the Agile Telecom portal, the system sends an HTTP POST to your endpoint each time a message status changes. This lets you track whether each SMS was actually delivered to the recipient's device.
Webhook Payload
Your endpoint receives a JSON payload like this:
Delivered successfully:
{
"channel": "SMS",
"eventType": "DELIVERY",
"messageId": "813d4183-8d15-4aa3-a820-8d16a435bf77",
"destination": "+393401234567",
"statusCode": 3,
"description": "delivered",
"eventDate": "2025-11-24T16:10:21Z",
"price": 0.075,
"numPart": 1,
"totalParts": 2
}
Undeliverable:
{
"channel": "SMS",
"eventType": "DELIVERY",
"messageId": "813d4183-8d15-4aa3-a820-8d16a435bf77",
"destination": "+393401234567",
"statusCode": 6,
"description": "Undeliverable",
"eventDate": "2025-11-24T16:10:21Z",
"price": 0.075,
"numPart": 1,
"totalParts": 1
}
Webhook Fields
| Field | Description |
|---|---|
channel | Always "SMS" for this API |
eventType | Always "DELIVERY" — fired when the carrier confirms the final status |
messageId | Matches the ID returned when you sent the message |
destination | Recipient's phone number |
statusCode | Numeric delivery status (see table below) |
description | Human-readable status label |
eventDate | ISO 8601 timestamp of the delivery event |
price | Cost charged for this message part |
numPart | Part number (for multipart SMS) |
totalParts | Total parts in the message |
Delivery Status Codes
| Code | Status | What It Means |
|---|---|---|
| 1 | Accepted | Message entered the delivery pipeline, waiting to be sent |
| 2 | Rejected | Validation failed before sending (invalid sender, restricted destination, etc.) |
| 3 | Delivered | Confirmed delivered to the recipient's device |
| 4 | Expired | Delivery window elapsed — the phone may have been off or unreachable |
| 5 | Deleted | Message removed before delivery (by policy or on request) |
| 6 | Undeliverable | Permanently failed — number doesn't exist, network error, etc. |
Error Codes
If the API rejects your request, it returns one of these error codes alongside an HTTP error status:
| Code | Meaning | What to Check |
|---|---|---|
| 8 | Server error | Retry after a brief delay; contact support if persistent |
| 26 | Sender not authorized | Register the sender alias in the portal before using it |
| 100 | IP not allowed | Your server IP must be whitelisted — contact support |
SMS Encoding Reference
| Encoding | Max Chars per Part | When Used |
|---|---|---|
| GSM 7-bit | 160 | Standard Latin characters, digits, common symbols |
| Unicode (UCS-2) | 70 | Emoji, Chinese/Japanese/Korean, Arabic, Cyrillic, etc. |
| Multipart GSM | 153 per part | GSM messages over 160 chars (7 chars per part reserved for UDH) |
| Multipart Unicode | 67 per part | Unicode messages over 70 chars |
The API detects the encoding automatically based on your message content. You don't need to specify it.
- Alphanumeric sender: max 11 characters (letters, digits, spaces). Must be pre-registered in the portal.
- Numeric sender: max 16 digits. Useful for two-way SMS where recipients can reply.
Postman Collection
The Postman Collection contains pre-configured requests for every example on this page. Import it and set your variables to start testing in seconds.
- Download the collection
- Import into Postman via File > Import
- Set
BASE_URL,API_KEY,DESTINATION, andSENDERin the collection variables - Run any request — check the response for
messageIdandaccepted: true
Postman can generate code snippets in 15+ languages. Open any request, click the Code icon (</>) on the right, and select your language.
Support
For technical assistance, contact help@agiletelecom.com.
What's Next?
- SMS REST API (Legacy) — older HTTP endpoint, compatible with existing integrations
- SMPP Protocol — binary protocol for high-throughput sending
- Inbound SMS — receive messages on your dedicated number