Send SMS
Send a single SMS message to a phone number.
Endpoint​
POST https://<api_key>:<api_token><subdomain>/v1/Accounts/<account_sid>/Sms/send
Request Parameters​
| Parameter | Required | Type | Description |
|---|---|---|---|
From | Mandatory | String | ExoPhone or approved Sender ID |
To | Mandatory | String | Recipient mobile number, preferably E.164 format |
Body | Mandatory | String | Message content, max 2000 characters |
EncodingType | Optional | String | plain (default) or unicode |
StatusCallback | Optional | String | URL to receive delivery status notifications |
DltEntityId | Mandatory (India) | String | DLT entity registration ID |
DltTemplateId | Optional | String | DLT-approved template ID |
SmsType | Optional | String | transactional, transactional_opt_in, or promotional |
CustomField | Optional | String | Reference identifier for your application |
Priority | Optional | String | normal (default) or high |
DLT Compliance (Required for India)
DLT (Distributed Ledger Technology) is a regulatory requirement by TRAI for all SMS sent to Indian phone numbers. You must:
- Register as a business entity on a DLT portal (Jio, Airtel, Vodafone-Idea, or BSNL)
- Get your DLT Entity ID — pass this as
DltEntityIdin every SMS request - Register message templates — get approved template IDs to pass as
DltTemplateId - Register your Sender ID (header) — e.g., "EXOTEL", used in the
Fromparameter
Without DLT registration, SMS to Indian numbers will be blocked by telecom operators. See the SMS Overview for more details.
Code Examples​
- cURL
- Node.js
- Python
- PHP
- Ruby
curl -X POST https://<your_api_key>:<your_api_token>@api.exotel.com/v1/Accounts/<your_sid>/Sms/send \
-d "From=EXOTEL" \
-d "To=+919876543210" \
-d "Body=Your OTP is 123456" \
-d "DltEntityId=1234567890" \
-d "DltTemplateId=9876543210"
const apiKey = '<your_api_key>';
const apiToken = '<your_api_token>';
const accountSid = '<your_sid>';
const url = `https://api.exotel.com/v1/Accounts/${accountSid}/Sms/send`;
const params = new URLSearchParams({
From: 'EXOTEL',
To: '+919876543210',
Body: 'Your OTP is 123456',
DltEntityId: '1234567890',
DltTemplateId: '9876543210',
});
const response = await fetch(url, {
method: 'POST',
headers: {
'Authorization': 'Basic ' + Buffer.from(`${apiKey}:${apiToken}`).toString('base64'),
'Content-Type': 'application/x-www-form-urlencoded',
},
body: params,
});
const data = await response.json();
console.log(data);
import requests
data = {
'From': 'EXOTEL',
'To': '+919876543210',
'Body': 'Your OTP is 123456',
'DltEntityId': '1234567890',
'DltTemplateId': '9876543210'
}
response = requests.post(
'https://<your_api_key>:<your_api_token>@api.exotel.com/v1/Accounts/<your_sid>/Sms/send',
data=data
)
print(response.json())
<?php
$data = array(
'From' => 'EXOTEL',
'To' => '+919876543210',
'Body' => 'Your OTP is 123456',
'DltEntityId' => '1234567890',
'DltTemplateId' => '9876543210'
);
$response = Requests::post(
'https://<your_api_key>:<your_api_token>@api.exotel.com/v1/Accounts/<your_sid>/Sms/send',
array(),
$data
);
?>
require 'net/http'
require 'uri'
uri = URI.parse('https://<your_api_key>:<your_api_token>@api.exotel.com/v1/Accounts/<your_sid>/Sms/send')
request = Net::HTTP::Post.new(uri)
request.set_form_data(
'From' => 'EXOTEL',
'To' => '+919876543210',
'Body' => 'Your OTP is 123456',
'DltEntityId' => '1234567890',
'DltTemplateId' => '9876543210'
)
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
http.request(request)
end
puts response.body
Response​
{
"SMSMessage": {
"Sid": "sms_sid_value",
"AccountSid": "your_sid",
"From": "EXOTEL",
"To": "+919876543210",
"Body": "Your OTP is 123456",
"Status": "queued",
"DateCreated": "2024-01-15 10:30:00",
"DateUpdated": "2024-01-15 10:30:00",
"DateSent": null,
"Price": null,
"Uri": "/v1/Accounts/your_sid/SMS/Messages/sms_sid_value"
}
}
Response Fields​
| Field | Type | Description |
|---|---|---|
Sid | String | Unique SMS identifier |
AccountSid | String | Your Exotel account SID |
From | String | Sender ID or ExoPhone |
To | String | Recipient number |
Body | String | Message content |
Status | String | Current SMS status (see Status Codes) |
DateCreated | DateTime | When the request was received |
DateSent | DateTime | When the SMS was sent to the operator |
Price | Double | Amount charged |
HTTP Status Codes​
| Code | Description |
|---|---|
| 200 | Request accepted successfully |
| 400 | Bad request — missing or invalid parameters |
| 401 | Authentication failed |
| 403 | Forbidden — insufficient permissions |
| 429 | Rate limit exceeded |
| 500 | Internal server error |
note
A 200 response means the SMS request was accepted, not that it was delivered. Use the StatusCallback webhook or the SMS Details endpoint to confirm delivery.
Error Responses​
When a request fails, the API returns an error JSON with details:
400 — Missing required parameter:
{
"RestException": {
"Status": 400,
"Message": "The 'From' parameter is required."
}
}
401 — Authentication failed:
{
"RestException": {
"Status": 401,
"Message": "Authentication failed. Verify your API key and token."
}
}
429 — Rate limit exceeded:
{
"RestException": {
"Status": 429,
"Message": "Rate limit exceeded. Max 200 requests per minute."
}
}
For a complete list of error codes and troubleshooting steps, see the Error Code Dictionary.