Document Verification

Complete guide to document verification workflows including verification types, status flows, and API operations

Document Verification

📘

Comprehensive Verification System

The Loan Origination System provides a robust verification framework to validate borrower information, documents, and eligibility before making lending decisions.

Document verification is a critical component of the loan origination process. The LOS verification system supports multiple verification types, automated and manual workflows, and provides comprehensive status tracking for all verification activities.


Verification Overview

The verification dashboard provides a centralized view of all verification workflows associated with an application. Each verification type follows a defined workflow with specific status transitions and completion criteria.

Verification Dashboard

The verification dashboard displays:

  • Verification Types - All available verification categories
  • Current Status - Real-time status of each verification
  • Document Requirements - Required documents for each verification
  • Results - Verification outcomes (Passed, Failed, Review)
📷

Screenshot: Verification Dashboard

The verification dashboard provides an overview of all verification workflows and their current status.

Verification Dashboard

Verification Types

The LOS supports multiple verification types, each designed to validate specific aspects of the borrower's application and eligibility.

TypeMethodDescriptionDocuments Required
Email Verification3rd PartyValidates email address ownershipNone
Application VerificationSystemAge check (18+ rule), basic eligibilityNone
ID VerificationDocument-basedIdentity document reviewDriver's License, Passport, State ID
Prefunding VerificationChecklistPre-funding checklist itemsVaries by checklist
Income VerificationDocument-basedProof of income validationPay stubs, W-2, Tax returns, Bank statements
Bank VerificationDocument/3rd PartyBank account validationBank statements, Account verification

Verification Status Flow

Each verification progresses through a series of statuses that reflect its current state in the workflow.

Data Status

StatusDescriptionIcon
NoneNot initiated
InitiatedVerification started🔄
Pending DocDocument required📄
InProgressUnder review🔄
CompletedFinished
FailedVerification failed

Verification Result

ResultDescriptionAction Required
PassedVerification successfulNone - can proceed
FailedVerification failedReview/Reject application
ReviewManual review neededBack office review required

Status Flow Diagram

%%{init: {'theme': 'base', 'themeVariables': {'background': '#ffffff', 'mainBkg': '#ffffff', 'clusterBkg': '#ffffff', 'clusterBorder': '#2563eb', 'titleColor': '#000000', 'primaryColor': '#2563eb', 'primaryTextColor': '#ffffff', 'lineColor': '#374151'}, 'flowchart': {'padding': 15, 'nodeSpacing': 25, 'rankSpacing': 30}}}%%
flowchart LR
    subgraph MainContainer[✅ Verification Status Flow]
        direction LR
        A[⬜ None] --> B[🔄 Initiated]
        B --> C[📄 Pending Doc]
        C --> D[🔄 InProgress]
        D --> E{⚖️ Result}
        E -->|✅| F[✅ Passed]
        E -->|❌| G[❌ Failed]
        E -->|📋| H[📋 Review]
        H --> D
    end
    
    style A fill:#6b7280,stroke:#4b5563,color:#fff
    style B fill:#2563eb,stroke:#1e40af,color:#fff
    style C fill:#d97706,stroke:#b45309,color:#fff
    style D fill:#2563eb,stroke:#1e40af,color:#fff
    style E fill:#6b7280,stroke:#4b5563,color:#fff
    style F fill:#059669,stroke:#047857,color:#fff
    style G fill:#dc2626,stroke:#b91c1c,color:#fff
    style H fill:#d97706,stroke:#b45309,color:#fff

Document Upload

Documents can be uploaded for verification through the API. The system supports multiple file types and allows multiple documents per verification category.

Accepted File Types

  • Images: JPG, JPEG, GIF, PNG
  • Documents: PDF

Document Categories

Documents are categorized by verification type:

  • ID_VERIFICATION - Identity documents
  • INCOME_VERIFICATION - Income proof documents
  • BANK_VERIFICATION - Bank account documents
  • PREFUNDING_VERIFICATION - Pre-funding checklist documents

Mandatory vs Optional

Some verifications require mandatory documents before they can be completed, while others accept optional supporting documents.


ID Verification Workflow

ID verification validates the borrower's identity using government-issued identification documents.

Workflow Steps

  1. Document Upload - Borrower or back office uploads ID document
  2. Status: In Progress - Verification begins automatically
  3. Review - Back office reviews document (if manual review required)
  4. Result - Verification marked as Passed, Failed, or Unverifiable
📷

Screenshot: ID Verification Modal

The ID verification modal allows back office staff to review uploaded identification documents and mark verification results.

ID Verification Modal

ID Verification Flow

%%{init: {'theme': 'base', 'themeVariables': {'background': '#ffffff', 'mainBkg': '#ffffff', 'clusterBkg': '#ffffff', 'clusterBorder': '#2563eb', 'titleColor': '#000000', 'primaryColor': '#2563eb', 'primaryTextColor': '#ffffff', 'lineColor': '#374151'}, 'flowchart': {'padding': 15, 'nodeSpacing': 25, 'rankSpacing': 30}}}%%
flowchart LR
    subgraph MainContainer[🆔 ID Verification Workflow]
        direction LR
        A[📄 Upload] --> B[🔄 InProgress]
        B --> C[👁️ Review]
        C --> D{⚖️ Result}
        D -->|✅| E[✅ Passed]
        D -->|❌| F[❌ Failed]
        D -->|❓| G[❓ Unverifiable]
    end
    
    style A fill:#2563eb,stroke:#1e40af,color:#fff
    style B fill:#2563eb,stroke:#1e40af,color:#fff
    style C fill:#d97706,stroke:#b45309,color:#fff
    style D fill:#6b7280,stroke:#4b5563,color:#fff
    style E fill:#059669,stroke:#047857,color:#fff
    style F fill:#dc2626,stroke:#b91c1c,color:#fff
    style G fill:#6b7280,stroke:#4b5563,color:#fff

Prefunding Verification

Prefunding verification uses a checklist-based approach where all items must pass before funding can proceed.

Checklist Items

  • All required documents uploaded
  • Identity verified
  • Income verified
  • Bank account verified
  • Credit check completed
  • Compliance checks passed

Requirements

  • All items must pass - No exceptions allowed
  • Configurable checklist - Items can be customized per product
  • Sequential completion - Items can be completed in any order

API Operations

Get Verification Status

Retrieve the current status of all verifications for an application.

curl -X GET 'https://loc.demo.kendra.lendfoundry.com/v1/darbaan/back-office/rest/api/applications/APP-2024-001234/verifications' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json'
const getVerifications = async (applicationId) => {
  const response = await fetch(
    `https://loc.demo.kendra.lendfoundry.com/v1/darbaan/back-office/rest/api/applications/${applicationId}/verifications`,
    {
      method: 'GET',
      headers: {
        'Authorization': 'Bearer YOUR_TOKEN',
        'Content-Type': 'application/json'
      }
    }
  );
  
  if (!response.ok) {
    throw new Error(`HTTP error! status: ${response.status}`);
  }
  
  return await response.json();
};
import requests

def get_verifications(application_id):
    url = f"https://loc.demo.kendra.lendfoundry.com/v1/darbaan/back-office/rest/api/applications/{application_id}/verifications"
    headers = {
        "Authorization": "Bearer YOUR_TOKEN",
        "Content-Type": "application/json"
    }
    
    response = requests.get(url, headers=headers)
    response.raise_for_status()
    return response.json()
public String getVerifications(String applicationId) throws Exception {
    HttpClient client = HttpClient.newHttpClient();
    HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create("https://loc.demo.kendra.lendfoundry.com/v1/darbaan/back-office/rest/api/applications/" + applicationId + "/verifications"))
        .header("Authorization", "Bearer YOUR_TOKEN")
        .header("Content-Type", "application/json")
        .GET()
        .build();
    
    HttpResponse<String> response = client.send(request,
        HttpResponse.BodyHandlers.ofString());
    return response.body();
}

Sample Response

{
  "records": [
    {
      "uid": "VER-001",
      "name": "Income Verification",
      "category": "financial",
      "status": "InProgress",
      "docRequired": true,
      "filerefs": [
        {
          "documentId": "DOC-123",
          "name": "W2_2024.pdf",
          "valid": true,
          "on": "2024-01-15T10:30:00Z"
        }
      ]
    },
    {
      "uid": "VER-002",
      "name": "ID Verification",
      "category": "identity",
      "status": "Completed",
      "verificationResult": "Passed"
    }
  ]
}

Upload Document for Verification

Upload a document for a specific verification category.

curl -X POST 'https://loc.demo.kendra.lendfoundry.com/v1/darbaan/back-office/rest/api/applications/APP-2024-001234/documents' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -F '[email protected]' \
  -F 'category=ID_VERIFICATION' \
  -F 'description=Drivers License'
const uploadDocument = async (applicationId, file, category, description) => {
  const formData = new FormData();
  formData.append('file', file);
  formData.append('category', category);
  formData.append('description', description);
  
  const response = await fetch(
    `https://loc.demo.kendra.lendfoundry.com/v1/darbaan/back-office/rest/api/applications/${applicationId}/documents`,
    {
      method: 'POST',
      headers: {
        'Authorization': 'Bearer YOUR_TOKEN'
      },
      body: formData
    }
  );
  
  if (!response.ok) {
    throw new Error(`HTTP error! status: ${response.status}`);
  }
  
  return await response.json();
};

// Usage
const fileInput = document.querySelector('input[type="file"]');
await uploadDocument('APP-2024-001234', fileInput.files[0], 'ID_VERIFICATION', 'Drivers License');
import requests

def upload_document(application_id, file_path, category, description):
    url = f"https://loc.demo.kendra.lendfoundry.com/v1/darbaan/back-office/rest/api/applications/{application_id}/documents"
    
    with open(file_path, 'rb') as f:
        files = {'file': f}
        data = {
            'category': category,
            'description': description
        }
        headers = {'Authorization': 'Bearer YOUR_TOKEN'}
        
        response = requests.post(url, files=files, data=data, headers=headers)
        response.raise_for_status()
        return response.json()

# Usage
upload_document('APP-2024-001234', 'drivers_license.pdf', 'ID_VERIFICATION', 'Drivers License')
public String uploadDocument(String applicationId, String filePath, String category, String description) throws Exception {
    HttpClient client = HttpClient.newHttpClient();
    
    // Create multipart form data
    String boundary = "----WebKitFormBoundary" + System.currentTimeMillis();
    String formData = "--" + boundary + "\r\n"
        + "Content-Disposition: form-data; name=\"file\"; filename=\"" + filePath + "\"\r\n"
        + "Content-Type: application/pdf\r\n\r\n"
        + new String(java.nio.file.Files.readAllBytes(java.nio.file.Paths.get(filePath)))
        + "\r\n--" + boundary + "\r\n"
        + "Content-Disposition: form-data; name=\"category\"\r\n\r\n"
        + category
        + "\r\n--" + boundary + "\r\n"
        + "Content-Disposition: form-data; name=\"description\"\r\n\r\n"
        + description
        + "\r\n--" + boundary + "--\r\n";
    
    HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create("https://loc.demo.kendra.lendfoundry.com/v1/darbaan/back-office/rest/api/applications/" + applicationId + "/documents"))
        .header("Authorization", "Bearer YOUR_TOKEN")
        .header("Content-Type", "multipart/form-data; boundary=" + boundary)
        .POST(HttpRequest.BodyPublishers.ofString(formData))
        .build();
    
    HttpResponse<String> response = client.send(request,
        HttpResponse.BodyHandlers.ofString());
    return response.body();
}

Complete Verification

Mark a verification as complete with a result (Passed, Failed, or Review).

curl -X POST 'https://loc.demo.kendra.lendfoundry.com/v1/darbaan/back-office/rest/api/verifications/VER-001/complete' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "result": "Passed",
    "notes": "Document verified successfully",
    "verifiedBy": "[email protected]"
  }'
const completeVerification = async (verificationId, result, notes, verifiedBy) => {
  const response = await fetch(
    `https://loc.demo.kendra.lendfoundry.com/v1/darbaan/back-office/rest/api/verifications/${verificationId}/complete`,
    {
      method: 'POST',
      headers: {
        'Authorization': 'Bearer YOUR_TOKEN',
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        result: result,
        notes: notes,
        verifiedBy: verifiedBy
      })
    }
  );
  
  if (!response.ok) {
    throw new Error(`HTTP error! status: ${response.status}`);
  }
  
  return await response.json();
};

// Usage
await completeVerification('VER-001', 'Passed', 'Document verified successfully', '[email protected]');
import requests

def complete_verification(verification_id, result, notes, verified_by):
    url = f"https://loc.demo.kendra.lendfoundry.com/v1/darbaan/back-office/rest/api/verifications/{verification_id}/complete"
    headers = {
        "Authorization": "Bearer YOUR_TOKEN",
        "Content-Type": "application/json"
    }
    data = {
        "result": result,
        "notes": notes,
        "verifiedBy": verified_by
    }
    
    response = requests.post(url, json=data, headers=headers)
    response.raise_for_status()
    return response.json()

# Usage
complete_verification('VER-001', 'Passed', 'Document verified successfully', '[email protected]')
public String completeVerification(String verificationId, String requestBody) throws Exception {
    HttpClient client = HttpClient.newHttpClient();
    HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create("https://loc.demo.kendra.lendfoundry.com/v1/darbaan/back-office/rest/api/verifications/" + verificationId + "/complete"))
        .header("Authorization", "Bearer YOUR_TOKEN")
        .header("Content-Type", "application/json")
        .POST(HttpRequest.BodyPublishers.ofString(requestBody))
        .build();
    
    HttpResponse<String> response = client.send(request,
        HttpResponse.BodyHandlers.ofString());
    return response.body();
}

Reinitiate Failed Verification

Reinitiate a failed verification when new documents are available or conditions have changed.

curl -X POST 'https://loc.demo.kendra.lendfoundry.com/v1/darbaan/back-office/rest/api/verifications/VER-001/reinitiate' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "reason": "New document uploaded"
  }'
const reinitiateVerification = async (verificationId, reason) => {
  const response = await fetch(
    `https://loc.demo.kendra.lendfoundry.com/v1/darbaan/back-office/rest/api/verifications/${verificationId}/reinitiate`,
    {
      method: 'POST',
      headers: {
        'Authorization': 'Bearer YOUR_TOKEN',
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        reason: reason
      })
    }
  );
  
  if (!response.ok) {
    throw new Error(`HTTP error! status: ${response.status}`);
  }
  
  return await response.json();
};

// Usage
await reinitiateVerification('VER-001', 'New document uploaded');
import requests

def reinitiate_verification(verification_id, reason):
    url = f"https://loc.demo.kendra.lendfoundry.com/v1/darbaan/back-office/rest/api/verifications/{verification_id}/reinitiate"
    headers = {
        "Authorization": "Bearer YOUR_TOKEN",
        "Content-Type": "application/json"
    }
    data = {
        "reason": reason
    }
    
    response = requests.post(url, json=data, headers=headers)
    response.raise_for_status()
    return response.json()

# Usage
reinitiate_verification('VER-001', 'New document uploaded')
public String reinitiateVerification(String verificationId, String reason) throws Exception {
    HttpClient client = HttpClient.newHttpClient();
    String requestBody = "{\"reason\":\"" + reason + "\"}";
    
    HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create("https://loc.demo.kendra.lendfoundry.com/v1/darbaan/back-office/rest/api/verifications/" + verificationId + "/reinitiate"))
        .header("Authorization", "Bearer YOUR_TOKEN")
        .header("Content-Type", "application/json")
        .POST(HttpRequest.BodyPublishers.ofString(requestBody))
        .build();
    
    HttpResponse<String> response = client.send(request,
        HttpResponse.BodyHandlers.ofString());
    return response.body();
}

Verification Best Practices

1. Document Quality

  • Clear images - Ensure documents are legible and complete
  • Proper format - Use supported file types (PDF, JPG, PNG)
  • Complete pages - Include all pages of multi-page documents

2. Verification Timing

  • Early initiation - Start verifications as soon as documents are available
  • Parallel processing - Initiate multiple verifications simultaneously
  • Status monitoring - Regularly check verification status via API

3. Error Handling

  • Failed verifications - Review failure reasons and reinitiate when appropriate
  • Missing documents - Request additional documents promptly
  • Manual review - Escalate to manual review when automated checks fail

Next Steps

ResourceDescription
Application LifecycleLearn about application status workflows
Loan OnboardingOnboard approved applications to LMS
API ReferenceComplete API endpoint documentation

Ready to Continue?

Return to Application Lifecycle to understand how verifications fit into the complete application workflow.