Developer API & Integration Guide
Welcome to the Message Panel REST API reference. Programmatically dispatch transactional SMS, trigger instant high-priority OTP verifications, inspect real-time delivery reports, and configure secure API keys with granular role-based permissions and IP allowlists.
Overview & Architecture
Message Panel provides a stateless, high-throughput REST API designed for enterprise SMS delivery and voice failover. All requests are authenticated via cryptographic API keys and protected by our distributed Anti-Fraud Shield.
Base URLs & Environments
| Environment | Base URL | Key Type | Billing Action |
|---|---|---|---|
| Production / Local | https://msg.srotindia.com/msg-api/api/v1 | msg_live_... | Real carrier dispatch & credit debit |
| Sandbox / Testing | https://msg.srotindia.com/msg-api/api/v1 | msg_test_... | Simulated delivery, ₹0 balance cost |
Standard Response Envelope
Every HTTP response from our API adheres strictly to a predictable JSON envelope structure with a rootsuccess boolean flag.
{
"success": true,
"data": {
"messageId": "msg_98f418b7",
"status": "queued",
"recipient": "+919876543210",
"creditsDeducted": 1,
"remainingCredits": 7691
},
"message": "SMS dispatched successfully"
}{
"success": false,
"error": "Access denied: Insufficient permission for action 'reports'",
"code": "FORBIDDEN_PERMISSION",
"details": {
"requiredScope": "reports",
"keyPermission": "send"
}
}API Key Authentication
Authenticate requests by sending your secret API key in the HTTP request headers. Both the standard custom header x-api-key and the standard Authorization: Bearer format are fully supported.
Key Formats & Secure Hashing
API Keys follow strict entropy standards and are prefixed for instant recognition in code reviews and secret scanners:
sha256(rawKey) and stores only the hash and prefix. Copy your key immediately upon generation — it cannot be recovered afterwards.IP Restriction & CIDR Allowlisting
When creating an API key, you can optionally configure an IP restriction. The API gateway validates every incoming HTTP request against the client IP extracted from x-forwarded-for or the TCP socket.
Requests from unauthorized IPs immediately receive HTTP 403 Forbidden with error code IP_RESTRICTED without debiting any credits.
Permission Scopes Matrix
Message Panel enforces granular Role-Based Access Control on every API key. Below is the complete matrix specifying exactly which endpoints, resources, and HTTP operations each permission level can access.
| Endpoint & Route | Action / Resource | Full Access | Send Only | Reports Only | Read Only |
|---|---|---|---|---|---|
| POST /sms/send | Dispatch single or transactional SMS | ✔ Allowed | ✔ Allowed | ❌ 403 Blocked | ❌ 403 Blocked |
| POST /otp/send | Generate & send 6-digit verification code | ✔ Allowed | ✔ Allowed | ❌ 403 Blocked | ❌ 403 Blocked |
| POST /otp/verify | Validate user OTP input token | ✔ Allowed | ✔ Allowed | ❌ 403 Blocked | ❌ 403 Blocked |
| GET /sms/logs | List SMS logs & recipient phone numbers | ✔ Allowed | ❌ 403 Blocked | ✔ Allowed | ❌ 403 Blocked |
| GET /sms/logs/:id | Inspect carrier DLR and delivery timeline | ✔ Allowed | ❌ 403 Blocked | ✔ Allowed | ❌ 403 Blocked |
| GET /templates | Fetch registered templates & DLT headers | ✔ Allowed | ❌ 403 Blocked | ❌ 403 Blocked | ✔ Allowed |
| GET /contacts | Read contact groups and phone lists | ✔ Allowed | ❌ 403 Blocked | ❌ 403 Blocked | ✔ Allowed |
| GET /wallet/balance | View remaining OTP credits | ✔ Allowed | ❌ 403 Blocked | ❌ 403 Blocked | ❌ 403 Blocked |
| POST /developer/keys | Create, rotate, or delete API keys | ✔ Allowed | ❌ 403 Blocked | ❌ 403 Blocked | ❌ 403 Blocked |
Full Access Permission
Unrestricted master access to every resource across your organization. Best suited for backend microservice orchestrators or internal core engines that need to send messages, read logs, query billing, and rotate keys.
Send Only Permission
Restricted strictly to outbound messaging. Keys with this scope can only invoke /sms/send and/otp/send. If a compromised microservice leaks this key, an attacker cannot read customer phone numbers, delivery logs, or wallet data.
Reports Only Permission
Read-only access to transmission receipts, carrier DLR records, and analytics. Designed specifically for business intelligence (BI) pipelines, data warehouses (Snowflake, BigQuery), and customer support lookup tools.
Read Only Permission
Enables inspecting static configuration, templates, sender IDs, and contact lists without ability to send messages or inspect real-time transmission logs.
Dispatch an immediate or scheduled transactional SMS to any global mobile number. Automatically validates E.164 phone number formatting and applies DLT template mapping.
Request Body (JSON)
| Parameter | Type | Required | Description |
|---|---|---|---|
| to | string | Yes | Destination phone in E.164 format (e.g. +919876543210). |
| message | string | Yes | Content of message. 160 chars GSM-7 per credit (70 for Unicode). |
| senderId | string | Optional | 6-character registered header ID (e.g. MSGPAN). |
| templateId | string | Optional | Registered TRAI DLT Template ID for Indian traffic. |
curl -X POST https://msg.srotindia.com/msg-api/api/v1/sms/send \
-H "Content-Type: application/json" \
-H "x-api-key: msg_live_your_api_key_here" \
-d '{
"to": "+919876543210",
"message": "Your verification code is 492104. Valid for 5 minutes.",
"senderId": "MSGPAN"
}'200 OK Response
{
"success": true,
"data": {
"messageId": "msg_98f418b7-6b45-4272-a169-e932b10a233b",
"to": "+919876543210",
"status": "queued",
"creditsDeducted": 1,
"remainingCredits": 7691,
"carrierSubmittedAt": "2026-10-23T13:00:00.000Z"
},
"message": "SMS dispatched successfully"
}Dispatches an OTP via TRAI-approved Service Implicit telecom routes. Supports two core transmission architectures: AutoGen OTP (internal crypto engine or 2Factor delegated) and Custom OTP (customer supplies their own pre-generated authentication code).
Request Body (JSON)
| Parameter | Type | Required | Description |
|---|---|---|---|
| mobile | string | Yes | Destination phone in E.164 format (e.g. +919876543210). |
| type | string | Optional | "autogen" (default) or "custom". |
| otp | string | Conditional | Required when type: "custom". Customer-supplied code (4–8 digits). |
| strategy | string | Optional | "internal" (default, resilient engine) or "twofactor". |
| otpLength | number | Optional | Length of generated code (4 or 6, default 6). |
| expirySeconds | number | Optional | Session validity window (default 600 seconds / 10 minutes). |
Example 1: AutoGen OTP (Internal Engine)
// POST /api/v1/otp/send
{
"mobile": "+919876543210",
"type": "autogen",
"strategy": "internal",
"otpLength": 6
}
// 201 Created Response:
{
"success": true,
"message": "OTP generated and dispatched successfully",
"data": {
"sessionId": "OTP_GEN_1792822088111_00AD91",
"mobile": "+919876543210",
"type": "autogen",
"strategy": "internal",
"expiresInSeconds": 600,
"senderId": "MSGPAN"
}
}Example 2: Custom OTP (Customer Supplied Code)
// POST /api/v1/otp/send
{
"mobile": "+919876543210",
"type": "custom",
"otp": "481920"
}
// 201 Created Response:
{
"success": true,
"message": "Custom OTP forwarded and dispatched via telecom gateway",
"data": {
"sessionId": "OTP_CUST_1792822088223_64C538",
"mobile": "+919876543210",
"type": "custom",
"strategy": "internal",
"expiresInSeconds": 600,
"senderId": "MSGPAN"
}
}Queued sends (202) and the status endpoint
OTPs are handed to a worker that sends the SMS and retries on temporary gateway problems. Normally the worker finishes within a moment and you get the 201 above. If it is still working, you get 202 Accepted with status: "QUEUED". Your credit is reserved right away and refunded if the SMS could not be sent.
sessionId is always our own ID. Use it with /otp/verify, and to ask where the SMS is:
GET /api/v1/otp/sessions/{sessionId}/status (needs your API key)
{ "success": true,
"data": { "sessionId": "...", "status": "QUEUED" | "SENT" | "FAILED",
"sentAt": "2026-10-27T10:00:01.000Z", "expiresAt": "...",
"error": "only when FAILED" } }Send an Idempotency-Key header to make retries safe: repeating a request with the same key returns the same session and never sends or charges twice. A code is valid for its full time starting from when the SMS is sent.
Validates the user-entered OTP against the stored session ID. Supports sessions generated internally as well as upstream 2-Factor delegated sessions. Enforces maximum 5 attempts and automatic session expiration.
Request Payload
{
"sessionId": "OTP_GEN_1792822088111_00AD91",
"otp": "314213"
}200 OK Response
{
"success": true,
"message": "OTP verified successfully",
"data": {
"verified": true,
"sessionId": "OTP_GEN_1792822088111_00AD91",
"phone": "+919876543210",
"type": "AUTOGEN",
"verifiedAt": "2026-10-24T06:08:08.204Z"
}
}Retrieves real-time paginated OTP verification sessions for your organization. Filter by verification status (PENDING, VERIFIED, EXPIRED, FAILED) or search by recipient mobile number.
Query Parameters
| Query Param | Type | Default | Description |
|---|---|---|---|
| page | number | 1 | Current page number. |
| limit | number | 15 | Records per page (max 50). |
| status | string | ALL | Filter by status: VERIFIED, PENDING, etc. |
Query paginated transmission logs with status filter (queued,sent, delivered,failed).
Query Parameters
| Parameter | Default | Description |
|---|---|---|
| page | 1 | Page number for offset pagination. |
| limit | 20 | Records per page (Max: 100). |
| status | all | Filter by status: delivered | failed | sent | queued. |
200 OK Response
{
"success": true,
"data": [
{
"id": "clx_981a20",
"recipient": "+919876543210",
"senderId": "MSGPAN",
"status": "delivered",
"credits": 1,
"submittedAt": "2026-10-23T12:50:00.000Z",
"deliveredAt": "2026-10-23T12:50:04.000Z",
"operatorCode": "AIRTEL-DELHI"
}
],
"pagination": { "total": 1420, "page": 1, "limit": 20, "totalPages": 71 }
}Retrieve real-time carrier delivery receipts (DLR), upstream telecom operator codes, dispatch timestamps, segment breakdown, and associated DLT template metadata for an individual transmission record by its ID.
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| id | string | Yes | The unique Message UUID or ID returned upon dispatch (e.g. d4e9b251-c031-482a-a5f1-389f46b14201). |
curl -X GET https://msg.srotindia.com/msg-api/api/v1/sms/logs/d4e9b251-c031-482a-a5f1-389f46b14201 \ -H "x-api-key: msg_live_your_api_key_here"
200 OK Response
{
"success": true,
"data": {
"id": "d4e9b251-c031-482a-a5f1-389f46b14201",
"orgId": "b5becc63-d569-4103-82ad-600842c7ba7e",
"phone": "+919876543210",
"senderId": "MSGPAN",
"content": "Your login code is 492104. Valid for 5 minutes.",
"charCount": 48,
"encoding": "GSM-7",
"segmentCount": 1,
"creditsCharged": 1,
"status": "DELIVERED",
"deliveryCode": "DELIVRD",
"deliveryCodeRaw": "000_SUCCESS",
"deliveredAt": "2026-10-24T06:10:04.120Z",
"isSandbox": false,
"createdAt": "2026-10-24T06:10:01.000Z",
"template": {
"id": "tpl_891230",
"templateName": "Login OTP Template",
"dltTemplateId": "1407161234567890123",
"category": "OTP"
}
},
"timestamp": "2026-10-24T06:10:05.000Z"
}Query how many OTP credits your organization has left. One credit is used for each OTP that is actually sent.
curl -X GET https://msg.srotindia.com/msg-api/api/v1/wallet/balance \ -H "x-api-key: msg_live_your_api_key_here"
200 OK Response
{
"success": true,
"data": {
"otpCredits": 10,
"plan": {
"name": "Growth",
"status": "ACTIVE",
"includedOtpCredits": 1000,
"endsAt": "2026-11-24T06:10:05.000Z",
"inEffect": true
}
},
"timestamp": "2026-10-24T06:10:05.000Z"
}Generate a new cryptographically secured API key with designated RBAC permission scopes (full, send, reports, read) and optional IPv4/CIDR network restriction. The raw secret is returned only once in the response payload.
Request Body (JSON)
| Parameter | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | Human-readable label (e.g. "Production Microservice"). |
| permission | string | Optional | Scope: full | send | reports | read (Default: full). |
| environment | string | Optional | Target environment: live | sandbox (Default: live). |
| ipRestrict | string | Optional | Static IPv4 address or CIDR range (e.g. 203.0.113.195 or 103.21.0.0/16). |
curl -X POST https://msg.srotindia.com/msg-api/api/v1/developer/keys \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_SESSION_TOKEN" \
-d '{
"name": "Production Microservice",
"permission": "send",
"environment": "live",
"ipRestrict": "203.0.113.195"
}'201 Created Response
{
"success": true,
"message": "API key generated successfully. Store the secret safely as it will not be displayed again.",
"data": {
"id": "key_e7829a4f",
"name": "Production Microservice",
"key": "msg_live_839f284a1e948301726a917240182c59",
"keyPrefix": "msg_live_839f...",
"permission": "send",
"ipRestrict": "203.0.113.195",
"environment": "live",
"createdAt": "2026-10-24T06:12:00.000Z"
},
"timestamp": "2026-10-24T06:12:00.000Z"
}HTTP Status Code Catalog
Our API uses conventional HTTP response codes to indicate the success or failure of an API request. Codes in the 2xx range indicate success, codes in the4xx range indicate an error that failed given the information provided, and codes in the 5xx range indicate an error with our servers.
| Code | Status | Meaning & Trigger Reason |
|---|---|---|
| 200 | OK | Everything worked as expected. |
| 201 | Created | Resource (API Key, Campaign, or SMS queue record) was created. |
| 400 | Bad Request | Missing required parameters (e.g. missing to phone number). |
| 401 | Unauthorized | No valid API key provided or key has been revoked. |
| 402 | Payment Required | No OTP credits left. Contact support to add more credits. |
| 403 | Forbidden | Key permission mismatch (e.g. Send Only hitting Logs) or IP not in allowlist. |
| 404 | Not Found | The requested resource (message ID, contact ID) does not exist. |
| 429 | Too Many Requests | Exceeded 1,000 req/min rate limit or Anti-Fraud Shield cooldown triggered. |
| 500 | Internal Server Error | Downstream telecom gateway timeout or internal service disruption. |
Application Error Codes Reference
When an API request fails, the code field in the error response contains a machine-readable string you can inspect in your catch handlers.
Your API key does not have the required permission scope. For example, sending with a 'Reports Only' key, or reading delivery logs with a 'Send Only' key.
The HTTP request originated from an IP address not included in the API key's configured IP restriction CIDR block.
The provided x-api-key could not be found or has been revoked.
Your organization has no OTP credits left, so the OTP was not sent.
An OTP or SMS was recently dispatched to this phone number within the 60-second cooldown window.
More than 5 OTP requests were dispatched to this phone number in the past 60 minutes.
Anti-Fraud Shield Rules
To protect your OTP credits against SMS pumping fraud, brute force OTP attacks, and bot swarms, all messaging endpoints are guarded by Redis-backed real-time sliding window algorithms:
Maximum 1 OTP request per phone number every 60 seconds. Prevents user double-clicking and SMS flooding.
Strict cap of 5 OTP dispatches per phone number per sliding 60-minute window. Thwarts persistent abusers.
Global rate limit of 20 requests per minute per IP address to block distributed botnet credential stuffing.
Need to run automated CI/CD unit tests or App Store review submissions without consuming credits or triggering the 60-second fraud cooldown? Use our built-in magic test number:
When dispatched to +919999999999, our engine bypasses carrier dispatch, skips wallet deduction, bypasses cooldown limits, and immediately accepts 123456.
Webhooks & Real-Time Delivery Receipts
Configure Webhooks under /dashboard/webhooks to receive HTTP POST notifications whenever a message status updates.
Supported Event Types
HMAC-SHA256 Signature Verification
Every webhook request includes an x-signature header computed as:
const crypto = require("crypto");
function verifyWebhook(payload, signature, secret) {
const hmac = crypto.createHmac("sha256", secret).update(JSON.stringify(payload)).digest("hex");
return crypto.timingSafeEqual(Buffer.from(hmac), Buffer.from(signature));
}OTP Widget: Install on Your Website
The widget is a ready-made popup. Your visitor enters a mobile number, receives a code, and enters it. You do not build any OTP screens and your API key never goes into the page. Create a widget under /dashboard/widget, list the websites allowed to use it, then paste:
<script src="https://YOUR_API_DOMAIN/widget/v1/widget.js"
data-widget-key="wgt_xxxxxxxxxxxxxxxxxxxxxxxx"
data-trigger="#verify-phone"
data-on-success="onPhoneVerified"></script>
<button id="verify-phone">Verify phone number</button>
<script>
function onPhoneVerified(result) {
// result.token -> send this to YOUR server (see next section)
// result.mobile -> masked number, e.g. +91******10
}
</script>Open it from your own code instead
MessagePanelOTP.open({
mobile: "9876543210", // optional, pre-fills the number
onSuccess: (r) => { /* r.token, r.mobile */ },
onError: (e) => { /* widget could not start */ },
onClose: (c) => { /* c.verified is true or false */ },
});- The widget only works on the websites you list (exact origin, such as
https://shop.example.com). Other sites are refused. - Test mode sends no SMS and uses no credits; the code is shown in the popup. Turn it off to go live.
- Each live send uses 1 OTP credit. Every widget has a daily send cap, and the usual per-number and per-IP limits apply.
- Indian mobile numbers (+91) only.
OTP Widget: Verify on Your Server
A browser can be faked, so the success callback alone proves nothing. Send result.token to your server and confirm it with your API key (a key with send or full permission). The token works once and expires after 10 minutes.
curl -X POST https://YOUR_API_DOMAIN/api/v1/widget/token/verify \
-H "x-api-key: msg_live_xxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"token":"TOKEN_FROM_THE_BROWSER"}'
{
"success": true,
"data": {
"verified": true,
"mobile": "+919876543210",
"widgetId": "…",
"sessionId": "…",
"verifiedAt": "2026-10-27T10:00:00.000Z"
}
}A reused, expired or tampered token returns 400. Only mark the phone as verified when you get verified: true.
Ready to integrate?
Generate your first API key and start sending live messages in less than 3 minutes.