One of the most widely deployed machine learning systems in production is one nobody writes tutorials about. It has no chat interface, no demo notebook, and no benchmark leaderboard. It runs every time somebody types a phone number into a signup form, and it decides — in the time before the page repaints — whether that person is worth sending a code to.
- The feature vector is not the number
- Three properties that break the textbook setup
- The error nobody measures
- Who is standing on the wrong side of the threshold
- The newest source of false positives is probably yours
- If you are shipping the product rather than the model
- What none of this changes
- The short version
Most users assume the check is a format validation. It is a classifier, and a fairly interesting one, because it has to work under conditions that break most of the assumptions a supervised learning course is built on.
The feature vector is not the number
The digits themselves carry almost no signal. What the model consumes is everything a lookup returns about them, plus everything the platform already knows about the surrounding context.
The intrinsic features come from a carrier lookup: the line type — mobile, landline, or VoIP — the country of issue, the carrier holding that range, whether the number has been ported between carriers, and roughly how long it has been in active service.
The relational features are where the accuracy actually comes from. How many accounts have referenced this number, or numbers adjacent to it in the same range, in the last few days. Whether the device fingerprint, IP, or payment instrument attached to this signup appears on accounts that were previously actioned. How the form was filled — paste versus keystroke, dwell time, field order.
That split matters if you are designing one of these systems, because it explains a result that surprises people: a number in isolation is weakly predictive, while the same number viewed as a node in a graph is strongly predictive. Signup fraud is rarely one account. It is a batch, and batches leave structure.
Three properties that break the textbook setup
The classes are wildly imbalanced. In most consumer products fraudulent signups are a small minority of all signups, often a fraction of a percent. A model that answers “legitimate” unconditionally will post an accuracy figure that looks excellent and is worth nothing. Evaluation has to move to precision and recall at a chosen operating point — and choosing that point is a business decision about the relative cost of a blocked customer versus an abusive account, not something the loss function can settle for you.
The distribution is adversarial, so it does not sit still. A cat classifier faces cats that do not read your model card. Fraud does. The moment a feature becomes decisive it becomes a target: numbers are aged before use, ranges are rotated, sessions are spread across devices to break the velocity signal. This is non-stationarity by construction rather than by accident, which means retraining cadence and feature rotation are part of the system design, not maintenance chores bolted on later.
Labels arrive late, incomplete, and dirty. Ground truth shows up weeks after the decision, as chargebacks, abuse reports, and manual review outcomes. Much of what gets labelled “fraud” is more accurately labelled “caught,” and the difference between those two things is invisible in the training set.
The error nobody measures
Here is the part that should interest anyone who has built a recommender or a ranking model, because it is the same pathology in different clothing.
When the model declines a signup, the code is never sent, the user gets a dead end, and they leave. No label is ever generated. You do not find out whether they were a legitimate customer, because you removed the only mechanism that could have told you.
So the two error types are not observed symmetrically. False negatives announce themselves — an abusive account eventually does something that costs money and produces a record. False positives produce silence, and silence does not appear in a confusion matrix. The next training round then learns from data your own model already filtered, and the decision boundary hardens around whatever it happened to decide last time. Your logs are generated by the policy you are trying to improve.
The mitigations are known, and mostly unglamorous. Hold out a small randomized slice that bypasses the model entirely, so you have an uncensored sample to estimate the true positive rate against. Track the downstream behaviour of borderline cases you approved, not just the ones you blocked. Treat support tickets and appeals as a window into false positives — a biased and tiny window, but the only direct one you have. And if you cannot afford an exploration slice, at minimum log the score distribution of rejections and go looking for a population sitting just past the threshold that does not look like abuse at all. There usually is one.
Who is standing on the wrong side of the threshold
That population is not random. It clusters in ways that are predictable once you look.
People whose primary line is a VoIP service, because it follows them across devices and countries. People whose number was issued in one country while they live in another — migrants, remote workers, anyone maintaining a business presence in a market they do not physically sit in. Recently ported numbers, and numbers activated last week, both of which look identical to a freshly acquired burner from the model’s point of view. Travellers rotating SIMs.
None of these are edge cases in the statistical sense. They are ordinary people falling outside a default assumption — one number, one country, one carrier, held for years — that the feature engineering quietly encodes.
The newest source of false positives is probably yours
Agentic systems have added a category that barely existed two years ago. An LLM agent driving a browser to complete a task on a user’s behalf produces exactly the timing signature, exactly the paste-not-type behaviour, and exactly the device reuse pattern that these models were trained to catch. The classifier is not malfunctioning. It is generalizing correctly from what scripted abuse looked like when the training data was collected.
The tempting engineering response is to make the agent look more human. That is a bad direction on the merits and worse in practice: it is an arms race against a system that retrains, and on most platforms it is a terms violation regardless of intent. The path that holds up is the boring one — use the official API, use OAuth where the platform offers a delegated authorization flow, and where a product exposes no programmatic surface at all, read that as the answer rather than an obstacle. Account creation is generally not a supported automation surface, and building on the assumption that it is means building on something that will break without warning.
If you are shipping the product rather than the model
The same system that scores your users also scores your own tests, and this is where teams get caught. A signup flow validated against one developer’s phone in one country tells you about one routing path. Aggregate delivery rate then stays green while a specific market quietly goes to zero, because the aggregate is dominated by wherever most of your traffic already is.
Testing that means something needs a real line in each market you serve, exercised against the live path rather than a mock — a local SIM where the team has people on the ground, and an online SMS verification service where it does not. The requirement in either case is the same: a genuine carrier line in the destination country, since a VoIP fixture reproduces the rejection rather than the delivery.
What none of this changes
Getting a code delivered clears one gate. It does not touch identity verification. Where a platform requires a document check, a liveness selfie, or national ID matching — regulated finance, payouts, government services — that gate is separate and deliberately not negotiable, and no amount of number sourcing or model understanding moves it. Nor does any of this override a service’s terms about who is permitted to hold an account.
And a note for anyone building the classifier: inherited blocklists are not ground truth. Importing a third-party list imports somebody else’s false positives, wrapped in the authority of a feature that looks objective. It will improve your offline metrics and quietly widen the population you cannot see.
The short version
Phone verification looks like plumbing and is actually a live classification problem, running under class imbalance, adversarial drift, and censored feedback — the three conditions that make a model degrade in ways its own metrics will not report.
If you operate one, the highest-value thing you can build is not another feature. It is a way of seeing the decisions you got wrong in the direction that stays silent.

Sandeep Kumar is the Founder & CEO of Aitude, a leading AI tools, research, and tutorial platform dedicated to empowering learners, researchers, and innovators. Under his leadership, Aitude has become a go-to resource for those seeking the latest in artificial intelligence, machine learning, computer vision, and development strategies.

