Step-by-step guide to the complete loan origination workflow from application submission through funding
Complete Loan Submission and Approval Workflow
End-to-End OriginationThis 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:
- Submit — Borrower submits loan application
- Docs — Upload required documents
- Verify — Complete verifications
- Credit — Pull credit report
- Underwrite — Review by underwriting team
- Decision — Approve or decline
- Fund — Process funding (if approved)
- 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 Type | Description |
|---|---|
bank_statement | Bank statements |
tax_return | Tax returns |
pay_stub | Pay stubs |
business_license | Business license |
financial_statement | Financial statements |
collateral_document | Collateral 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)
TipUse 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
| Error | HTTP Status | Resolution |
|---|---|---|
| Validation Error | 422 | Fix invalid fields and retry |
| Missing Documents | 422 | Upload required documents |
| Application Not Found | 404 | Verify application UID |
| Rate Limit | 429 | Wait and retry with backoff |
| Already Boarded | 409 | Use existing loan number |
Best Practices
| ✅ DO | ❌ DON'T |
|---|---|
| Handle errors gracefully | Fail silently |
| Use webhooks for status updates | Poll excessively |
| Upload documents promptly | Delay document collection |
| Verify prerequisites before boarding | Skip validation |
| Log all workflow steps | Ignore audit trail |
Support
Questions about the origination workflow?
- Email: [email protected]
- Related: API Integration Flow
Application Approved?See Transition to LMS for detailed boarding instructions.
