Verification playground
The whole integration in three steps, running live against the sandbox with nothing mocked. Create a session, run the real flow, then compare what the browser told your page with what the server says happened.
Create a session
You are hereA session is one verification for one person, valid once. Your backend mints it with your secret key and hands the browser only the URL that comes back.
The server-side call, in your language
curl -X POST https://api.dev.auvral.com/v1/sessions \
-H "x-api-key: $AUVRAL_API_KEY" \
-H "content-type: application/json" \
-d '{"flowId":"<your-flow-id>","referenceId":"user_8842","allowedOrigins":["https://yourapp.com"],"locale":"en"}'Run the verification
The SDK opens the hosted flow over your page in an iframe it owns. Photograph a document, take a selfie, reach a verdict, exactly as your user would.
https://sdk.dev.auvral.com/v1/verification.jsNothing running yet
Create a session in step 1 and the verification flow opens here, in a fullscreen overlay drawn by the SDK on top of this page, exactly as it would on yours.
The browser-side call, one script tag and one init
<script src="https://sdk.dev.auvral.com/v1/verification.js"></script>
<button id="verify">Verify identity</button>
<script>
document.getElementById("verify").addEventListener("click", () => {
const frame = new AuvralVerification().init({
url: "https://verify.example.com/c/<code>",
onResult: ({ status, verificationId, sessionId }) => {
// UI only. Show a spinner, enable a button, update copy.
// Do NOT unlock anything here; wait for the webhook.
console.log("result", status, verificationId, sessionId);
},
onClose: ({ reason }) => {
// "completed" | "user_dismissed" | "programmatic"
console.log("closed", reason);
},
onError: ({ code, message }) => {
// "missing_url" | "already_open"
console.error(code, message);
},
});
// frame.close() closes it from your side. It is idempotent.
});
</script>See what happened
The browser tells you what to render. The server tells you what is true. Both are here, on one timeline, so you can see which arrives first and which one you are allowed to believe.
What the browser said
no result yet
postMessage and onResult. Move your UI on this. Trust it with nothing.
What the server said
no server confirmation yet
GET /v1/sessions/{sessionId} asked with your API key, or a signed webhook. This is what may change your database.
Offsets are relative to the first line. Server rows are stamped by the server clock, every other row by yours.
Nothing yet. Create a session and run the flow.
This button posts a perfectly shaped VERIFICATION_RESULT with status: "verified" from this page. No verification, no iframe, no Auvral. Watch it land on the timeline: any user can do the same from their devtools, and only a server-side read can settle it.
The webhook, which is the part that may change your database
POST /your/webhook/endpoint
user-agent: Auvral-Webhooks/2
content-type: application/json
x-auvral-event-id: 3f1c9a54-8e2b-4d77-9f10-6a2c5b8e1d43
x-auvral-event-type: VERIFICATION_SUCCEEDED
x-auvral-delivery-id: b7e21a09-4c6d-42f1-83aa-19d0c7f5e2b8
x-auvral-attempt: 1
x-auvral-timestamp: 1787712835
x-auvral-signature: v1=9c5b1e0d7a3f48c26b91ad5e7f30c8241b6de9a07c45f2318ed6b0a94c7f52d1
{
"id": "3f1c9a54-8e2b-4d77-9f10-6a2c5b8e1d43",
"type": "verification.succeeded",
"createdAt": "2026-08-27T21:33:55.284Z",
"data": {
"verification": {
"id": "0198f2c1-5a7b-7d3e-9f42-c8b06a1e94d7",
"status": "VERIFIED",
"flowId": "01a02fdf-4e4b-7b4b-956b-7d6de62fbe38",
"flowName": "Verificación QR",
"sessionId": "0f8d7260-d083-4ed4-b38b-c3bd3f5db31f",
"referenceId": "user_8842",
"metadata": { "orderId": "order-1234" },
"startedAt": "2026-08-27T21:33:12.006Z",
"endedAt": "2026-08-27T21:33:55.101Z",
"steps": [
{ "name": "document_verification", "status": "VERIFIED" },
{ "name": "biometric_verification", "status": "VERIFIED" }
]
}
}
}A sample of what Auvral POSTs to the endpoint you register per flow in the dashboard. The playground does not receive these. Note x-auvral-event-id: it is stable across all six delivery attempts, so it is your deduplication key.