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>"
}
'import requests
url = "https://app.easyotp.dev/api/v1/verify"
payload = {
"verification_id": "<string>",
"code": "<string>"
}
headers = {
"Authorization": "<authorization>",
"Content-Type": "<content-type>"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: '<authorization>', 'Content-Type': '<content-type>'},
body: JSON.stringify({verification_id: '<string>', code: '<string>'})
};
fetch('https://app.easyotp.dev/api/v1/verify', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.easyotp.dev/api/v1/verify",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'verification_id' => '<string>',
'code' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: <authorization>",
"Content-Type: <content-type>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://app.easyotp.dev/api/v1/verify"
payload := strings.NewReader("{\n \"verification_id\": \"<string>\",\n \"code\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "<authorization>")
req.Header.Add("Content-Type", "<content-type>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://app.easyotp.dev/api/v1/verify")
.header("Authorization", "<authorization>")
.header("Content-Type", "<content-type>")
.body("{\n \"verification_id\": \"<string>\",\n \"code\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.easyotp.dev/api/v1/verify")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = '<authorization>'
request["Content-Type"] = '<content-type>'
request.body = "{\n \"verification_id\": \"<string>\",\n \"code\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"verified": true,
"message": "Code verified successfully",
"request_id": "7b4d6022-7260-4568-b6b7-29c366c47bbc"
}
{
"success": true,
"verified": false,
"message": "Invalid code",
"request_id": "7b4d6022-7260-4568-b6b7-29c366c47bbc"
}
{
"success": true,
"verified": false,
"message": "Code expired",
"request_id": "7b4d6022-7260-4568-b6b7-29c366c47bbc"
}
{
"success": true,
"verified": false,
"message": "Code already used",
"request_id": "7b4d6022-7260-4568-b6b7-29c366c47bbc"
}
Endpoints
Verify Code
Verify a verification code that was previously sent
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>"
}
'import requests
url = "https://app.easyotp.dev/api/v1/verify"
payload = {
"verification_id": "<string>",
"code": "<string>"
}
headers = {
"Authorization": "<authorization>",
"Content-Type": "<content-type>"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: '<authorization>', 'Content-Type': '<content-type>'},
body: JSON.stringify({verification_id: '<string>', code: '<string>'})
};
fetch('https://app.easyotp.dev/api/v1/verify', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.easyotp.dev/api/v1/verify",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'verification_id' => '<string>',
'code' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: <authorization>",
"Content-Type: <content-type>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://app.easyotp.dev/api/v1/verify"
payload := strings.NewReader("{\n \"verification_id\": \"<string>\",\n \"code\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "<authorization>")
req.Header.Add("Content-Type", "<content-type>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://app.easyotp.dev/api/v1/verify")
.header("Authorization", "<authorization>")
.header("Content-Type", "<content-type>")
.body("{\n \"verification_id\": \"<string>\",\n \"code\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.easyotp.dev/api/v1/verify")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = '<authorization>'
request["Content-Type"] = '<content-type>'
request.body = "{\n \"verification_id\": \"<string>\",\n \"code\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"verified": true,
"message": "Code verified successfully",
"request_id": "7b4d6022-7260-4568-b6b7-29c366c47bbc"
}
{
"success": true,
"verified": false,
"message": "Invalid code",
"request_id": "7b4d6022-7260-4568-b6b7-29c366c47bbc"
}
{
"success": true,
"verified": false,
"message": "Code expired",
"request_id": "7b4d6022-7260-4568-b6b7-29c366c47bbc"
}
{
"success": true,
"verified": false,
"message": "Code already used",
"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
string
required
Bearer token with your API key:
Bearer YOUR_API_KEYstring
required
Must be
application/jsonBody Parameters
string
required
The verification ID returned from the send endpointExample:
"11f951d5-32d1-4b49-bdda-7da248e2615c"string
required
The verification code to check. Must be a numeric string between 4-10 digits.Example:
"123456"Response
boolean
Always
true for successful requests (even if the code is invalid)boolean
true if the code was correct and not expired, false otherwisestring
Human-readable result message. Possible values:
"Code verified successfully""Invalid code""Code expired""Code already used"
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"
}'
const response = await fetch('https://app.easyotp.dev/api/v1/verify', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json',
},
body: JSON.stringify({
verification_id: '11f951d5-32d1-4b49-bdda-7da248e2615c',
code: '123456'
})
});
const data = await response.json();
if (data.verified) {
console.log('Code verified successfully!');
} else {
console.log('Verification failed:', data.message);
}
import requests
response = requests.post(
'https://app.easyotp.dev/api/v1/verify',
headers={
'Authorization': 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json',
},
json={
'verification_id': '11f951d5-32d1-4b49-bdda-7da248e2615c',
'code': '123456'
}
)
data = response.json()
if data['verified']:
print('Code verified successfully!')
else:
print(f"Verification failed: {data['message']}")
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
type VerifyRequest struct {
VerificationID string `json:"verification_id"`
Code string `json:"code"`
}
type VerifyResponse struct {
Success bool `json:"success"`
Verified bool `json:"verified"`
Message string `json:"message"`
RequestID string `json:"request_id"`
}
func main() {
payload := VerifyRequest{
VerificationID: "11f951d5-32d1-4b49-bdda-7da248e2615c",
Code: "123456",
}
jsonData, _ := json.Marshal(payload)
req, _ := http.NewRequest(
"POST",
"https://app.easyotp.dev/api/v1/verify",
bytes.NewBuffer(jsonData),
)
req.Header.Set("Authorization", "Bearer YOUR_API_KEY")
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, _ := client.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var result VerifyResponse
json.Unmarshal(body, &result)
if result.Verified {
fmt.Println("Code verified successfully!")
} else {
fmt.Printf("Verification failed: %s\n", result.Message)
}
}
Response Examples
{
"success": true,
"verified": true,
"message": "Code verified successfully",
"request_id": "7b4d6022-7260-4568-b6b7-29c366c47bbc"
}
{
"success": true,
"verified": false,
"message": "Invalid code",
"request_id": "7b4d6022-7260-4568-b6b7-29c366c47bbc"
}
{
"success": true,
"verified": false,
"message": "Code expired",
"request_id": "7b4d6022-7260-4568-b6b7-29c366c47bbc"
}
{
"success": true,
"verified": false,
"message": "Code already used",
"request_id": "7b4d6022-7260-4568-b6b7-29c366c47bbc"
}
Error Responses
{
"error": "verification_id is required",
"request_id": "7b4d6022-7260-4568-b6b7-29c366c47bbc"
}
{
"error": "code is required",
"request_id": "7b4d6022-7260-4568-b6b7-29c366c47bbc"
}
{
"error": "code must be a numeric string between 4-10 digits",
"request_id": "7b4d6022-7260-4568-b6b7-29c366c47bbc"
}
{
"error": "API key is required. Provide it via Authorization: Bearer <token> or x-api-key header",
"request_id": "7b4d6022-7260-4568-b6b7-29c366c47bbc"
}
{
"error": "API key is disabled",
"request_id": "7b4d6022-7260-4568-b6b7-29c366c47bbc"
}
{
"error": "Verification not found",
"request_id": "7b4d6022-7260-4568-b6b7-29c366c47bbc"
}
{
"success": true,
"verified": false,
"message": "Too many failed attempts. Please try again in 15 minutes or request a new code.",
"request_id": "7b4d6022-7260-4568-b6b7-29c366c47bbc",
"retry_after": 900
}
{
"error": "Internal server error",
"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
429 response. Users should either:
- Wait for the lockout period to expire
- 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 theexpires_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
Always getting 'Invalid code' even with the correct code
Always getting 'Invalid code' even with the correct code
Make sure you’re using the exact
verification_id returned from the send endpoint. Verification IDs are case-sensitive UUIDs.Code expired immediately
Code expired immediately
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.Getting 404 - Verification not found
Getting 404 - Verification not found
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.