Handling results
Once a verification runs, you have three ways to receive the verdict. Each fits a different architecture.
Quick comparison
| Method | When to use | Latency | Reliability |
|---|---|---|---|
| Widget events | Single-page apps, immediate UX feedback | <100ms | Good |
| Polling | Server-side flows, no public webhook URL | 2-5s typical | Good |
| Webhooks | Production B2B, async processing | 1-3s typical | Best |
For most production systems, webhooks are the recommended approach. Polling is fine for prototypes.
Method 1 — Widget events
The widget emits a veridia:complete event in the browser as soon as the API accepts the verification. Easiest path for SPAs.
What you get
{
"verificationId": "vf_AG07CDWRRFQV4T05ZXG2",
"userRef": "customer-12345",
"status": "queued" // or "processing" or "completed"
}
The status is the initial state. The verdict isn't here yet — you still need to fetch it.
Pattern
<veridia-widget id="kyc" publishable-key="qv_pub_..."></veridia-widget>
<script>
document.getElementById('kyc').addEventListener('veridia:complete', async (e) => {
const { verificationId } = e.detail;
// Send to your own backend, which calls GET /v1/verify/:id with the SECRET key
const response = await fetch('/api/kyc-completed', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ verificationId }),
});
const result = await response.json();
console.log('Verdict:', result.verdict); // approved / review / rejected
});
</script>
:::warning Never call /v1/verify/:id from the browser
The browser-facing publishable key (qv_pub_*) is intentionally limited. To fetch verdicts, use a server-side request with your secret key (qv_sec_*). Otherwise anyone could poll any verification.
:::
Method 2 — Polling GET /v1/verify/:id
If your backend received the verificationId (from the event or via a form submission), poll the status endpoint until it's completed.
curl
curl -X GET https://api.xxuxe.online/v1/verify/vf_AG07CDWRRFQV4T05ZXG2 \
-H "Authorization: Bearer qv_sec_YOUR_SECRET_KEY"
Response when ready:
{
"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"
}
JavaScript / Node.js
async function pollVerification(verificationId, maxAttempts = 30) {
for (let i = 0; i < maxAttempts; i++) {
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, 1000)); // wait 1s
}
throw new Error('Verification did not complete in time');
}
const result = await pollVerification('vf_AG07CDWRRFQV4T05ZXG2');
console.log('Verdict:', result.verdict);
Python
import os
import time
import requests
def poll_verification(verification_id, max_attempts=30):
headers = {"Authorization": f"Bearer {os.environ['VERIDIA_SECRET_KEY']}"}
for _ in range(max_attempts):
r = requests.get(
f"https://api.xxuxe.online/v1/verify/{verification_id}",
headers=headers,
timeout=10,
)
r.raise_for_status()
data = r.json()
if data["status"] in ("completed", "failed"):
return data
time.sleep(1)
raise TimeoutError("Verification did not complete in time")
result = poll_verification("vf_AG07CDWRRFQV4T05ZXG2")
print("Verdict:", result["verdict"])
PHP
<?php
function pollVerification(string $verificationId, int $maxAttempts = 30): array {
$secretKey = $_ENV['VERIDIA_SECRET_KEY'];
for ($i = 0; $i < $maxAttempts; $i++) {
$ch = curl_init("https://api.xxuxe.online/v1/verify/$verificationId");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Authorization: Bearer $secretKey",
]);
$response = curl_exec($ch);
curl_close($ch);
$data = json_decode($response, true);
if (in_array($data['status'], ['completed', 'failed'], true)) {
return $data;
}
sleep(1);
}
throw new RuntimeException("Verification did not complete in time");
}
$result = pollVerification("vf_AG07CDWRRFQV4T05ZXG2");
echo "Verdict: " . $result['verdict'] . "\n";
Method 3 — Webhooks (recommended for production)
Webhooks are push-based: when a verification completes, Veridia sends a signed HTTP POST to your endpoint. No polling, no missed events, full audit trail.
Setup
- In your dashboard, go to Webhooks
- Set the webhook URL to your endpoint (e.g.,
https://yourapp.com/webhooks/veridia) - Copy the webhook secret — you'll need it to verify signatures
- Done. Every completed verification triggers a POST.
What you receive
{
"event": "verification.review_required",
"verificationId": "vf_AG07CDWRRFQV4T05ZXG2",
"tenantId": "tn_default_demo",
"userRef": "customer-12345",
"verdict": "review",
"confidence": 77.85,
"scores": {
"faceMatch": 99.5,
"liveness": 88.0,
"ocrConfidence": 20.0,
"docQuality": 75.7
},
"flags": [
{ "level": "warn", "text": "heavy_glare" }
],
"completedAt": "2026-05-01T18:39:08Z"
}
The event field will be one of:
verification.approvedverification.rejectedverification.review_required
The HTTP request includes a signature header you must verify (see signature verification for the exact algorithm).
Why prefer webhooks
| Concern | Polling | Webhooks |
|---|---|---|
| Latency | 1s polling = 1s avg delay | <100ms after completion |
| API calls | Many | Just the one delivery |
| Reliability | Your code must run | Veridia retries 5 times with exponential backoff |
| Audit trail | None | Full delivery log in dashboard |
| Cold starts | Wakes serverless every poll | Fires once |
Acting on the verdict
Whatever method you use, the action is the same:
function onVerdict(result) {
switch (result.verdict) {
case 'approved':
// Mark user as KYC'd, enable full access
enableUserAccount(result.userRef);
break;
case 'review':
// Send to manual review queue, pause sensitive actions
queueForReview(result.verificationId, result.flags);
break;
case 'rejected':
// Block, ask user to retry or contact support
blockUserKyc(result.userRef, result.flags);
break;
default:
// Should never happen, log it
console.error('Unknown verdict:', result.verdict);
}
}
What's next
Quickstart complete. From here, depending on what you're building:
- Widget docs — every attribute, event, and styling option
- API Reference — full REST API for server-side integrations
- Webhooks — signature verification, retry behavior, examples
- Compliance — data retention, SEPRELAD, LGPD, GDPR
Need help? Contact support.