Skip to main content

OTP

Multi-channel one-time password (OTP) verification with support for email, SMS, and WhatsApp.

Send OTP

Send OTP codes across multiple channels simultaneously:

const result = await sendmator.otp.send({
channels: ['email', 'sms'],
recipients: {
email: 'user@example.com',
sms: '+14155552671'
}
});

console.log('Session ID:', result.data.session_id);
console.log('Expires at:', result.data.expires_at);
console.log('Channels:', result.data.channels);

Sandbox Mode

Enable sandbox mode to receive OTPs in the API response (for testing):

const result = await sendmator.otp.send({
channels: ['email'],
recipients: {
email: 'test@example.com'
},
sandbox_mode: true
});

// In sandbox mode, OTPs are returned in response
console.log('Email OTP:', result.data.otps?.email);
warning

Never use sandbox_mode in production. OTPs should only be sent to users, never returned in API responses.

Multi-Channel OTP

Send OTP through multiple channels for redundancy:

const result = await sendmator.otp.send({
channels: ['email', 'sms', 'whatsapp'],
recipients: {
email: 'user@example.com',
sms: '+14155552671',
whatsapp: '+14155552671'
},
add_contact: true // Automatically add to contacts after verification
});

Verify OTP

Verify the OTP codes provided by the user:

const result = await sendmator.otp.verify({
session_token: 'session-token-from-send-response',
otps: {
email: '123456',
sms: '123456'
}
});

if (result.data.verified) {
console.log('✅ OTP verified successfully');
console.log('Email verified:', result.data.verification_results.email?.success);
console.log('SMS verified:', result.data.verification_results.sms?.success);
} else {
console.log('❌ Verification failed');
}

Verify Single Channel

Verify OTP from a single channel:

const result = await sendmator.otp.verify({
session_token: sessionToken,
otps: {
email: '123456'
}
});

if (result.data.verified) {
// OTP is valid
console.log('Email verified!');
}

Resend OTP

Resend OTP using the same session:

const result = await sendmator.otp.resend({
session_token: 'session-token-from-send-response'
});

console.log('OTP resent to:', result.data.channels);
console.log('New expiry:', result.data.expires_at);

Complete Options

await sendmator.otp.send({
// Channels to send OTP (required)
channels: ['email', 'sms', 'whatsapp'],

// Recipients for each channel (required)
recipients: {
email: 'user@example.com',
sms: '+14155552671',
whatsapp: '+14155552671'
},

// Optional settings
add_contact: true, // Add verified contacts to contacts table
sandbox_mode: false, // Return OTPs in response (testing only)
metadata: { // Custom tracking data
purpose: 'login',
ip_address: '192.168.1.1'
}
});

Examples

Login Verification

async function loginWithOTP(email: string) {
// Step 1: Send OTP
const sendResult = await sendmator.otp.send({
channels: ['email'],
recipients: { email },
metadata: {
action: 'login',
timestamp: new Date().toISOString()
}
});

const sessionToken = sendResult.data.session_id;

// Store session token in session/database
return sessionToken;
}

async function verifyLoginOTP(sessionToken: string, code: string) {
// Step 2: Verify OTP
const verifyResult = await sendmator.otp.verify({
session_token: sessionToken,
otps: { email: code }
});

if (verifyResult.data.verified) {
// Grant access
return { success: true };
} else {
return { success: false, message: 'Invalid OTP' };
}
}

Phone Verification

async function verifyPhoneNumber(phone: string): Promise<string> {
const result = await sendmator.otp.send({
channels: ['sms'],
recipients: { sms: phone },
add_contact: true, // Add to contacts after verification
metadata: {
action: 'phone_verification'
}
});

return result.data.session_id;
}

async function confirmPhone(sessionToken: string, code: string): Promise<boolean> {
const result = await sendmator.otp.verify({
session_token: sessionToken,
otps: { sms: code }
});

return result.data.verified;
}

Multi-Factor Authentication (MFA)

async function sendMFACode(userId: string, userEmail: string, userPhone: string) {
// Send OTP through both email and SMS for added security
const result = await sendmator.otp.send({
channels: ['email', 'sms'],
recipients: {
email: userEmail,
sms: userPhone
},
metadata: {
user_id: userId,
action: 'mfa',
required_channels: 2 // Require both channels
}
});

return result.data.session_id;
}

async function verifyMFA(sessionToken: string, emailCode: string, smsCode: string) {
const result = await sendmator.otp.verify({
session_token: sessionToken,
otps: {
email: emailCode,
sms: smsCode
}
});

// Check if both channels verified
const emailVerified = result.data.verification_results.email?.success;
const smsVerified = result.data.verification_results.sms?.success;

return emailVerified && smsVerified;
}

Password Reset

async function requestPasswordReset(email: string) {
const result = await sendmator.otp.send({
channels: ['email'],
recipients: { email },
metadata: {
action: 'password_reset'
}
});

// Store session token with user record
return {
sessionToken: result.data.session_id,
expiresAt: result.data.expires_at
};
}

async function resetPasswordWithOTP(
sessionToken: string,
code: string,
newPassword: string
) {
// Verify OTP
const verifyResult = await sendmator.otp.verify({
session_token: sessionToken,
otps: { email: code }
});

if (verifyResult.data.verified) {
// OTP is valid, update password
// updateUserPassword(newPassword);
return { success: true };
}

return { success: false, message: 'Invalid or expired code' };
}

Resend with Cooldown

async function resendOTPWithCooldown(sessionToken: string, lastSentAt: Date) {
const cooldownSeconds = 60; // 1 minute cooldown
const now = new Date();
const secondsSinceLastSend = (now.getTime() - lastSentAt.getTime()) / 1000;

if (secondsSinceLastSend < cooldownSeconds) {
const remainingSeconds = Math.ceil(cooldownSeconds - secondsSinceLastSend);
throw new Error(`Please wait ${remainingSeconds} seconds before resending`);
}

const result = await sendmator.otp.resend({
session_token: sessionToken
});

return {
success: true,
expiresAt: result.data.expires_at
};
}

Response Types

Send OTP Response

{
success: true,
data: {
session_id: "uuid-session-token",
expires_at: "2024-01-15T10:35:00.000Z",
channels: ["email", "sms"],
recipients: {
email: "user@example.com",
sms: "+14155552671"
},
otps: { // Only in sandbox_mode
email: "123456",
sms: "123456"
}
}
}

Verify OTP Response

{
success: true,
data: {
verified: true,
verification_results: {
email: {
success: true,
message: "OTP verified successfully"
},
sms: {
success: true,
message: "OTP verified successfully"
}
}
}
}

Best Practices

  1. Use Multiple Channels - Offer email and SMS for better delivery rates
  2. Set Expiration - OTPs should expire quickly (5-10 minutes)
  3. Rate Limiting - Implement cooldown between resend requests
  4. Attempt Limits - Limit verification attempts (3-5 attempts)
  5. Secure Storage - Never log or store OTP codes
  6. Clear Sessions - Invalidate session tokens after successful verification
  7. Sandbox Testing - Use sandbox_mode only in development/testing
  8. Add Contacts - Use add_contact flag for verified users

Security Considerations

  • ✅ OTPs are automatically generated (secure random)
  • ✅ OTPs expire after a short time
  • ✅ Session tokens are unique per request
  • ✅ Verification invalidates the OTP
  • ✅ Rate limiting prevents abuse
  • ❌ Never return OTPs in production responses
  • ❌ Never log OTP codes
  • ❌ Never send OTPs via insecure channels

Channels

ChannelUse CaseProsCons
emailEmail verification, password resetHigh delivery rate, freeSlower delivery
smsPhone verification, MFAFast deliveryCosts money
whatsappInternational users, MFAGlobal reach, reliableRequires template approval

Next Steps