use case · sms

Stop SMS spam before it reaches your team.

Updated May 12, 2026

Inbound SMS to a support short-code or business number attracts the same junk that poisons every modern channel — phishing, "DEAR CUSTOMER" gift-card scams, package-delivery lures. One Siftfy call inside your Twilio webhook handler classifies the message body in under 10ms and saves your support queue the noise.

Why classify inbound, not outbound

Outbound spam is a carrier problem — A2P registration, 10DLC campaigns, your messaging provider's content policies. Inbound is your problem. People reply to your number with content you can't control, and the carriers don't filter it before delivery. That's where Siftfy goes: in the handler that receives the inbound POST from Twilio (or MessageBird, Plivo, Bandwidth — same shape), before you forward to a human.

Webhook handler

Twilio expects TwiML in the response within roughly 10 seconds. A Siftfy call adds sub-10ms — well inside the budget. Set a 2-second timeout so a Siftfy outage can never hang the webhook.

javascript
// Twilio inbound-SMS webhook (Express). Twilio expects a
// TwiML <Response> within ~10s; the Siftfy hop is sub-10ms so we comfortably
// fit the budget.
import express from "express";
import twilio from "twilio";

const app = express();
app.use(express.urlencoded({ extended: false }));

const SIFTFY_KEY = process.env.SIFTFY_KEY!;
const SPAM_THRESHOLD = 0.85;

app.post("/sms/inbound", async (req, res) => {
  const from = req.body.From as string;
  const body = (req.body.Body as string ?? "").trim();
  const messageSid = req.body.MessageSid as string;

  if (!body) {
    return res.type("text/xml").send(new twilio.twiml.MessagingResponse().toString());
  }

  let probability = 0;
  try {
    const resp = await fetch("https://api.siftfy.io/v1/predict", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "X-API-Key": SIFTFY_KEY,
      },
      body: JSON.stringify({ text: body }),
      signal: AbortSignal.timeout(2000),
    });
    if (resp.ok) {
      ({ spam_probability: probability } = await resp.json());
    }
  } catch (err) {
    // Fall open: route to a human rather than auto-replying with garbage.
    console.error("siftfy failed", err);
  }

  // Persist every inbound for audit, regardless of classification.
  await db.smsInbound.insert({ from, body, messageSid, probability });

  if (probability >= SPAM_THRESHOLD) {
    // Drop without responding. Twilio still needs a 200 with empty TwiML
    // so the webhook doesn't get marked as failing.
    return res.type("text/xml").send(new twilio.twiml.MessagingResponse().toString());
  }

  // Real message — hand off to your normal support routing.
  await routeToSupport({ from, body, messageSid });
  return res.type("text/xml").send(new twilio.twiml.MessagingResponse().toString());
});

Threshold for SMS

SMS skews shorter than email or comments — typical inbound is one to three sentences. Calibration holds, but very short messages ("YES", "STOP") naturally score lower because there's less signal. Plant 0.85 as a starting threshold. If you find yourself missing scams, lower to 0.80 and watch your false-positive rate; if real customers are getting silently dropped, raise to 0.90.

Whatever you pick, persist the score and message body so you can audit the boundary cases over time.

Edge cases

  • One-word replies. "YES" / "NO" / "STOP" are STOP/HELP keywords for compliance and should never go through classification — handle them in a dedicated branch first.
  • MMS / media. Siftfy classifies text. If the inbound has a MediaUrl0, classify only the message body (which may be empty); fall back to your media scanner for the attachment.
  • Encoded / Unicode tricks. Some spammers send Cyrillic lookalikes for "FREE" or "WIN". The model handles these natively, but they will score slightly lower than plain ASCII. Pair with a Unicode-confusable detector if your volume justifies it.
  • Compliance. SMS spam laws (TCPA, CTIA guidelines) mean you generally need to keep the conversation log even for blocked messages. Always persist before deciding whether to forward.
Start free

10,000 classifications / month free. See /v1/predict for the response shape, or related patterns: contact forms, comments.