MobileSasaDocs

Inbound webhooks

Every MO (mobile-originated) message that matches your keyword or dedicated code is forwarded to your HTTPS endpoint as JSON, as it arrives.

POSThttps://your-app.example.com/inbound (your configured URL)
Payload
{
  "provider": "safaricom",
  "shortcode": "40770",
  "msisdn": "254712345678",
  "message": "JOIN Amina Odhiambo Nakuru",
  "link_id": "8f3a12bc45",
  "received_at": "2026-07-11T10:15:42+03:00"
}

Parameters

FieldTypeRequiredDescription
providerstringNoCarrier the message arrived from (safaricom, airtel, …).
shortcodestringNoThe destination — your shortcode or long number.
msisdnstringNoThe sender's phone, E.164 without +.
messagestringNoThe full text, keyword included.
link_idstringNoSafaricom LinkID — needed if you reply through on-demand shortcode flows; absent on other carriers.
received_atstringNoRFC3339 timestamp.

Your response

Return any 2xx quickly. The body is ignored — replies are sent via the send API or per-keyword auto-replies, not the webhook response.

Design for retries and bursts

  • Treat delivery as at-least-once — dedupe on msisdn + received_at (or hash the payload).
  • A radio mention can spike hundreds of texts a minute — queue internally, respond 200 immediately, process async.
  • HTTPS is required; keep the endpoint path unguessable if you don't validate a shared secret.

A minimal receiver

Node.js (Express)
app.post("/inbound", (req, res) => {
  res.sendStatus(200); // ack first, work later

  const { msisdn, message, shortcode } = req.body;
  const [keyword, ...rest] = message.trim().split(/\s+/);

  queue.add("inbound-sms", {
    from: msisdn,
    keyword: keyword.toUpperCase(),
    details: rest.join(" "),
    via: shortcode,
  });
});