SMS pumping
Watch for rapid requests, low completion, unusual destinations, number ranges and traffic that grows without matching product activity.
Verification SMS
A verification message is one step in an authentication system. The application must generate and protect the code, limit requests and guesses, handle resends, verify once, expire safely and measure whether users complete the flow.
End-to-end flow
Keep the verification transaction separate from the SMS transport record, but link them through internal identifiers for traceability.
Recommended baseline
| Control | Practical starting point | Why it matters |
|---|---|---|
| Code length | At least 6 random digits; use a larger space for higher-risk actions. | Reduces success from guessing while remaining usable. |
| Expiry | Often 2–5 minutes, adjusted for delivery conditions and transaction risk. | Limits the window in which a stolen code can be used. |
| Successful use | Exactly once; invalidate the transaction atomically. | Prevents replay after verification succeeds. |
| Guess attempts | Commonly 3–5 per transaction before invalidation or step-up. | Constrains online brute-force attempts. |
| Resend cooldown | Delay the resend action and increase delay after repeated requests. | Reduces duplicate SMS, user confusion and pumping abuse. |
| Request limits | Limit by phone number, account, IP, device, session and destination. | A single identifier is easy for an attacker to rotate. |
| Code storage | Store a hash or keyed verifier with expiry and transaction context. | Reduces exposure if a verification data store is accessed. |
| Response wording | Use the same outward response for existing and unknown accounts. | Reduces account and phone-number enumeration. |
Application logic
requestOtp(user, phone, context):
normalize phone to E.164
enforce limits for user + phone + IP + device + country
run risk and destination checks
generate code with a cryptographic random source
store keyedVerifier(code), expiresAt, attempts = 0
submit an approved SMS template
return the same neutral response for every account state
verifyOtp(transactionId, submittedCode):
load transaction with a write lock
reject if expired, consumed or attempts exhausted
increment attempts atomically
compare keyedVerifier(submittedCode) in constant time
if valid: mark consumed and complete the authorized action
record outcome without logging the plaintext code
Fraud and abuse
Watch for rapid requests, low completion, unusual destinations, number ranges and traffic that grows without matching product activity.
Do not let OTP replace password, session and risk controls. Rate-limit login attempts and use step-up checks for sensitive actions.
Keep outward response wording and timing consistent so attackers cannot learn whether a phone number or account exists.
For high-value actions, combine SMS with transaction context, device signals, cooldowns or a stronger factor where appropriate.
Prefer one active transaction, invalidate superseded codes and show a countdown instead of issuing an immediate new SMS.
Allow only markets required by the product, set spend and velocity alerts, and require review before enabling new high-risk destinations.
Delivery engineering
Verification metrics
| Metric | Definition | What it helps diagnose |
|---|---|---|
| Request-to-acceptance | Accepted API requests ÷ valid verification requests. | Client validation, authentication, limits and provider availability. |
| Final delivery rate | Delivered messages ÷ messages with a final delivery outcome. | Number quality, carrier rejection, route and template issues. |
| p50 / p95 delivery latency | Median and tail time from submission to delivery signal. | OTP experience degradation hidden by average latency. |
| Verification completion | Successful verifications ÷ verification transactions started. | The combined effect of delivery, UX, expiry and fraud controls. |
| Resend rate | Transactions with a resend ÷ transactions started. | Delayed SMS, unclear UX, premature retries or user confusion. |
| Attempt exhaustion | Transactions blocked after maximum guesses. | Brute force, user error, stale codes or confusing resend behavior. |
| Cost per completed verification | Billable segments ÷ successful verifications, valued at route cost. | Real efficiency across routes, templates and resend policies. |
User experience
Show the masked destination, expiry window and when resend becomes available. Do not imply delivery is instant or ask a user to request repeated codes while the first message is still in flight.
Use a numeric input mode where appropriate, allow password managers and operating-system one-time-code autofill, preserve paste, and keep focus and error messages accessible to assistive technology.
Provide a support or alternative verification path for lost numbers and accessibility needs, but require appropriate identity checks so recovery does not become an easier route around authentication.
Fallback design
If another channel is available, apply the same transaction identity, expiry, attempt limit and risk policy. Do not issue parallel valid codes across multiple channels without a clear invalidation rule.
Use observed p95 delivery latency to set the resend timer instead of encouraging an immediate duplicate request.
Maintain one active verification transaction and define whether a resend reuses or replaces the code.
For account recovery or high-value actions, require additional proof when device, location or behavior is unusual.
A common starting range is two to five minutes, but the correct value depends on transaction risk, observed delivery latency and user accessibility. Shorten high-risk actions and avoid extending validity simply to hide delivery problems.
Either model can work if it is explicit. A new code must atomically invalidate the old one; reusing a code must not extend the original risk window indefinitely. Keep only one active verification truth.
Only when combined with short expiry, strict attempt limits, request-rate limits, secure generation, protected storage and transaction binding. Higher-risk actions may require a larger code space or stronger factor.
No. It usually means the request entered processing. Use documented delivery reports and, most importantly, verification completion to evaluate the full user journey.
Restrict destinations, rate-limit multiple identifiers, monitor low-completion traffic, set spend and velocity alerts, require product context, and review sudden changes before expanding capacity.
Connect secure application logic, route-specific delivery, status tracking and completion metrics in one controlled flow.