Documentation

Examples

Every example below does the same thing — sends one SMS through the gateway — so you can lift the one that matches your stack. Replace <phone-ip> with the address the app shows after you tap Start, and the sk_local_... value with your API key.

The app can also generate these for you: its Copy example menu produces ready-to-run snippets for cURL, C#, Node.js, and Flutter, already filled in with your live URL and key.

cURL

curl -X POST http://<phone-ip>:8080/api/sms/send \
  -H "Authorization: Bearer sk_local_xxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"to":"09171234567","message":"Hello from Hiras"}'

Add "simSlot": 2 to the body to send from a specific SIM on a multi-SIM phone.

Node.js

const res = await fetch("http://<phone-ip>:8080/api/sms/send", {
  method: "POST",
  headers: {
    "Authorization": "Bearer sk_local_xxxxxxxxxxxxxxxxxxxxxxxx",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ to: "09171234567", message: "Hello from Hiras" }),
});

const data = await res.json();
if (!data.success) throw new Error(data.error?.message ?? "send failed");
console.log(data.status); // "queued"

C#

using var http = new HttpClient();
http.DefaultRequestHeaders.Authorization =
    new("Bearer", "sk_local_xxxxxxxxxxxxxxxxxxxxxxxx");

var payload = new { to = "09171234567", message = "Hello from Hiras" };
var res = await http.PostAsJsonAsync(
    "http://<phone-ip>:8080/api/sms/send", payload);

res.EnsureSuccessStatusCode();
var body = await res.Content.ReadFromJsonAsync<JsonElement>();
Console.WriteLine(body.GetProperty("status").GetString()); // "queued"

Flutter

import 'package:http/http.dart' as http;
import 'dart:convert';

final res = await http.post(
  Uri.parse('http://<phone-ip>:8080/api/sms/send'),
  headers: {
    'Authorization': 'Bearer sk_local_xxxxxxxxxxxxxxxxxxxxxxxx',
    'Content-Type': 'application/json',
  },
  body: jsonEncode({'to': '09171234567', 'message': 'Hello from Hiras'}),
);

final data = jsonDecode(res.body);
if (data['success'] != true) {
  throw Exception(data['error']?['message'] ?? 'send failed');
}
print(data['status']); // "queued"

What a success looks like

All four return the same envelope:

{
  "success": true,
  "mode": "real",
  "smsSent": true,
  "messageId": "msg_...",
  "status": "queued",
  "segments": 1
}

status: "queued" means Android has accepted the message for sending — it later becomes sent or failed. It is not a carrier delivery receipt. Check a message’s outcome with GET /api/messages/:id.

Testing without spending a text

Turn on Dry Run and every example above returns a realistic response with "mode": "dry-run" and "smsSent": false — no SIM, no load. Turn on Failure Simulation to make them return a specific error so you can test your failure path.

Next

  • REST API — every endpoint and field.
  • Webhooks — receive inbound SMS, not just send.