API Documentation

Authentication

All API endpoints require authentication using a Bearer token. Include your authentication token in the Authorization header with every request.

Getting Your API Token

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.

How to Authenticate

Include the Authorization header in your requests:

Authorization: Bearer [YOUR_API_TOKEN]

Send SMS

Send an SMS message to a specified phone number using your preferred sender ID.

Endpoint

POST /api/send

Required Parameters

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)

Response Example

{ "id": 12345, "segments": 1, "status": "pending", "cost": "1.000", "balance": "99.0000" }

Code Examples

$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!" }'

Check Balance

Get your current account balance (credit value).

Endpoint

GET /api/balance

Parameters

No parameters required.

Response Example

{ "value": "99.0000" }

Code Examples

$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"

Rate Limiting

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.

Exceeding 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.

Rate Limit Headers

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 minute

Best Practices to Avoid Rate Limits

Here are recommended strategies to ensure your application stays within rate limits:

  • Implement Request Queuing - Queue API requests and process them gradually rather than sending them all at once.
  • Monitor Rate Limit Headers - Check the X-RateLimit-Remaining header and slow down your requests when it approaches zero.
  • Use Exponential Backoff - When you receive a 429 response, wait an increasing amount of time before retrying (e.g., 1s, 2s, 4s, 8s).
  • Cache Results - Cache the response from the /api/balance endpoint locally to avoid unnecessary repeated requests.
  • Space Out Requests - Distribute requests evenly over time rather than sending them in bursts. Aim for approximately 1 request per second to stay well below the 60 requests per minute limit.

Delivery Report Callbacks

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.

Setting Up Your Callback URL

To enable delivery report callbacks, configure your callback URL in the Consumers section of your account. The callback URL must:

  • Be a valid HTTPS URL (HTTP is not supported for security reasons)
  • Accept POST requests
  • Return a 200 OK status code to acknowledge receipt
  • Process requests within 15 seconds

Callback Payload

When 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" }

Callback Payload Fields

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)

Message Status Values

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

Implementing a Callback Handler

// 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); } }

Best Practices for Callbacks

  • Acknowledge Quickly - Return a 200 OK response immediately. Don't perform heavy processing before responding.
  • Handle Duplicates - Process callbacks asynchronously and idempotently. The same callback may be retried multiple times; use id as a unique identifier.
  • Log All Callbacks - Keep detailed logs of all received callbacks for debugging and reconciliation.
  • Use HTTPS - Always use HTTPS for your callback URL to ensure data security in transit.
  • Validate Data - Always validate the incoming JSON data before processing to ensure data integrity.
  • Timeout Handling - Ensure your callback handler completes within 15 seconds. If it times out, the request may be retried.

Error Handling

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