Complete Loan Submission and Approval Workflow

Step-by-step guide to the complete loan origination workflow from application submission through funding

Complete Loan Submission and Approval Workflow

📘

End-to-End Origination

This guide walks through the complete loan origination workflow from application submission through funding, with code examples for each stage.

This workflow covers the entire Application Submission APIs (LOS) process, culminating in the handoff to Active Loan Servicing APIs (LMS) for servicing.


Workflow Overview

%%{init: {'theme': 'base', 'themeVariables': {'background': '#ffffff', 'mainBkg': '#ffffff', 'clusterBkg': '#f9fafb', 'clusterBorder': '#2563eb', 'primaryColor': '#2563eb', 'primaryTextColor': '#ffffff', 'titleColor': '#000000', 'lineColor': '#374151'}, 'flowchart': {'padding': 25, 'nodeSpacing': 40, 'rankSpacing': 50}}}%%
flowchart TB
    subgraph WORKFLOW[📋 Complete Origination Workflow]
        A[📝 1. Submit] --> B[📄 2. Docs]
        B --> C[✅ 3. Verify]
        C --> D[📊 4. Credit]
        D --> E[🔍 5. Underwrite]
        E --> F{⚖️ 6. Decision}
        F -->|✅ Approved| G[💵 7. Fund]
        F -->|❌ Declined| H[❌ Close]
        G --> I[🏦 8. Board]
    end
    
    style A fill:#2563eb,stroke:#1e40af,color:#fff
    style B fill:#2563eb,stroke:#1e40af,color:#fff
    style C fill:#2563eb,stroke:#1e40af,color:#fff
    style D fill:#2563eb,stroke:#1e40af,color:#fff
    style E fill:#d97706,stroke:#b45309,color:#fff
    style F fill:#6b7280,stroke:#4b5563,color:#fff
    style G fill:#059669,stroke:#047857,color:#fff
    style H fill:#dc2626,stroke:#b91c1c,color:#fff
    style I fill:#059669,stroke:#047857,color:#fff

Workflow Steps:

  1. Submit — Borrower submits loan application
  2. Docs — Upload required documents
  3. Verify — Complete verifications
  4. Credit — Pull credit report
  5. Underwrite — Review by underwriting team
  6. Decision — Approve or decline
  7. Fund — Process funding (if approved)
  8. Board — Hand off to LMS for servicing

Step 1: Submit Application

Endpoint

POST /v1/darbaan/back-office/rest/api/borrower-application-intake

Request Payload

{
  "borrower_name": "John Doe",
  "ssn": "123-45-6789",
  "email": "[email protected]",
  "phone": "(555) 123-4567",
  "loan_amount": 50000,
  "loan_purpose": "Business Expansion",
  "loan_term": 60,
  "collateral_description": "Commercial property"
}

Code Examples

curl -X POST "https://loc.demo.kendra.lendfoundry.com/v1/darbaan/back-office/rest/api/borrower-application-intake" \
  -H "Authorization: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "borrower_name": "John Doe",
    "ssn": "123-45-6789",
    "email": "[email protected]",
    "phone": "(555) 123-4567",
    "loan_amount": 50000,
    "loan_purpose": "Business Expansion"
  }'
const submitApplication = async (applicationData, apiKey) => {
  const response = await fetch(
    'https://loc.demo.kendra.lendfoundry.com/v1/darbaan/back-office/rest/api/borrower-application-intake',
    {
      method: 'POST',
      headers: {
        'Authorization': apiKey,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(applicationData)
    }
  );
  
  if (!response.ok) {
    throw new Error(`HTTP error! status: ${response.status}`);
  }
  
  return await response.json();
};

// Usage
const result = await submitApplication({
  borrower_name: 'John Doe',
  ssn: '123-45-6789',
  email: '[email protected]',
  phone: '(555) 123-4567',
  loan_amount: 50000,
  loan_purpose: 'Business Expansion'
}, process.env.LOS_API_KEY);

console.log(`Application submitted: ${result.application_number}`);
import requests
import os

def submit_application(application_data, api_key):
    """Submit new loan application."""
    url = "https://loc.demo.kendra.lendfoundry.com/v1/darbaan/back-office/rest/api/borrower-application-intake"
    
    headers = {
        "Authorization": api_key,
        "Content-Type": "application/json"
    }
    
    response = requests.post(url, json=application_data, headers=headers)
    response.raise_for_status()
    
    return response.json()

# Usage
api_key = os.getenv('LOS_API_KEY')
result = submit_application({
    "borrower_name": "John Doe",
    "ssn": "123-45-6789",
    "email": "[email protected]",
    "phone": "(555) 123-4567",
    "loan_amount": 50000,
    "loan_purpose": "Business Expansion"
}, api_key)

print(f"Application submitted: {result['application_number']}")
application_uid = result['uid']
public class ApplicationSubmission {
    private final String apiKey;
    private final HttpClient client;
    
    public ApplicationSubmission(String apiKey) {
        this.apiKey = apiKey;
        this.client = HttpClient.newHttpClient();
    }
    
    public String submitApplication(String applicationJson) throws Exception {
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://loc.demo.kendra.lendfoundry.com/v1/darbaan/back-office/rest/api/borrower-application-intake"))
            .header("Authorization", apiKey)
            .header("Content-Type", "application/json")
            .POST(HttpRequest.BodyPublishers.ofString(applicationJson))
            .build();
        
        HttpResponse<String> response = client.send(request, 
            HttpResponse.BodyHandlers.ofString());
        return response.body();
    }
}

Success Response

{
  "uid": "0x1234567890abcdef",
  "application_number": "APP-2025-001234",
  "status": "submitted",
  "created_at": "2025-12-15T10:00:00Z"
}

Step 2: Upload Documents

Endpoint

POST /v1/darbaan/back-office/rest/api/upload-document

Common Document Types

Document TypeDescription
bank_statementBank statements
tax_returnTax returns
pay_stubPay stubs
business_licenseBusiness license
financial_statementFinancial statements
collateral_documentCollateral documentation

Code Examples

def upload_document(application_uid, file_path, document_type, api_key):
    """Upload document for application."""
    url = "https://loc.demo.kendra.lendfoundry.com/v1/darbaan/back-office/rest/api/upload-document"
    
    headers = {
        "Authorization": api_key
    }
    
    with open(file_path, 'rb') as f:
        files = {"file": f}
        data = {
            "application_uid": application_uid,
            "document_type": document_type,
            "description": f"Uploaded {document_type}"
        }
        
        response = requests.post(url, files=files, data=data, headers=headers)
        response.raise_for_status()
    
    return response.json()

# Usage
doc_result = upload_document(
    application_uid=application_uid,
    file_path="./bank_statement.pdf",
    document_type="bank_statement",
    api_key=api_key
)
print(f"Document uploaded: {doc_result['document_id']}")

Step 3: Check Pending Requirements

Endpoint

POST /v1/darbaan/back-office/rest/api/get-todo

Request

{
  "uid": "0x1234567890abcdef",
  "kind": "borrower-application"
}

Code Examples

def get_pending_requirements(application_uid, api_key):
    """Get pending requirements for application."""
    url = "https://loc.demo.kendra.lendfoundry.com/v1/darbaan/back-office/rest/api/get-todo"
    
    headers = {
        "Authorization": api_key,
        "Content-Type": "application/json"
    }
    
    data = {
        "uid": application_uid,
        "kind": "borrower-application"
    }
    
    response = requests.post(url, json=data, headers=headers)
    response.raise_for_status()
    
    return response.json()

# Usage
requirements = get_pending_requirements(application_uid, api_key)
print(f"Pending requirements: {len(requirements.get('pending_requirements', []))}")

Response

{
  "pending_requirements": [
    {
      "requirement_type": "document",
      "document_type": "tax_return",
      "status": "pending",
      "description": "Upload 2024 tax return"
    },
    {
      "requirement_type": "verification",
      "verification_type": "income_verification",
      "status": "pending",
      "description": "Verify income"
    }
  ]
}

Step 4: Complete Verifications

Add Stipulation

Endpoint:

POST /v1/darbaan/back-office/rest/api/add-stipulations

Request:

{
  "application_uid": "0x1234567890abcdef",
  "verification_type": "income_verification",
  "description": "Verify borrower income",
  "required": true
}

Approve/Reject Verification

Endpoint:

POST /v1/darbaan/back-office/rest/api/approve-reject-verification

Request:

{
  "verification_id": "VER123",
  "status": "approved",
  "notes": "Document verified - income confirmed"
}

Step 5: Pull Credit Report

Endpoint

POST /v1/darbaan/back-office/rest/api/get-credit-data

Request

{
  "application_uid": "0x1234567890abcdef",
  "borrower_ssn": "123-45-6789"
}

Response

{
  "credit_score": 720,
  "credit_bureau": "Experian",
  "report_date": "2025-12-15",
  "details": {
    "accounts": [],
    "inquiries": [],
    "public_records": []
  }
}

Step 6: Underwriting Review

Create Underwriting Task

Endpoint:

POST /v1/darbaan/back-office/rest/api/create-task

Request:

{
  "application_uid": "0x1234567890abcdef",
  "task_type": "underwriting_review",
  "assigned_to": "[email protected]",
  "due_date": "2025-12-20",
  "priority": "high"
}

Check Application Status

Endpoint:

POST /v1/darbaan/back-office/rest/api/get-borrower-application-by-uid
def check_application_status(application_uid, api_key):
    """Check current application status."""
    url = "https://loc.demo.kendra.lendfoundry.com/v1/darbaan/back-office/rest/api/get-borrower-application-by-uid"
    
    headers = {
        "Authorization": api_key,
        "Content-Type": "application/json"
    }
    
    response = requests.post(url, json={"uid": application_uid}, headers=headers)
    response.raise_for_status()
    
    return response.json()

# Usage
app_data = check_application_status(application_uid, api_key)
print(f"Status: {app_data['status']}")

Step 7: Approval and Funding

Wait for Approval

import time

def wait_for_approval(application_uid, api_key, timeout=3600, poll_interval=60):
    """Poll for application approval."""
    start_time = time.time()
    
    while time.time() - start_time < timeout:
        app_data = check_application_status(application_uid, api_key)
        status = app_data['status']
        
        if status == 'approved':
            print("✅ Application approved!")
            return True
        elif status == 'declined':
            print("❌ Application declined")
            return False
        
        print(f"⏳ Current status: {status}. Checking again in {poll_interval}s...")
        time.sleep(poll_interval)
    
    raise TimeoutError("Approval timeout exceeded")

# Usage
is_approved = wait_for_approval(application_uid, api_key)
💡

Tip

Use webhooks instead of polling for production implementations. See the Webhooks Guide for details.


Step 8: Board Loan to LMS

Once approved, board the loan to Active Loan Servicing APIs for servicing.

Endpoint

POST /v1/lms-application-processor/application/submit/business/onboard

Request

{
  "application_uid": "0x1234567890abcdef",
  "application_number": "APP-2025-001234",
  "loan_amount": 50000,
  "interest_rate": 5.5,
  "loan_term": 60,
  "loan_start_date": "2025-12-20",
  "funding_date": "2025-12-20"
}

Code Examples

curl -X POST "https://api.demo.lms.lendfoundry.com/v1/lms-application-processor/application/submit/business/onboard" \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "application_uid": "0x1234567890abcdef",
    "application_number": "APP-2025-001234",
    "loan_amount": 50000,
    "interest_rate": 5.5,
    "loan_term": 60,
    "loan_start_date": "2025-12-20"
  }'
const boardLoan = async (boardingData, jwtToken) => {
  const response = await fetch(
    'https://api.demo.lms.lendfoundry.com/v1/lms-application-processor/application/submit/business/onboard',
    {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${jwtToken}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(boardingData)
    }
  );
  
  return await response.json();
};

const result = await boardLoan({
  application_uid: '0x1234567890abcdef',
  application_number: 'APP-2025-001234',
  loan_amount: 50000,
  interest_rate: 5.5,
  loan_term: 60,
  loan_start_date: '2025-12-20'
}, jwtToken);

console.log(`Loan boarded: ${result.loan_number}`);
def board_loan_to_lms(boarding_data, jwt_token):
    """Board approved loan to LMS."""
    url = "https://api.demo.lms.lendfoundry.com/v1/lms-application-processor/application/submit/business/onboard"
    
    headers = {
        "Authorization": f"Bearer {jwt_token}",
        "Content-Type": "application/json"
    }
    
    response = requests.post(url, json=boarding_data, headers=headers)
    response.raise_for_status()
    
    return response.json()

# Usage
result = board_loan_to_lms({
    "application_uid": application_uid,
    "application_number": "APP-2025-001234",
    "loan_amount": 50000,
    "interest_rate": 5.5,
    "loan_term": 60,
    "loan_start_date": "2025-12-20"
}, jwt_token)

print(f"Loan boarded: {result['loan_number']}")
public String boardLoan(String boardingJson, String jwtToken) throws Exception {
    HttpClient client = HttpClient.newHttpClient();
    HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create("https://api.demo.lms.lendfoundry.com/v1/lms-application-processor/application/submit/business/onboard"))
        .header("Authorization", "Bearer " + jwtToken)
        .header("Content-Type", "application/json")
        .POST(HttpRequest.BodyPublishers.ofString(boardingJson))
        .build();
    
    HttpResponse<String> response = client.send(request, 
        HttpResponse.BodyHandlers.ofString());
    return response.body();
}

Success Response

{
  "loan_number": "LN-2025-001234",
  "status": "boarded",
  "message": "Loan successfully boarded to LMS",
  "loan_id": "12345",
  "boarded_at": "2025-12-15T10:30:00Z"
}

Complete Workflow Class

import requests
import time
import os

class OriginationWorkflow:
    """Complete loan origination workflow."""
    
    def __init__(self, los_api_key, lms_jwt_token):
        self.los_api_key = los_api_key
        self.lms_jwt_token = lms_jwt_token
        self.los_base = "https://loc.demo.kendra.lendfoundry.com/v1/darbaan/back-office/rest/api"
        self.lms_base = "https://api.demo.lms.lendfoundry.com/v1"
    
    def submit_application(self, application_data):
        """Step 1: Submit application."""
        response = requests.post(
            f"{self.los_base}/borrower-application-intake",
            json=application_data,
            headers={"Authorization": self.los_api_key, "Content-Type": "application/json"}
        )
        response.raise_for_status()
        return response.json()
    
    def upload_document(self, application_uid, file_path, doc_type):
        """Step 2: Upload document."""
        with open(file_path, 'rb') as f:
            response = requests.post(
                f"{self.los_base}/upload-document",
                files={"file": f},
                data={"application_uid": application_uid, "document_type": doc_type},
                headers={"Authorization": self.los_api_key}
            )
        response.raise_for_status()
        return response.json()
    
    def check_status(self, application_uid):
        """Check application status."""
        response = requests.post(
            f"{self.los_base}/get-borrower-application-by-uid",
            json={"uid": application_uid},
            headers={"Authorization": self.los_api_key, "Content-Type": "application/json"}
        )
        response.raise_for_status()
        return response.json()["status"]
    
    def wait_for_approval(self, application_uid, timeout=3600):
        """Step 6: Wait for approval decision."""
        start = time.time()
        while time.time() - start < timeout:
            status = self.check_status(application_uid)
            if status == "approved":
                return True
            elif status == "declined":
                return False
            time.sleep(60)
        raise TimeoutError("Approval timeout")
    
    def board_loan(self, application_uid, loan_details):
        """Step 8: Board loan to LMS."""
        response = requests.post(
            f"{self.lms_base}/lms-application-processor/application/submit/business/onboard",
            json={**loan_details, "application_uid": application_uid},
            headers={"Authorization": f"Bearer {self.lms_jwt_token}", "Content-Type": "application/json"}
        )
        response.raise_for_status()
        return response.json()

# Usage
workflow = OriginationWorkflow(
    los_api_key=os.getenv('LOS_API_KEY'),
    lms_jwt_token=os.getenv('LMS_JWT_TOKEN')
)

# Execute workflow
result = workflow.submit_application({
    "borrower_name": "John Doe",
    "email": "[email protected]",
    "loan_amount": 50000
})
application_uid = result["uid"]

if workflow.wait_for_approval(application_uid):
    loan = workflow.board_loan(application_uid, {
        "loan_amount": 50000,
        "interest_rate": 5.5,
        "loan_term": 60
    })
    print(f"Loan boarded: {loan['loan_number']}")

Error Handling

ErrorHTTP StatusResolution
Validation Error422Fix invalid fields and retry
Missing Documents422Upload required documents
Application Not Found404Verify application UID
Rate Limit429Wait and retry with backoff
Already Boarded409Use existing loan number

Best Practices

✅ DO❌ DON'T
Handle errors gracefullyFail silently
Use webhooks for status updatesPoll excessively
Upload documents promptlyDelay document collection
Verify prerequisites before boardingSkip validation
Log all workflow stepsIgnore audit trail

Support

Questions about the origination workflow?


Application Approved?

See Transition to LMS for detailed boarding instructions.