🔐 Authentication
Each request must include the following header:
Authorization: Bearer your_token_here
1. Send SMS
https://devsms.uz/api/send_sms.php
Request Body:
{
"phone": "998901234567",
"message": "Test SMS xabari",
"from": "4546",
"callback_url": "https://your-domain.com/sms-callback"
}
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
| phone | string | Yes | Phone number (998901234567) |
| message | string | Yes | SMS message text |
| from | string | No | From (default: 4546) |
| callback_url | string | No | URL to receive SMS status updates (http:// or https://) |
| type | string | No | SMS type: eskiz (default), simple, universal_otp |
Response:
// To Uzbekistan number
{
"success": true,
"message": "SMS muvaffaqiyatli yuborildi",
"data": {
"sms_id": 123,
"request_id": "uuid-here",
"status": "sent",
"parts_count": 1,
"total_cost": "$0.005",
"balance": "$0.079",
"type": "eskiz"
}
}
1.1 Universal OTP SMS
https://devsms.uz/api/send_sms.php
Send verification codes using Eskiz-approved universal OTP templates. Company name is checked by AI moderation.
Universal templates are pre-approved by Eskiz. You only provide the company name and OTP code. If the company name is inappropriate, the SMS is NOT sent and your balance is NOT charged (the response returns charged: false). Abuse protection: after 20 CONSECUTIVE rejections, universal OTP is suspended for 24 hours — the counter resets to zero as soon as one name passes moderation. Every response includes reject_streak and remaining_attempts.
Request Body:
{
"phone": "998901234567",
"type": "universal_otp",
"template_type": 1,
"service_name": "TechShop",
"otp_code": "5678",
"callback_url": "https://your-site.com/callback"
}
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
| phone | string | Yes | Phone number (998901234567) |
| type | string | Yes | "universal_otp" |
| template_type | integer | Yes | Template type: 1=Operation verification, 2=Password reset, 3=Registration, 4=Login |
| service_name | string | Yes | Company/service name (2-50 chars, letters, numbers, spaces, dots, hyphens only) |
| otp_code | string | Yes | OTP verification code (4-8 digits) |
| callback_url | string | No | URL to receive SMS status updates (http:// or https://) |
About callbacks: a status callback is delivered only when the SMS was ACTUALLY sent to Eskiz. If the company name fails AI moderation, no SMS is sent and no request_id is created — in that case no callback arrives. That is exactly what charged: false in the response means.
Available templates:
MyService tizimi: {service_name} xizmatida amaliyotni tasdiqlash kodi: {otp_code}
MyService tizimi: {service_name} xizmatida parolni tiklash uchun tasdiqlash kodi: {otp_code}
MyService tizimi: {service_name} xizmatiga ro'yxatdan o'tish uchun tasdiqlash kodi: {otp_code}
MyService tizimi: {service_name} xizmatiga kirish uchun tasdiqlash kodi: {otp_code}
Response:
{
"success": true,
"message": "SMS muvaffaqiyatli yuborildi",
"data": {
"sms_id": 456,
"request_id": "uuid-here",
"status": "sent",
"parts_count": 1,
"total_cost": "$0.005",
"balance": "$0.079",
"type": "universal_otp"
}
}
Blocked SMS response:
{
"success": false,
"error": "Korxona nomi nomaqbul deb topildi: ... SMS yuborilmadi, lekin to'lov yechildi."
}
2. Get SMS History
https://devsms.uz/api/get_history.php
Query Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
| limit | integer | 50 | Number of SMS (max: 200) |
| offset | integer | 0 | Offset |
| status | string | null | Status filter (sent, delivered, failed) |
Example:
GET https://devsms.uz/api/get_history.php?limit=10&offset=0&status=delivered
Response:
{
"success": true,
"data": {
"history": [
{
"id": 123,
"phone_number": "998901234567",
"message": "Salom!",
"status": "delivered",
"parts_count": 1,
"total_cost": "$0.005",
"from_number": "4546",
"eskiz_request_id": "uuid-here",
"eskiz_message_id": "msg-id",
"sent_at": "2026-03-09 10:30:00",
"delivered_at": "2026-03-09 10:30:05",
"failed_at": null,
"created_at": "2026-03-09 10:29:58"
}
],
"count": 1,
"limit": 10,
"offset": 0
}
}
Status Timestamps
| Parameter | Description |
|---|---|
| created_at | Time SMS was created |
| sent_at | Time SMS was sent |
| delivered_at | Time SMS was delivered |
| failed_at | Time SMS failed |
3. Get Balance
https://devsms.uz/api/get_balance.php
Response:
{
"success": true,
"data": {
"balance": "$0.083",
"sms_price": "$0.005",
"statistics": {
"total_sms": 100,
"total_spent": "$0.414",
"today_sms": 10,
"today_spent": "$0.042",
"month_sms": 50,
"month_spent": "$0.207" }
}
}
4. Get SMS Status
https://devsms.uz/api/get_status.php
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
| sms_id | integer | SMS ID (database ID) |
| request_id | string | Eskiz request ID |
Example:
GET https://devsms.uz/api/get_status.php?sms_id=123
or
GET https://devsms.uz/api/get_status.php?request_id=uuid-here
Response:
{
"success": true,
"data": {
"id": 123,
"phone_number": "998901234567",
"message": "Salom!",
"status": "delivered",
"parts_count": 1,
"total_cost": "$0.005",
"from_number": "4546",
"eskiz_request_id": "uuid-here",
"eskiz_message_id": "msg-id",
"sent_at": "2026-03-09 10:30:00",
"delivered_at": "2026-03-09 10:30:05",
"failed_at": null,
"created_at": "2026-03-09 10:29:58",
"updated_at": "2026-03-09 10:30:05"
}
}
🔔 Callback URL
When you include the callback_url parameter when sending an SMS, a POST request will be sent to your URL when the SMS status changes (sent, delivered, failed).
When callback is sent:
Data sent to your URL:
{
"sms_id": 123,
"request_id": "uuid-here",
"phone": "998901234567",
"status": "delivered",
"sent_at": "2026-03-09 10:30:00",
"delivered_at": "2026-03-09 10:30:05",
"failed_at": null,
"timestamp": "2026-03-09 10:30:05"
}
Parameters:
| Parameter | Type | Description |
|---|---|---|
| sms_id | integer | SMS ID (database ID) |
| request_id | string | Eskiz request ID |
| phone | string | Phone number (998901234567) |
| status | string | Status filter (sent, delivered, failed) |
| sent_at | string|null | Time SMS was sent |
| delivered_at | string|null | Time SMS was delivered |
| failed_at | string|null | Time SMS failed |
| timestamp | string | Time SMS was created |
Note: Callback is sent with a 5 second timeout. If your server does not respond, no retry is attempted.
✅ callback_url requirements
For security, callback_url is validated twice: when the SMS is submitted, and again at the moment the callback is delivered. The following are rejected:
- Any scheme other than http:// or https://
- Internal and reserved IP ranges (127.0.0.1, 10.x, 172.16-31.x, 192.168.x, 169.254.x, ::1 and others)
- localhost and test/placeholder domains (example.com, test.com and similar)
- Domains that do not resolve in DNS
- URLs longer than 500 characters
At delivery time the host is re-validated and pinned to the confirmed IP address (DNS-rebinding protection). Redirects are not followed, the response is capped at 10 KB, and it is never returned to you.
🔐 Verifying callback authenticity (signature)
Every callback is sent with two additional headers. They prove the request genuinely came from DevSMS — without them, anyone who knows your callback URL could post a forged status.
X-DevSMS-Timestamp: 1786500000
X-DevSMS-Signature: sha256=9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08
Your signing key is in your cabinet: Settings → API. Never share it.
How to verify:
- Check that X-DevSMS-Timestamp is within 5 minutes of the current time (replay protection).
- Compute HMAC-SHA256 with your webhook key over the string "{timestamp}.{raw_body}".
- Compare the result to X-DevSMS-Signature using hash_equals (constant-time comparison).
<?php
// XOM body — json_decode qilmasdan!
$rawBody = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_DEVSMS_SIGNATURE'] ?? '';
$timestamp = $_SERVER['HTTP_X_DEVSMS_TIMESTAMP'] ?? '';
$secret = 'whsec_...'; // kabinetdagi kalitingiz
if (!is_numeric($timestamp) || abs(time() - (int)$timestamp) > 300) {
http_response_code(403); exit('stale');
}
$expected = 'sha256=' . hash_hmac('sha256', $timestamp . '.' . $rawBody, $secret);
if (!hash_equals($expected, $signature)) {
http_response_code(403); exit('bad signature');
}
// Imzo to'g'ri — endi ma'lumotga ishonish mumkin
$data = json_decode($rawBody, true);
IMPORTANT: the signature is computed over the RAW request body. Do not parse the JSON and re-encode it — key order will change and the signature will not match.
Nothing changed for existing integrations: the payload is unchanged and the headers are purely additive. You can add verification whenever you are ready.
📝 SMS Statuses
| Status | Description |
|---|---|
| pending | SMS not yet sent (pending) |
| sent | SMS sent (accepted by Eskiz) |
| delivered | SMS delivered to recipient |
| failed | SMS failed (rejected or undeliverable) |
❌ Errors
When an error occurs, the following format is returned:
{
"success": false,
"error": "Xatolik xabari"
}
HTTP Status Codes:
| Code | Description |
|---|---|
| 200 | Success |
| 400 | Bad Request |
| 401 | Authentication Error |
| 403 | Forbidden |
| 404 | Not Found |
| 500 | Server Error |
Tip:
You can use Postman or cURL to test the API.
🔧 cURL Examples
Send SMS:
curl -X POST https://devsms.uz/api/send_sms.php \
-H "Authorization: Bearer your_token" \
-H "Content-Type: application/json" \
-d '{
"phone": "998901234567",
"message": "Test SMS"
}'
Get Balance:
curl -X GET https://devsms.uz/api/get_balance.php \
-H "Authorization: Bearer your_token"
💻 Code Examples
Select programming language:
<?php
// DevSMS API - PHP Example
$token = "your_api_token_here";
$baseUrl = "https://devsms.uz/api";
// SMS Yuborish
function sendSMS($token, $baseUrl, $phone, $message) {
$ch = curl_init("$baseUrl/send_sms.php");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer $token",
"Content-Type: application/json"
],
CURLOPT_POSTFIELDS => json_encode([
"phone" => $phone,
"message" => $message
])
]);
$response = curl_exec($ch);
curl_close($ch);
return json_decode($response, true);
}
// Balansni olish
function getBalance($token, $baseUrl) {
$ch = curl_init("$baseUrl/get_balance.php");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer $token"
]
]);
$response = curl_exec($ch);
curl_close($ch);
return json_decode($response, true);
}
// Ishlatish
$result = sendSMS($token, $baseUrl, "998901234567", "Salom!");
print_r($result);
$balance = getBalance($token, $baseUrl);
print_r($balance);
?>