cURL
curl --location 'https://auth.otpless.app/auth/v2/validate/token' \
--header 'Content-Type: application/json' \
--header 'clientId: YOUR_CLIENT_ID' \
--header 'clientSecret: YOUR_CLIENT_SECRET' \
--data '{
"token": "1efbffd3ebd64132b7b9d0c08ac063b3"
}'import requests
url = "https://auth.otpless.app/auth/v2/validate/token"
payload = { "token": "<string>" }
headers = {
"clientId": "<api-key>",
"clientSecret": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
clientId: '<api-key>',
clientSecret: '<api-key>',
'Content-Type': 'application/json'
},
body: JSON.stringify({token: '<string>'})
};
fetch('https://auth.otpless.app/auth/v2/validate/token', 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://auth.otpless.app/auth/v2/validate/token",
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([
'token' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"clientId: <api-key>",
"clientSecret: <api-key>"
],
]);
$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://auth.otpless.app/auth/v2/validate/token"
payload := strings.NewReader("{\n \"token\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("clientId", "<api-key>")
req.Header.Add("clientSecret", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://auth.otpless.app/auth/v2/validate/token")
.header("clientId", "<api-key>")
.header("clientSecret", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"token\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://auth.otpless.app/auth/v2/validate/token")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["clientId"] = '<api-key>'
request["clientSecret"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"token\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"token": "e9e3f030c1384b55bb42f2ecf8fd1786",
"status": "SUCCESS",
"userId": "MO-ceb09c6e14cf4733b72a4317f30a3f0f",
"timestamp": "2026-05-12T07:19:42Z",
"identities": [
{
"identityType": "MOBILE",
"identityValue": "91XXXXXXXXXX",
"channel": "SILENT_AUTH",
"methods": [
"SILENT_AUTH"
],
"verified": true,
"verifiedAt": "2026-05-12T07:19:42Z"
}
],
"deviceFingerprinting": {
"status": "SUCCESS",
"sessionId": "8481663f-ceec-499d-ba04-2afd1527cdb8",
"deviceId": "781b21f7-220b-4f44-9155-6261f8564924",
"newDevice": false,
"riskAssessment": {
"sessionRiskLevel": "HIGH",
"deviceRiskLevel": "HIGH",
"sessionRiskScore": 99,
"deviceRiskScore": 95,
"ipFraudScore": 0,
"flags": {
"isAppTampered": true,
"debuggingEnabled": true,
"googlePlayStoreInstall": false
}
},
"deviceContext": {
"brand": "iQOO",
"model": "I2410",
"os": "Android",
"osVersion": "16",
"screenResolution": "1080x2238",
"totalRamBytes": 7804567552,
"simInfo": {
"totalSimsUsed": 5,
"activeSims": [
{
"id": 4,
"slotIndex": 0,
"carrierName": "Vi India"
},
{
"id": 1,
"slotIndex": 1,
"carrierName": "airtel"
}
]
}
},
"networkContext": {
"ipAddress": "xxx.xxx.xxx.xxx",
"isp": "Bharti Airtel Limited",
"location": {
"city": "Ahmedabad",
"region": "Gujarat",
"country": "India"
}
}
}
}Backend API
Auth + Device Fingerprint
Validates an OTPless auth token and returns the device fingerprint in one backend-to-backend call. Requires clientId and clientSecret.
- Read
deviceFingerprinting.status(SUCCESS,FAILED,PENDING,TIMEOUT) before using the data. PENDINGandTIMEOUToccur only in ASYNC mode.- A
FAILEDfingerprint does not mean auth failed; the top-levelstatusis stillSUCCESS.
POST
/
auth
/
v2
/
validate
/
token
cURL
curl --location 'https://auth.otpless.app/auth/v2/validate/token' \
--header 'Content-Type: application/json' \
--header 'clientId: YOUR_CLIENT_ID' \
--header 'clientSecret: YOUR_CLIENT_SECRET' \
--data '{
"token": "1efbffd3ebd64132b7b9d0c08ac063b3"
}'import requests
url = "https://auth.otpless.app/auth/v2/validate/token"
payload = { "token": "<string>" }
headers = {
"clientId": "<api-key>",
"clientSecret": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
clientId: '<api-key>',
clientSecret: '<api-key>',
'Content-Type': 'application/json'
},
body: JSON.stringify({token: '<string>'})
};
fetch('https://auth.otpless.app/auth/v2/validate/token', 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://auth.otpless.app/auth/v2/validate/token",
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([
'token' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"clientId: <api-key>",
"clientSecret: <api-key>"
],
]);
$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://auth.otpless.app/auth/v2/validate/token"
payload := strings.NewReader("{\n \"token\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("clientId", "<api-key>")
req.Header.Add("clientSecret", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://auth.otpless.app/auth/v2/validate/token")
.header("clientId", "<api-key>")
.header("clientSecret", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"token\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://auth.otpless.app/auth/v2/validate/token")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["clientId"] = '<api-key>'
request["clientSecret"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"token\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"token": "e9e3f030c1384b55bb42f2ecf8fd1786",
"status": "SUCCESS",
"userId": "MO-ceb09c6e14cf4733b72a4317f30a3f0f",
"timestamp": "2026-05-12T07:19:42Z",
"identities": [
{
"identityType": "MOBILE",
"identityValue": "91XXXXXXXXXX",
"channel": "SILENT_AUTH",
"methods": [
"SILENT_AUTH"
],
"verified": true,
"verifiedAt": "2026-05-12T07:19:42Z"
}
],
"deviceFingerprinting": {
"status": "SUCCESS",
"sessionId": "8481663f-ceec-499d-ba04-2afd1527cdb8",
"deviceId": "781b21f7-220b-4f44-9155-6261f8564924",
"newDevice": false,
"riskAssessment": {
"sessionRiskLevel": "HIGH",
"deviceRiskLevel": "HIGH",
"sessionRiskScore": 99,
"deviceRiskScore": 95,
"ipFraudScore": 0,
"flags": {
"isAppTampered": true,
"debuggingEnabled": true,
"googlePlayStoreInstall": false
}
},
"deviceContext": {
"brand": "iQOO",
"model": "I2410",
"os": "Android",
"osVersion": "16",
"screenResolution": "1080x2238",
"totalRamBytes": 7804567552,
"simInfo": {
"totalSimsUsed": 5,
"activeSims": [
{
"id": 4,
"slotIndex": 0,
"carrierName": "Vi India"
},
{
"id": 1,
"slotIndex": 1,
"carrierName": "airtel"
}
]
}
},
"networkContext": {
"ipAddress": "xxx.xxx.xxx.xxx",
"isp": "Bharti Airtel Limited",
"location": {
"city": "Ahmedabad",
"region": "Gujarat",
"country": "India"
}
}
}
}Validate an OTPless auth token and retrieve device fingerprint data in a single backend call. Call this after the OTPless SDK returns an auth token; the response includes both authentication details and device fingerprint data.
Device fingerprinting mode. The OTPless SDK supports two modes, ASYNC and SYNC, configured by your native developer at the SDK level. Both modes use this same endpoint; only the timing of when the token is available to your app differs.
Integration flows
Both modes call this same endpoint. The difference is only in when the SDK returns the auth token to your app.ASYNC mode
Authentication and device fingerprinting run concurrently. The SDK returns the auth token as soon as authentication completes, without waiting for fingerprinting to finish. The fingerprint result is attached by the OTPless backend when your backend calls validate token.| Step | Actor | Action |
|---|---|---|
| 1 | Client App | OTPless SDK initiates authentication. Device fingerprinting runs in the background concurrently. |
| 2 | OTPless SDK | Authentication completes and returns the auth token to the Client App. Fingerprinting continues in the background. |
| 3 | Client App | Receives the auth token and sends it to the Client Backend. |
| 4 | Client Backend | Calls POST /auth/v2/validate/token with the token to validate authentication and retrieve device fingerprint data. |
| 5 | OTPless Backend | Validates the token, attaches the fingerprint result (if available), and returns the full response. |
| 6 | Client Backend | Reads deviceFingerprinting.status: SUCCESS includes full data, FAILED means fingerprinting errored, PENDING means fingerprinting not yet complete, TIMEOUT means the threshold time was exceeded. |
SYNC mode
The SDK returns the auth token only after both authentication and device fingerprinting have completed. This guarantees fingerprint data is always available when your backend calls validate token.| Step | Actor | Action |
|---|---|---|
| 1 | Client App | OTPless SDK initiates authentication and device fingerprinting together. |
| 2 | OTPless SDK | Waits for both authentication and device fingerprinting to complete before returning. |
| 3 | Client App | Receives the auth token only after fingerprinting is complete. Sends the token to the Client Backend. |
| 4 | Client Backend | Calls POST /auth/v2/validate/token with the token. |
| 5 | OTPless Backend | Validates the token and returns the full response with device fingerprint data. |
| 6 | Client Backend | Reads deviceFingerprinting.status: SUCCESS includes full fingerprint data, FAILED includes message only. |
PENDING and TIMEOUT can only occur in ASYNC mode, where the SDK returns the auth token before fingerprinting completes. In SYNC mode, the SDK waits for fingerprinting to finish, so the status is always SUCCESS or FAILED.Authorizations
The clientId used for API authentication.
The clientSecret used for API authentication.
Body
application/json
Payload containing the auth token received from the OTPless SDK.
The auth token received from the OTPless SDK.
Response
Authentication completed. Check deviceFingerprinting.status for the fingerprint result.
The auth token passed in the request.
SUCCESS when authentication passed.
Example:
"SUCCESS"
Unique user identifier assigned by OTPless.
ISO 8601 timestamp of the authentication event.
Show child attributes
Show child attributes
Device fingerprint result. Present when Device Fingerprinting is enabled.
Show child attributes
Show child attributes