# Phone Verification Service Low-Level Design: OTP Generation, SMS Delivery, and Fraud Prevention

Source: https://www.techinterview.org/post/3233469495/lld-phone-verification/
Updated: 2026-04-17 · techinterview.org

## What Phone Verification Accomplishes

Phone verification confirms that a user possesses a specific phone number at registration or for two-factor authentication. Unlike email, phone delivery is synchronous and near-instant, making it suitable for time-sensitive OTPs. The design must handle SMS delivery reliability, abuse prevention, international numbers, and the cost implications of SMS at scale.

## OTP Generation: HOTP and TOTP

One-time passwords can be generated two ways. **HOTP** (HMAC-based OTP, RFC 4226) uses a counter: `OTP = HOTP(secret, counter)` where `HOTP = truncate(HMAC-SHA1(secret, counter))`, producing a 6-digit code. The counter increments on each new OTP request. **TOTP** (Time-based OTP, RFC 6238) replaces the counter with a time step: `counter = floor(current_time / 30)`, producing a code that changes every 30 seconds. For SMS verification, HOTP with a per-request counter stored in Redis is simpler: you generate one code per verification attempt, store it, and verify against it. TOTP is more common for authenticator app scenarios.

For a 6-digit OTP, the search space is 1,000,000. Combined with a short TTL (10 minutes) and attempt limiting (5 tries max), brute force is not feasible.

## OTP Storage in Redis


```
Key:   verify_otp:{e164_phone_number}
Value: {
  "otp": "847291",
  "attempts": 0,
  "expires_at": 1730000000
}
TTL:   600 seconds (10 minutes)
```

Store the OTP, attempt counter, and expiry in a single Redis hash. The Redis TTL automatically cleans up expired OTPs. On each failed verification attempt, increment `attempts`. If `attempts` reaches 5, delete the key and return a lockout error — the user must request a new OTP. This prevents brute-force enumeration of the OTP space.

## Rate Limiting OTP Requests

Limit OTP request rate by phone number to prevent SMS flooding. Use two Redis counters: a short window (3 requests per 10 minutes) to prevent rapid resend abuse, and a longer window (10 requests per 24 hours) to limit daily cost exposure per number. Return HTTP 429 with retry-after when either limit is hit. Track by E.164-normalized phone number to prevent bypass via number formatting variations (`+14155552671` vs `14155552671`).

## SMS Delivery via Twilio / AWS SNS

Validate the phone number before attempting delivery. Use a number validation API (Twilio Lookup, Google libphonenumber) to confirm the number is valid E.164 format and is a mobile number (not a landline, which cannot receive SMS). Reject invalid numbers before calling the SMS provider — this saves cost and gives faster user feedback.

Send via Twilio or AWS SNS with a delivery status callback webhook. Log the provider's message SID and initial status (`queued`, `sent`, `delivered`, `failed`, `undelivered`). Delivery failures (carrier rejection, unreachable number) should surface to the user as a retryable error with a clear message, not a generic "something went wrong."

## Verification Flow and Timing-Safe Comparison

Full flow: (1) User requests OTP for their phone number. (2) Server validates number format, checks rate limits, generates OTP, stores in Redis, sends SMS. (3) User enters 6-digit code. (4) Server retrieves stored OTP from Redis, increments attempt counter, compares codes using a constant-time comparison. (5) On match: mark phone as verified, delete OTP from Redis. (6) On mismatch: return error with remaining attempts count.

Use constant-time string comparison (e.g., `hmac.compare_digest` in Python, `crypto.timingSafeEqual` in Node.js) to prevent timing attacks. A character-by-character comparison that short-circuits on the first mismatch leaks information about how many leading digits are correct.

## Voice Call Fallback

Some users cannot receive SMS: feature phones with full inboxes, corporate devices with SMS blocked, VOIP numbers. Offer a voice call fallback that reads the OTP aloud using text-to-speech. Twilio and AWS Connect both support this. Present the fallback option after one failed SMS delivery or explicitly in the UI. Voice calls are more expensive than SMS — rate-limit voice call attempts more aggressively (1 per 10 minutes).

## SIM Swap Fraud Detection

SIM swap attacks occur when a fraudster ports a victim's number to a new SIM, then completes SMS-based authentication. Mitigations: (1) Check if the phone number was ported recently using carrier lookup APIs (Twilio offers this via the Lookup API with `PortedDate`). A number ported within the last 24–48 hours is high risk. (2) Check account age at the carrier — very new SIMs are suspicious. (3) For high-value actions (large withdrawals, password resets), consider requiring additional verification when SIM swap indicators are present.

## International Handling and Cost Controls

SMS costs vary dramatically by country — a message to the US costs $0.0079 via Twilio, but messages to some countries cost $0.20+. International numbers can be used for SMS pumping fraud: bots register with premium-rate numbers and collect revenue from the SMS termination fees. Controls: geo-restrict SMS to countries where your service operates, maintain a blocklist of known premium-rate prefixes, set a daily cost budget per account, and flag accounts that verify multiple numbers in short succession.

Within the OTP validity window, if a user requests a resend, return the same OTP rather than generating a new one. This avoids sending two valid OTPs simultaneously and reduces delivery cost for impatient users who tap "resend" before the first SMS arrives.

See also: [Netflix Interview Guide 2026: Streaming Architecture, Recommendation Systems, and Engineering Excellence](https://www.techinterview.org/post/3233460274/netflix-interview-guide-2026-streaming-architecture-recommendation-systems-and-engineering-excellence/)

See also: [Scale AI Interview Guide 2026: Data Infrastructure, RLHF Pipelines, and ML Engineering](https://www.techinterview.org/post/3233460275/scale-ai-interview-guide-2026-data-infrastructure-rlhf-pipelines-and-ml-engineering/)

See also: [Stripe Interview Guide 2026: Process, Bug Bash Round, and Payment Systems](https://www.techinterview.org/post/3233460268/stripe-interview-guide-2026-process-bug-bash-round-and-payment-systems/)
