Skip to main content
POST
/
api
/
v1
/
verify
Verify Code
curl --request POST \
  --url https://app.easyotp.dev/api/v1/verify \
  --header 'Authorization: <authorization>' \
  --header 'Content-Type: <content-type>' \
  --data '{
  "verification_id": "<string>",
  "code": "<string>"
}'
{
  "success": true,
  "verified": true,
  "message": "Code verified successfully",
  "request_id": "7b4d6022-7260-4568-b6b7-29c366c47bbc"
}

Request

Verify a code that was sent using the send endpoint. Codes can only be verified once and must not be expired.

Headers

Authorization
string
required
Bearer token with your API key: Bearer YOUR_API_KEY
Content-Type
string
required
Must be application/json

Body Parameters

verification_id
string
required
The verification ID returned from the send endpointExample: "11f951d5-32d1-4b49-bdda-7da248e2615c"
code
string
required
The verification code to check. Must be a numeric string between 4-10 digits.Example: "123456"

Response

success
boolean
Always true for successful requests (even if the code is invalid)
verified
boolean
true if the code was correct and not expired, false otherwise
message
string
Human-readable result message. Possible values:
  • "Code verified successfully"
  • "Invalid code"
  • "Code expired"
  • "Code already used"
request_id
string
Unique request identifier for debuggingExample: "7b4d6022-7260-4568-b6b7-29c366c47bbc"

Examples

curl -X POST https://app.easyotp.dev/api/v1/verify \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "verification_id": "11f951d5-32d1-4b49-bdda-7da248e2615c",
    "code": "123456"
  }'

Response Examples

{
  "success": true,
  "verified": true,
  "message": "Code verified successfully",
  "request_id": "7b4d6022-7260-4568-b6b7-29c366c47bbc"
}

Error Responses

{
  "error": "verification_id is required",
  "request_id": "7b4d6022-7260-4568-b6b7-29c366c47bbc"
}

Rate Limiting

To prevent brute-force attacks, this endpoint limits failed verification attempts:
  • 5 failed attempts per verification code
  • 15-minute lockout after exceeding the limit
  • Automatic reset on successful verification
When locked out, you’ll receive a 429 response. Users should either:
  1. Wait for the lockout period to expire
  2. Request a new verification code
In addition to verification attempt limits, API keys are also rate limited to 120 requests per minute across all endpoints. See the Rate Limits section for more details.
Implement client-side retry limits (3-5 attempts) to provide better UX before hitting the server limit.

Understanding Verification States

A verification code can be in one of several states:
1

Valid

Code has been sent, not yet used, and not expired. Can be verified successfully.
2

Used

Code has been verified successfully once. Cannot be used again (prevents replay attacks).
3

Expired

Code has exceeded its expiration time. Cannot be verified.
4

Invalid

The provided code doesn’t match the sent code.

Best Practices

Limit verification attempts: Implement client-side limits on how many times a user can attempt to verify a code (e.g., 3-5 attempts) to prevent brute force attacks.
Store verification IDs securely: Keep verification IDs server-side, associated with user sessions. Never expose them in URLs or client-side JavaScript.
Check the verified field: Always check the verified field in the response, not just the HTTP status code. A 200 response with verified: false means verification failed.
No credit consumed: Verification attempts do not consume credits, only sending codes does.

Security Considerations

Single Use Codes

Once a code is verified successfully, it cannot be used again. This prevents replay attacks where an attacker might intercept a code and try to use it themselves.

Automatic Expiration

All codes automatically expire based on the expires_in parameter from the send request. This limits the window of opportunity for attackers.

Rate Limiting

Both send and verify endpoints are rate-limited to prevent abuse. If you need higher limits, contact our support team.

Integration Example

Here’s a complete example of integrating verification into a user registration flow:
const express = require('express');
const app = express();

const sessions = new Map();

app.post('/auth/request-code', async (req, res) => {
  const { phoneNumber } = req.body;
  
  const response = await fetch('https://app.easyotp.dev/api/v1/send', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.EASYOTP_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      channel: 'sms',
      recipient: phoneNumber,
      message: 'Your MyApp verification code is: {code}',
      expires_in: 300
    })
  });
  
  const data = await response.json();
  
  const sessionId = generateSessionId();
  sessions.set(sessionId, {
    phoneNumber,
    verificationId: data.verification_id,
    verified: false
  });
  
  res.json({ sessionId });
});

app.post('/auth/verify-code', async (req, res) => {
  const { sessionId, code } = req.body;
  
  const session = sessions.get(sessionId);
  if (!session) {
    return res.status(400).json({ error: 'Invalid session' });
  }
  
  const response = await fetch('https://app.easyotp.dev/api/v1/verify', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.EASYOTP_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      verification_id: session.verificationId,
      code: code
    })
  });
  
  const data = await response.json();
  
  if (data.verified) {
    session.verified = true;
    res.json({ success: true });
  } else {
    res.json({ success: false, message: data.message });
  }
});

Troubleshooting

Make sure you’re using the exact verification_id returned from the send endpoint. Verification IDs are case-sensitive UUIDs.
Check that your server time is synchronized. If the server time is incorrect, codes may appear expired immediately. Also verify the expires_in parameter when sending.
This usually means the verification_id doesn’t exist or was created with a different API key. Each API key has its own isolated verification space.