Back to Blog
Getting Started

What is OTP? How One-Time Passwords Work (And How to Send Them)

Collins VidzroCollins Vidzro2026-08-0911 min read
What is OTP? How One-Time Passwords Work (And How to Send Them)

What is OTP? How One-Time Passwords Work (And How to Send Them)

If you've ever signed up for a bank app, confirmed a purchase, or logged into an account from a new device, you've used a One-Time Password (OTP). It's one of the most common security mechanisms on the internet — and also one of the most misunderstood.

This guide breaks down what OTP actually means, the different ways it's delivered, how the underlying flow works, and how to add it to your own application correctly.


1. What is an OTP?

A One-Time Password (OTP) is a numeric or alphanumeric code that is valid for only a single login session or transaction, and typically expires after a short window of time (usually 60 seconds to 10 minutes).

Unlike a regular password, an OTP is:

  • Single-use — it can't be reused once verified (or expired)
  • Time-bound — it stops working after a short TTL (time-to-live)
  • Randomly generated — a new code is issued for every request

Because it's generated fresh each time and expires quickly, an intercepted or leaked OTP has a very short window in which it's actually useful to an attacker — which is why OTP is a core building block of two-factor authentication (2FA).


2. How Does OTP Work? (The Full Flow)

At a high level, every OTP flow follows the same five steps, regardless of channel:

  1. Trigger: A user requests a login, signup, or sensitive action (e.g., a password reset or a payment).
  2. Generate: Your server generates a random code (commonly 4–6 digits) and stores it — associated with that user/session — along with an expiry timestamp.
  3. Deliver: The code is sent to the user through a channel they control: SMS, email, WhatsApp, or a push notification from an authenticator app.
  4. Submit: The user reads the code and enters it back into your app.
  5. Verify: Your server checks the submitted code against the stored one and its expiry. If it matches and hasn't expired, access is granted; the code is then invalidated so it can't be reused.
[ User requests login ]
        │
        ▼
[ Server generates OTP + stores with TTL ]
        │
        ▼
[ OTP delivered via SMS / Email / WhatsApp ]
        │
        ▼
[ User submits code ]
        │
        ▼
[ Server verifies code + expiry ] ──▶ Match: grant access, invalidate code
                                   └▶ No match / expired: reject, allow retry

3. Types of OTP Delivery

Not all OTPs are delivered the same way. Each channel has different trade-offs in reach, speed, and cost.

Delivery MethodHow It WorksBest ForTrade-offs
SMS OTPCode sent as a text message to the user's phoneUniversal reach, works on any phone, no app requiredDepends on carrier delivery speed; can be delayed on poor networks
Email OTPCode sent to the user's inboxUsers without reliable SMS reception, low-cost fallbackSlower (seconds to minutes), spam-filter risk
WhatsApp OTPCode delivered via a WhatsApp template messageHigh engagement, rich formattingRequires the user to have WhatsApp and have opted in
Voice OTPAn automated call reads the code aloudAccessibility, users who can't read SMSHigher cost per delivery, requires answering a call
App-based (TOTP)An authenticator app (e.g., Google Authenticator) generates a code locally using a shared secret and the current timeNo network dependency once set up, most phishing-resistantRequires the user to install and set up an app first

Most production systems don't rely on a single channel — they use SMS as the primary channel with email or WhatsApp as an automatic fallback if the first delivery attempt fails.


4. OTP vs TOTP: What's the Difference?

These two terms get confused often:

  • OTP (One-Time Password) is the general concept — any single-use, time-limited code, regardless of how it's generated or delivered.
  • TOTP (Time-based One-Time Password) is a specific algorithm (defined in RFC 6238) where the code is derived from a shared secret key and the current time, computed independently by both the server and an authenticator app — no network round-trip needed to deliver it.

In short: all TOTP codes are OTPs, but not all OTPs are TOTP. An SMS code your server generates and sends is an OTP; a 6-digit code Google Authenticator shows you is a TOTP.


5. Sending an SMS OTP: A Working Example

Here's what generating and sending an OTP actually looks like using the Sendexa API. First, the request to send the code:

curl -X POST https://api.sendexa.co/v1/sms/send \
  -u "$SENDEXA_API_KEY:$SENDEXA_API_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "+233501234567",
    "from": "SENDEXA",
    "message": "Your verification code is 849301. It expires in 5 minutes."
  }'

And the equivalent using the Node.js SDK, with a minimal generate-and-verify implementation around it:

import { Sendexa } from "@sendexa/sdk";
import crypto from "crypto";

const sendexa = new Sendexa({
  apiKey: process.env.SENDEXA_API_KEY,
  apiSecret: process.env.SENDEXA_API_SECRET,
});

// In-memory store for demo purposes — use Redis or your database in production.
const otpStore = new Map();

function generateOTP() {
  return crypto.randomInt(100000, 999999).toString();
}

export async function requestOTP(phoneNumber) {
  const code = generateOTP();
  const expiresAt = Date.now() + 5 * 60 * 1000; // 5 minutes

  otpStore.set(phoneNumber, { code, expiresAt, attempts: 0 });

  await sendexa.sms.send({
    to: phoneNumber,
    from: "SENDEXA",
    message: `Your verification code is ${code}. It expires in 5 minutes.`,
  });
}

export function verifyOTP(phoneNumber, submittedCode) {
  const record = otpStore.get(phoneNumber);
  if (!record) return { success: false, reason: "No OTP requested" };
  if (Date.now() > record.expiresAt) {
    otpStore.delete(phoneNumber);
    return { success: false, reason: "OTP expired" };
  }
  if (record.attempts >= 3) {
    otpStore.delete(phoneNumber);
    return { success: false, reason: "Too many attempts" };
  }

  record.attempts += 1;
  if (record.code !== submittedCode) {
    return { success: false, reason: "Invalid code" };
  }

  otpStore.delete(phoneNumber);
  return { success: true };
}

6. OTP Security Best Practices

A working OTP flow isn't automatically a secure one. A few rules matter more than the rest:

Keep the TTL short

2–5 minutes is the sweet spot. Long-lived codes give attackers a bigger window and don't meaningfully help legitimate users.

Rate-limit aggressively

Limit how many OTPs can be requested per phone number and per IP address in a given window. Without this, your SMS bill becomes an attack surface — a technique known as SMS pumping or toll fraud, where bots trigger OTPs to premium-rate numbers to generate carrier payouts at your expense.

Cap verification attempts

Three to five tries per code, then invalidate it and force a new request. This blocks brute-forcing a 6-digit space.

Never reuse or extend a code

Each new request should generate a fresh code, not resend or extend the previous one.

Use a fallback channel

If SMS delivery fails or is delayed, automatically retry via email or WhatsApp rather than leaving the user stuck.

Don't roll your own crypto

Use a cryptographically secure random number generator (like crypto.randomInt in Node.js) — never Math.random() — for code generation.


7. Common OTP Mistakes to Avoid

  • Making codes too long or too short. 6 digits is the industry standard — long enough to resist guessing, short enough to type from memory.
  • Sending the same code twice. If a user requests a resend, generate a new code and invalidate the old one.
  • No expiry at all. An OTP with no TTL is just a weak permanent password.
  • Logging OTP codes in plaintext. Treat OTP codes as sensitive data in your logs and error tracking.
  • Relying on a single delivery channel with no fallback, which turns a routine carrier delay into a failed signup.

Conclusion

OTP is deceptively simple on the surface — generate a code, send it, check it — but the details around expiry, rate limiting, and fallback delivery are what separate a secure implementation from one that quietly leaks money to fraud or loses users to failed deliveries.

If you're building OTP verification into your product, Sendexa gives you a single API for SMS, email, and WhatsApp delivery, so your fallback logic is a few lines of code instead of three separate vendor integrations.

#OTP#Authentication#Security#SMS API#Guides
Collins Vidzro

Collins Vidzro

Founder & Lead Developer at Sendexa, writing about high-throughput communication APIs, security, and digital inclusion.

Share: