Skip to main content

Resend OTP

Resend a new OTP for an existing session when the user didn't receive the original code or it expired.

Endpointโ€‹

POST /api/v1/otp/resend

Headersโ€‹

X-API-Key: sk_live_your_api_key
Content-Type: application/json

Why Use This Endpoint?โ€‹

  • ๐Ÿ”„ User Convenience - Allow users to request a new OTP without restarting the flow
  • โšก Quick Recovery - Resend to all original channels with one request
  • ๐ŸŽฏ Same Session - Maintains the same session token for seamless UX
  • ๐Ÿงช Sandbox Support - Returns new OTP in sandbox mode for testing
  • ๐Ÿ“Š Attempt Reset - Resets verification attempts for the session

Core Conceptโ€‹

If a user doesn't receive the OTP or it expires, they can request a resend. The API generates a new OTP for the same session and sends it to all original channels. The session token remains valid.

Request Structureโ€‹

Required Fieldsโ€‹

FieldTypeDescription
session_tokenstringSession token from the original send OTP request

Use Case Examplesโ€‹

Example 1: User Didn't Receive OTPโ€‹

User didn't receive the original OTP and requests a resend.

curl -X POST https://api.sendmator.com/api/v1/otp/resend \
-H "X-API-Key: sk_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"session_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}'

Success Response:

{
"success": true,
"expires_at": "2024-01-15T11:00:00.000Z",
"message": "OTP resent successfully"
}

Example 2: Resend in Sandbox Modeโ€‹

Testing resend functionality in sandbox mode.

curl -X POST https://api.sendmator.com/api/v1/otp/resend \
-H "X-API-Key: sk_test_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"session_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}'

Sandbox Response:

{
"success": true,
"expires_at": "2024-01-15T11:00:00.000Z",
"message": "OTP resent successfully",
"sandbox_otps": {
"email": "789012",
"sms": "789012"
}
}

Example 3: Session Already Verifiedโ€‹

User tries to resend after already verifying.

curl -X POST https://api.sendmator.com/api/v1/otp/resend \
-H "X-API-Key: sk_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"session_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}'

Error Response:

{
"statusCode": 400,
"message": "Session already verified",
"error": "Bad Request"
}

Example 4: Session Locked (Too Many Attempts)โ€‹

User exhausted verification attempts and session is locked.

curl -X POST https://api.sendmator.com/api/v1/otp/resend \
-H "X-API-Key: sk_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"session_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}'

Error Response:

{
"statusCode": 400,
"message": "Session is locked due to too many failed attempts",
"error": "Bad Request"
}

Response Fieldsโ€‹

Success Responseโ€‹

FieldTypeDescription
successbooleanAlways true for successful resend
expires_atstringISO 8601 timestamp when session expires
messagestringHuman-readable success message
sandbox_otpsobjectNew OTP codes (only in sandbox mode)

Error Responseโ€‹

{
"statusCode": 400,
"message": "Error description",
"error": "Bad Request"
}

Common Error Responsesโ€‹

Status CodeError MessageCauseSolution
400Session already verifiedSession was successfully verifiedNo resend needed
400Session is lockedToo many failed verification attemptsStart new session with send OTP
400Session not foundInvalid or expired tokenStart new session with send OTP
401Invalid API keyAPI key missing or incorrectCheck API key
402Insufficient balanceNot enough wallet creditsAdd credits to wallet
429Rate limit exceededToo many resend requestsWait before retrying

How Resend Worksโ€‹

  1. Validates Session - Checks if session exists and is still active
  2. Generates New OTP - Creates a new OTP code for all channels
  3. Sends to All Channels - Resends to the same channels as original request
  4. Resets Attempts - Verification attempt counter is reset
  5. Keeps Same Token - Session token remains unchanged
  6. Updates Expiry - Session expiry time is extended

Best Practicesโ€‹

1. User Experienceโ€‹

  • Show Resend Button - Display after initial OTP is sent
  • Rate Limit UI - Disable button for 30-60 seconds after resend
  • Clear Feedback - Show success message when OTP is resent
  • Expiry Timer - Show countdown and auto-enable resend when expired
  • Alternative Channels - Suggest trying different channel if available

2. Rate Limitingโ€‹

Implement client-side rate limiting:

const [canResend, setCanResend] = useState(false);
const [countdown, setCountdown] = useState(60);

const handleResend = async () => {
try {
const response = await fetch('/api/v1/otp/resend', {
method: 'POST',
headers: {
'X-API-Key': 'sk_live_your_api_key',
'Content-Type': 'application/json'
},
body: JSON.stringify({
session_token: sessionToken
})
});

const data = await response.json();

if (response.ok) {
showSuccess('OTP resent successfully');

// Disable resend button for 60 seconds
setCanResend(false);
setCountdown(60);

const timer = setInterval(() => {
setCountdown(prev => {
if (prev <= 1) {
setCanResend(true);
clearInterval(timer);
return 0;
}
return prev - 1;
});
}, 1000);
}
} catch (error) {
showError('Failed to resend OTP');
}
};

3. Error Handlingโ€‹

Handle different error scenarios gracefully:

const handleResend = async () => {
try {
const response = await resendOtp(sessionToken);

if (response.success) {
showMessage('New OTP sent successfully');
}
} catch (error) {
if (error.message.includes('already verified')) {
// Redirect to success page
window.location.href = '/dashboard';
} else if (error.message.includes('locked')) {
// Session locked, need new OTP session
showError('Too many attempts. Please start over.');
redirectToResendFlow();
} else if (error.message.includes('not found')) {
// Session expired, restart flow
showError('Session expired. Starting new verification.');
restartOtpFlow();
} else {
showError('Failed to resend. Please try again.');
}
}
};

4. Integration Patternsโ€‹

Pattern 1: Simple Resendโ€‹

// Minimal resend implementation
const resendOtp = async (token) => {
const response = await fetch('/api/v1/otp/resend', {
method: 'POST',
headers: {
'X-API-Key': 'sk_live_your_api_key',
'Content-Type': 'application/json'
},
body: JSON.stringify({ session_token: token })
});

return response.json();
};

Pattern 2: Resend with Countdownโ€‹

// Resend with countdown timer
const ResendButton = ({ sessionToken }) => {
const [countdown, setCountdown] = useState(60);
const [canResend, setCanResend] = useState(false);

useEffect(() => {
if (countdown > 0) {
const timer = setTimeout(() => setCountdown(countdown - 1), 1000);
return () => clearTimeout(timer);
} else {
setCanResend(true);
}
}, [countdown]);

const handleResend = async () => {
await resendOtp(sessionToken);
setCountdown(60);
setCanResend(false);
};

return (
<button onClick={handleResend} disabled={!canResend}>
{canResend ? 'Resend OTP' : `Resend in ${countdown}s`}
</button>
);
};

When to Use Resendโ€‹

Good Use Casesโ€‹

  • User didn't receive the OTP
  • OTP was delayed or lost in delivery
  • User accidentally deleted the message
  • User wants to try a different device
  • Session is about to expire

When NOT to Useโ€‹

  • Session already verified (redirect to success page)
  • Session is locked (start new session with send OTP)
  • User wants to change phone/email (start new session with send OTP)

Rate Limitsโ€‹

Resend endpoint limits:

  • 3 resends per session - Prevents abuse
  • 5 resends per 10 minutes per user
  • 20 resends per hour per IP address

Next Stepsโ€‹