GET /v1/verify/:id
Fetch the current state of a verification. This is the endpoint you poll after /submit to get the verdict.
GET https://api.xxuxe.online/v1/verify/vf_AG07CDWRRFQV4T05ZXG2
Authentication
Bearer token. Use a secret key here, not a publishable one. The publishable key is intentionally limited to /init and /submit from whitelisted origins — fetching verdicts from the browser would let anyone enumerate other tenants' data.
Authorization: Bearer qv_sec_YOUR_SECRET_KEY
Path parameter
| Parameter | Type | Description |
|---|---|---|
:id | string | The verificationId returned by /init (e.g., vf_AG07CDWRRFQV4T05ZXG2) |
Example request
curl
curl -X GET https://api.xxuxe.online/v1/verify/vf_AG07CDWRRFQV4T05ZXG2 \
-H "Authorization: Bearer qv_sec_YOUR_SECRET_KEY"
JavaScript / Node.js
const response = await fetch(
`https://api.xxuxe.online/v1/verify/${verificationId}`,
{
headers: {
'Authorization': `Bearer ${process.env.VERIDIA_SECRET_KEY}`,
},
}
);
const data = await response.json();
console.log(data.status, data.verdict);
Python
import os
import requests
response = requests.get(
f"https://api.xxuxe.online/v1/verify/{verification_id}",
headers={"Authorization": f"Bearer {os.environ['VERIDIA_SECRET_KEY']}"},
)
response.raise_for_status()
data = response.json()
print(data["status"], data.get("verdict"))
PHP
<?php
$ch = curl_init("https://api.xxuxe.online/v1/verify/$verificationId");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Authorization: Bearer " . $_ENV['VERIDIA_SECRET_KEY'],
]);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);
echo $data['status'] . " " . ($data['verdict'] ?? '');
Response
200 OK
While processing:
{
"verificationId": "vf_AG07CDWRRFQV4T05ZXG2",
"status": "processing",
"submittedAt": "2026-05-01T18:39:05Z"
}
When complete:
{
"verificationId": "vf_AG07CDWRRFQV4T05ZXG2",
"status": "completed",
"verdict": "approved",
"confidence": 87.4,
"scores": {
"ocrConfidence": 78.0,
"faceMatch": 96.2,
"liveness": 91.5,
"docQuality": 85.0
},
"flags": [],
"submittedAt": "2026-05-01T18:39:05Z",
"completedAt": "2026-05-01T18:39:08Z"
}
When the user needs manual review:
{
"verificationId": "vf_AG07CDWRRFQV4T05ZXG2",
"status": "completed",
"verdict": "review",
"confidence": 64.5,
"scores": {
"ocrConfidence": 88.0,
"faceMatch": 71.2,
"liveness": 88.0,
"docQuality": 75.0
},
"flags": [
{ "level": "warn", "text": "low_face_match" },
{ "level": "info", "text": "heavy_glare" }
],
"submittedAt": "2026-05-01T18:39:05Z",
"completedAt": "2026-05-01T18:39:08Z"
}
Response fields
| Field | Type | Always present | Description |
|---|---|---|---|
verificationId | string | Yes | The verification ID |
status | string | Yes | queued, processing, completed, or failed |
verdict | string | Only when completed | approved, review, or rejected |
confidence | number | Only when completed | Overall weighted confidence score (0-100) |
scores | object | Only when completed | Breakdown of individual signals |
scores.ocrConfidence | number | When OCR ran | Self-reported VLM confidence in document text extraction (0-100) |
scores.faceMatch | number | Always | Biometric similarity between selfie and document photo (0-100) |
scores.liveness | number | Always | Anti-spoofing score — confirms it's a live person (0-100) |
scores.docQuality | number | Always | Document image quality score (0-100) |
flags | array | Yes | Quality / risk flags. Empty [] when clean |
flags[].level | string | Yes | info, warn, or critical |
flags[].text | string | Yes | Machine-readable flag code (e.g., heavy_glare, low_face_match) |
submittedAt | string | Yes | ISO 8601 timestamp when /submit was called |
completedAt | string | Only when completed or failed | ISO 8601 timestamp when verdict was determined |
Status state machine
A verification moves through these states:
queued -> processing -> completed
-> failed
| Status | What it means | Next |
|---|---|---|
queued | Job is waiting in the ML backend queue | Will move to processing within seconds |
processing | Pipeline is actively running | Will move to completed or failed |
completed | Verdict is ready | Terminal state — no more changes |
failed | Pipeline could not complete (rare) | Terminal state — see flags for cause |
Verdicts explained
| Verdict | What it means | What you should do |
|---|---|---|
approved | High confidence, all signals pass | Trust the user, complete onboarding |
review | Mixed signals, manual check recommended | Send to your review queue |
rejected | Low confidence or critical flag | Block, ask user to retry, or escalate |
The threshold between verdicts is configurable per tenant (defaults: <60 rejected, 60-80 review, >=80 approved).
Common flags
| Flag | Level | Meaning |
|---|---|---|
heavy_glare | warn | Document has reflective glare obscuring text |
low_face_match | warn | Selfie and document photo look different |
low_liveness | warn | Anti-spoofing score below threshold |
low_doc_quality | warn | Document image too blurry or low-resolution |
expired_document | critical | Document expiry date has passed |
mrz_mismatch | critical | MRZ checksum failed (potentially tampered passport) |
name_mismatch | warn | Submitted name doesn't fuzzy-match OCR'd name |
age_under_minimum | critical | Calculated age below tenant's minimum (default 18) |
Polling pattern
For polling, use a bounded retry loop with backoff:
async function waitForVerdict(verificationId, timeoutMs = 30000) {
const start = Date.now();
let interval = 500; // start at 500ms
while (Date.now() - start < timeoutMs) {
const response = await fetch(
`https://api.xxuxe.online/v1/verify/${verificationId}`,
{
headers: { 'Authorization': `Bearer ${process.env.VERIDIA_SECRET_KEY}` },
}
);
const data = await response.json();
if (data.status === 'completed' || data.status === 'failed') {
return data;
}
await new Promise(r => setTimeout(r, interval));
interval = Math.min(interval * 1.5, 3000); // exponential backoff, cap 3s
}
throw new Error('Verification timed out');
}
Better than polling: use webhooks. They fire as soon as the verdict is ready — no polling, no missed events.
Errors
| HTTP | Error code | When |
|---|---|---|
404 | verification_not_found | The ID doesn't exist, or belongs to another tenant |
401 | unauthorized / invalid_key | Auth issue. See Authentication |
429 | rate_limited | Hit the /verify/:id rate limit (600 req/min by default) |
500 | internal | Backend issue. Include the requestId in your support ticket |
Full catalog: Errors.
Notes
- Tenant ownership is enforced — you can only fetch verifications created with your own API keys
- The endpoint is safe to poll up to 600 times per minute by default. Need more? Contact support
- Once a verification is
completedorfailed, the response is immutable — you can cache it indefinitely submittedAtandcompletedAtare ISO 8601 in UTC
What's next
- Errors — full error code reference
- Webhooks — get verdicts pushed instead of polling
- Compliance — data retention and regulatory details