Tutorial: Creating Your First Verification Session

This tutorial walks you through the core Verifa integration — creating a verification session, guiding a user through identity capture, and getting the result back. By the end, you’ll understand the full session lifecycle and have a working end-to-end flow in sandbox.

What you’ll learn

  • Creating a verification session via the API
  • Redirecting a user to the capture flow
  • How sandbox mode works (choosing verification outcomes)
  • Retrieving the full verification result
  • Connecting session results to your webhook handler

Prerequisites

  • A Verifa account (sign up)
  • A sandbox API key (starts with vk_sandbox_). Find it under Developers > API > API Keys in the dashboard.

API Keys page showing a sandbox key under Developers > API > API Keys

The session lifecycle

Before we start, here’s the path a session takes:

pending → capturing → sandbox_pending → completed
StatusWhat’s happening
pendingSession created, waiting for the user to open the capture URL
capturingUser is actively going through the capture flow
sandbox_pendingCapture complete, waiting for you to choose a sandbox outcome
completedVerification finished — result available

In live mode, sandbox_pending is replaced by processing (async verification), but the rest is the same.

Step 1: Create a session

Call the API to create a new verification session. The only required field is your API key — everything else is optional.

$curl -X POST https://api.withverifa.com/api/v1/sessions \
> -H "X-API-Key: vk_sandbox_your_key_here" \
> -H "Content-Type: application/json" \
> -d '{
> "external_ref": "tutorial_user_1"
> }'

The response includes the capture_url — the link you’ll send your user to:

1{
2 "id": "session_abc123",
3 "status": "pending",
4 "external_ref": "tutorial_user_1",
5 "capture_url": "https://app.withverifa.com/verify/tok_...",
6 "qr_code_data_url": "data:image/png;base64,...",
7 "is_sandbox": true,
8 "created_at": "2026-04-01T12:00:00Z",
9 "expires_at": "2026-04-01T12:20:00Z"
10}
FieldDescription
idSession ID — use this to retrieve results later
capture_urlURL to redirect the user to for identity capture
qr_code_data_urlQR code image (base64 PNG) that encodes the capture URL — useful for mobile
is_sandboxtrue because we used a sandbox API key
expires_atWhen the capture token expires (user must start before this)

The external_ref field is optional but strongly recommended. It’s your internal user ID — you’ll use it to correlate webhook events and results back to users in your system.

Optional: Create a session from the dashboard

You can also create sessions directly from the dashboard. Navigate to Verifications in the sidebar and click + New Verification in the top right corner.

Verifications page with arrows pointing to the sidebar and the + New Verification button

The New Verification modal lets you fill in:

  • External Reference — your internal user/case ID (you can click Generate to auto-fill one)
  • Applicant Name — optional name for the verification
  • Applicant Email — optional email (used if you enable email invites)
  • Email invite to applicant — toggle to send the verification link directly to the applicant’s email
  • Workflow — select which workflow to use (defaults to your org’s default workflow)

Click Create Verification when you’re ready.

New Verification modal showing External Reference, Applicant Name, Email, and Workflow fields

After creation, a Verification Created modal appears with the capture URL and a QR code. Click Copy to grab the capture URL, or scan the QR code on a mobile device. You can also click View Session to go to the session detail page, or Create Another to start a new one.

Verification Created modal showing the capture URL, QR code, and session ID

What you can configure

The create session endpoint accepts several optional fields:

FieldTypeDescription
external_refstringYour internal user ID for correlation
metadataobjectArbitrary key-value data stored with the session
countrystringISO 3166-1 alpha-2 country code (e.g., "US")
workflow_idstringBind the session to a specific workflow
redirect_urlstringWhere to redirect the user after capture completes
send_emailbooleanSend a verification invite email (requires applicant_email in metadata)
applicant_namestringName for the invite email

For this tutorial, we only need external_ref. You can explore the other options once you have the basic flow working.

Step 2: Walk through the capture flow

In a real integration, you’d redirect the user to the capture_url or display it in an iframe/popup via verifa.js. For this tutorial, just open the capture URL in your browser.

The capture flow walks the user through several steps. Here’s what each one looks like:

The first screen explains what data Verifa will collect and asks for consent. The user checks the consent box and clicks Continue.

Capture consent screen — "Identity Verification", consent checkbox, and Continue button

2b. Document type

The user selects what type of government-issued ID they’ll provide:

  • Driver’s License — front and back required
  • Passport — photo page only
  • State ID / National ID — front only

Document type selection — Driver's License, Passport, or State ID / National ID

2c. Document country and state

Next, the user selects where their ID was issued. For US documents, they also select the issuing state.

Document details — Country and State dropdowns with Continue button

2d. Scan the front of the ID

The user is prompted to scan the front of their document. Tips are shown for best results: use good lighting, avoid glare, and place the ID on a dark flat surface. Click I’m ready to open the camera.

Scan front of ID prompt with tips and I'm ready button

The camera opens with a document frame. The user can switch between Auto (captures automatically when the document is detected) and Manual mode. There’s also an upload button in the bottom-right corner to upload an existing photo instead.

Camera viewfinder for scanning the front of the ID

2e. Review the front

After capture, the user reviews the photo. They can Submit photo to continue or Retake photo to try again. They can also change the ID type if they picked the wrong one.

Review front of ID — Submit photo and Retake photo buttons

In sandbox mode, you can upload any image — Verifa won’t run real OCR. The “Photo quality is low” warning is normal for test images. Just click Submit photo to continue.

2f. Scan the back of the ID

For document types that require a back scan (like driver’s licenses), the user is prompted to flip their ID over. The tips remind them to keep the barcode fully visible.

Scan back of ID prompt — flip your ID over, make sure barcode is visible

After scanning, the same review screen appears for the back:

Review back of ID — Submit photo and Retake photo buttons

2g. Selfie capture

If the workflow includes facial recognition (most do), the user takes a live selfie. This is compared against the photo on the ID document to verify the person presenting the ID is the same person pictured on it.

2h. User information (if configured)

If the workflow’s capture requirements include user information fields (name, email, phone, etc.), the user fills those in before submitting. See the Creating a Workflow tutorial for how to configure which fields are collected.

Step 3: Choose a sandbox outcome

After completing the capture flow in sandbox mode, the session moves to sandbox_pending status. The capture UI presents a Choose Verification Outcome screen with preset options:

Sandbox outcome selector — Approved, Rejected (Face Mismatch, Underage, Expired ID, Unreadable), and Needs Review

PresetResultWhat it simulates
ApprovedapprovedEverything passes — face match, age check, readable document
Rejected — Face MismatchrejectedSelfie doesn’t match the ID photo
Rejected — UnderagerejectedPerson is under the minimum age requirement
Rejected — Expired IDrejectedThe ID document appears to be expired
Rejected — UnreadablerejectedDocument text is not legible enough to extract data
Needs Reviewneeds_reviewBorderline face match — queued for manual review

Select Approved for this tutorial. The session completes immediately and Verifa dispatches a session.approved webhook (if you have an endpoint configured).

After selecting an outcome, you’ll see a confirmation screen:

Verification complete — "We're reviewing your details", sandbox verification completed

The user can now close this page. In a real integration, they’d be redirected to your redirect_url if you set one.

All sandbox presets return mock PII data:

1{
2 "first_name": "Jane",
3 "last_name": "Doe",
4 "date_of_birth": "1997-06-15",
5 "address_line1": "123 Test Street",
6 "city": "San Francisco",
7 "state": "CA",
8 "zip_code": "94102",
9 "document_number": "D1234567"
10}

Step 4: Check the session status

After choosing an outcome, poll the session to confirm it completed:

$curl https://api.withverifa.com/api/v1/sessions/session_abc123 \
> -H "X-API-Key: vk_sandbox_your_key_here"
1{
2 "id": "session_abc123",
3 "status": "completed",
4 "external_ref": "tutorial_user_1",
5 "is_sandbox": true,
6 "metadata": {},
7 "country": null,
8 "document_type": "drivers_license",
9 "retention_status": "active",
10 "tags": [],
11 "created_at": "2026-04-01T12:00:00Z",
12 "updated_at": "2026-04-01T12:02:00Z"
13}

The status is now completed. Note that the session response itself doesn’t include the verification results — for that, you need the result endpoint.

Step 5: Retrieve the verification result

Fetch the full result with extracted data, check outcomes, and scores:

$curl https://api.withverifa.com/api/v1/sessions/session_abc123/result \
> -H "X-API-Key: vk_sandbox_your_key_here"
1{
2 "session_id": "session_abc123",
3 "status": "approved",
4 "is_sandbox": true,
5 "face_match_passed": true,
6 "face_match_score": 0.95,
7 "age_check_passed": true,
8 "extracted_age": 28,
9 "extracted_data": {
10 "first_name": "Jane",
11 "last_name": "Doe",
12 "date_of_birth": "1997-06-15",
13 "address_line1": "123 Test Street",
14 "city": "San Francisco",
15 "state": "CA",
16 "zip_code": "94102",
17 "document_number": "D1234567",
18 "document_type": "drivers_license",
19 "document_issuing_state": "CA",
20 "document_issuing_country": "US"
21 },
22 "check_results": {
23 "face_match": {
24 "status": "passed",
25 "score": 0.95
26 },
27 "age_check": {
28 "status": "passed",
29 "age": 28
30 }
31 },
32 "rejection_reason": null,
33 "processing_mode": "simulated",
34 "country_used": "US",
35 "created_at": "2026-04-01T12:02:00Z"
36}

Understanding the result

FieldDescription
status"approved", "rejected", or "needs_review"
face_match_passedWhether the selfie matched the document photo
face_match_scoreConfidence score (0.0 to 1.0)
age_check_passedWhether the applicant meets the minimum age
extracted_ageAge calculated from the document’s date of birth
extracted_dataPII extracted from the document (decrypted)
check_resultsPer-check breakdown with status and details
rejection_reasonHuman-readable reason (only present for rejections)
processing_mode"simulated" (sandbox), "automatic" (live), or "manual" (reviewed)

In live mode, which PII fields are returned via the API depends on your organization’s api_pii_fields allowlist. SSN, for example, is only included if explicitly enabled.

Step 6: View the session in the dashboard

You can also view completed sessions in the dashboard. Navigate to Verifications in the sidebar to see all your sessions with their status, result, risk level, and other details.

Verifications list showing the completed session with approved result

Click on a session to open the detail page. Here you can see:

  • Session Details — session ID, status, environment, country, IP address, timestamps, and retention status
  • Result — the overall result (approved/rejected) and face match score
  • Checks — individual check outcomes (Document Scan, Facial Recognition, Age Verification) organized by category
  • Documents — uploaded ID images
  • Risk — risk assessment details
  • Workflow — which workflow version processed the session
  • Webhooks — delivery history for this session’s events

Session detail page showing checks — Document Scan, Facial Recognition, and Age Verification all passed

Step 7: Connect it to webhooks

Polling the API works, but webhooks are the recommended way to get notified when a session completes. If you followed the Receiving Webhooks tutorial, your endpoint will receive a session.approved event like this:

1{
2 "event": "session.approved",
3 "session_id": "session_abc123",
4 "external_ref": "tutorial_user_1",
5 "status": "approved",
6 "is_sandbox": true,
7 "identity_id": "identity_def789",
8 "result": {
9 "face_match_passed": true,
10 "age_check_passed": true,
11 "extracted_data": {
12 "first_name": "Jane",
13 "last_name": "Doe",
14 "date_of_birth": "1997-06-15"
15 }
16 }
17}

See the Handling Webhook Events tutorial for how to build handlers for each event type.

Putting it all together

Here’s the complete integration in a single backend endpoint:

1import os
2import requests
3from fastapi import FastAPI, Request
4from fastapi.responses import RedirectResponse
5
6app = FastAPI()
7API_KEY = os.environ["VERIFA_API_KEY"]
8API_BASE = "https://api.withverifa.com/api/v1"
9
10@app.post("/verify")
11async def start_verification(request: Request):
12 """Create a session and redirect to the capture flow."""
13 body = await request.json()
14
15 session = requests.post(
16 f"{API_BASE}/sessions",
17 headers={"X-API-Key": API_KEY},
18 json={
19 "external_ref": body["user_id"],
20 "redirect_url": "https://your-app.com/verification-complete",
21 },
22 ).json()
23
24 # Store session.id linked to your user
25 # await db.users.update(body["user_id"], verifa_session_id=session["id"])
26
27 return {"capture_url": session["capture_url"], "session_id": session["id"]}
28
29@app.get("/verification-complete")
30async def verification_complete():
31 """User lands here after capture. Show a waiting page."""
32 return {"message": "Verification submitted. We'll notify you when it's complete."}
33
34@app.post("/webhooks/verifa")
35async def handle_webhook(request: Request):
36 """Receive verification results via webhook."""
37 # (Add signature verification — see the webhook tutorial)
38 event = await request.json()
39
40 if event["event"] == "session.approved":
41 # Activate the user
42 user_id = event["external_ref"]
43 # await db.users.update(user_id, verified=True)
44 print(f"User {user_id} verified!")
45
46 return {"status": "ok"}

Try different sandbox presets

Now that you have the flow working, try the other sandbox presets to see how your integration handles rejections and edge cases:

Try thisWhat happens
Select Rejected — Face Mismatchsession.declined webhook fires with rejection_reason
Select Needs Reviewsession.requires-review webhook fires, session shows in Cases
Let the session expire (wait for expires_at)session.expired webhook fires

This lets you test all the code paths in your webhook handler before going live.

Next steps