Skip to main content

Person Verification

Verify an individual end-to-end with document capture, biometric liveness, face matching, database checks, and screening. This guide covers initiating a journey, redirecting the user, and interpreting structured results.

What Person Verification checks

A Person Verification journey orchestrates up to six check types:
  1. Identity Document Capture. Passport, national ID, or driving licence scanned with OCR.
  2. Biometric Liveness. Selfie with liveness detection to confirm the user is physically present.
  3. Face Match. Compares the selfie against the document photo.
  4. Document Authenticity. Tamper detection and fraud analysis on the captured document.
  5. Database Verification. Name, address, and date of birth validated against official databases.
  6. PEP/Sanctions Screening (WorldCheck). Screening on the individual across PEP, sanctions, adverse media, and watchlist databases.
Checks 1 through 4 require user interaction and use the async journey flow. Checks 5 and 6 can run server-side without user interaction. See Server-side Person Verification below.

Data tiers

Tier 1: Minimum

The bare minimum to initiate a Person Verification journey.
{
  "first_name": "Jane",
  "last_name": "Smith",
  "country": "GB"
}

Tier 2: Standard

Adds date of birth, contact details, and address. This is the recommended minimum for production.
{
  "first_name": "Jane",
  "last_name": "Smith",
  "country": "GB",
  "date_of_birth": "1990-06-15",
  "email": "jane.smith@example.com",
  "phone": "+447700900123",
  "address": "42 Baker Street",
  "city": "London",
  "postal_code": "NW1 6XE"
}

Tier 3: Full

Adds nationality, document type preference, and external reference for idempotency.
{
  "first_name": "Jane",
  "last_name": "Smith",
  "country": "GB",
  "date_of_birth": "1990-06-15",
  "email": "jane.smith@example.com",
  "phone": "+447700900123",
  "address": "42 Baker Street",
  "city": "London",
  "postal_code": "NW1 6XE",
  "nationality": "GB",
  "document_type": "passport",
  "external_reference": "YOUR-REF-2026-0123"
}

Flow overview

Your App                    Zenoo                     User
   |                          |                         |
   |---- 1. POST /kyc/init -->|                         |
   |<--- Tokens (pull+start) -|                         |
   |                          |                         |
   |---- 2. Redirect user -------------------------------->|
   |                          |<-- 3. Doc + selfie ------|
   |                          |--- 4. Process checks --->|
   |                          |                         |
   |<--- 5. Webhook ----------|                         |
   |---- 6. GET /results ---->|                         |
   |<--- Full results --------|                         |
1

Initiate the journey

Person Verification uses async Model 2 because the user must interact with the verification UI. Post to the /kyc/init endpoint.
curl -X POST \
  "https://instance.prod.onboardapp.io/api/gateway/execute/{project_hash}/kyc/init" \
  -H "Content-Type: application/json" \
  -H "X-API-KEY: your-api-key" \
  -d '{
    "first_name": "Jane",
    "last_name": "Smith",
    "date_of_birth": "1990-06-15",
    "email": "jane.smith@example.com",
    "phone": "+447700900123",
    "country": "GB",
    "address": "42 Baker Street",
    "city": "London",
    "postal_code": "NW1 6XE",
    "external_reference": "YOUR-REF-2026-0123"
  }'
Response:
{
  "tokens": {
    "pull": "eyJhbGciOiJIUzI1NiJ9.pull-token...",
    "start": "eyJhbGciOiJIUzI1NiJ9.start-token..."
  }
}
Store both tokens immediately:
  • pull. Retrieves verification results.
  • start. Constructs the verification URL for the user.
2

Construct the verification URL and redirect the user

Build the URL from the start token and your project hash:
https://instance.prod.onboardapp.io/{project_hash}/?t={start_token}

Web integration

<a
  href="https://instance.prod.onboardapp.io/{project_hash}/?t={start_token}"
  target="_blank"
>
  Verify Your Identity
</a>
Or redirect server-side:
server.js
res.redirect(
  302,
  `https://instance.prod.onboardapp.io/${projectHash}/?t=${startToken}`
);

Mobile integration

Open the URL in a WebView or the system browser. The verification UI is responsive and works on all screen sizes.
let url = URL(string: "https://instance.prod.onboardapp.io/\(projectHash)/?t=\(startToken)")!
UIApplication.shared.open(url)

URL expiry

Verification URLs expire after 24 hours. If the user does not complete the flow within this window, you receive a journey.expired webhook and must initiate a new journey.
3

User completes verification

The user goes through an interactive flow:
  1. Document selection. Choose a document type: passport, national ID, or driving licence.
  2. Document capture. Photograph the front (and back if applicable) of the document.
  3. Selfie capture. Take a selfie for biometric matching.
  4. Liveness check. Follow on-screen prompts to prove physical presence.
  5. Submission. Data is sent to Zenoo for processing.
Server-side processing typically completes 10 to 30 seconds after the user submits.
4

Receive results

Configure a webhook endpoint to receive verification.completed events.
{
  "event_type": "verification.completed",
  "timestamp": "2026-01-15T14:35:00Z",
  "journey_id": "jrn-abc123",
  "callback_reference": "YOUR-REF-2026-0123",
  "status": "completed",
  "data": { "...": "..." }
}
See Webhooks Guide for setup.

Option B: Poll for results

curl -X GET \
  "https://instance.prod.onboardapp.io/api/gateway/sharable-payload/{pull_token}"
StatusMeaning
200 with JSON bodyResults are ready
204 No ContentStill processing, retry after a delay
404Invalid or expired token

Option C: Both

Use webhooks for real-time notification and polling as a fallback for missed webhooks. This is the most resilient pattern.
5

Interpret the results

A complete Person Verification response includes identity verification, document details, biometric results, screening, phone verification, email verification, address verification, and compliance metadata.
{
  "case_reference": "AML-2026-0123",
  "external_reference": "YOUR-REF-2026-0123",
  "processing_status": "complete",
  "completed_at": "2026-01-15T14:35:30Z",

  "identity": {
    "verified": true,
    "decision": "Pass",
    "score": 95,
    "authentication_id": "auth-xyz789"
  },

  "document": {
    "verified": true,
    "document_type": "Passport",
    "document_country": "GB",
    "document_number": "123456789",
    "validation_result": "PASSED",
    "tamper_result": "Pass",
    "extracted_data": {
      "full_name": "JANE ELIZABETH SMITH",
      "date_of_birth": "1990-06-15",
      "expiration_date": "2030-12-01",
      "gender": "F",
      "nationality": "GBR",
      "mrz_line_1": "P<GBRSMITH<<JANE<ELIZABETH<<<<<<<<<<<<<<<<<<",
      "mrz_line_2": "1234567890GBR9006150F3012013<<<<<<<<<<<<<<00"
    }
  },

  "biometric": {
    "liveness_verified": true,
    "liveness_assessment": "Live",
    "liveness_score": 98.5,
    "face_match": true,
    "face_match_score": 96.2
  },

  "screening": {
    "pep_status": "No Hit",
    "sanctions_status": "No Hit",
    "adverse_media_status": "No Hit",
    "watchlist_status": "No Hit",
    "screening_provider": "WorldCheck",
    "screening_completed_at": "2026-01-15T14:35:10Z",
    "total_matches": 0
  },

  "phone_verification": {
    "verified": true,
    "phone_number": "+447700900123",
    "carrier": "Vodafone UK",
    "line_type": "mobile",
    "verified_at": "2026-01-15T14:35:12Z"
  },

  "email_verification": {
    "verified": true,
    "email": "jane.smith@example.com",
    "domain_valid": true,
    "disposable": false,
    "verified_at": "2026-01-15T14:35:12Z"
  },

  "address_verification": {
    "verified": true,
    "match_level": "Full",
    "normalized_address": "42 Baker Street, London, NW1 6XE, GB",
    "verified_at": "2026-01-15T14:35:14Z"
  },

  "compliance_metadata": {
    "cdd_completed": true,
    "cdd_completed_at": "2026-01-15T14:35:30Z",
    "edd_required": false,
    "reviewed_by": "system",
    "risk_tier": "Low"
  }
}

Decision matrix

IdentityDocumentBiometricScreeningAction
PassPassPassNo HitAuto-approve
PassPassPassHitManual screening review required
PassAttentionPassNo HitManual document review
PassPassFailNo HitRequest new biometric capture
FailAnyAnyAnyReject or request re-submission
AnyFailAnyAnyReject document, request new submission
AnyAnyAnySanctions HitImmediate halt. Escalate to compliance.
Only auto-approve the Pass / Pass / Pass / No Hit combination. Any other outcome should trigger manual review or re-submission.
A document ATTENTION result means the document requires human review, not necessarily rejection. Check document.validation_result before making a pass/fail decision.

Check dimension statuses

Identity statuses:
StatusMeaning
PassName, address, and DOB confirmed against database records
FailCould not verify identity against databases
Document statuses:
StatusMeaning
PassDocument is authentic, not expired, and data matches
AttentionPossible issue detected (blurry image, minor inconsistency). Manual review needed.
FailDocument rejected (expired, tampered, unrecognized)
Biometric statuses:
StatusMeaning
PassLiveness confirmed and face matches document photo
FailLiveness check failed or face does not match
Screening statuses:
StatusMeaning
No HitNo matches in PEP, sanctions, adverse media, or watchlist databases
HitOne or more potential matches found. Review required.

Handling edge cases

journey.abandoned

The user closed the browser or navigated away before completing verification.
{
  "event_type": "journey.abandoned",
  "journey_id": "jrn-abc123",
  "callback_reference": "YOUR-REF-2026-0123"
}
The original verification URL remains valid until it expires. Send the user a reminder with the same link. If the URL has expired, initiate a new journey.

journey.expired

The 24-hour URL expired before the user completed the flow.
{
  "event_type": "journey.expired",
  "journey_id": "jrn-abc123",
  "callback_reference": "YOUR-REF-2026-0123"
}
Initiate a new journey and send the user a fresh link.

Document issues

If the document fails authenticity checks, document.validation_result will be ATTENTION or FAILED. Common causes:
  • Blurry or glare-obscured image
  • Expired document
  • Tamper indicators detected
  • Document type not supported for the selected country
Check document.validation_result before making a pass/fail decision. An ATTENTION result means the document requires human review, not necessarily rejection.

Server-side Person Verification (no user interaction)

For database lookups, phone verification, and screening that do not require document capture, use the sync endpoint directly.
curl -X POST \
  "https://instance.prod.onboardapp.io/api/gateway/execute/{project_hash}/kyc/api" \
  -H "Content-Type: application/json" \
  -H "X-API-KEY: your-api-key" \
  -H "X-SYNC-TIMEOUT: 30000" \
  -d '{
    "first_name": "Jane",
    "last_name": "Smith",
    "date_of_birth": "1990-06-15",
    "phone": "+447700900123",
    "email": "jane.smith@example.com",
    "country": "GB",
    "address": "42 Baker Street",
    "city": "London",
    "postal_code": "NW1 6XE"
  }'
This runs database verification, phone checks, email checks, address checks, and screening synchronously. Results are returned in the response body. No document capture or biometric checks are performed. Use server-side Person Verification for:
  • Batch processing existing customer data
  • Pre-screening before initiating a full journey
  • Ongoing monitoring re-checks on known individuals
  • Flows where you already have verified identity documents from another source

Next steps