A one-time password (OTP) is the most common proof that a person controls a phone number. It's also one of the easiest flows to get subtly wrong. This guide walks through how OTP verification works, what to build, and what to avoid.
What is an OTP?
An OTP is a short, single-use code — usually four to six digits — that you send to a user and ask them to type back. If they can, you know they have access to the phone number (or device) you sent it to.
The goal isn't to be clever. It's to be fast, reliable and hard to abuse.
You'll typically use OTPs for:
- Sign-up verification — confirming a real number before creating an account.
- Login and 2FA — adding a second factor to passwords.
- Transaction confirmation — approving payments, deliveries or sensitive changes.
The OTP lifecycle
Every verification follows the same four steps.
- Generate a random code on your server.
- Store a hash of it with an expiry and an attempt counter.
- Deliver it over SMS or WhatsApp.
- Verify the code the user submits, then invalidate it.
Generating a secure code
Use a cryptographically secure random source — never Math.random().
import { randomInt } from "node:crypto";
export function generateOtp(length = 6) {
return String(randomInt(0, 10 ** length)).padStart(length, "0");
}
Sending it with OTPIQ
Delivery is a single API call. OTPIQ handles carrier routing and automatic fallback for you.
curl -X POST "https://api.otpiq.com/api/sms" \
-H "Authorization: Bearer $OTPIQ_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"phoneNumber": "9647501234567",
"smsType": "verification",
"provider": "auto"
}'
Let routing do the work
Using provider: "auto" lets the platform pick the best channel for each number and retry on failure — so one blocked route doesn't mean a lost user.
SMS vs. WhatsApp
Both channels work; they just trade off differently.
| SMS | ||
|---|---|---|
| Reach | Any phone with signal | Users with WhatsApp installed |
| Cost | Higher per message | Typically lower |
| Delivery speed | Seconds, carrier-dependent | Near-instant over data |
| Best for | Universal fallback | Cost-sensitive, high-volume flows |
Security checklist
Don't skip these
An unprotected OTP endpoint is an invitation for SMS-pumping fraud and brute-force attacks.
- Expire codes after 5 minutes or less.
- Limit verification to 3–5 attempts per code.
- Rate-limit sends per phone number and per IP.
- Store a hash of the code, not the code itself.
- Make codes single-use — invalidate on success.
Mistakes that hurt conversion
Most failed verifications aren't attacks — they're friction. Watch for:
- Slow delivery with no feedback. Show a countdown and a clear "Resend" option.
- Codes that expire too quickly on congested networks.
- No autofill. Format the message so the OS can offer the code automatically.
- No fallback channel. If SMS fails, offer WhatsApp.
Final thoughts
Great OTP flows feel invisible: the code arrives in seconds, autofills, and works the first time. Get the lifecycle right, protect the endpoints, and measure delivery — everything else is polish.
- #otp
- #authentication
- #sms
- #security
- #iraq
