How to Verify a Phone Number in Your App with an SMS Code
Want to confirm a phone number really belongs to your user? You only need two API calls: one to send a code and one to check it. We'll walk through both.

You've seen this a thousand times. You sign up for an app, and a few seconds later your phone buzzes with a text: "Your code is 418209." You type it in, and you're through.
That little code is called a one-time password, or OTP. It's how an app proves that the phone number you gave really belongs to you. If you're building an app, you'll want it for sign-ups, password resets, big transactions, and anywhere else you need to be sure someone is reachable.
The idea is simple. The part around it isn't. Someone has to generate the code safely, get the text delivered on local networks, handle numbers typed in different formats, expire old codes, stop people guessing, and stop abuse. You could build all that. Or you could make two API calls.
How it works
It's a two-step conversation with our API.
Step 1: Send the code
POST /v1/phone/verify/start
{
"phone": "08031234567",
"template": "Your code is {code}. It expires in {minutes} minutes.",
"reference": "signup-8842"
}
We create the code, text it to your user, and send you back an id, the cleaned-up phone number, when the code expires, and your own reference so you can match it to the right sign-up.
A few things make this easier than you'd expect:
- You don't need to reformat numbers. If your user types
08031234567, we turn it into+2348031234567for you. - You control the message. Write your own wording in
template. It just needs to contain{code}. You can also use{minutes}. - Your
referencecomes back unchanged, so you can link the attempt to whatever session or sign-up it belongs to.
Step 2: Check what they typed
POST /v1/phone/verify/check
{ "id": "phv_01hxyz...", "code": "418209" }
We tell you whether it matched. And checking is free. You pay when a message is actually sent, and if the text can't be delivered, you're refunded in full.
The full details are in the API reference.
What you don't have to build
This is the part that saves the most time:
- Safe code generation, with codes that are never stored in plain text.
- Delivering the SMS and formatting the number.
- Expiry, so an old code can't be used later.
- Attempt limits, so someone can't try every possible code. After too many wrong guesses, that attempt is dead.
- Send limits per customer and per phone number, which help protect you from people abusing your sign-up to spam others or run up your bill.
Test it without texting strangers
While you're building, use a test key. A test key gives you the exact same response, but no real SMS is sent and nothing is charged. Nobody's phone gets a surprise message. Once your flow works, swap in a live key.
Where this fits in your app
- Sign-up: confirm the number is real and reachable, and cut down on throwaway accounts.
- Logging in from a new device: a quick extra check.
- Account recovery: a second way to prove it's really them.
- Sensitive actions: adding a new payee, changing account details, large transfers.
- Delivery and ride apps: make sure the number a driver or courier will call actually works.
You can go a step further by pairing it with an identity check. Verify the person with a BVN or NIN and the phone with an OTP, and you've tied a real identity to a number they can actually answer.
Being honest about SMS codes
SMS is familiar and convenient, but it isn't the strongest option. Criminals can sometimes take over a phone number through a "SIM swap," and people can be tricked into reading a code aloud. For everyday use, it's a big step up from nothing. For high-value actions, add another layer, such as a liveness and face check.
Try it
Create an account, generate a test key, and send yourself a code from the dashboard to see how it feels. And once it's working, our guide to getting OTPs right covers expiry, retries and abuse.




Comments
Questions and corrections welcome. Be kind; links are limited to keep spam out.