Integration

Send SMS from Node.js through your own Android phone

Node 18 or newer, built-in fetch, no package to install. One helper turns any parameters into a form POST, and the same helper sends WhatsApp. An Express route receives replies.

Form-encoded POST, JSON back, HTTP always 200. Read the status field.

  • 1 requestPOST /api/send/sms with secret, device, phone, message.
  • 0 per messageSends ride on the SIM plan of the phone you paired.
  • 5 secondsWebhook timeout for replies. Ack fast, work later.

lang_undefined

lang_undefined

lang_undefined · lang_undefined REST API

lang_undefined

  1. Pair an Android phone in your dashboard (Devices, Add device, scan the QR code) and copy its device ID.
  2. Create an API key under Tools, API Keys, with the sms_send permission (and wa_send for WhatsApp). Copy the secret into an environment variable; never into source.
  3. Node.js 18 or newer (for built-in fetch). Older Node: npm install undici and import fetch from it.

lang_undefined

One call helper, three exported functions. URLSearchParams builds the form body the API expects.

// Node 18+ (built-in fetch). No SDK needed.
const SHARKSMS = "https://sharksms.com/api";
const { SHARKSMS_SECRET, SHARKSMS_DEVICE } = process.env;

async function call(path, params) {
  const res = await fetch(`${SHARKSMS}${path}`, {
    method: "POST",
    headers: { "Content-Type": "application/x-www-form-urlencoded" },
    body: new URLSearchParams({ secret: SHARKSMS_SECRET, ...params }), // form body, not JSON
  });
  const body = await res.json();          // HTTP is always 200; read body.status
  if (body.status !== 200) throw new Error(`SharkSMS ${body.status}: ${body.message}`);
  return body.data;
}

export const sendSms = (phone, message, sim = 1) =>
  call("/send/sms", { mode: "devices", device: SHARKSMS_DEVICE, sim, phone, message });

export const sendWhatsApp = (recipient, message) =>
  call("/send/whatsapp", { account: process.env.SHARKSMS_WA_ACCOUNT, recipient, type: "text", message });

export async function smsStatus(id) {
  const q = new URLSearchParams({ secret: SHARKSMS_SECRET, id, type: "sent" });
  const body = await (await fetch(`${SHARKSMS}/get/sms.message?${q}`)).json();
  return body.data.status;                 // queued | pending | sent | failed
}

// usage
const { messageId } = await sendSms("+15551234567", "Order #4821 shipped. Arriving Thursday.");
console.log(messageId, await smsStatus(messageId));

To receive replies, add a webhook in your dashboard pointing at an Express route like this one. Acknowledge with 200 inside five seconds and do the work after.

// Express: receive replies (see /docs/webhooks)
import express from "express";
const app = express();
app.use(express.urlencoded({ extended: true }));
app.post("/sharksms/webhook", (req, res) => {
  if (req.body.secret !== process.env.SHARKSMS_WEBHOOK_SECRET) return res.sendStatus(403);
  res.sendStatus(200);
  const { type, data } = req.body;      // data.phone, data.message
  // ...
});

lang_undefined

Send to your own phone first. Then call GET /api/get/sms.message with the returned messageId and type=sent and watch it go queued, sent. If it stays queued, the paired phone is off, offline or battery-optimised; see the Android gateway page.

lang_undefined

  • The API reads a form body, not JSON. A JSON body is ignored and you get status 400 for missing parameters.
  • HTTP is always 200. Branch on the JSON status field, never on the HTTP status.
  • No deduplication. A retry after a timeout can send twice; record the messageId on success and check status before resending.
  • Do not JSON.stringify the body. The API reads form fields; URLSearchParams sets the right content type for you.

Straight answers

Do I need an SDK?

No. It is one HTTPS POST with a form body. Any language that can make an HTTP request can send. The snippets on this page are complete.

Can my code receive replies?

Yes. Add a webhook under Tools, Webhooks and every incoming SMS or WhatsApp message is POSTed to your URL as a form body with the sender, the text and a timestamp. See the webhooks reference.

Can I use axios instead of fetch?

Yes. Pass a URLSearchParams instance as the body, or set the Content-Type header to application/x-www-form-urlencoded and a querystring-encoded string. Everything else on this page is unchanged.