All API endpoints require authentication using a Bearer token. Include your authentication token in the Authorization header with every request.
To obtain your API token, log in to your account and navigate to Consumers where you can view and manage your consumer tokens. Each consumer has a unique API token that you can show, hide, copy, or regenerate. Keep your token secure and never share it publicly.
Include the Authorization header in your requests:
Authorization: Bearer [YOUR_API_TOKEN]
Send an SMS message to a specified phone number using your preferred sender ID.
POST /api/send
| Parameter | Type | Description |
|---|---|---|
| from | string | Sender ID (max 15 characters) |
| to | string | Recipient phone number in E.164 format (e.g., +60123456789, max 20 characters) |
| text | string | Message content (max 1000 characters) |
{ "id": 12345, "segments": 1, "status": "pending", "cost": "1.000", "balance": "99.0000" }
$ch = curl_init(); curl_setopt($ch, CURLOPT_URL, 'https://dr.onsms.opy.la/api/send'); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'Authorization: Bearer [YOUR_API_TOKEN]', 'Content-Type: application/json', ]); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([ 'from' => 'SENDER123', 'to' => '+60123456789', 'text' => 'Hello, this is a test message!', ])); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $response = curl_exec($ch); curl_close($ch); $data = json_decode($response, true); echo 'Message ID: ' . $data['id']; echo 'Cost: ' . $data['cost']; echo 'New Balance: ' . $data['balance'];
use Illuminate\Support\Facades\Http; $response = Http::withToken('[YOUR_API_TOKEN]') ->post('https://dr.onsms.opy.la/api/send', [ 'from' => 'SENDER123', 'to' => '+60123456789', 'text' => 'Hello, this is a test message!', ]); $data = $response->json(); echo 'Message ID: ' . $data['id']; echo 'Cost: ' . $data['cost']; echo 'New Balance: ' . $data['balance'];
curl -X POST https://dr.onsms.opy.la/api/send \ -H "Authorization: Bearer [YOUR_API_TOKEN]" \ -H "Content-Type: application/json" \ -d '{ "from": "SENDER123", "to": "+60123456789", "text": "Hello, this is a test message!" }'
Get your current account balance (credit value).
GET /api/balance
No parameters required.
{ "value": "99.0000" }
$ch = curl_init(); curl_setopt($ch, CURLOPT_URL, 'https://dr.onsms.opy.la/api/balance'); curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'Authorization: Bearer [YOUR_API_TOKEN]', 'Content-Type: application/json', ]); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $response = curl_exec($ch); curl_close($ch); $data = json_decode($response, true); echo 'Your Balance: ' . $data['value'];
use Illuminate\Support\Facades\Http; $response = Http::withToken('[YOUR_API_TOKEN]') ->get('https://dr.onsms.opy.la/api/balance'); $balance = $response->json('value'); echo 'Your Balance: ' . $balance;
curl -X GET https://dr.onsms.opy.la/api/balance \ -H "Authorization: Bearer [YOUR_API_TOKEN]" \ -H "Content-Type: application/json"
To ensure fair usage and maintain service stability, API requests are rate limited to 60 requests per minute per consumer . Each consumer account has its own independent rate limit bucket, so requests from different API tokens do not affect each other's rate limits.
When you exceed the rate limit, the API will return a 429 Too Many Requests response. Your request will be rejected and you should retry after waiting. We recommend implementing exponential backoff in your application to handle rate limiting gracefully.
Each API response includes rate limit information in the response headers:
X-RateLimit-Limit - Maximum requests allowed per minute (60)X-RateLimit-Remaining - Number of requests remaining in the current minuteHere are recommended strategies to ensure your application stays within rate limits:
X-RateLimit-Remaining header and slow down your requests when it approaches zero.429 response, wait an increasing amount of time before retrying (e.g., 1s, 2s, 4s, 8s)./api/balance endpoint locally to avoid unnecessary repeated requests.Delivery report callbacks allow you to receive real-time updates about the status of your sent SMS messages. When a message is delivered, the system will send an HTTP POST request to your registered callback URL with the delivery status details.
To enable delivery report callbacks, configure your callback URL in the Consumers section of your account. The callback URL must:
200 OK status code to acknowledge receiptWhen a message status changes, we will POST the following JSON data to your callback URL:
{ "id": 12345, "segments": 1, "status": "delivered", "cost": "0.5000", "sent_at": "2024-01-23T15:00:00Z", "delivered_at": "2024-01-23T15:00:30Z" }
| Field | Type | Description |
|---|---|---|
| id | integer | Unique identifier of the message (matches the ID returned by /api/send) |
| segments | integer | Number of SMS segments the message was split into |
| status | string | Current message status (e.g., pending , sent , delivered , undelivered , expired , rejected ) |
| cost | string | Credit cost of the message with 4 decimal places |
| sent_at | string null | Timestamp when the message was sent (null if not yet sent) |
| delivered_at | string null | Timestamp of the latest status update (null if not yet delivered) |
| Status | Description |
|---|---|
| pending | Message is queued and waiting to be processed |
| sent | Message has been sent to the network operator |
| delivered | Message was successfully delivered to the recipient |
| undelivered | Message delivery failed |
| expired | Message expired before delivery |
| rejected | Message was rejected by the network operator |
// Get the raw POST data $input = file_get_contents('php://input'); $data = json_decode($input, true); // Extract callback data $message_id = $data['id']; $status = $data['status']; $segments = $data['segments']; $cost = $data['cost']; $sent_at = $data['sent_at']; $delivered_at = $data['delivered_at']; // Log the delivery report $log_message = sprintf( "[%s] Message %d: %s (segments: %d, cost: %s)\n", $delivered_at ?? $sent_at, $message_id, $status, $segments, $cost ); file_put_contents('delivery_reports.log', $log_message, FILE_APPEND); // Update your database with the delivery status // UPDATE messages SET status = ?, delivered_at = ? WHERE id = ? // Always return 200 OK to acknowledge receipt http_response_code(200); echo json_encode(['success' => true]);
namespace App\Http\Controllers\Webhooks; use App\Models\Message; use Illuminate\Http\Request; use Illuminate\Support\Facades\Log; class DeliveryReportController extends Controller { public function handle(Request $request) { // Validate the incoming callback data $validated = $request->validate([ 'id' => 'required|integer', 'segments' => 'required|integer', 'status' => 'required|string', 'cost' => 'required|string', 'sent_at' => 'required|string', 'delivered_at' => 'nullable|string', ]); // Update the message status in database Message::find($validated['id'])?->update([ 'status' => $validated['status'], 'delivered_at' => $validated['delivered_at'], ]); // Log for monitoring Log::info('Delivery report received', $validated); // Return 200 OK to acknowledge receipt return response()->json(['success' => true], 200); } }
200 OK response immediately. Don't perform heavy processing before responding.id as a unique identifier.The API returns appropriate HTTP status codes. Check the response body for error details.
| Status Code | Description |
|---|---|
| 200 | Success |
| 401 | Unauthorized - Invalid or missing API token |
| 422 | Validation Error - Invalid parameters |
| 429 | Too Many Requests - Rate limit exceeded |
| 500 | Server Error |