The first sign is usually the invoice. SMS spend doubles in a month, the new sends concentrate in countries the product has no users in, and almost none of the verification codes are ever entered. Someone has pointed a bot at the "send me a code" endpoint, and every message it triggers earns a cut of the termination fee for whoever controls the receiving numbers. The industry term is artificially inflated traffic, or AIT, and it costs businesses on the order of a billion dollars a year. It is also one of the most mechanically preventable forms of abuse, because the entire attack lives on one endpoint you control.

SMS pumping is a sibling of the signup abuse covered in fake account prevention: the same bot traffic, aimed at extracting carrier fees instead of accounts.

How the scheme works

International SMS termination is a paid supply chain. When your platform sends a verification code to a number abroad, the message passes through aggregators and carriers, each taking a fee. The fraud exploits the revenue-share at the end of that chain:

  1. A fraudster obtains a financial interest in a block of phone numbers, either through a complicit or compromised carrier, a premium-rate range, or a grey-route operator that shares termination revenue.
  2. They find any public endpoint that sends SMS to an arbitrary number: OTP login, signup verification, “text me the app” links, delivery notifications.
  3. Automation submits their number ranges to that endpoint at volume, often through residential proxies and with plausible pacing to stay under naive rate limits.
  4. Your platform pays for every send. The fraudster collects their share per message. No code is ever entered, because no human is on the receiving end.

The economics explain the persistence. The attacker has effectively zero marginal cost and a direct per-message payout, so the attack continues until the endpoint stops paying out. The Mobile Ecosystem Forum, the industry body that tracks messaging fraud, estimated AIT cost businesses around $1.6 billion in 2023 and put it at 5 to 40% of international application-to-person SMS traffic depending on the route (MEF). Twilio, which sees the problem across its customer base, describes AIT as one of the dominant messaging-fraud categories and ships dedicated tooling against it (Twilio).

Why it hides

SMS pumping is unusually good at impersonating success. Verification requests go up, which dashboards report as signup growth. The spend rises smoothly rather than in one spike, because the operator throttles to avoid attention. And the costs land in a telecom invoice reviewed by finance, while the traffic lands in product metrics reviewed by growth, so the two halves of the picture often meet only after months.

The reliable early indicator is the conversion gap. Legitimate OTP flows complete at high rates; pumped traffic completes at nearly zero. Verification completion rate, broken down by destination country and number prefix, is the single metric worth alerting on. A country whose completion rate sits below 10% while volume climbs is not a growth market.

The defense, in order of leverage

Put bot detection in front of the send. The send-code endpoint is a high-value route and deserves the same scrutiny as login or checkout, which it rarely gets. Scoring the session before the SMS goes out (device intelligence, headless and automation artifacts, behavioral presence) removes the bulk of the attack, because the traffic is scripted and looks it. This is the structural fix; everything else narrows the residual.

Rate-limit on device identity, not IP. Pumping operations rotate residential exits, so per-IP limits underperform here for the same reason they do in credential stuffing. A per-device budget on verification sends, keyed to the visitor fingerprint, holds up against rotation: one device requesting codes for forty numbers is a fraud signal regardless of how many IPs it used.

Constrain the destination space. Most products serve a known set of countries. Default-deny SMS to the rest, and require explicit review to open a new corridor; the highest-payout pumping ranges concentrate in a recognizable set of destinations. Validate numbers with a library like libphonenumber and reject malformed, premium-rate, and known-VOIP ranges before sending.

Add friction only on the suspicious slice. An invisible proof-of-work or equivalent challenge before the send, applied when the session score is ambiguous, raises the attacker’s marginal cost from zero without taxing real users. Exponential backoff per device on repeated requests does the same for retry loops.

Use the platform-level shields too. Twilio’s Verify Fraud Guard and the equivalents at other providers block sends to ranges with detected pumping patterns across their whole network. They are worth enabling and they catch what your local view cannot see, but they act after your request reaches the provider and on their schedule. Treating them as the only control means paying for the learning period of every new range.

Reduce the dependence. Every flow moved off SMS shrinks the attack surface and the invoice: email-first verification where assurance allows, authenticator apps and push for second factors, and passkeys, which remove the OTP entirely for returning login. Many products keep SMS only as a fallback and see both fraud and cost drop.

A note on the adjacent scam

SMS pumping has a user-facing cousin: OTP triggering as harassment or as cover traffic for account takeover attempts, where the attacker floods a victim with codes. The controls overlap almost entirely, since both ride the same endpoint with the same automation. A send-code route hardened against pumping is hardened against both.

How Foil supports it

Foil sits exactly where the structural fix goes: in front of the send. The SDK scores the session during the form interaction and produces a sealed token; the application verifies it and gates the SMS on the verdict, with per-device velocity as the backstop:

import { safeVerifyFoilToken } from "@abxy/foil-server";

app.post('/api/send-verification', async (req, res) => {
  const result = safeVerifyFoilToken(req.body.foilToken, process.env.FOIL_SECRET_KEY);
  if (!result.ok) {
    return res.status(403).json({ error: 'verification_unavailable' });
  }

  const { decision, visitor_fingerprint } = result.data;
  if (decision.verdict !== 'human' || decision.risk_score > 0.2 || !visitor_fingerprint) {
    return res.status(403).json({ error: 'verification_unavailable' });
  }

  const sends = await otp.recentSendsByVisitor(visitor_fingerprint.id);
  if (sends.last24h >= 5) {
    return res.status(429).json({ error: 'too_many_requests' });
  }

  await sms.sendCode(req.body.phone);
});

Because the verdict is computed server-side from sealed signals, the pumping operator cannot study the client to learn what tripped it. The result the invoice sees: sends track verified humans again. For the broader signup picture, continue with fake account prevention and bot mitigation.

Further reading