OTP Guide
Use the built-in Verify API to launch OTP, login, and fraud-check flows without building your own code-store layer first.
1. Create a Verify service
A Verify service defines the code length, expiry, retry budget, and SMS template once so every app flow stays consistent.
import MailSetu from 'mailsetu'
const client = new MailSetu(process.env.MAILSETU_API_KEY!)
const service = await client.verify.services.create({
name: 'Login OTP',
codeLength: 6,
ttlSeconds: 300,
maxAttempts: 5,
template: 'Your {{serviceName}} code is {{code}}. It expires in {{ttlMinutes}} minutes.',
})
console.log(service.id)2. Start a verification
Starting a verification creates a code, stores only a hash at rest, sends the SMS, and returns a verification ID that your app can track.
const verification = await client.verify.start({
serviceId: 'vs_123',
to: '+919876543210',
idempotencyKey: 'otp:user_123:login:2026-05-09T14:30',
metadata: {
flow: 'login',
userId: 'user_123',
},
})
console.log(verification.id)3. Check the code
Your frontend should collect the code from the user and send it back to your backend. Your backend then calls Verify check.
const result = await client.verify.check('verify_123', '482913')
if (result.approved) {
// create session / mark phone verified / continue checkout
}Operational safeguards
For production OTP systems: • Keep traffic transactional and use live sender configuration for real users • Pass an idempotency key from your app so retries do not create duplicate sends • Use sandbox keys in staging so QA can test flows without hitting carriers • Track verification status from the dashboard when debugging onboarding or checkout friction
