REST API Reference
The SmsNova API is developer-first and organized around standard REST. Send SMS through paired Android phones, read received messages, filter by specific sender numbers, and receive real-time webhook push events.
https://sms.ullass.com/api/v1Authentication
All requests require your secret organization API Key transmitted via the standard HTTP Authorization header using the Bearer scheme. You can create and revoke keys in your API Keys Dashboard.
Authorization: Bearer sn_live_9a8b7c6d5e4f3a2b1c0d
1. Dispatching Outbound SMS
/messages/sendAlias: /messagesQueues an outgoing SMS message and pushes it instantly to your paired Android phone over WebSocket. The phone's native telephony hardware dispatches the SMS using the designated SIM slot or smart cost-optimized routing.
Request Body (JSON)
{
"to": "+8801712345678", // Recipient phone in E.164 or local format ("017...")
"message": "Your PosNova verification code is 492019. Valid for 5 minutes.",
"simSlot": 0, // Optional: 0 (SIM 1), 1 (SIM 2), or omitted for Auto-Routing
"priority": "high", // Optional: "high" (OTPs, alarms) | "normal" (Promotional)
"metadata": { // Optional: Pass arbitrary key-value pairs for webhooks
"orderId": "ORD-1092",
"customerId": "CUST-881"
}
}Response (202 Accepted)
{
"success": true,
"messageId": "msg_1727201948_a9f1b2c",
"status": "queued",
"recipient": "+8801712345678",
"deviceId": "dev_samsung_a54_node1",
"simSlot": 0,
"createdAt": "2026-09-25T07:45:00.000Z"
}2. Reading & Querying Phone SMS
Query messages received on your connected Android devices. You can read all inbox SMS, filter by specific customer phone numbers, retrieve incoming bank/MFS payment alerts (bKash, Nagad, Rocket), or search message text.
/messagesLists messages matching your query filters with pagination support.
Query Parameters
| Parameter | Type | Example | Description |
|---|---|---|---|
| from | string | +8801712345678 or bKash | Filter by sender phone number or alphanumeric sender ID (e.g. bKash, 16247, 16167). |
| to | string | +8801700000001 | Filter by the receiving SIM phone number. |
| type | string | inbound | outbound | Filter by direction. Use inbound to read SMS received on your phone. |
| search | string | TrxID or POSNOVA | Case-insensitive substring search in the SMS content. |
| limit | number | 20 (Max: 100) | Number of messages per page (default: 20). |
| offset | number | 0 | Pagination offset. |
Example: Read SMS from a Specific Customer
curl -X GET "https://sms.ullass.com/api/v1/messages?from=%2B8801712345678&type=inbound" \ -H "Authorization: Bearer sn_live_9a8b7c6d5e4f3a2b1c0d"
Example: Read Incoming bKash / Bank Payment SMS
curl -X GET "https://sms.ullass.com/api/v1/messages?from=bKash&type=inbound&limit=10" \ -H "Authorization: Bearer sn_live_9a8b7c6d5e4f3a2b1c0d"
Response (200 OK)
{
"success": true,
"total": 1,
"limit": 20,
"offset": 0,
"messages": [
{
"id": "msg_1727202101_bkash",
"type": "inbound",
"from": "bKash",
"senderNumber": "16247",
"to": "+8801700000001",
"body": "You have received Tk 1,500.00 from 01712345678. Ref POSNOVA-1082. Fee Tk 0.00. Balance Tk 48,210.00. TrxID 9A8B7C6D5E at 24/09/2026 20:45",
"simSlot": 0,
"status": "received",
"receivedAt": "2026-09-24T20:45:10.000Z",
"deviceId": "dev_samsung_a54_node1",
"parsedPayment": {
"provider": "bKash",
"amount": 1500.0,
"currency": "BDT",
"trxId": "9A8B7C6D5E",
"senderPhone": "01712345678",
"reconciled": true
}
}
]
}/messages/:idRetrieves the full record and status of a single message by its unique ID.
3. Webhooks & Real-Time Inbound Callbacks
Instead of polling /messages, configure a Webhook URL in your Webhooks Dashboard. SmsNova will deliver an immediate HTTP POST payload to your endpoint whenever a phone receives an SMS or completes delivery.
payment.receivedFires when an incoming SMS matches one of the MFS regex patterns (bKash, Nagad, Rocket, M-Pesa, OPay). Contains fully structured payment metadata ready for zero-latency order reconciliation.
{
"event": "payment.received",
"id": "evt_91028401",
"deviceId": "dev_samsung_a54_node1",
"timestamp": "2026-09-24T20:45:10.000Z",
"data": {
"provider": "bKash",
"amount": 1500.00,
"currency": "BDT",
"trxId": "9A8B7C6D5E",
"senderPhone": "01712345678",
"simSlot": 0,
"rawText": "You have received Tk 1,500.00 from 01712345678. Ref POSNOVA-1082. Fee Tk 0.00. Balance Tk 48,210.00. TrxID 9A8B7C6D5E at 24/09/2026 20:45"
}
}sms.receivedFires for any general inbound SMS received on your phone from any customer or unknown number.
{
"event": "sms.received",
"id": "evt_91028402",
"deviceId": "dev_samsung_a54_node1",
"timestamp": "2026-09-24T21:05:00.000Z",
"data": {
"from": "+8801712345678",
"to": "+8801700000001",
"body": "Amar order delivery kobe hobe? Order ID #4912",
"simSlot": 0
}
}Webhook Security & HMAC SHA-256 Verification
Every webhook request includes an X-SmsNova-Signature header generated using HMAC SHA-256 with your Webhook Secret. Always verify this signature in your server before processing transactions.
Node.js / Express Webhook Receiver
import express from "express";
import crypto from "crypto";
const app = express();
app.use(express.json());
const WEBHOOK_SECRET = process.env.SMSNOVA_WEBHOOK_SECRET;
app.post("/api/webhooks/smsnova", (req, res) => {
const signature = req.headers["x-smsnova-signature"];
const computedSignature = crypto
.createHmac("sha256", WEBHOOK_SECRET)
.update(JSON.stringify(req.body))
.digest("hex");
if (signature !== computedSignature) {
return res.status(401).send("Invalid Webhook Signature");
}
const { event, data } = req.body;
if (event === "payment.received") {
console.log(`✅ Verified ${data.provider} Tk ${data.amount} from ${data.senderPhone} (TrxID: ${data.trxId})`);
// Reconcile order in your database e.g., PosNova Order Mark as Paid
} else if (event === "sms.received") {
console.log(`📩 Inbound SMS from ${data.from}: ${data.body}`);
}
res.status(200).json({ received: true });
});
app.listen(4000, () => console.log("Webhook server listening on port 4000"));Next.js App Router Webhook Receiver
// app/api/webhooks/smsnova/route.ts
import { NextResponse } from "next/server";
import crypto from "crypto";
export async function POST(req: Request) {
const rawBody = await req.text();
const signature = req.headers.get("x-smsnova-signature");
const secret = process.env.SMSNOVA_WEBHOOK_SECRET || "";
const expected = crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
if (signature !== expected) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
const payload = JSON.parse(rawBody);
if (payload.event === "payment.received") {
// Instant POS / ERP Reconciliation
const { amount, trxId, senderPhone } = payload.data;
// updateDatabase({ trxId, status: "PAID", amount });
}
return NextResponse.json({ success: true });
}4. Device Dynamic Pairing Handshake
/devices/pairGenerates a cryptographically signed 3-minute TTL pairing token. The token is embedded into the QR code displayed on the web dashboard and scanned by the Android app.
Request Body (JSON)
{
"orgId": "org_ullass_posnova"
}Response (200 OK)
{
"success": true,
"endpoint": "https://sms.ullass.com/api",
"pairingToken": "pair_1790300726607_2vlc8dqy",
"orgId": "org_ullass_posnova",
"expiresAt": 1790300906607,
"qrDataString": "{\"v\":1,\"ep\":\"https://sms.ullass.com/api\",\"tok\":\"pair_1790300726607_2vlc8dqy\",\"org\":\"org_ullass_posnova\"}"
}