Implementing OTP Resend Cooldown
How to implement a polished OTP resend flow with cooldown timer, exponential back-off, server-side enforcement and clear UX. Patterns for web and mobile.
Resend cooldowns prevent users from accidentally (or maliciously) firing repeated OTPs. Combined with server-side enforcement, they cap your wallet exposure.
Why Cooldown Matters
- Wallet protection — every resend costs.
- Carrier rate-limit avoidance.
- User confusion reduction (which OTP is current?).
Progressive Cooldown
- Resend 1: 30s.
- Resend 2: 60s.
- Resend 3: 120s.
- Past resend 3: lock out, restart flow.
Server-Side Enforcement
async function canResend(phone: string) {
const k = \`resend:\${phone}\`;
const count = parseInt(await redis.get(k) ?? '0');
const since = await redis.ttl(k);
const required = [30, 60, 120][count] ?? 999;
return since < (required - 1) ? false : true;
}
UX Patterns
- Disabled button with countdown text.
- Auto-focus OTP input after send.
- Allow one-tap voice fallback after the second resend fails.
FAQ
Combine with idempotency keys to handle network retries cleanly.
StartMessaging Team
StartMessaging Team