Overview
The Loan Recovery System automates the detection and management of loan defaults with integrated Twilio SMS and voice communication capabilities. It provides a comprehensive workflow for managing borrower contacts, recovery actions, and CDM event tracking. Code Reference:app/services/loan_recovery_service.py, app/services/twilio_service.py, app/api/recovery_routes.py, client/src/components/LoanRecoverySidebar.tsx
Key Features
Automatic Default Detection
The system automatically detects two types of defaults:Payment Defaults
- Detection Method: Monitors
PaymentEventrecords withpayment_status == "pending" - Calculation: Compares payment due date (created_at + 30 days) with current date
- Severity Levels:
- Low: 1-7 days past due
- Medium: 8-30 days past due
- High: 31-60 days past due
- Critical: 60+ days past due
app/services/loan_recovery_service.py (detect_payment_defaults method)
Covenant Breaches
- Detection Method: Monitors
LoanAssetrecords withrisk_status == "BREACH" - Trigger: Satellite verification detects compliance breach (e.g., NDVI below threshold)
- Severity: Typically “high” for all breaches
app/services/loan_recovery_service.py (detect_covenant_breaches method)
Twilio Integration
SMS Notifications
- E.164 Format Validation: Phone numbers validated before sending
- Status Callbacks: Real-time delivery status updates via webhooks
- Message Tracking: Twilio message SID stored for status tracking
- Error Handling: Comprehensive error handling with detailed error messages
app/services/twilio_service.py (send_sms method)
Voice Calls
- TwiML Generation: Automatic TwiML response generation
- Interactive Calls: Support for digit gathering and interactive responses
- Status Tracking: Call SID tracking and status monitoring
- Multi-Language: Support for different language codes
app/services/twilio_service.py (make_voice_call, generate_twiml_response methods)
Webhook Support
- Signature Verification: Secure webhook signature validation
- Status Updates: Automatic status updates for SMS and voice calls
- Delivery Tracking: Real-time delivery and read receipts
app/api/twilio_routes.py (webhook handlers)
Recovery Actions
The system supports multiple recovery action types with automatic escalation:Action Types
sms_reminder: SMS notification (days 1-3)voice_call: Voice call notification (days 4-7)email: Email notification (days 8-30)escalation: Escalated communication (30+ days)
app/services/loan_recovery_service.py (trigger_recovery_actions method)
Action Status
pending: Action created but not executedsent: Action executed successfullydelivered: Message delivered (SMS only)failed: Action failed with errorresponded: Borrower responded to communication
app/db/models.py (RecoveryAction model)
Message Templates
- Jinja2 Templating: Dynamic message generation with template variables
- Severity-Based Templates: Different templates for low/medium/high/critical
- Default Type Templates: Separate templates for payment defaults vs covenant breaches
- Fallback Templates: Default templates if custom templates not found
app/services/recovery_template_service.py
Borrower Contact Management
- Contact Information: Phone numbers, email addresses, preferred contact methods
- Primary Contacts: Designate primary contact for each deal
- Contact Preferences: Timezone, preferred hours, communication preferences
- Active/Inactive: Manage contact status
app/db/models.py (BorrowerContact model)
CDM Event Integration
All recovery operations generate CDM-compliant events:Loan Default Events
app/models/cdm_events.py (generate_cdm_loan_default function)
Recovery Action Events
app/models/cdm_events.py (generate_cdm_recovery_action function)
Workflow
1. Default Detection
Automatic Detection:- System periodically checks for overdue payments
- System monitors loan assets for breach status
- Defaults are created automatically when detected
- CDM events are generated for each default
- Navigate to Loan Recovery sidebar
- Click “Detect Defaults” button
- System scans for new defaults
- Results displayed in default list
app/services/loan_recovery_service.py (detect_payment_defaults, detect_covenant_breaches methods)
2. Recovery Action Creation
Automatic Triggering:- System automatically determines action types based on days past due
- Actions are created and executed immediately
- Messages are sent via Twilio
- Status is tracked in real-time
- Select a default from the list
- Click “Trigger Actions” button
- Choose action types (or let system auto-determine)
- Actions are created and executed
- Status updates displayed in real-time
app/services/loan_recovery_service.py (trigger_recovery_actions method)
3. Action Execution
SMS Execution:- Validate phone number (E.164 format)
- Generate message content from template
- Send SMS via Twilio API
- Store Twilio message SID
- Update action status to “sent”
- Generate CDM event
- Validate phone number (E.164 format)
- Generate TwiML response
- Initiate call via Twilio API
- Store Twilio call SID
- Update action status to “sent”
- Generate CDM event
app/services/loan_recovery_service.py (execute_recovery_action method)
4. Status Updates
Webhook Processing:- Twilio sends webhook to
/api/twilio/webhook/smsor/api/twilio/webhook/voice - Signature is verified for security
- RecoveryAction record is updated with new status
- CDM event is updated if needed
- Frontend is notified of status change
app/api/twilio_routes.py (webhook handlers)
User Interface
Loan Recovery Sidebar
Location:client/src/components/LoanRecoverySidebar.tsx
Features:
- Default List: View all active defaults with filtering by status, severity, deal
- Default Detail View: Comprehensive default information including:
- Default type and reason
- Amount overdue (if payment default)
- Days past due
- Severity level
- Status
- Related recovery actions
- Recovery Actions:
- View all actions for a default
- Trigger new actions
- Execute pending actions
- View action status and delivery information
- Borrower Contacts:
- View borrower contact information
- Add new contacts
- Update contact details
- Filtering: Filter defaults by status, severity, deal ID
- Real-time Updates: Status updates via webhooks
API Endpoints
Default Management
List all loan defaults with optional filtering. Query Parameters:deal_id: Filter by deal IDstatus: Filter by status (open, in_recovery, resolved, written_off)severity: Filter by severity (low, medium, high, critical)page: Page number (default: 1)limit: Items per page (default: 50, max: 100)
app/api/recovery_routes.py (default endpoints)
Recovery Actions
Trigger recovery actions for a loan default. Request Body:default_id: Filter by default IDstatus: Filter by statusdeal_id: Filter by deal IDpage: Page numberlimit: Items per page
app/api/recovery_routes.py (action endpoints)
Borrower Contacts
List all borrower contacts. Query Parameters:deal_id: Filter by deal ID
app/api/recovery_routes.py (contact endpoints)
Twilio Webhooks
SMS status callback endpoint (called by Twilio). Request: Form data from Twilio with signature verification Response: 200 OK Voice call status callback endpoint (called by Twilio). Request: Form data from Twilio with signature verification Response: TwiML response or 200 OK Generic status callback endpoint. Code Reference:app/api/twilio_routes.py (webhook endpoints)
Configuration
Environment Variables
Enable Twilio integration. Default:
falseTwilio Account SID (required if TWILIO_ENABLED=true)
Twilio Auth Token (required if TWILIO_ENABLED=true)
Twilio phone number in E.164 format (e.g., +1234567890)
Enable SMS functionality. Default:
trueEnable voice call functionality. Default:
trueBase URL for Twilio webhook callbacks (e.g., https://yourdomain.com/api/twilio)
Database Models
LoanDefault
Table:loan_defaults
Key Fields:
loan_id: Loan identifier (string)deal_id: Deal identifier (foreign key)default_type: Type (payment_default, covenant_breach, infraction)default_date: Date when default occurreddefault_reason: Reason for defaultamount_overdue: Amount overdue (for payment defaults)days_past_due: Number of days past dueseverity: Severity level (low, medium, high, critical)status: Status (open, in_recovery, resolved, written_off)cdm_events: JSONB array of CDM events
app/db/models.py (LoanDefault class, lines 2366-2409)
RecoveryAction
Table:recovery_actions
Key Fields:
loan_default_id: Foreign key to LoanDefaultaction_type: Type (sms_reminder, voice_call, email, escalation, legal_notice)communication_method: Method (sms, voice, email)recipient_phone: Recipient phone number (E.164 format)recipient_email: Recipient email addressmessage_template: Template name usedmessage_content: Actual message senttwilio_message_sid: Twilio message SID (for SMS)twilio_call_sid: Twilio call SID (for voice)status: Status (pending, sent, delivered, failed, responded)scheduled_at: Scheduled execution timesent_at: Actual send timedelivered_at: Delivery time (SMS)error_message: Error message if failed
app/db/models.py (RecoveryAction class, lines 2412-2465)
BorrowerContact
Table:borrower_contacts
Key Fields:
deal_id: Foreign key to Dealuser_id: Foreign key to User (if borrower is a user)contact_name: Contact namephone_number: Phone number (E.164 format)email: Email addresspreferred_contact_method: Preferred method (sms, voice, email)contact_preferences: JSONB preferences (timezone, hours, etc.)is_primary: Primary contact flagis_active: Active status flag
app/db/models.py (BorrowerContact class, lines 2468-2507)
Message Templates
Template Location
Templates are stored inapp/templates/recovery/ directory.
Template Naming Convention
{communication_method}_{default_type}.txt: Base template{communication_method}_{default_type}_{severity}.txt: Severity-specific template{communication_method}_escalation.txt: Escalation template
sms_payment_default.txt: Base SMS template for payment defaultssms_payment_default_high.txt: High severity SMS templatevoice_covenant_breach.txt: Voice template for covenant breachessms_escalation.txt: Escalation SMS template
Template Variables
Available template variables:borrower_name: Borrower contact namedefault_type: Type of defaultdefault_reason: Reason for defaultamount_overdue: Amount overdue (if payment default)days_past_due: Days past dueseverity: Severity levelloan_id: Loan identifierdeal_id: Deal identifier
app/services/recovery_template_service.py (render_template method)
Best Practices
- Timely Detection: Configure default detection to run regularly (e.g., daily)
- Multi-Channel Approach: Use SMS, voice, and email for better reach
- Compliance: Ensure all communications comply with regulations (TCPA, GDPR)
- Documentation: Maintain complete audit trail via CDM events
- Follow-up: Track borrower responses and adjust recovery strategy
- Phone Number Validation: Always validate phone numbers in E.164 format
- Error Handling: Monitor failed actions and retry if appropriate
- Template Customization: Customize message templates for your organization
Troubleshooting
SMS Not Delivered
Possible Causes:- Invalid phone number format (must be E.164)
- Twilio account balance insufficient
- Phone number not SMS-capable
- Twilio account restrictions
- Verify phone number format:
+[country code][number](e.g.,+1234567890) - Check Twilio Console for delivery status
- Verify Twilio account balance
- Review Twilio error codes in action metadata
Voice Calls Not Working
Possible Causes:- Phone number not voice-capable
- TwiML response format incorrect
- Webhook URL not accessible
- Twilio account restrictions
- Verify phone number has Voice capabilities in Twilio
- Check TwiML response format
- Ensure webhook URL is publicly accessible
- Review call logs in Twilio Console
Defaults Not Detected
Possible Causes:- PaymentEvent records not in “pending” status
- LoanAsset records not in “BREACH” status
- Date calculations incorrect
- Verify PaymentEvent payment_status
- Check LoanAsset risk_status
- Review default detection logic in service
Additional Resources
Last Updated: 2026-01-14
Code Reference:
app/services/loan_recovery_service.py, app/services/twilio_service.py, app/api/recovery_routes.py, client/src/components/LoanRecoverySidebar.tsx