Comprehensive guide to field-level validation rules, business logic constraints, and status transitions.
Validation Rules and Business Logic
Data Quality & ComplianceThis document provides comprehensive validation rules for all API fields, business logic constraints, and status transition rules to ensure data quality and regulatory compliance.
Understanding validation rules helps you build robust integrations that minimize errors and streamline processing.
Validation 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': 45, 'rankSpacing': 50}}}%%
flowchart LR
subgraph VAL[📋 Validation Layers]
A[📝 Field Format] --> B[📊 Business Rules]
B --> C[🔄 State Machine]
C --> D[✅ Valid Request]
end
style A fill:#2563eb,stroke:#1e40af,color:#fff
style B fill:#d97706,stroke:#b45309,color:#fff
style C fill:#2563eb,stroke:#1e40af,color:#fff
style D fill:#059669,stroke:#047857,color:#fff
| Validation Layer | Description | Example |
|---|---|---|
| Field Format | Data type, format, length | SSN must be 9 digits |
| Business Rules | Logic constraints | Loan amount ≤ product max |
| State Machine | Valid transitions | Draft → Submitted (valid) |
Field-Level Validation Rules
Loan Amount
| Property | Value |
|---|---|
| Field | loan_amount |
| Type | number |
| Required | ✅ Yes |
Validation Rules:
| Rule | Value |
|---|---|
| Minimum | $1,000 |
| Maximum | Varies by product (typically $50,000 - $500,000) |
| Precision | Up to 2 decimal places |
| Format | Positive number only |
Error Codes:
| Code | Description |
|---|---|
INVALID_LOAN_AMOUNT | Amount outside valid range |
LOAN_AMOUNT_EXCEEDS_LIMIT | Amount exceeds product maximum |
Examples:
// ✅ Valid
{ "loan_amount": 50000 }
// ❌ Invalid - below minimum
{ "loan_amount": 500 }
// ❌ Invalid - exceeds typical product limit
{ "loan_amount": 1000000 }Interest Rate
| Property | Value |
|---|---|
| Field | interest_rate |
| Type | number |
| Required | ✅ Yes (for certain products) |
Validation Rules:
| Rule | Value |
|---|---|
| Minimum | 0.00% |
| Maximum | 25.00% |
| Precision | Up to 4 decimal places |
| Format | Percentage (e.g., 5.5 for 5.5%) |
Error Code: INVALID_INTEREST_RATE
Loan Term
| Property | Value |
|---|---|
| Field | loan_term |
| Type | integer |
| Required | ✅ Yes |
Validation Rules:
| Rule | Value |
|---|---|
| Minimum | 12 months |
| Maximum | 360 months (30 years) |
| Format | Integer only |
Error Code: INVALID_LOAN_TERM
SSN (Social Security Number)
| Property | Value |
|---|---|
| Field | ssn |
| Type | string |
| Required | ✅ Yes (for individual borrowers) |
Validation Rules:
| Rule | Value |
|---|---|
| Format | XXX-XX-XXXX or XXXXXXXXX |
| Length | 9 digits |
| Pattern | Valid SSN format |
Error Codes:
| Code | Description |
|---|---|
INVALID_SSN_FORMAT | Format incorrect |
INVALID_SSN | SSN validation failed |
Examples:
// ✅ Valid - with hyphens
{ "ssn": "123-45-6789" }
// ✅ Valid - without hyphens
{ "ssn": "123456789" }
// ❌ Invalid - wrong length
{ "ssn": "123-45-67" }Email Address
| Property | Value |
|---|---|
| Field | email |
| Type | string |
| Required | ⚠️ Conditional (required for portal access) |
Validation Rules:
| Rule | Value |
|---|---|
| Format | RFC 5322 valid email |
| Max Length | 255 characters |
| Domain | Must be valid domain |
Error Code: INVALID_EMAIL_FORMAT
Phone Number
| Property | Value |
|---|---|
| Field | phone |
| Type | string |
| Required | ✅ Yes |
Validation Rules:
| Rule | Value |
|---|---|
| Format | (XXX) XXX-XXXX or XXX-XXX-XXXX or XXXXXXXXXX |
| Length | 10 digits (US format) |
| Pattern | Valid US phone number |
Error Code: INVALID_PHONE_FORMAT
Date Fields
| Property | Value |
|---|---|
| Fields | application_date, birth_date, loan_start_date, etc. |
| Type | string (ISO 8601) |
| Required | ⚠️ Varies by field |
Validation Rules:
| Rule | Value |
|---|---|
| Format | YYYY-MM-DD or YYYY-MM-DDTHH:mm:ssZ |
| Range | Must be valid calendar date |
Business Rules:
| Field | Rule |
|---|---|
birth_date | Must be in the past |
loan_start_date | Must be today or future |
application_date | Cannot be more than 30 days in future |
Error Codes:
| Code | Description |
|---|---|
INVALID_DATE_FORMAT | Format incorrect |
INVALID_DATE_RANGE | Date outside valid range |
Examples:
// ✅ Valid - ISO 8601 date
{ "loan_start_date": "2025-12-20" }
// ✅ Valid - ISO 8601 datetime
{ "created_at": "2025-12-15T10:30:00Z" }
// ❌ Invalid - wrong format
{ "application_date": "12/15/2025" }Business Rule Validations
Loan Amount vs Product Limits
Rule: Loan amount cannot exceed product maximum limit
def validate_loan_amount(loan_amount, product):
"""Validate loan amount against product limits."""
if loan_amount < product.min_amount:
raise ValidationError(
code="LOAN_AMOUNT_BELOW_MINIMUM",
message=f"Loan amount ${loan_amount} below minimum ${product.min_amount}",
details={"min_amount": product.min_amount, "provided": loan_amount}
)
if loan_amount > product.max_amount:
raise ValidationError(
code="LOAN_AMOUNT_EXCEEDS_LIMIT",
message=f"Loan amount exceeds product maximum of ${product.max_amount}",
details={"max_amount": product.max_amount, "provided": loan_amount}
)Debt-to-Income Ratio (DTI)
Rule: DTI ratio must be below product threshold (typically 43%)
def validate_dti_ratio(total_monthly_debt, monthly_income, max_dti=43):
"""Validate debt-to-income ratio."""
if monthly_income <= 0:
raise ValidationError(
code="INVALID_INCOME",
message="Monthly income must be greater than zero"
)
dti_ratio = (total_monthly_debt / monthly_income) * 100
if dti_ratio > max_dti:
raise ValidationError(
code="DTI_TOO_HIGH",
message=f"DTI ratio {dti_ratio:.1f}% exceeds maximum {max_dti}%",
details={"dti_ratio": dti_ratio, "max_dti": max_dti}
)
return dti_ratioCredit Score Requirements
Rule: Credit score must meet product minimum
def validate_credit_score(credit_score, product):
"""Validate credit score against product requirements."""
if credit_score < product.min_credit_score:
raise ValidationError(
code="CREDIT_SCORE_TOO_LOW",
message=f"Credit score {credit_score} below minimum {product.min_credit_score}",
details={
"credit_score": credit_score,
"min_required": product.min_credit_score
}
)Collateral Requirements
Rule: Collateral value must be sufficient for loan amount
def validate_collateral(loan_amount, collateral_value, ltv_max=80):
"""Validate loan-to-value ratio."""
if collateral_value <= 0:
raise ValidationError(
code="INVALID_COLLATERAL_VALUE",
message="Collateral value must be greater than zero"
)
ltv_ratio = (loan_amount / collateral_value) * 100
if ltv_ratio > ltv_max:
raise ValidationError(
code="INSUFFICIENT_COLLATERAL",
message=f"LTV ratio {ltv_ratio:.1f}% exceeds maximum {ltv_max}%",
details={
"ltv_ratio": ltv_ratio,
"max_ltv": ltv_max,
"required_collateral": loan_amount / (ltv_max / 100)
}
)Required vs Optional Fields
Application Submission APIs (LOS)
Required Fields
| Field | Type | Description |
|---|---|---|
borrower_name | string | Full name of borrower |
loan_amount | number | Requested loan amount |
loan_purpose | string | Purpose of loan |
ssn | string | Social Security Number |
email | string | Email address |
phone | string | Phone number |
Optional Fields
| Field | Type | Description |
|---|---|---|
co_borrower_name | string | Co-borrower name |
business_name | string | Business name (business loans) |
ein | string | Employer Identification Number |
collateral_description | string | Collateral description |
notes | string | Additional notes |
Active Loan Servicing APIs (LMS)
Required Fields (Loan Boarding)
| Field | Type | Description |
|---|---|---|
application_uid | string | LOS application UID |
loan_amount | number | Loan amount |
interest_rate | number | Interest rate |
loan_term | integer | Loan term in months |
loan_start_date | string | Loan start date |
Status Transition Rules
Application Status Transitions
%%{init: {'theme': 'base', 'themeVariables': {'background': '#ffffff', 'mainBkg': '#ffffff', 'clusterBkg': '#f9fafb', 'clusterBorder': '#2563eb', 'primaryColor': '#2563eb', 'primaryTextColor': '#ffffff', 'titleColor': '#000000', 'lineColor': '#374151'}, 'flowchart': {'padding': 25, 'nodeSpacing': 45, 'rankSpacing': 50}}}%%
flowchart LR
subgraph APP[📋 Application Status Flow]
A[Draft] --> B[Submitted]
B --> C[Under Review]
C --> D[Approved]
C --> E[Declined]
D --> F[Funded]
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:#059669,stroke:#047857,color:#fff
style E fill:#dc2626,stroke:#b91c1c,color:#fff
style F fill:#059669,stroke:#047857,color:#fff
Valid Transitions:
| From | To | Requirements |
|---|---|---|
Draft | Submitted | Always allowed |
Submitted | Under Review | Automatic or manual |
Under Review | Approved | All verifications complete |
Under Review | Declined | Decline reason required |
Approved | Funded | Funding details required |
Invalid Transitions:
| ❌ Transition | Reason |
|---|---|
Draft → Approved | Cannot skip statuses |
Approved → Under Review | Cannot go backwards |
Declined → Approved | Final state |
Error Code: INVALID_STATUS_TRANSITION
Loan Status Transitions
%%{init: {'theme': 'base', 'themeVariables': {'background': '#ffffff', 'mainBkg': '#ffffff', 'clusterBkg': '#f9fafb', 'clusterBorder': '#2563eb', 'primaryColor': '#2563eb', 'primaryTextColor': '#ffffff', 'titleColor': '#000000', 'lineColor': '#374151'}, 'flowchart': {'padding': 25, 'nodeSpacing': 45, 'rankSpacing': 50}}}%%
flowchart LR
subgraph LOAN[💰 Loan Status Flow]
A[Boarding] --> B[Active]
B --> C[Current]
C --> D[Paid Off]
B --> E[Delinquent]
E --> F[Collections]
F --> G[Charged Off]
E --> C
end
style A fill:#6b7280,stroke:#4b5563,color:#fff
style B fill:#2563eb,stroke:#1e40af,color:#fff
style C fill:#059669,stroke:#047857,color:#fff
style D fill:#059669,stroke:#047857,color:#fff
style E fill:#d97706,stroke:#b45309,color:#fff
style F fill:#dc2626,stroke:#b91c1c,color:#fff
style G fill:#374151,stroke:#1f2937,color:#fff
Valid Transitions:
| From | To | Trigger |
|---|---|---|
Boarding | Active | Boarding complete |
Active | Current | Payments on schedule |
Active | Delinquent | Payment overdue |
Delinquent | Current | Payment received |
Delinquent | Collections | Days past due threshold |
Collections | Charged Off | Write-off decision |
Current | Paid Off | Final payment received |
Error Response Format
All validation errors follow this format:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Request failed validation",
"details": [
{
"field": "loan_amount",
"issue": "MAX_LIMIT_EXCEEDED",
"message": "Loan amount exceeds maximum product limit",
"max": 50000,
"provided": 75000
}
],
"trace_id": "a1b2c3d4e5"
}
}Comprehensive Validation Example
class LoanApplicationValidator:
"""Complete validation for loan applications."""
def __init__(self, product_config):
self.product = product_config
def validate(self, application):
"""Run all validations on application."""
errors = []
# Field format validations
errors.extend(self._validate_ssn(application.get('ssn')))
errors.extend(self._validate_email(application.get('email')))
errors.extend(self._validate_phone(application.get('phone')))
errors.extend(self._validate_dates(application))
# Business rule validations
errors.extend(self._validate_loan_amount(application.get('loan_amount')))
errors.extend(self._validate_dti(application))
errors.extend(self._validate_collateral(application))
if errors:
raise ValidationError(
code="VALIDATION_ERROR",
message="Application validation failed",
details=errors
)
return True
def _validate_ssn(self, ssn):
"""Validate SSN format."""
import re
errors = []
if not ssn:
errors.append({"field": "ssn", "issue": "REQUIRED_FIELD_MISSING"})
elif not re.match(r'^\d{3}-?\d{2}-?\d{4}$', ssn):
errors.append({"field": "ssn", "issue": "INVALID_FORMAT"})
return errors
def _validate_loan_amount(self, amount):
"""Validate loan amount against product limits."""
errors = []
if not amount:
errors.append({"field": "loan_amount", "issue": "REQUIRED_FIELD_MISSING"})
elif amount < self.product.min_amount:
errors.append({
"field": "loan_amount",
"issue": "BELOW_MINIMUM",
"min": self.product.min_amount,
"provided": amount
})
elif amount > self.product.max_amount:
errors.append({
"field": "loan_amount",
"issue": "EXCEEDS_MAXIMUM",
"max": self.product.max_amount,
"provided": amount
})
return errors
# ... additional validation methods
# Usage
validator = LoanApplicationValidator(product_config)
try:
validator.validate(application_data)
print("✅ Validation passed")
except ValidationError as e:
print(f"❌ Validation failed: {e.details}")Validation Quick Reference
| Field | Format | Required | Error Code |
|---|---|---|---|
loan_amount | Number, $1K-$500K | ✅ | INVALID_LOAN_AMOUNT |
interest_rate | Number, 0-25% | ✅ | INVALID_INTEREST_RATE |
loan_term | Integer, 12-360 | ✅ | INVALID_LOAN_TERM |
ssn | XXX-XX-XXXX | ✅ | INVALID_SSN_FORMAT |
email | RFC 5322 | ⚠️ | INVALID_EMAIL_FORMAT |
phone | 10 digits | ✅ | INVALID_PHONE_FORMAT |
date | YYYY-MM-DD | ⚠️ | INVALID_DATE_FORMAT |
Support
Questions about validation rules?
- Email: [email protected]
- API Reference: See endpoint documentation for field-specific rules
Understanding Validation?Return to API Integration Flow to continue your integration.
