Mobile-friendly document capture and verification display
onSelectDocType(type) → Switches between passport and driving licenceonTakePhoto() → Opens camera API for document captureonUploadFile(file) → Validates and uploads document imageonConfirmIdentity() → Accepts verification results and proceeds to CM05onRetryVerification() → Allows customer to upload different documentDocument upload and verification orchestration
/api/v1/applications/{application_id}/identity/uploadPOST /api/v1/applications/app_20251222_143023_usr123/identity/upload Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... Content-Type: multipart/form-data // Form Fields: document_type: "passport" // or "driving_licence" document_side: "front" // or "back" for driving licence file: // Binary image data (JPG/PNG/PDF, max 10MB) capture_method: "camera" // or "upload"
{
"request_id": "req_cm04_20251222_143512",
"status": "success",
"verification_id": "onfido_check_abc123xyz",
"verification_result": {
"status": "complete",
"result": "clear",
"extracted_data": {
"full_name": "John Smith",
"date_of_birth": "1985-06-15",
"document_type": "UK Passport",
"document_number": "123456789",
"expiry_date": "2030-06-15",
"nationality": "British"
},
"checks_performed": {
"document_authenticity": {
"status": "clear",
"confidence": 0.99
},
"facial_similarity": {
"status": "clear",
"confidence": 0.98
},
"aml_screening": {
"status": "clear",
"pep_match": false,
"sanctions_match": false,
"watchlist_match": false
},
"director_match": {
"status": "clear",
"matched_company": "Smith's Artisan Café Ltd",
"company_number": "12345678"
}
}
},
"processing_time_ms": 12340,
"timestamp": "2025-12-22T14:35:24Z"
}
/api/v1/identity/verification/{verification_id}/status{
"verification_id": "onfido_check_abc123xyz",
"status": "in_progress", // or "complete", "failed"
"current_step": "facial_similarity",
"progress_percentage": 60,
"estimated_completion_seconds": 8
}
Customer uploads passport or driving licence photo. System validates file format, size, and image quality.
Onfido's AI extracts text data from document (name, DOB, document number, expiry date).
AI checks for document tampering, forgery, and validates security features.
Compares photo on document to selfie (if required) using facial recognition.
Screens name and DOB against global watchlists for politically exposed persons and sanctions.
Total Processing Time: 10-15 seconds (all checks run in parallel where possible)
Onfido integration and verification workflow
// Verification Orchestration Flow async function verifyIdentity(applicationId, documentFile) { // 1. Create Onfido Applicant const applicant = await onfido.createApplicant({ first_name: customer.firstName, last_name: customer.lastName, email: customer.email }); // 2. Upload Document const document = await onfido.uploadDocument({ applicant_id: applicant.id, type: 'passport', file: documentFile, side: 'front' }); // 3. Create Check (runs all verifications) const check = await onfido.createCheck({ applicant_id: applicant.id, report_names: [ 'document', // Authenticity + OCR 'facial_similarity_photo', // Face match 'watchlist_aml' // PEP/Sanctions ] }); // 4. Poll for Results (or use webhook) const result = await pollCheckStatus(check.id); // 5. Parse and Return return { status: result.status, // 'complete' result: result.result, // 'clear', 'consider', 'reject' extractedData: parseOnfidoData(result), checksPerformed: aggregateChecks(result.reports) }; }
function determineVerificationOutcome(onfidoResult) { // Extract individual check results const documentCheck = onfidoResult.reports.find(r => r.name === 'document'); const facialCheck = onfidoResult.reports.find(r => r.name === 'facial_similarity_photo'); const amlCheck = onfidoResult.reports.find(r => r.name === 'watchlist_aml'); // Auto-Reject Criteria if (documentCheck.result === 'rejected') { return { decision: 'REJECT', reason: 'Document authenticity failed', requiresManualReview: false }; } if (amlCheck.properties.matches.length > 0) { return { decision: 'REJECT', reason: 'PEP or sanctions match found', requiresManualReview: true, // Compliance review matches: amlCheck.properties.matches }; } // Consider (Manual Review) if (documentCheck.result === 'consider' || facialCheck.properties.score < 0.95) { return { decision: 'CONSIDER', reason: 'Document quality or facial match below threshold', requiresManualReview: true }; } // Auto-Approve return { decision: 'CLEAR', reason: 'All checks passed', requiresManualReview: false }; }
AI analyzes security features: holograms, UV markings, microprinting, font consistency, MRZ validation. Detects tampering, forgery, and photo substitution with 99% accuracy.
Optical character recognition extracts: full name, date of birth, document number, nationality, issue/expiry dates. Supports 2,500+ document types from 195 countries.
Biometric comparison of document photo to selfie using deep learning facial recognition. 99.8% accuracy with liveness detection to prevent spoofing attacks.
Checks if applicant is a Politically Exposed Person. Searches PEP databases from 240+ countries including heads of state, senior government officials, and their close associates.
Screens against global sanctions lists: OFAC (US), UN, EU, UK HM Treasury. Includes individuals and entities subject to financial restrictions.
Validates that applicant is listed as active director of the company in Companies House records. Matches name and date of birth with official government data.
Confirms applicant is 18+ years old based on extracted date of birth. Required for legal lending compliance in the UK.
Verifies document is current and not expired. Checks issue and expiry dates against current date. Rejects expired documents automatically.
Onfido SDK integration and webhook processing
POST /webhooks/onfidoPOST /webhooks/onfido X-SHA2-Signature: {signature} { "payload": { "resource_type": "check", "action": "check.completed", "object": { "id": "onfido_check_abc123xyz", "status": "complete", "result": "clear", "href": "/v3/checks/onfido_check_abc123xyz" } } } // Handler logic: // 1. Validate webhook signature // 2. Fetch full check details from Onfido API // 3. Update application status in database // 4. Send real-time update to customer UI (WebSocket)
Onfido KYC platform
AI-powered KYC and AML compliance platform
https://api.onfido.com/v3// 1. Create Applicant POST /v3/applicants { "first_name": "John", "last_name": "Smith", "email": "john@example.com" } // 2. Upload Document POST /v3/documents { "applicant_id": "{applicant_id}", "type": "passport", "side": "front", "file": // multipart/form-data } // 3. Create Check POST /v3/checks { "applicant_id": "{applicant_id}", "report_names": [ "document", "facial_similarity_photo", "watchlist_aml" ] } // 4. Retrieve Check Results GET /v3/checks/{check_id} // Returns: status, result, reports array with detailed findings
Identity verification records and compliance audit trail
CREATE TABLE identity_verifications ( verification_id VARCHAR(50) PRIMARY KEY, application_id VARCHAR(50) REFERENCES applications, onfido_check_id VARCHAR(50), onfido_applicant_id VARCHAR(50), -- Extracted Identity Data (Encrypted) full_name TEXT, date_of_birth DATE, document_type VARCHAR(50), document_number TEXT, expiry_date DATE, nationality VARCHAR(50), -- Verification Results status VARCHAR(20), -- 'clear', 'consider', 'reject' authenticity_score DECIMAL(3,2), facial_match_score DECIMAL(3,2), aml_status VARCHAR(20), pep_match BOOLEAN, sanctions_match BOOLEAN, director_verified BOOLEAN, -- Metadata document_image_url TEXT, -- S3 pre-signed URL processing_time_ms INTEGER, created_at TIMESTAMP, updated_at TIMESTAMP ); CREATE INDEX idx_app_id ON identity_verifications(application_id); CREATE INDEX idx_status ON identity_verifications(status);
UPDATE applications SET status = 'identity_verified', verified_name = 'John Smith', verified_dob = '1985-06-15', kyc_passed = true, kyc_completed_at = '2025-12-22 14:35:24', updated_at = '2025-12-22 14:35:24' WHERE application_id = 'app_20251222_143023_usr123';
// Log 1: Document uploaded { "log_id": "log_cm04_20251222_143512", "event_type": "identity_document_uploaded", "screen": "CM04", "user_id": "usr_olivia_thompson", "application_id": "app_20251222_143023_usr123", "timestamp": "2025-12-22T14:35:12Z", "details": { "document_type": "passport", "file_size_bytes": 2458000, "capture_method": "camera", "device": "iPhone 14 Pro" } } // Log 2: Verification completed { "log_id": "log_cm04_20251222_143524", "event_type": "identity_verification_completed", "screen": "CM04", "user_id": "usr_olivia_thompson", "timestamp": "2025-12-22T14:35:24Z", "details": { "onfido_check_id": "onfido_check_abc123xyz", "status": "clear", "checks_passed": { "document_authenticity": true, "facial_similarity": true, "aml_screening": true, "director_match": true }, "extracted_name": "John Smith", "extracted_dob": "1985-06-15", "processing_time_ms": 12340 } }
Photo is blurry, dark, or document edges cut off
Passport or driving licence past expiry date
Onfido detects tampering, forgery, or photo substitution
Face on document doesn't match selfie (<95% confidence)
Name appears on PEP or sanctions watchlist
Extracted name not found in Companies House director list
Onfido service takes >60 seconds or is unavailable
Date of birth indicates age <18 years
Same document already used for different application
Customer tried uploading document >5 times
| Field Name | Data Type | Description | Example Value |
|---|---|---|---|
| verification_id | VARCHAR(50) | Primary key, unique identifier | ver_20251222_143524_usr123 |
| application_id | VARCHAR(50) | Foreign key to applications | app_20251222_143023_usr123 |
| onfido_check_id | VARCHAR(50) | Onfido verification ID | onfido_check_abc123xyz |
| full_name | TEXT | Extracted name (encrypted) | John Smith |
| date_of_birth | DATE | Extracted DOB | 1985-06-15 |
| document_type | VARCHAR(50) | Type of ID document | UK Passport |
| document_number | TEXT | Document number (encrypted) | 123456789 |
| expiry_date | DATE | Document expiry date | 2030-06-15 |
| status | VARCHAR(20) | Verification outcome | clear / consider / reject |
| authenticity_score | DECIMAL(3,2) | Document authenticity (0-1) | 0.99 |
| facial_match_score | DECIMAL(3,2) | Biometric match score (0-1) | 0.98 |
| pep_match | BOOLEAN | PEP screening result | false |
| sanctions_match | BOOLEAN | Sanctions screening result | false |
| director_verified | BOOLEAN | Companies House director match | true |
| processing_time_ms | INTEGER | Verification duration | 12340 |
| created_at | TIMESTAMP | Record creation time | 2025-12-22 14:35:24 |