All articles

The SMS code that stops arriving: what SHAFT filtering actually blocks for an adult classifieds site

10 min read

The code that never arrives, and no one tells you why

A visitor signs up, types a phone number, and waits for the six-digit code that lets them log in. On a good day it lands in three seconds. On a bad day it never lands at all, and nothing in the product tells anyone why. The signup form does not show an error. The phone carrier does not send a bounce. The message simply does not exist on the other end, and the only visible symptom is a slightly higher drop-off rate that could be blamed on a dozen other things.

That silence is worse than an outage, because an outage gets fixed. A carrier-side content filter that has quietly decided your traffic belongs to a blocked category does not get fixed by restarting a server or rotating an API key. It gets fixed only by understanding what triggered it, and most operators never get that far, because the rejection reason lives in a developer console they never open, phrased in language written for engineers, not for the person who owns the business.

The mechanism behind this is not a rumor or a guess. It has an acronym, a set of published error codes from the companies that route the traffic, and a direct line to the one word that sits in the name of the business: adult. US carriers filter commercial text messages against a small set of content categories, and one of those categories is sex and adult content. A classifieds directory for escorts sits inside it by definition, whatever the actual text of the message says.

This is not the same failure covered elsewhere on this blog. Email deliverability breaks because spam filters score a message on sender reputation and formatting, and a careful sender can climb back out with better authentication and warmup. SMS filtering for adult content works differently: it is not a reputation score that recovers over time, it is a category match that a carrier's system checks once, at registration, and often will not let you resubmit at all.

The SHAFT filter: what carriers actually flag, in their own words

The industry shorthand for the blocked categories is SHAFT: sex, hate, alcohol, firearms, and tobacco. It is not a term the carriers coined for marketing; it shows up inside the rejection codes that messaging platforms return when a submission fails. Twilio's own error documentation for code 30953 states plainly that a campaign was rejected because it contains sex or adult content that violates CTIA SHAFT guidelines, and adds a detail that matters more than the rejection itself: that category of rejection is not eligible for resubmission. There is no appeal form, no revised wording that fixes it, no second attempt through the normal registration flow.

The same pattern shows up on the toll-free number side, which many operators reach for specifically because it feels less bureaucratic than the standard long-code registration. Error 30441 covers a toll-free number verification that gets refused because the reviewer, looking at the actual website, the opt-in flow, or the sample messages submitted with the application, decides the use case "appears to be sex-related." The rejection is not limited to what the text message itself says. It looks at the site the phone number is registered to represent, which for a classifieds directory is the whole business.

What makes this different from a payment processor closing an account is the total absence of a review conversation. A payment processor terminates a merchant with a notice period, a reason code, and sometimes a compliance officer who will discuss it. A carrier's automated content filter returns a rejection code to a developer dashboard and moves on. Nobody at the carrier is deciding your business does not deserve service; a rule written years ago for a different reason is doing that automatically, and it does not know or care that the messages in question are harmless six-digit login codes.

The category match happens at the level of the registered brand and use case, not just the literal words inside each message. A campaign gets registered with a business name, a description of what the messages are for, and often a link to the website. If any of those three things reads as adult content, the filter can reject the whole campaign even though the actual SMS text is nothing but a number and the word "code." This is the detail operators miss most often: sanitizing the message body does not sanitize the registration that sits behind it.

Why "just switch to Verify" is not the full fix

The standard advice, once an operator understands the problem, is to stop trying to register a full commercial messaging campaign and use a dedicated authentication product instead, such as Twilio's Verify API or an equivalent from another provider. This advice is correct as far as it goes: a verification-only product built for one-time codes is explicitly exempted from the full brand-and-campaign registration process that trips the SHAFT filter, and Twilio's own onboarding documentation confirms that a business sending nothing but OTP codes does not need to register a Brand or Campaign at all when using that product.

The exemption comes with a condition that rarely makes it into the advice people repeat to each other: it applies specifically to sending through the provider's own shared, pooled numbers, not to routing verification codes through a phone number your business registered and owns. Point that same OTP traffic at your own 10DLC number, and the standard registration rules are back, SHAFT category and all. The workaround is real, but it is a workaround tied to a specific delivery path, not a general exemption for any message that happens to contain a six-digit code.

There is also a cost to taking the exemption. Carriers give registered, vetted campaigns priority delivery and higher throughput specifically because the business behind them has been checked. A shared pooled number carrying unregistered-adjacent traffic sits lower in that priority order, which shows up as a small but real increase in delivery delay and occasional throttling during high-volume periods, such as a marketing push or a spike in new signups after a feature launch. For a small directory this rarely matters. For one running promotions or seasonal surges in new advertiser sign-ups, the gap between "delivered in two seconds" and "delivered in twenty" is the gap between a completed signup and an abandoned one.

The filter that decides whether a message clears also looks past the sending path at signals a business rarely controls directly: how closely the actual content of messages matches the use case that was approved, and how the sending number's reputation is trending. A support message here, a promotional blast there, sent through the same channel set up for clean OTP delivery, can shift that signal even without a single sexual word appearing anywhere. Keeping the verification channel completely separate from every other kind of message the business sends is not a nice-to-have here; it is the only way the exemption keeps working.

Going around registration does not work anymore

Before any of this filtering tightened, the common shortcut was simply not registering at all: buy a long-code number, send messages, accept that some fraction would get filtered, and treat the loss as a cost of doing business. That shortcut is gone. Twilio's own changelog on the shutdown of unregistered US 10DLC messaging describes a filtering regime that escalated through the middle of 2023 and, after the end of August that year, became a full block of all unregistered US-bound traffic, returned to the sender as error code 30034. This is not heavier filtering with a surcharge attached. It is traffic that does not arrive, full stop, with no fee that buys it back.

The distinction matters because a business that registered once, got flagged, and quietly stopped paying attention to its registration status is not in the older, softer regime either. A campaign whose trust score has dropped, whether from a content mismatch, a spike in opt-out complaints, or simple inactivity, drifts toward the same treatment as unregistered traffic: lower throughput, more filtering, and no clear notice that anything changed, because the block happens on the carrier's infrastructure, several steps removed from wherever the business is watching its own logs.

This is the same shape of risk this blog has already described for hosting, CDN, and domain infrastructure: a vendor several layers removed from the product can turn off a piece of it on their own schedule, for their own policy reasons, and the first sign is the absence of something that used to work. The difference with SMS filtering is that the "off switch" is not one company's decision. It is a rule enforced jointly by every major US carrier, applied consistently regardless of which messaging platform sits in the middle, which means switching providers does not route around it the way switching a host sometimes routes around a single company's policy.

There is no legitimate way to disguise a classifieds directory as something else during registration to dodge the category match. Doing so risks a permanent rejection on the one submission a campaign type may get, and a rejection tied to misrepresentation is a worse outcome than never registering, because it forecloses the legitimate path too. The honest route, however inconvenient, is the only one that leaves a second attempt on the table.

What to build before the phone number stops being reliable

The first concrete step is to stop treating the phone number as guaranteed infrastructure and build a second channel into the signup and login flow from the start, not after the first support ticket about a missing code. An authenticator app code, a magic link sent by email, or a backup code generated at signup all route around a carrier filter entirely, because none of them depend on a message category that a phone network is scanning. None of these options work outside the flow you build for them, so building at least one now costs far less than retrofitting it after a filter starts eating a visible share of signups.

The second step is separating channels completely: whatever number or product sends one-time codes should never also send a promotional text, a re-engagement nudge, or a payment reminder. Mixing message types on one sending path is exactly the pattern that erodes the reputation signal a filter is watching, and it puts the one channel a business actually needs, the login code, at risk to protect a marketing channel that has other options.

The third step is registering honestly and treating the outcome as final. Because a SHAFT-category rejection typically cannot be resubmitted, the registration should go in once, with an accurate business description, after deciding in advance whether the business can accept the throughput and priority trade-offs of a Verify-style exemption or needs the fuller registration and is prepared for it to be refused. Guessing and hoping wastes the one attempt that matters.

The fourth step is watching delivery data directly rather than waiting for a user to complain. Every major messaging platform exposes delivery receipts and error codes per message; a rising share of codes like 30034 or a falling delivery rate on the verification channel specifically is the earliest available signal that something has changed, and it arrives long before it shows up as a drop in completed signups on a monthly report. An operator who checks that number monthly catches a filtering change in weeks. One who only checks conversion catches it in a quarter, after it has already cost real business.

None of this makes the phone number unusable. It makes it what it actually is: a channel a business borrows from carriers who did not build it with an adult classifieds directory in mind, and who reserve the right to say no without explaining themselves. Treating it that way, with a real fallback and a clean separation between what it carries, is what keeps a login flow working on the day the filter decides your business is the one it was written to stop.

Try the DEMO

Escort directory software, ready to go