NamoID public betaBuilding customer authentication? Get setup help and share feedback with other builders.Join the Slack community
NamoID
All posts
NamoID Blog

Email OTP for Indian Startups: Cost, Delivery, and Abuse

Email OTP is cheap to send and familiar to users. It is also easy to abuse, dependent on inbox delivery, and not phishing-resistant. Treating it as “generate six digits and send an email” leaves the expensive parts—reputation, throttling, replay, enumeration, and recovery—until production.

For an early India-facing product, email OTP can be a practical low-friction sign-in method. Use it with a strict risk boundary: protect ordinary accounts, offer stronger methods for sensitive actions, and build the sending controls before sharing the link publicly.

Email cost is the smallest line item

As checked on 15 July 2026, Amazon SES lists outbound email at US$0.10 per 1,000 messages, excluding data transfer and optional features. Its free tier includes up to 3,000 eligible message charges per month for the first 12 months after SES use begins, subject to AWS's current eligibility terms.

At that published base price:

Monthly OTP emailsBase SES sending charge
1,000$0.10
10,000$1.00
100,000$10.00
1,000,000$100.00

These are provider charges, not total authentication cost. Add engineering, monitoring, support, bounce handling, incident response, and any paid deliverability features. Recheck the provider page before budgeting; cloud prices and free-tier terms change.

The dangerous cost is unbounded demand. A public endpoint that sends an email for every request lets a script consume quota, damage sender reputation, and send unwanted messages to people who never visited your product.

Delivery depends on domain reputation

An OTP that arrives after the login flow expires is a failed authentication, even if the email provider reports it as delivered.

Configure the sending domain before testing conversion:

  • sign messages with DKIM;
  • publish SPF for every legitimate sender;
  • publish DMARC and monitor aggregate reports;
  • align the visible From domain with SPF or DKIM authentication;
  • use TLS and valid sending infrastructure;
  • process bounces and complaints;
  • keep transactional authentication mail separate from marketing campaigns;
  • avoid URL shorteners, unnecessary tracking, and attachment-heavy templates.

Google's current sender guidelines require SPF or DKIM for all senders to personal Gmail accounts. Senders above 5,000 messages per day to Gmail accounts face additional SPF, DKIM, DMARC, alignment, and other requirements. Google says to keep reported spam rates below 0.3%; that is a ceiling, not a target.

Use a recognizable sender name and subject. Put the code and expiry near the top. State what to do when the recipient did not request it. A transactional OTP email should not be disguised as a newsletter.

Email OTP has a clear security ceiling

Email OTP proves access to an inbox at that moment. It does not prove a legal identity, protect a compromised mailbox, or bind the code cryptographically to your real domain.

NIST SP 800-63B says email must not be used for out-of-band authentication under its assurance framework, citing password-only mailbox access, interception, and rerouting risk. It also states that manually entered authentication outputs are not phishing-resistant because an impostor site can relay them.

That guidance matters even when your startup is not required to implement a US federal assurance level. Do not market email OTP as strong MFA or use it alone for high-impact operations such as changing payout details, exporting sensitive records, or administering privileged access.

Offer passkeys or another phishing-resistant method when the account risk grows. Require recent or stepped-up authentication for sensitive changes.

OTP state must be single-use and short-lived

A safe OTP record needs more than a code:

challenge_id
environment_id
normalized_identifier_hash
code_hash
created_at
expires_at
attempt_count
consumed_at
superseded_at

Hash the code rather than storing it in plaintext. Scope it to the environment, identifier, and transaction. Mark it consumed atomically so two concurrent submissions cannot both win.

When a user requests a replacement, invalidate the previous active code. Otherwise the verification window contains several valid secrets and a user cannot tell which email to trust. Issuing a new code must not reset failed-attempt counters; NIST makes the same point for short authentication secrets.

NamoID currently uses six-digit codes with a five-minute lifetime. Successful verification consumes the challenge, and resend creates a replacement rather than extending the old secret.

Rate limits need several dimensions

One per-IP limit is not enough. Attackers rotate IPs, and offices or mobile carriers can place many legitimate users behind one address.

Apply overlapping limits to:

DimensionWhat it limits
IdentifierHarassment of one inbox
IP addressSimple automation from one source
IP + identifierRepeated targeting by one source
EnvironmentDistributed spend and reputation damage
Verification challengeOnline guessing
Project daily send capWorst-case provider exposure

Use a resend cooldown in the UI. Disable the button visibly, retain the identifier, and explain when another message can be requested. Do not reveal internal counters, observed traffic, or exact defense thresholds in browser errors.

NamoID's current baseline permits three initial OTP sends per minute and ten per hour for a rate-limit identity. Resends are tighter: one per minute and five per hour. Verification allows five attempts per minute and thirty per hour. Platform email sending also has a default project cap of 1,000 accepted sends per UTC day. These are current product defaults, not universal recommendations; tune policy to traffic and risk.

Responses must resist account enumeration

If the sign-in endpoint says “user not found” for one address and “code sent” for another, an attacker can build a customer list.

Return equivalent public responses for existing and unknown identifiers where the flow permits it. Keep status codes, body shape, and broad timing behavior close. Internally, log a reason code without putting the email address or OTP into logs.

Be careful with the copy. “If the address can continue, check its inbox” is less revealing than confirming account existence. The browser still needs a useful recovery path: edit the address, resend after cooldown, use another enabled method, or contact support.

Stop unwanted-email abuse before sending

Rate limits control volume; they do not prove the requester owns the address. That distinction is crucial for waitlists and other public acquisition flows.

For ordinary email OTP sign-in, the received code itself proves inbox access before a session is created. For a waitlist, do not create an applicant record or send a confirmation merely because someone typed an address. Challenge suspicious automation, send a verification code, and create the waitlist request only after successful verification.

Add these controls to exposed flows:

  1. Bot challenge based on risk, not as the only defense.
  2. Per-identifier, per-IP, and environment send limits.
  3. Generic browser errors without internal rate-limit details.
  4. One active code per transaction.
  5. No user or waitlist record until the required ownership check succeeds.
  6. A daily circuit breaker that fails closed when accounting is unavailable.
  7. Audit events for blocks and cap breaches without secrets or raw identifiers.

This is the difference between verification and harassment infrastructure. Read the broader authentication rate-limiting guide for layered buckets and failure behavior.

Measure the complete delivery funnel

Provider acceptance is not user success. Track a privacy-minimised funnel by environment:

send requested
  -> blocked by abuse control
  -> accepted by provider
  -> bounced or complained
  -> code submitted
  -> verification succeeded
  -> session created

Useful operational metrics include send-to-success latency, resend rate, bounce rate, complaint rate, expiry rate, failed attempts per challenge, rate-limit blocks, and project-cap utilization. Do not put full email addresses, OTP values, session tokens, or provider credentials in metric labels.

A spike in resends can mean delayed delivery, confusing UI, or abuse. A high provider-acceptance rate with low verification success can mean inbox placement trouble. Measure the user outcome, not just the API call.

Use a production checklist

Before enabling email OTP publicly, verify:

  • SPF, DKIM, and DMARC pass for the visible sender domain;
  • production is out of the provider sandbox and quotas are understood;
  • bounce and complaint events reach operations;
  • codes expire, are single-use, and older codes are superseded;
  • send, resend, verify, environment, and daily caps are enforced;
  • errors are generic in the browser and actionable for a real user;
  • logs and analytics redact identifiers and secrets;
  • unknown-account behavior does not enumerate users;
  • passkey or step-up policy exists for higher-risk operations;
  • alerting detects send failures, abuse blocks, and cap exhaustion.

Email OTP is a convenience method, not a complete security strategy. Its direct provider cost can be tiny. The production work is controlling who can trigger mail, preserving sender reputation, making codes truly one-time, and giving users a stronger path when the account matters.

Related posts