Which copies of a user's phone number your SaaS should keep, and which it should never have made
Views and first-hand experience in this guest post are the author’s own. Published by Elvento Labs, the maker of Ghost. How we research and review
Most founders can tell you whether their database is encrypted. Far fewer can tell you how many copies of a single customer's phone number exist across their stack, or who can read each one. This article gives you a way to find those copies, a rule for deciding which ones to keep, and a one-hour audit you can run this week.
When a founder asks us whether their product handles phone numbers safely, the question usually means "is the database locked down?" It almost always is. Managed databases are encrypted at rest by default, access is behind a login, and the column itself is rarely the problem.
The better question is: how many places does one phone number end up in after a user signs up, and does each of those places actually need it? We have been building and taking over production systems since 2014, and the answer we find is nearly always "more places than anyone listed, and most of them do not need it."
Why a phone number deserves more care than an email address
An email address is easy to replace. People keep throwaway addresses, aliases and work accounts. A phone number is different in three ways that matter to your users.
First, it rarely changes. Many people keep the same number for years, so a number leaked from your product today can still identify that person long after they have stopped using it.
Second, it links identities. The same number sits behind someone's messaging apps, their bank, their delivery accounts and their dating profile. A leaked email ties a person to one service. A leaked number ties them to most of the services they use.
Third, it is often a key, not just a contact detail. If your product, or any other product, lets a user reset their account with a code sent by SMS, then whoever controls that number controls the account. SIM swap and number porting attacks exist precisely because of this. The US National Institute of Standards and Technology now classes SMS and voice one-time codes as a "restricted" authenticator in its digital identity guidelines (NIST SP 800-63B), meaning organisations that use them are expected to offer an alternative and to account for those risks.
Put together: a phone number is closer to a password than to a mailing address. Treat its copies accordingly.
Follow one number through signup
Pick a real test account and follow its number from the signup form outwards. In a typical small SaaS product, this is where it goes:
| Where the copy lives | How it got there | What usually needs to happen |
|---|---|---|
| Your user table or auth provider | The signup form | Keep. This is the one copy you need. |
| Your SMS provider's message logs | Every verification code and notification you send | Keep only for as long as the provider requires; check its log retention settings. |
| Application logs | A developer logging the request body while debugging, then never removing it | Remove. Log the user ID instead. |
| Error tracker | Request payloads attached automatically to exceptions | Scrub the field before the event leaves your server. |
| Product analytics | The number sent as a "user property" so someone could filter by it | Remove. Analytics needs a user ID, never a contact detail. |
| Email and CRM tools | A sync that copies every user field "in case marketing needs it" | Send only the fields with a documented purpose. |
| Support desk | Agents pasting the number into tickets, or an integration that shows full profiles | Show a masked version by default. |
| Spreadsheets and exports | A one-off export for a sales call or a data cleanup | Delete after use, and ask where the last one went. |
| Backups | Every nightly snapshot of the database | Keep, but know how long they live and include them in your deletion story. |
| AI prompts and model logs | A support summary or classification feature that sends the whole ticket | Redact before the call leaves your system. |
Most teams recognise the first and last rows of their own stack. The middle rows are where copies accumulate, because each one was added by a different person for a reasonable short-term reason.
One question we recommend founders put to anyone who handles their data, including their own team: name every system on your deletion runbook, out loud. It is a hard question to fake. If the list stops at the database while analytics, the email tool and the support desk go unmentioned, you have found the gap.
Keep one copy, and give every other system a user ID
The rule we apply is simple: one system owns the phone number, and everything downstream refers to the person by an internal ID.
That sounds obvious, but it changes how you build integrations. Instead of syncing the full user record to your analytics tool, you send user_8f2c and the events. Instead of attaching the request body to an error report, you attach the user ID, so an engineer can look the person up through the one system that is allowed to hold the number, with its own access control and audit trail.
We have run this pattern in regulated health-tech work, where samples travel through labs, warehouses and analytics under pseudonymous identifiers, and a single service owns the link back to the person. A SaaS product with ten thousand users does not need the same ceremony, but it benefits from the same shape: most of your stack can work perfectly well without knowing who anyone is.
For people who do need to see the number, mask by default. A support agent confirming "is this the number ending in 321?" does not need the other digits on screen. If someone needs the full number, make revealing it a deliberate action that records who did it and when. That single log line turns "someone on the team must have leaked it" into a question you can actually answer.
Decide whether your users should ever see each other's numbers
If your product connects people, such as a marketplace, a booking tool, a service platform or a community, there is a second question: does one user ever see another user's number?
Many products expose numbers by accident. A booking confirmation includes the provider's mobile. A marketplace listing shows the seller's contact details because the first version had no messaging. Once shared, that number is outside your control for good, and so is everything it links to.
There are two deliberate alternatives. The first is in-app messaging, which keeps the conversation inside your product and under your moderation. The second is a relay or masked number, where each party sees a number that forwards to the other and can be retired when the transaction ends. Both have a cost. In-app messaging is a feature you must build, moderate and keep running. Relay numbers cost money per message and will be bypassed by users who decide to swap real numbers anyway. Neither is free, but choosing one on purpose is much cheaper than discovering that your product has been handing out personal numbers since launch.
Your SMS endpoint is an attack surface, not just a feature
Any form that sends an SMS to a number the visitor types in is a way for a stranger to spend your money. SMS pumping fraud, which providers also call artificially inflated traffic, works exactly like this: automated scripts request verification codes to ranges of numbers controlled by the attacker's partners, and the attacker takes a share of the resulting charges. Twilio and other SMS providers publish guidance on defending against it.
The defences are unglamorous: rate limit code requests per number, per IP address and per session; only allow destination countries you actually serve; put a CAPTCHA or other bot check in front of the send; and set an alert on SMS spend that someone actually reads.
That last point is not hypothetical. When we took over one PHP monolith, our integration inventory turned up an SMS gateway on a prepaid balance that nobody was monitoring. Nothing had gone wrong yet, but a pumping attack would have drained it silently, and the first symptom would have been real users no longer receiving their login codes.
On the authentication side, if SMS codes are your only second factor, offer an authenticator app or passkeys as well, and do not let a phone number alone recover an account that holds anything valuable.
Where this advice does not apply
Some of these measures are easy to overdo, and a few of them do not work the way people expect.
Do not treat hashing as anonymisation. There are only so many valid phone numbers in any country, so an unsalted hash of a phone number can be reversed by simply hashing every possible number and comparing. A keyed hash is better for matching records, but the result is still personal data under GDPR, because anyone holding the key can link it back.
Do not build a tokenisation vault for an internal tool with forty users. The one-copy rule, masked display and a clean deletion runbook cover most small products. Heavier infrastructure is for when you have regulatory obligations or a volume of data that justifies it.
And do not strip the number from places where it is genuinely the point. Your SMS provider needs it to send the message. A delivery driver may need to call the customer at the door. The goal is not zero copies, it is that every copy has a reason you could explain to the user it belongs to.
A one-hour audit you can run this week
Take one real account, ideally a colleague's with their permission, and search for its phone number in:
- your application logs and error tracker
- your analytics tool, including user properties and event payloads
- your email, CRM and support tools
- any shared drive where exports live
- your SMS provider's dashboard, including message logs and their retention setting
- the prompts and logs of any AI feature you run
For every hit, write down why that system has it and who can see it. Anything without a reason goes on a list to remove. Then check whether your deletion process, whether a script or a checklist, would actually reach each system on the list.
Start on Monday with the error tracker and the analytics tool. They are the two places phone numbers most often end up by accident, and the two easiest to fix: in both cases the change is usually a few lines of configuration that swap the number for a user ID. The rest of the list can follow, but those two alone will remove most of the copies nobody meant to make.
See also
