Developer docs

Webhooks: incoming SMS and WhatsApp, pushed to your server

A webhook POSTs to your URL the moment an SMS, a WhatsApp message or a USSD reply arrives on your paired phone or linked account. No polling. Three event types, one form-encoded payload shape.

lang_undefined

lang_undefined

lang_undefined

Create a webhook

In your dashboard open Tools → Webhooks → Add webhook. Give it a name, the destination URL on your server, and tick the events it should fire for (SMS, WhatsApp, USSD). A secret is generated for you; copy it from the row's copy button. Every delivery carries that secret so your endpoint can reject anything that did not come from SharkSMS.

The destination must be reachable from the public internet over HTTPS and answer within 5 seconds.

Phone numbers you will receive

You only ever get phone-number identities. A direct message arrives with data[phone] as an international number with a leading + (for example +639760713666); a WhatsApp group message arrives with the full group ID ending in @g.us. Anything else is dropped before it reaches your webhook, auto-replies or flows, so you do not need to defend against odd sender shapes.

Events and payloads

All three events are POSTed as an HTML form body (application/x-www-form-urlencoded) with secret, type, and a nested data array. Not JSON. In PHP that is $_POST["data"]["phone"]; in Express it is req.body["data[phone]"] with the default urlencoded parser, or req.body.data.phone with extended: true.

sms

Fired right after an inbound SMS is saved.

secret=WEBHOOK_SECRET
type=sms
data[id]=2
data[rid]=10593
data[sim]=1
data[device]=00000000-0000-0000-d57d-f30cb6a89289
data[phone]=+639760713666
data[message]=Hello World!
data[timestamp]=1645684231

id is the received message's ID, rid the phone's own message ID, device your device ID, timestamp Unix time of receipt.

whatsapp

Fired right after an inbound WhatsApp message is saved.

secret=WEBHOOK_SECRET
type=whatsapp
data[id]=2
data[wid]=+639760713666
data[phone]=+639760666713
data[message]=Hello World!
data[attachment]=https://sharksms.com/uploads/whatsapp/received/.../file.jpg
data[timestamp]=1645684231

wid is your receiving WhatsApp account's number; phone is the sender (a number, or a @g.us group ID). attachment is a download URL when the message carried media, otherwise false.

ussd

Fired once the carrier answers a USSD request you sent through the API.

secret=WEBHOOK_SECRET
type=ussd
data[id]=98
data[sim]=1
data[device]=00000000-0000-0000-d57d-f30cb6a89289
data[code]=*143#
data[response]=Your balance is 12.40. Valid until 30 Sep.
data[timestamp]=1645684231

What your endpoint must return

HTTP 200. SharkSMS checks the response code of every call: on 200 it logs "Webhook Triggered!"; on anything else, including no answer, it logs "Webhook Failed!" (both visible under Tools → Logger). The call uses a 5-second connect and total timeout and does not retry. A slow endpoint is indistinguishable from a failed one, so acknowledge fast and do the work afterwards.

A minimal receiver in PHP:

<?php
$secret = "WEBHOOK_SECRET";                 // from Tools -> Webhooks

if (($_POST["secret"] ?? "") !== $secret) {
    http_response_code(403);
    exit;
}

$type = $_POST["type"];                     // "sms" | "whatsapp" | "ussd"
$data = $_POST["data"];                     // array, shape depends on $type

// Acknowledge first, then work. Anything slower than 5s counts as failed.
http_response_code(200);
if (function_exists("fastcgi_finish_request")) fastcgi_finish_request();

if ($type === "sms" && strtoupper(trim($data["message"])) === "C") {
    // cancel the appointment for $data["phone"] ...
}

Node.js (Express):

import express from "express";
const app = express();
app.use(express.urlencoded({ extended: true }));   // data[phone] -> req.body.data.phone

app.post("/sharksms/webhook", (req, res) => {
  if (req.body.secret !== process.env.SHARKSMS_WEBHOOK_SECRET) return res.sendStatus(403);
  res.sendStatus(200);                                // ack first
  const { type, data } = req.body;
  if (type === "whatsapp") queue.add("inbound", data); // do the work off the request
});

Test it

Every row in Tools → Webhooks has a Simulate button. Pick an event type and it POSTs a realistic fake payload (a sample number, "Hello World!", random IDs) to your URL with your real secret, in exactly the shape above. Use it to confirm the endpoint is reachable and the secret check works before waiting on a real message.

Replies to keyword flows. If you also run a flow or auto-reply on the same number, the webhook still fires for every inbound message. Flows answer the customer; your webhook informs your system. They do not compete.

Next: Android gateway setup, or back to the REST API.