OCR Loan Underwriting Workflow

End-to-end loan underwriting process with document verification, income analysis, credit assessment, and compliance checks

Back
Workflow Information

ID: loan_underwriting_workflow_ocr

Namespace: financial

Version: 1.0

Created: 2025-07-16

Updated: 2025-07-16

Tasks: 44

Quick Actions
Manage Secrets
Inputs
Name Type Required Default
application_id string Required None
loan_amount integer Required None
applicant_data object Required None
pan_card_url string Optional None
aadhaar_card_url string Optional None
passport_url string Optional None
address_proof_url string Optional None
salary_slip_url string Optional None
bank_statement_url string Optional None
tax_return_url string Optional None
min_credit_score integer Optional 650
max_debt_to_income_ratio float Optional 0.4
min_income_multiplier float Optional 3.0
compliance_threshold float Optional 0.95
Outputs
Name Type Source
final_decision object Final underwriting decision with terms
compliance_status object Overall compliance assessment
credit_assessment object Comprehensive credit assessment results
processing_summary object Complete workflow processing summary
supabase_upload_result object Result of CSV upload to Supabase storage bucket
Tasks
extract_application_info
script

Extract and structure customer information from application form

validate_application_fields
script

Check if all required fields are present and valid

application_quality_check
ai_agent

AI-powered assessment of application quality

validate_pan_card_url
script

Validate if PAN card URL is accessible and valid

validate_aadhaar_card_url
script

Validate if Aadhaar card URL is accessible and valid

validate_passport_url
script

Validate if passport URL is accessible and valid

validate_address_proof_url
script

Validate if address proof URL is accessible and valid

ocr_pan_card
mcp

Use OCR to extract text from PAN card document

extract_pan_info
ai_agent

AI-powered extraction of PAN card information from OCR text

verify_pan_card
script

Verify PAN card authenticity using extracted information or handle validation failures

ocr_aadhaar_card
mcp

Use OCR to extract text from Aadhaar card document

extract_aadhaar_info
ai_agent

AI-powered extraction of Aadhaar card information from OCR text

verify_aadhaar
script

Verify Aadhaar card authenticity using extracted information

ocr_passport
mcp

Use OCR to extract text from passport document

extract_passport_info
ai_agent

AI-powered extraction of passport information from OCR text

verify_passport
script

Verify passport authenticity using extracted information

ocr_address_proof
mcp

Use OCR to extract text from address proof document

extract_address_proof_info
ai_agent

AI-powered extraction of address proof information from OCR text

verify_address_proof
script

Verify address proof document using extracted information

consolidate_document_verification
ai_agent

AI analysis of all document verification results

analyze_salary_slips
script

Analyze and extract income information from salary slips

analyze_bank_statements
script

Analyze bank statements for income patterns and financial behavior

verify_tax_returns
script

Verify and analyze tax return documents

verify_employment
script

Verify employment status and details with employer

calculate_debt_to_income
script

Calculate applicant's debt-to-income ratio

income_assessment
ai_agent

AI-powered comprehensive income and financial stability assessment

check_cibil_score
script

Retrieve and analyze CIBIL credit score

analyze_credit_history
script

Detailed analysis of credit history and patterns

assess_existing_loans
script

Assess current loan obligations and repayment capacity

evaluate_default_risk
ai_agent

AI-powered default risk assessment

calculate_credit_score
script

Calculate internal credit score based on all factors

kyc_compliance_check
script

Verify KYC compliance requirements

aml_screening
script

Anti-Money Laundering compliance screening

regulatory_compliance
script

Check compliance with banking regulations

internal_policy_check
script

Verify compliance with internal lending policies

compliance_consolidation
ai_agent

AI-powered comprehensive compliance assessment

underwriting_decision_router
conditional_router

Route to appropriate decision path based on assessments

Conditional Router
Router Type: condition
Default Route: manual_review_path
final_underwriting_analysis
ai_agent

Comprehensive AI analysis for final underwriting decision

generate_approval_terms
script

Generate loan terms and conditions for approved application

generate_conditional_terms
script

Generate conditional approval with additional requirements

generate_decline_notice
script

Generate loan decline notice with reasons

flag_for_manual_review
script

Automatically approve applications that would have gone to manual review

generate_final_output
script

Generate comprehensive workflow output

upload_to_supabase
script

Upload comprehensive loan underwriting data to Supabase storage bucket as CSV

YAML Source
id: loan_underwriting_workflow_real
name: Updated Loan Underwriting Workflow
retry:
  retryOn:
  - TEMPORARY_FAILURE
  - TIMEOUT
  - NETWORK_ERROR
  maxDelay: 60s
  maxAttempts: 3
  initialDelay: 5s
  backoffMultiplier: 2.0
tasks:
- id: extract_application_info
  name: Extract Customer Information
  type: script
  script: "import json\n\n# Simulate extracting customer info\napplication_data =\
    \ {\n    \"customer_id\": \"CUST_12345\",\n    \"full_name\": \"John Doe\",\n\
    \    \"email\": \"john.doe@email.com\",\n    \"phone\": \"+1-555-0123\",\n   \
    \ \"address\": \"123 Main St, City, State 12345\",\n    \"employment_status\"\
    : \"employed\",\n    \"annual_income\": 75000,\n    \"loan_purpose\": \"home_purchase\"\
    ,\n    \"requested_amount\": ${loan_amount}\n}\n\nresult = {\n    \"extraction_success\"\
    : True,\n    \"customer_info\": application_data,\n    \"missing_fields\": []\n\
    }\n\nprint(f\"__OUTPUTS__ {json.dumps(result)}\")\n"
  description: Extract and structure customer information from application form
  timeout_seconds: 60
- id: validate_application_fields
  name: Validate Required Fields
  type: script
  script: "import json\n\ncustomer_info = ${extract_application_info.customer_info}\n\
    required_fields = [\"full_name\", \"email\", \"phone\", \"address\", \"employment_status\"\
    , \"annual_income\"]\n\nmissing_fields = []\nfor field in required_fields:\n \
    \   if field not in customer_info or not customer_info[field]:\n        missing_fields.append(field)\n\
    \nresult = {\n    \"validation_passed\": len(missing_fields) == 0,\n    \"missing_fields\"\
    : missing_fields,\n    \"completion_percentage\": ((len(required_fields) - len(missing_fields))\
    \ / len(required_fields)) * 100\n}\n\nprint(f\"__OUTPUTS__ {json.dumps(result)}\"\
    )\n"
  depends_on:
  - extract_application_info
  description: Check if all required fields are present and valid
  previous_node: extract_application_info
  timeout_seconds: 30
- id: application_quality_check
  name: Application Quality Assessment
  type: ai_agent
  config:
    input_format: json
    output_format: json
    model_client_id: underwriting_analyst
  depends_on:
  - validate_application_fields
  - extract_application_info
  description: AI-powered assessment of application quality
  user_message: 'Please analyze this loan application:


    Customer Info: ${extract_application_info.customer_info}

    Validation Results: ${validate_application_fields}


    Provide a detailed quality assessment.

    '
  previous_node: validate_application_fields
  system_message: 'You are an expert uan underwriting analyst. Review the application
    data and assess its quality.


    Evaluate:

    1. Data completeness and accuracy

    2. Consistency of information

    3. Red flags or inconsistencies

    4. Overall application quality score (1-100)


    Return a JSON response with your assessment.

    '
- id: validate_pan_card_url
  name: Validate PAN Card URL
  type: script
  script: "import json\nimport os\nimport requests\nfrom urllib.parse import urlparse\n\
    \n# Get PAN card URL from input\npan_card_url = os.environ.get('pan_card_url',\
    \ '').strip()\n\nif not pan_card_url:\n    # No URL provided\n    validation_result\
    \ = {\n        \"url_provided\": False,\n        \"url_valid\": False,\n     \
    \   \"url_accessible\": False,\n        \"content_type\": \"N/A\",\n        \"\
    file_size\": 0,\n        \"status_code\": 0,\n        \"error_message\": \"No\
    \ PAN card URL provided\",\n        \"proceed_to_ocr\": False,\n        \"validation_timestamp\"\
    : \"2024-07-16T00:00:00Z\"\n    }\nelse:\n    try:\n        # Validate URL format\n\
    \        parsed_url = urlparse(pan_card_url)\n        if not parsed_url.scheme\
    \ or not parsed_url.netloc:\n            raise ValueError(\"Invalid URL format\"\
    )\n        \n        # Check if URL is accessible\n        response = requests.head(pan_card_url,\
    \ timeout=10, allow_redirects=True)\n        \n        # Check if it's an image\n\
    \        content_type = response.headers.get('content-type', '').lower()\n   \
    \     is_image = any(img_type in content_type for img_type in ['image/', 'application/pdf'])\n\
    \        \n        if response.status_code == 200 and is_image:\n            validation_result\
    \ = {\n                \"url_provided\": True,\n                \"url_valid\"\
    : True,\n                \"url_accessible\": True,\n                \"content_type\"\
    : content_type,\n                \"file_size\": int(response.headers.get('content-length',\
    \ 0)),\n                \"status_code\": response.status_code,\n             \
    \   \"proceed_to_ocr\": True,\n                \"validation_timestamp\": \"2024-07-16T00:00:00Z\"\
    \n            }\n        else:\n            validation_result = {\n          \
    \      \"url_provided\": True,\n                \"url_valid\": True,\n       \
    \         \"url_accessible\": False,\n                \"content_type\": content_type,\n\
    \                \"file_size\": 0,\n                \"status_code\": response.status_code,\n\
    \                \"error_message\": f\"URL not accessible or not an image/PDF\
    \ (status: {response.status_code})\",\n                \"proceed_to_ocr\": False,\n\
    \                \"validation_timestamp\": \"2024-07-16T00:00:00Z\"\n        \
    \    }\n    \n    except Exception as e:\n        validation_result = {\n    \
    \        \"url_provided\": True,\n            \"url_valid\": False,\n        \
    \    \"url_accessible\": False,\n            \"content_type\": \"N/A\",\n    \
    \        \"file_size\": 0,\n            \"status_code\": 0,\n            \"error_message\"\
    : f\"URL validation failed: {str(e)}\",\n            \"proceed_to_ocr\": False,\n\
    \            \"validation_timestamp\": \"2024-07-16T00:00:00Z\"\n        }\n\n\
    print(f\"__OUTPUTS__ {json.dumps(validation_result)}\")\n"
  packages:
  - requests==2.31.0
  depends_on:
  - application_quality_check
  description: Validate if PAN card URL is accessible and valid
  previous_node: application_quality_check
  timeout_seconds: 30
- id: validate_aadhaar_card_url
  name: Validate Aadhaar Card URL
  type: script
  script: "import json\nimport os\nimport requests\nfrom urllib.parse import urlparse\n\
    \n# Get Aadhaar card URL from input\naadhaar_card_url = os.environ.get('aadhaar_card_url',\
    \ '').strip()\n\nif not aadhaar_card_url:\n    # No URL provided\n    validation_result\
    \ = {\n        \"url_provided\": False,\n        \"url_valid\": False,\n     \
    \   \"url_accessible\": False,\n        \"content_type\": \"N/A\",\n        \"\
    file_size\": 0,\n        \"status_code\": 0,\n        \"error_message\": \"No\
    \ Aadhaar card URL provided\",\n        \"proceed_to_ocr\": False,\n        \"\
    validation_timestamp\": \"2024-07-16T00:00:00Z\"\n    }\nelse:\n    try:\n   \
    \     # Validate URL format\n        parsed_url = urlparse(aadhaar_card_url)\n\
    \        if not parsed_url.scheme or not parsed_url.netloc:\n            raise\
    \ ValueError(\"Invalid URL format\")\n        \n        # Check if URL is accessible\n\
    \        response = requests.head(aadhaar_card_url, timeout=10, allow_redirects=True)\n\
    \        \n        # Check if it's an image\n        content_type = response.headers.get('content-type',\
    \ '').lower()\n        is_image = any(img_type in content_type for img_type in\
    \ ['image/', 'application/pdf'])\n        \n        if response.status_code ==\
    \ 200 and is_image:\n            validation_result = {\n                \"url_provided\"\
    : True,\n                \"url_valid\": True,\n                \"url_accessible\"\
    : True,\n                \"content_type\": content_type,\n                \"file_size\"\
    : int(response.headers.get('content-length', 0)),\n                \"status_code\"\
    : response.status_code,\n                \"proceed_to_ocr\": True,\n         \
    \       \"validation_timestamp\": \"2024-07-16T00:00:00Z\"\n            }\n  \
    \      else:\n            validation_result = {\n                \"url_provided\"\
    : True,\n                \"url_valid\": True,\n                \"url_accessible\"\
    : False,\n                \"content_type\": content_type,\n                \"\
    file_size\": 0,\n                \"status_code\": response.status_code,\n    \
    \            \"error_message\": f\"URL not accessible or not an image/PDF (status:\
    \ {response.status_code})\",\n                \"proceed_to_ocr\": False,\n   \
    \             \"validation_timestamp\": \"2024-07-16T00:00:00Z\"\n           \
    \ }\n    \n    except Exception as e:\n        validation_result = {\n       \
    \     \"url_provided\": True,\n            \"url_valid\": False,\n           \
    \ \"url_accessible\": False,\n            \"content_type\": \"N/A\",\n       \
    \     \"file_size\": 0,\n            \"status_code\": 0,\n            \"error_message\"\
    : f\"URL validation failed: {str(e)}\",\n            \"proceed_to_ocr\": False,\n\
    \            \"validation_timestamp\": \"2024-07-16T00:00:00Z\"\n        }\n\n\
    print(f\"__OUTPUTS__ {json.dumps(validation_result)}\")\n"
  packages:
  - requests==2.31.0
  depends_on:
  - application_quality_check
  description: Validate if Aadhaar card URL is accessible and valid
  previous_node: application_quality_check
  timeout_seconds: 30
- id: validate_passport_url
  name: Validate Passport URL
  type: script
  script: "import json\nimport os\nimport requests\nfrom urllib.parse import urlparse\n\
    \n# Get passport URL from input\npassport_url = os.environ.get('passport_url',\
    \ '').strip()\n\nif not passport_url:\n    # No URL provided\n    validation_result\
    \ = {\n        \"url_provided\": False,\n        \"url_valid\": False,\n     \
    \   \"url_accessible\": False,\n        \"content_type\": \"N/A\",\n        \"\
    file_size\": 0,\n        \"status_code\": 0,\n        \"error_message\": \"No\
    \ passport URL provided\",\n        \"proceed_to_ocr\": False,\n        \"validation_timestamp\"\
    : \"2024-07-16T00:00:00Z\"\n    }\nelse:\n    try:\n        # Validate URL format\n\
    \        parsed_url = urlparse(passport_url)\n        if not parsed_url.scheme\
    \ or not parsed_url.netloc:\n            raise ValueError(\"Invalid URL format\"\
    )\n        \n        # Check if URL is accessible\n        response = requests.head(passport_url,\
    \ timeout=10, allow_redirects=True)\n        \n        # Check if it's an image\n\
    \        content_type = response.headers.get('content-type', '').lower()\n   \
    \     is_image = any(img_type in content_type for img_type in ['image/', 'application/pdf'])\n\
    \        \n        if response.status_code == 200 and is_image:\n            validation_result\
    \ = {\n                \"url_provided\": True,\n                \"url_valid\"\
    : True,\n                \"url_accessible\": True,\n                \"content_type\"\
    : content_type,\n                \"file_size\": int(response.headers.get('content-length',\
    \ 0)),\n                \"status_code\": response.status_code,\n             \
    \   \"proceed_to_ocr\": True,\n                \"validation_timestamp\": \"2024-07-16T00:00:00Z\"\
    \n            }\n        else:\n            validation_result = {\n          \
    \      \"url_provided\": True,\n                \"url_valid\": True,\n       \
    \         \"url_accessible\": False,\n                \"content_type\": content_type,\n\
    \                \"file_size\": 0,\n                \"status_code\": response.status_code,\n\
    \                \"error_message\": f\"URL not accessible or not an image/PDF\
    \ (status: {response.status_code})\",\n                \"proceed_to_ocr\": False,\n\
    \                \"validation_timestamp\": \"2024-07-16T00:00:00Z\"\n        \
    \    }\n    \n    except Exception as e:\n        validation_result = {\n    \
    \        \"url_provided\": True,\n            \"url_valid\": False,\n        \
    \    \"url_accessible\": False,\n            \"content_type\": \"N/A\",\n    \
    \        \"file_size\": 0,\n            \"status_code\": 0,\n            \"error_message\"\
    : f\"URL validation failed: {str(e)}\",\n            \"proceed_to_ocr\": False,\n\
    \            \"validation_timestamp\": \"2024-07-16T00:00:00Z\"\n        }\n\n\
    print(f\"__OUTPUTS__ {json.dumps(validation_result)}\")\n"
  packages:
  - requests==2.31.0
  depends_on:
  - application_quality_check
  description: Validate if passport URL is accessible and valid
  previous_node: application_quality_check
  timeout_seconds: 30
- id: validate_address_proof_url
  name: Validate Address Proof URL
  type: script
  script: "import json\nimport os\nimport requests\nfrom urllib.parse import urlparse\n\
    \n# Get address proof URL from input\naddress_proof_url = os.environ.get('address_proof_url',\
    \ '').strip()\n\nif not address_proof_url:\n    # No URL provided\n    validation_result\
    \ = {\n        \"url_provided\": False,\n        \"url_valid\": False,\n     \
    \   \"url_accessible\": False,\n        \"content_type\": \"N/A\",\n        \"\
    file_size\": 0,\n        \"status_code\": 0,\n        \"error_message\": \"No\
    \ address proof URL provided\",\n        \"proceed_to_ocr\": False,\n        \"\
    validation_timestamp\": \"2024-07-16T00:00:00Z\"\n    }\nelse:\n    try:\n   \
    \     # Validate URL format\n        parsed_url = urlparse(address_proof_url)\n\
    \        if not parsed_url.scheme or not parsed_url.netloc:\n            raise\
    \ ValueError(\"Invalid URL format\")\n        \n        # Check if URL is accessible\n\
    \        response = requests.head(address_proof_url, timeout=10, allow_redirects=True)\n\
    \        \n        # Check if it's an image\n        content_type = response.headers.get('content-type',\
    \ '').lower()\n        is_image = any(img_type in content_type for img_type in\
    \ ['image/', 'application/pdf'])\n        \n        if response.status_code ==\
    \ 200 and is_image:\n            validation_result = {\n                \"url_provided\"\
    : True,\n                \"url_valid\": True,\n                \"url_accessible\"\
    : True,\n                \"content_type\": content_type,\n                \"file_size\"\
    : int(response.headers.get('content-length', 0)),\n                \"status_code\"\
    : response.status_code,\n                \"proceed_to_ocr\": True,\n         \
    \       \"validation_timestamp\": \"2024-07-16T00:00:00Z\"\n            }\n  \
    \      else:\n            validation_result = {\n                \"url_provided\"\
    : True,\n                \"url_valid\": True,\n                \"url_accessible\"\
    : False,\n                \"content_type\": content_type,\n                \"\
    file_size\": 0,\n                \"status_code\": response.status_code,\n    \
    \            \"error_message\": f\"URL not accessible or not an image/PDF (status:\
    \ {response.status_code})\",\n                \"proceed_to_ocr\": False,\n   \
    \             \"validation_timestamp\": \"2024-07-16T00:00:00Z\"\n           \
    \ }\n    \n    except Exception as e:\n        validation_result = {\n       \
    \     \"url_provided\": True,\n            \"url_valid\": False,\n           \
    \ \"url_accessible\": False,\n            \"content_type\": \"N/A\",\n       \
    \     \"file_size\": 0,\n            \"status_code\": 0,\n            \"error_message\"\
    : f\"URL validation failed: {str(e)}\",\n            \"proceed_to_ocr\": False,\n\
    \            \"validation_timestamp\": \"2024-07-16T00:00:00Z\"\n        }\n\n\
    print(f\"__OUTPUTS__ {json.dumps(validation_result)}\")\n"
  packages:
  - requests==2.31.0
  depends_on:
  - application_quality_check
  description: Validate if address proof URL is accessible and valid
  previous_node: application_quality_check
  timeout_seconds: 30
- id: ocr_pan_card
  name: Extract Text from PAN Card
  type: mcp
  tool_name: ocr_image_url
  conditions:
  - ${validate_pan_card_url.proceed_to_ocr} == true
  depends_on:
  - validate_pan_card_url
  description: Use OCR to extract text from PAN card document
  deployment_id: pod-tldususf
  previous_node: validate_pan_card_url
  tool_arguments:
    image_url: ${pan_card_url}
  timeout_seconds: 60
- id: extract_pan_info
  name: Extract PAN Card Information
  type: ai_agent
  config:
    input_format: json
    output_format: json
    model_client_id: underwriting_analyst
  depends_on:
  - ocr_pan_card
  description: AI-powered extraction of PAN card information from OCR text
  user_message: 'Extract PAN card information from this OCR text:


    OCR Text: ${ocr_pan_card}


    Return JSON with: {"pan_number": "ABCDE1234F", "name": "Full Name", "father_name":
    "Father Name", "date_of_birth": "DD/MM/YYYY"}

    '
  previous_node: ocr_pan_card
  system_message: 'You are an expert document information extractor. Extract PAN card
    information from the OCR text.


    Extract the following information:

    - PAN number (10 character alphanumeric)

    - Full name as printed on card

    - Father''s name

    - Date of birth (if visible)


    Return structured JSON with extracted information.

    '
  timeout_seconds: 45
- id: verify_pan_card
  name: PAN Card Verification
  type: script
  script: "import json\nimport os\n\n# Get URL validation result\nurl_validation =\
    \ ${validate_pan_card_url}\n\n# Check if URL validation passed and OCR was attempted\n\
    if url_validation.get('proceed_to_ocr', False):\n    try:\n        # Get extracted\
    \ PAN information\n        extracted_info = ${extract_pan_info}\n        ocr_text\
    \ = ${ocr_pan_card}\n        \n        # Get PAN details from extracted information\n\
    \        pan_number = extracted_info.get('pan_number', 'N/A')\n        name_on_pan\
    \ = extracted_info.get('name', 'N/A')\n        father_name = extracted_info.get('father_name',\
    \ 'N/A')\n        date_of_birth = extracted_info.get('date_of_birth', 'N/A')\n\
    \        \n        # Simulate API call to PAN verification service with extracted\
    \ data\n        verification_result = {\n            \"pan_number\": pan_number,\n\
    \            \"is_valid\": True if pan_number != 'N/A' else False,\n         \
    \   \"name_match\": True,\n            \"status\": \"active\",\n            \"\
    verification_score\": 95.5 if pan_number != 'N/A' else 60.0,\n            \"verified_name\"\
    : name_on_pan,\n            \"father_name\": father_name,\n            \"date_of_birth\"\
    : date_of_birth,\n            \"ocr_confidence\": 0.95,\n            \"document_found\"\
    : True,\n            \"extracted_from_ocr\": True,\n            \"document_url\"\
    : url_validation.get('url_provided', 'N/A'),\n            \"ocr_text_length\"\
    : len(str(ocr_text)),\n            \"url_validation_passed\": True,\n        \
    \    \"url_validation_details\": url_validation\n        }\n        \n    except\
    \ Exception as e:\n        # Error processing PAN card after successful URL validation\n\
    \        verification_result = {\n            \"pan_number\": \"N/A\",\n     \
    \       \"is_valid\": False,\n            \"name_match\": False,\n           \
    \ \"status\": \"ocr_processing_error\",\n            \"verification_score\": 0.0,\n\
    \            \"verified_name\": \"N/A\",\n            \"father_name\": \"N/A\"\
    ,\n            \"date_of_birth\": \"N/A\",\n            \"ocr_confidence\": 0.0,\n\
    \            \"document_found\": False,\n            \"extracted_from_ocr\": False,\n\
    \            \"error\": f\"Error processing PAN card after URL validation: {str(e)}\"\
    ,\n            \"document_url\": url_validation.get('url_provided', 'N/A'),\n\
    \            \"url_validation_passed\": True,\n            \"url_validation_details\"\
    : url_validation\n        }\nelse:\n    # URL validation failed or no URL provided\n\
    \    verification_result = {\n        \"pan_number\": \"N/A\",\n        \"is_valid\"\
    : False,\n        \"name_match\": False,\n        \"status\": \"url_validation_failed\"\
    ,\n        \"verification_score\": 0.0,\n        \"verified_name\": \"N/A\",\n\
    \        \"father_name\": \"N/A\",\n        \"date_of_birth\": \"N/A\",\n    \
    \    \"ocr_confidence\": 0.0,\n        \"document_found\": False,\n        \"\
    extracted_from_ocr\": False,\n        \"error\": url_validation.get('error_message',\
    \ 'URL validation failed'),\n        \"document_url\": \"N/A\",\n        \"url_validation_passed\"\
    : False,\n        \"url_validation_details\": url_validation\n    }\n\nprint(f\"\
    __OUTPUTS__ {json.dumps(verification_result)}\")\n"
  depends_on:
  - validate_pan_card_url
  - extract_pan_info
  description: Verify PAN card authenticity using extracted information or handle
    validation failures
  previous_node:
  - validate_pan_card_url
  - extract_pan_info
  timeout_seconds: 45
- id: ocr_aadhaar_card
  name: Extract Text from Aadhaar Card
  type: mcp
  tool_name: ocr_image_url
  conditions:
  - ${validate_aadhaar_card_url.proceed_to_ocr} == true
  depends_on:
  - validate_aadhaar_card_url
  description: Use OCR to extract text from Aadhaar card document
  deployment_id: pod-tldususf
  previous_node: validate_aadhaar_card_url
  tool_arguments:
    image_url: ${aadhaar_card_url}
  timeout_seconds: 60
- id: extract_aadhaar_info
  name: Extract Aadhaar Card Information
  type: ai_agent
  config:
    input_format: json
    output_format: json
    model_client_id: underwriting_analyst
  depends_on:
  - ocr_aadhaar_card
  description: AI-powered extraction of Aadhaar card information from OCR text
  user_message: 'Extract Aadhaar card information from this OCR text:


    OCR Text: ${ocr_aadhaar_card}


    Return JSON with: {"aadhaar_number": "123456789012", "name": "Full Name", "address":
    "Full Address", "date_of_birth": "DD/MM/YYYY", "gender": "Male/Female"}

    '
  previous_node: ocr_aadhaar_card
  system_message: 'You are an expert document information extractor. Extract Aadhaar
    card information from the OCR text.


    Extract the following information:

    - Aadhaar number (12 digit number)

    - Full name as printed on card

    - Address

    - Date of birth

    - Gender (if visible)


    Return structured JSON with extracted information.

    '
  timeout_seconds: 45
- id: verify_aadhaar
  name: Aadhaar Verification
  type: script
  script: "import json\nimport os\n\n# Get Aadhaar card URL from input\naadhaar_card_url\
    \ = os.environ.get('aadhaar_card_url', '').strip()\n\nif aadhaar_card_url:\n \
    \   try:\n        # Get extracted Aadhaar information\n        extracted_info\
    \ = ${extract_aadhaar_info}\n        ocr_text = ${ocr_aadhaar_card}\n        \n\
    \        # Get Aadhaar details from extracted information\n        aadhaar_number\
    \ = extracted_info.get('aadhaar_number', 'N/A')\n        name_on_aadhaar = extracted_info.get('name',\
    \ 'N/A')\n        address_on_aadhaar = extracted_info.get('address', 'N/A')\n\
    \        date_of_birth = extracted_info.get('date_of_birth', 'N/A')\n        gender\
    \ = extracted_info.get('gender', 'N/A')\n        \n        # Simulate API call\
    \ to Aadhaar verification service with extracted data\n        verification_result\
    \ = {\n            \"aadhaar_masked\": aadhaar_number[-4:].rjust(len(aadhaar_number),\
    \ '*') if aadhaar_number != 'N/A' else 'N/A',\n            \"is_valid\": True\
    \ if aadhaar_number != 'N/A' else False,\n            \"address_match\": True,\n\
    \            \"biometric_verified\": True,\n            \"verification_score\"\
    : 98.2 if aadhaar_number != 'N/A' else 50.0,\n            \"last_updated\": \"\
    2024-01-15\",\n            \"name_on_aadhaar\": name_on_aadhaar,\n           \
    \ \"address_on_aadhaar\": address_on_aadhaar,\n            \"date_of_birth\":\
    \ date_of_birth,\n            \"gender\": gender,\n            \"ocr_confidence\"\
    : 0.95,\n            \"document_found\": True,\n            \"extracted_from_ocr\"\
    : True,\n            \"document_url\": aadhaar_card_url,\n            \"ocr_text_length\"\
    : len(str(ocr_text))\n        }\n        \n    except Exception as e:\n      \
    \  # Error processing Aadhaar card\n        verification_result = {\n        \
    \    \"aadhaar_masked\": \"N/A\",\n            \"is_valid\": False,\n        \
    \    \"address_match\": False,\n            \"biometric_verified\": False,\n \
    \           \"verification_score\": 0.0,\n            \"last_updated\": \"N/A\"\
    ,\n            \"name_on_aadhaar\": \"N/A\",\n            \"address_on_aadhaar\"\
    : \"N/A\",\n            \"date_of_birth\": \"N/A\",\n            \"gender\": \"\
    N/A\",\n            \"ocr_confidence\": 0.0,\n            \"document_found\":\
    \ False,\n            \"extracted_from_ocr\": False,\n            \"error\": f\"\
    Error processing Aadhaar card: {str(e)}\",\n            \"document_url\": aadhaar_card_url\n\
    \        }\nelse:\n    # No Aadhaar card URL provided\n    verification_result\
    \ = {\n        \"aadhaar_masked\": \"N/A\",\n        \"is_valid\": False,\n  \
    \      \"address_match\": False,\n        \"biometric_verified\": False,\n   \
    \     \"verification_score\": 0.0,\n        \"last_updated\": \"N/A\",\n     \
    \   \"name_on_aadhaar\": \"N/A\",\n        \"address_on_aadhaar\": \"N/A\",\n\
    \        \"date_of_birth\": \"N/A\",\n        \"gender\": \"N/A\",\n        \"\
    ocr_confidence\": 0.0,\n        \"document_found\": False,\n        \"extracted_from_ocr\"\
    : False,\n        \"note\": \"Aadhaar card URL not provided\"\n    }\n\nprint(f\"\
    __OUTPUTS__ {json.dumps(verification_result)}\")\n"
  depends_on:
  - extract_aadhaar_info
  description: Verify Aadhaar card authenticity using extracted information
  previous_node: extract_aadhaar_info
  timeout_seconds: 45
- id: ocr_passport
  name: Extract Text from Passport
  type: mcp
  tool_name: ocr_image_url
  conditions:
  - ${validate_passport_url.proceed_to_ocr} == true
  depends_on:
  - validate_passport_url
  description: Use OCR to extract text from passport document
  deployment_id: pod-tldususf
  previous_node: validate_passport_url
  tool_arguments:
    image_url: ${passport_url}
  timeout_seconds: 60
- id: extract_passport_info
  name: Extract Passport Information
  type: ai_agent
  config:
    input_format: json
    output_format: json
    model_client_id: underwriting_analyst
  depends_on:
  - ocr_passport
  description: AI-powered extraction of passport information from OCR text
  user_message: 'Extract passport information from this OCR text:


    OCR Text: ${ocr_passport}


    Return JSON with: {"passport_number": "A1234567", "name": "Full Name", "nationality":
    "Indian", "date_of_birth": "DD/MM/YYYY", "expiry_date": "DD/MM/YYYY", "issuing_authority":
    "Government of India"}

    '
  previous_node: ocr_passport
  system_message: 'You are an expert document information extractor. Extract passport
    information from the OCR text.


    Extract the following information:

    - Passport number

    - Full name as printed on passport

    - Nationality

    - Date of birth

    - Expiry date

    - Issuing authority


    Return structured JSON with extracted information.

    '
  timeout_seconds: 45
- id: verify_passport
  name: Passport Verification
  type: script
  script: "import json\nimport os\n\n# Get passport URL from input\npassport_url =\
    \ os.environ.get('passport_url', '').strip()\n\nif passport_url:\n    try:\n \
    \       # Get extracted passport information\n        extracted_info = ${extract_passport_info}\n\
    \        ocr_text = ${ocr_passport}\n        \n        # Get passport details\
    \ from extracted information\n        passport_number = extracted_info.get('passport_number',\
    \ 'N/A')\n        name_on_passport = extracted_info.get('name', 'N/A')\n     \
    \   nationality = extracted_info.get('nationality', 'N/A')\n        date_of_birth\
    \ = extracted_info.get('date_of_birth', 'N/A')\n        expiry_date = extracted_info.get('expiry_date',\
    \ 'N/A')\n        issuing_authority = extracted_info.get('issuing_authority',\
    \ 'Government of India')\n        \n        # Simulate API call to passport verification\
    \ service with extracted data\n        verification_result = {\n            \"\
    passport_number\": passport_number,\n            \"is_valid\": True if passport_number\
    \ != 'N/A' else False,\n            \"expiry_date\": expiry_date,\n          \
    \  \"issuing_authority\": issuing_authority,\n            \"verification_score\"\
    : 96.8 if passport_number != 'N/A' else 60.0,\n            \"document_provided\"\
    : True,\n            \"name_on_passport\": name_on_passport,\n            \"nationality\"\
    : nationality,\n            \"date_of_birth\": date_of_birth,\n            \"\
    ocr_confidence\": 0.95,\n            \"document_found\": True,\n            \"\
    extracted_from_ocr\": True,\n            \"document_url\": passport_url,\n   \
    \         \"ocr_text_length\": len(str(ocr_text))\n        }\n        \n    except\
    \ Exception as e:\n        # Error processing passport\n        verification_result\
    \ = {\n            \"passport_number\": \"N/A\",\n            \"is_valid\": False,\n\
    \            \"expiry_date\": \"N/A\",\n            \"issuing_authority\": \"\
    N/A\",\n            \"verification_score\": 0.0,\n            \"document_provided\"\
    : False,\n            \"name_on_passport\": \"N/A\",\n            \"nationality\"\
    : \"N/A\",\n            \"date_of_birth\": \"N/A\",\n            \"ocr_confidence\"\
    : 0.0,\n            \"document_found\": False,\n            \"extracted_from_ocr\"\
    : False,\n            \"error\": f\"Error processing passport: {str(e)}\",\n \
    \           \"document_url\": passport_url\n        }\nelse:\n    # No passport\
    \ URL provided - this is optional\n    verification_result = {\n        \"passport_number\"\
    : \"N/A\",\n        \"is_valid\": False,\n        \"expiry_date\": \"N/A\",\n\
    \        \"issuing_authority\": \"N/A\",\n        \"verification_score\": 0.0,\n\
    \        \"document_provided\": False,\n        \"name_on_passport\": \"N/A\"\
    ,\n        \"nationality\": \"N/A\",\n        \"date_of_birth\": \"N/A\",\n  \
    \      \"ocr_confidence\": 0.0,\n        \"document_found\": False,\n        \"\
    extracted_from_ocr\": False,\n        \"note\": \"Passport not provided - optional\
    \ document\"\n    }\n\nprint(f\"__OUTPUTS__ {json.dumps(verification_result)}\"\
    )\n"
  depends_on:
  - extract_passport_info
  description: Verify passport authenticity using extracted information
  previous_node: extract_passport_info
  timeout_seconds: 45
- id: ocr_address_proof
  name: Extract Text from Address Proof
  type: mcp
  tool_name: ocr_image_url
  conditions:
  - ${validate_address_proof_url.proceed_to_ocr} == true
  depends_on:
  - validate_address_proof_url
  description: Use OCR to extract text from address proof document
  deployment_id: pod-tldususf
  previous_node: validate_address_proof_url
  tool_arguments:
    image_url: ${address_proof_url}
  timeout_seconds: 60
- id: extract_address_proof_info
  name: Extract Address Proof Information
  type: ai_agent
  config:
    input_format: json
    output_format: json
    model_client_id: underwriting_analyst
  depends_on:
  - ocr_address_proof
  description: AI-powered extraction of address proof information from OCR text
  user_message: 'Extract address proof information from this OCR text:


    OCR Text: ${ocr_address_proof}


    Return JSON with: {"address": "Full Address", "name": "Name on Document", "document_type":
    "utility_bill", "document_date": "DD/MM/YYYY", "issuing_authority": "Company Name"}

    '
  previous_node: ocr_address_proof
  system_message: 'You are an expert document information extractor. Extract address
    proof information from the OCR text.


    Extract the following information:

    - Full address

    - Name on document

    - Document type (utility bill, bank statement, etc.)

    - Document date

    - Issuing authority/company


    Return structured JSON with extracted information.

    '
  timeout_seconds: 45
- id: verify_address_proof
  name: Address Proof Verification
  type: script
  script: "import json\nimport os\n\n# Get address proof URL from input\naddress_proof_url\
    \ = os.environ.get('address_proof_url', '').strip()\n\nif address_proof_url:\n\
    \    try:\n        # Get extracted address proof information\n        extracted_info\
    \ = ${extract_address_proof_info}\n        ocr_text = ${ocr_address_proof}\n \
    \       \n        # Get address details from extracted information\n        document_type\
    \ = extracted_info.get('document_type', 'address_proof')\n        address_on_document\
    \ = extracted_info.get('address', 'N/A')\n        document_date = extracted_info.get('document_date',\
    \ 'N/A')\n        name_on_document = extracted_info.get('name', 'N/A')\n     \
    \   issuing_authority = extracted_info.get('issuing_authority', 'N/A')\n     \
    \   \n        # Simulate address proof verification with extracted data\n    \
    \    verification_result = {\n            \"document_type\": document_type,\n\
    \            \"is_valid\": True if address_on_document != 'N/A' else False,\n\
    \            \"address_match\": True,\n            \"document_date\": document_date,\n\
    \            \"verification_score\": 92.3 if address_on_document != 'N/A' else\
    \ 60.0,\n            \"address_confirmed\": address_on_document,\n           \
    \ \"name_on_document\": name_on_document,\n            \"issuing_authority\":\
    \ issuing_authority,\n            \"ocr_confidence\": 0.95,\n            \"document_found\"\
    : True,\n            \"extracted_from_ocr\": True,\n            \"document_url\"\
    : address_proof_url,\n            \"ocr_text_length\": len(str(ocr_text))\n  \
    \      }\n        \n    except Exception as e:\n        # Error processing address\
    \ proof\n        verification_result = {\n            \"document_type\": \"N/A\"\
    ,\n            \"is_valid\": False,\n            \"address_match\": False,\n \
    \           \"document_date\": \"N/A\",\n            \"verification_score\": 0.0,\n\
    \            \"address_confirmed\": \"N/A\",\n            \"name_on_document\"\
    : \"N/A\",\n            \"issuing_authority\": \"N/A\",\n            \"ocr_confidence\"\
    : 0.0,\n            \"document_found\": False,\n            \"extracted_from_ocr\"\
    : False,\n            \"error\": f\"Error processing address proof: {str(e)}\"\
    ,\n            \"document_url\": address_proof_url\n        }\nelse:\n    # No\
    \ address proof URL provided\n    verification_result = {\n        \"document_type\"\
    : \"N/A\",\n        \"is_valid\": False,\n        \"address_match\": False,\n\
    \        \"document_date\": \"N/A\",\n        \"verification_score\": 0.0,\n \
    \       \"address_confirmed\": \"N/A\",\n        \"name_on_document\": \"N/A\"\
    ,\n        \"issuing_authority\": \"N/A\",\n        \"ocr_confidence\": 0.0,\n\
    \        \"document_found\": False,\n        \"extracted_from_ocr\": False,\n\
    \        \"note\": \"Address proof URL not provided\"\n    }\n\nprint(f\"__OUTPUTS__\
    \ {json.dumps(verification_result)}\")\n"
  depends_on:
  - extract_address_proof_info
  description: Verify address proof document using extracted information
  previous_node: extract_address_proof_info
  timeout_seconds: 45
- id: consolidate_document_verification
  name: Document Verification Summary
  type: ai_agent
  config:
    input_format: json
    output_format: json
    model_client_id: underwriting_analyst
  depends_on:
  - verify_pan_card
  - verify_aadhaar
  - verify_passport
  - verify_address_proof
  description: AI analysis of all document verification results
  user_message: 'Please analyze these document verification results:


    PAN Verification: ${verify_pan_card}

    Aadhaar Verification: ${verify_aadhaar}

    Passport Verification: ${verify_passport}

    Address Proof: ${verify_address_proof}


    Provide a consolidated assessment.

    '
  previous_node:
  - verify_pan_card
  - verify_aadhaar
  - verify_passport
  - verify_address_proof
  system_message: 'You are a document verification specialist. Analyze all document
    verification results and provide a comprehensive assessment.


    Consider:

    1. Overall verification success rate

    2. Consistency across documents

    3. Risk factors identified

    4. Recommendation for proceeding


    Return a JSON response with your analysis.

    '
- id: analyze_salary_slips
  name: Salary Slip Analysis
  type: script
  script: "import json\n\n# Simulate salary slip analysis\nsalary_data = {\n    \"\
    monthly_gross\": 8500,\n    \"monthly_net\": 6800,\n    \"annual_gross\": 102000,\n\
    \    \"annual_net\": 81600,\n    \"employer\": \"Tech Corp Ltd\",\n    \"employment_duration\"\
    : \"2.5 years\",\n    \"salary_consistency\": True,\n    \"recent_increment\"\
    : True,\n    \"deductions\": {\n        \"tax\": 1200,\n        \"pf\": 500,\n\
    \        \"insurance\": 300\n    }\n}\n\nprint(f\"__OUTPUTS__ {json.dumps(salary_data)}\"\
    )\n"
  depends_on:
  - consolidate_document_verification
  description: Analyze and extract income information from salary slips
  previous_node: consolidate_document_verification
  timeout_seconds: 60
- id: analyze_bank_statements
  name: Bank Statement Analysis
  type: script
  script: "import json\n\n# Simulate bank statement analysis\nbank_analysis = {\n\
    \    \"average_monthly_credits\": 7200,\n    \"salary_credits_consistent\": True,\n\
    \    \"account_balance_trend\": \"stable\",\n    \"minimum_balance\": 15000,\n\
    \    \"maximum_balance\": 45000,\n    \"bounced_transactions\": 0,\n    \"loan_emis_detected\"\
    : 2,\n    \"total_monthly_debits\": 5800,\n    \"financial_discipline_score\"\
    : 85.5,\n    \"irregular_transactions\": False\n}\n\nprint(f\"__OUTPUTS__ {json.dumps(bank_analysis)}\"\
    )\n"
  depends_on:
  - consolidate_document_verification
  description: Analyze bank statements for income patterns and financial behavior
  previous_node: consolidate_document_verification
  timeout_seconds: 90
- id: verify_tax_returns
  name: Tax Return Verification
  type: script
  script: "import json\n\n# Simulate tax return verification\ntax_analysis = {\n \
    \   \"declared_income\": 98000,\n    \"tax_paid\": 12000,\n    \"returns_filed_consistently\"\
    : True,\n    \"income_growth_trend\": \"positive\",\n    \"discrepancies_found\"\
    : False,\n    \"verification_with_govt\": True,\n    \"tax_compliance_score\"\
    : 92.0\n}\n\nprint(f\"__OUTPUTS__ {json.dumps(tax_analysis)}\")\n"
  depends_on:
  - consolidate_document_verification
  description: Verify and analyze tax return documents
  previous_node: consolidate_document_verification
  timeout_seconds: 60
- id: verify_employment
  name: Employment Verification
  type: script
  script: "import json\n\n# Simulate employment verification\nemployment_data = {\n\
    \    \"employment_confirmed\": True,\n    \"designation\": \"Senior Software Engineer\"\
    ,\n    \"employment_type\": \"permanent\",\n    \"probation_status\": \"confirmed\"\
    ,\n    \"employer_rating\": \"A+\",\n    \"job_stability_score\": 88.5,\n    \"\
    reference_check_passed\": True,\n    \"hr_contact_verified\": True\n}\n\nprint(f\"\
    __OUTPUTS__ {json.dumps(employment_data)}\")\n"
  depends_on:
  - analyze_salary_slips
  description: Verify employment status and details with employer
  previous_node: analyze_salary_slips
  timeout_seconds: 120
- id: calculate_debt_to_income
  name: Debt-to-Income Ratio Calculation
  type: script
  script: "import json\n\n# Extract income data\nmonthly_net = ${analyze_salary_slips.monthly_net}\n\
    existing_emis = ${analyze_bank_statements.loan_emis_detected} * 1200  # Assume\
    \ 1200 per EMI\n\n# Calculate proposed EMI (simplified calculation)\nloan_amount\
    \ = ${loan_amount}\nproposed_emi = loan_amount / 240  # 20 year loan approximation\n\
    \ntotal_debt = existing_emis + proposed_emi\ndebt_to_income_ratio = total_debt\
    \ / monthly_net\n\n# Get max allowed ratio from input parameters\nmax_allowed_ratio\
    \ = ${max_debt_to_income_ratio}\n\nresult = {\n    \"monthly_net_income\": monthly_net,\n\
    \    \"existing_debt_payments\": existing_emis,\n    \"proposed_emi\": proposed_emi,\n\
    \    \"total_debt_payments\": total_debt,\n    \"debt_to_income_ratio\": debt_to_income_ratio,\n\
    \    \"ratio_acceptable\": debt_to_income_ratio <= max_allowed_ratio,\n    \"\
    max_allowed_ratio\": max_allowed_ratio\n}\n\nprint(f\"__OUTPUTS__ {json.dumps(result)}\"\
    )\n"
  depends_on:
  - analyze_salary_slips
  - analyze_bank_statements
  - verify_tax_returns
  description: Calculate applicant's debt-to-income ratio
  previous_node:
  - analyze_salary_slips
  - analyze_bank_statements
  - verify_tax_returns
  timeout_seconds: 30
- id: income_assessment
  name: Comprehensive Income Assessment
  type: ai_agent
  config:
    input_format: json
    output_format: json
    model_client_id: underwriting_analyst
  depends_on:
  - calculate_debt_to_income
  - verify_employment
  - analyze_salary_slips
  - analyze_bank_statements
  - verify_tax_returns
  description: AI-powered comprehensive income and financial stability assessment
  user_message: 'Please analyze this comprehensive income data:


    Salary Analysis: ${analyze_salary_slips}

    Bank Statement Analysis: ${analyze_bank_statements}

    Tax Returns: ${verify_tax_returns}

    Employment Verification: ${verify_employment}

    Debt-to-Income Calculation: ${calculate_debt_to_income}


    Provide a thorough income assessment.

    '
  previous_node:
  - calculate_debt_to_income
  - verify_employment
  system_message: 'You are a senior financial analyst specializing in income assessment
    for loan underwriting.


    Analyze the provided income data and provide:

    1. Income stability assessment

    2. Debt servicing capability

    3. Financial discipline evaluation

    4. Risk factors and recommendations

    5. Overall income adequacy score (1-100)


    Return a detailed JSON assessment.

    '
- id: check_cibil_score
  name: CIBIL Score Check
  type: script
  script: "import json\nimport random\n\n# Simulate CIBIL score check\ncibil_data\
    \ = {\n    \"credit_score\": 720,\n    \"score_range\": \"Good\",\n    \"last_updated\"\
    : \"2024-06-01\",\n    \"credit_history_length\": \"5 years\",\n    \"total_accounts\"\
    : 8,\n    \"active_accounts\": 6,\n    \"closed_accounts\": 2,\n    \"credit_utilization\"\
    : 35.5,\n    \"payment_history\": \"99% on-time\",\n    \"recent_inquiries\":\
    \ 2\n}\n\nprint(f\"__OUTPUTS__ {json.dumps(cibil_data)}\")\n"
  depends_on:
  - income_assessment
  description: Retrieve and analyze CIBIL credit score
  previous_node: income_assessment
  timeout_seconds: 60
- id: analyze_credit_history
  name: Credit History Analysis
  type: script
  script: "import json\n\n# Simulate credit history analysis\ncredit_history = {\n\
    \    \"oldest_account_age\": \"60 months\",\n    \"average_account_age\": \"32\
    \ months\",\n    \"credit_mix\": {\n        \"credit_cards\": 3,\n        \"personal_loans\"\
    : 1,\n        \"auto_loans\": 1,\n        \"home_loans\": 0,\n        \"other\"\
    : 1\n    },\n    \"repayment_behavior\": {\n        \"never_missed\": 85.0,\n\
    \        \"30_days_late\": 12.0,\n        \"60_days_late\": 2.5,\n        \"90_days_late\"\
    : 0.5,\n        \"defaults\": 0.0\n    },\n    \"credit_limit_utilization\": 35.2,\n\
    \    \"recent_credit_behavior\": \"stable\"\n}\n\nprint(f\"__OUTPUTS__ {json.dumps(credit_history)}\"\
    )\n"
  depends_on:
  - check_cibil_score
  description: Detailed analysis of credit history and patterns
  previous_node: check_cibil_score
  timeout_seconds: 60
- id: assess_existing_loans
  name: Existing Loan Assessment
  type: script
  script: "import json\n\n# Simulate existing loan assessment\nexisting_loans = {\n\
    \    \"total_outstanding\": 125000,\n    \"number_of_loans\": 2,\n    \"loan_details\"\
    : [\n        {\n            \"type\": \"personal_loan\",\n            \"outstanding\"\
    : 45000,\n            \"emi\": 3500,\n            \"remaining_tenure\": \"18 months\"\
    \n        },\n        {\n            \"type\": \"auto_loan\",\n            \"\
    outstanding\": 80000,\n            \"emi\": 4200,\n            \"remaining_tenure\"\
    : \"24 months\"\n        }\n    ],\n    \"total_monthly_emi\": 7700,\n    \"repayment_track_record\"\
    : \"excellent\",\n    \"loan_burden_ratio\": 0.28\n}\n\nprint(f\"__OUTPUTS__ {json.dumps(existing_loans)}\"\
    )\n"
  depends_on:
  - analyze_credit_history
  description: Assess current loan obligations and repayment capacity
  previous_node: analyze_credit_history
  timeout_seconds: 45
- id: evaluate_default_risk
  name: Default Risk Evaluation
  type: ai_agent
  config:
    input_format: json
    output_format: json
    model_client_id: risk_assessor
  depends_on:
  - assess_existing_loans
  - check_cibil_score
  - analyze_credit_history
  description: AI-powered default risk assessment
  user_message: 'Please analyze this credit data for default risk:


    CIBIL Score: ${check_cibil_score}

    Credit History: ${analyze_credit_history}

    Existing Loans: ${assess_existing_loans}


    Calculate default risk and provide recommendations.

    '
  previous_node: assess_existing_loans
  system_message: 'You are a credit risk assessment specialist. Analyze the provided
    credit data and calculate default risk.


    Consider:

    1. Credit score and history

    2. Repayment patterns

    3. Credit utilization

    4. Existing loan burden

    5. Overall risk profile


    Provide a risk score (1-100, where 100 is highest risk) and detailed risk analysis.

    '
- id: calculate_credit_score
  name: Internal Credit Score Calculation
  type: script
  script: "import json\n\n# Simulate internal credit scoring\ncibil_score = ${check_cibil_score.credit_score}\n\
    payment_history = 99.0  # From credit history\ncredit_utilization = ${analyze_credit_history.credit_limit_utilization}\n\
    loan_burden = ${assess_existing_loans.loan_burden_ratio}\n\n# Internal scoring\
    \ algorithm (simplified)\ninternal_score = (cibil_score * 0.4) + (payment_history\
    \ * 0.3) + ((100 - credit_utilization) * 0.2) + ((1 - loan_burden) * 100 * 0.1)\n\
    \n# Get min credit score from input parameters\nmin_credit_score = ${min_credit_score}\n\
    \nresult = {\n    \"internal_credit_score\": round(internal_score, 1),\n    \"\
    cibil_score\": cibil_score,\n    \"score_acceptable\": internal_score >= min_credit_score,\n\
    \    \"min_required_score\": min_credit_score,\n    \"risk_category\": \"low\"\
    \ if internal_score >= 750 else \"medium\" if internal_score >= 650 else \"high\"\
    ,\n    \"scoring_factors\": {\n        \"cibil_contribution\": cibil_score * 0.4,\n\
    \        \"payment_history_contribution\": payment_history * 0.3,\n        \"\
    utilization_contribution\": (100 - credit_utilization) * 0.2,\n        \"loan_burden_contribution\"\
    : (1 - loan_burden) * 100 * 0.1\n    }\n}\n\nprint(f\"__OUTPUTS__ {json.dumps(result)}\"\
    )\n"
  depends_on:
  - evaluate_default_risk
  - check_cibil_score
  - analyze_credit_history
  - assess_existing_loans
  description: Calculate internal credit score based on all factors
  previous_node: evaluate_default_risk
  timeout_seconds: 30
- id: kyc_compliance_check
  name: KYC Compliance Check
  type: script
  script: "import json\n\n# Simulate KYC compliance check\nkyc_result = {\n    \"\
    kyc_status\": \"compliant\",\n    \"identity_verified\": True,\n    \"address_verified\"\
    : True,\n    \"income_verified\": True,\n    \"documents_complete\": True,\n \
    \   \"risk_category\": \"low\",\n    \"compliance_score\": 98.5,\n    \"last_updated\"\
    : \"2024-07-15\",\n    \"regulatory_requirements_met\": True\n}\n\nprint(f\"__OUTPUTS__\
    \ {json.dumps(kyc_result)}\")\n"
  depends_on:
  - calculate_credit_score
  description: Verify KYC compliance requirements
  previous_node: calculate_credit_score
  timeout_seconds: 60
- id: aml_screening
  name: AML Screening
  type: script
  script: "import json\n\n# Simulate AML screening\naml_result = {\n    \"aml_status\"\
    : \"cleared\",\n    \"watchlist_check\": \"no_matches\",\n    \"pep_screening\"\
    : \"not_identified\",\n    \"sanctions_check\": \"cleared\",\n    \"adverse_media\"\
    : \"no_hits\",\n    \"risk_rating\": \"low\",\n    \"compliance_score\": 96.8,\n\
    \    \"screening_date\": \"2024-07-15\",\n    \"manual_review_required\": False\n\
    }\n\nprint(f\"__OUTPUTS__ {json.dumps(aml_result)}\")\n"
  depends_on:
  - kyc_compliance_check
  description: Anti-Money Laundering compliance screening
  previous_node: kyc_compliance_check
  timeout_seconds: 90
- id: regulatory_compliance
  name: Regulatory Requirements Check
  type: script
  script: "import json\n\n# Simulate regulatory compliance check\nregulatory_result\
    \ = {\n    \"rbi_guidelines_met\": True,\n    \"lending_norms_compliant\": True,\n\
    \    \"documentation_complete\": True,\n    \"disclosure_requirements_met\": True,\n\
    \    \"consumer_protection_compliant\": True,\n    \"data_privacy_compliant\"\
    : True,\n    \"compliance_score\": 97.2,\n    \"audit_trail_complete\": True,\n\
    \    \"regulatory_risk\": \"minimal\"\n}\n\nprint(f\"__OUTPUTS__ {json.dumps(regulatory_result)}\"\
    )\n"
  depends_on:
  - aml_screening
  description: Check compliance with banking regulations
  previous_node: aml_screening
  timeout_seconds: 45
- id: internal_policy_check
  name: Internal Policy Compliance
  type: script
  script: "import json\n\n# Simulate internal policy check\npolicy_result = {\n  \
    \  \"loan_amount_within_limits\": True,\n    \"ltv_ratio_acceptable\": True,\n\
    \    \"income_criteria_met\": True,\n    \"age_criteria_met\": True,\n    \"employment_criteria_met\"\
    : True,\n    \"credit_score_threshold_met\": True,\n    \"geographic_restrictions_met\"\
    : True,\n    \"policy_compliance_score\": 95.5,\n    \"exceptions_required\":\
    \ [],\n    \"policy_version\": \"2024.1\"\n}\n\nprint(f\"__OUTPUTS__ {json.dumps(policy_result)}\"\
    )\n"
  depends_on:
  - regulatory_compliance
  description: Verify compliance with internal lending policies
  previous_node: regulatory_compliance
  timeout_seconds: 30
- id: compliance_consolidation
  name: Compliance Assessment Summary
  type: ai_agent
  config:
    input_format: json
    output_format: json
    model_client_id: compliance_reviewer
  depends_on:
  - internal_policy_check
  - kyc_compliance_check
  - aml_screening
  - regulatory_compliance
  description: AI-powered comprehensive compliance assessment
  user_message: 'Please analyze these compliance check results:


    KYC Compliance: ${kyc_compliance_check}

    AML Screening: ${aml_screening}

    Regulatory Compliance: ${regulatory_compliance}

    Internal Policy: ${internal_policy_check}


    Provide a consolidated compliance assessment.

    '
  previous_node: internal_policy_check
  system_message: 'You are a compliance officer specializing in loan underwriting
    compliance.


    Review all compliance check results and provide:

    1. Overall compliance status

    2. Risk assessment

    3. Any compliance gaps or concerns

    4. Recommendations for proceeding

    5. Compliance confidence score (1-100)


    Return a comprehensive compliance assessment in JSON format.

    '
- id: underwriting_decision_router
  name: Underwriting Decision Router
  type: conditional_router
  conditions:
  - name: high_risk_path
    route: decline_path
    condition: ${calculate_credit_score.internal_credit_score} < 600 || ${calculate_debt_to_income.debt_to_income_ratio}
      > 0.50
  - name: conditional_approval_path
    route: conditional_path
    condition: ${calculate_credit_score.internal_credit_score} >= 600 && ${calculate_credit_score.internal_credit_score}
      < 700
  - name: standard_approval_path
    route: approval_path
    condition: ${calculate_credit_score.internal_credit_score} >= 700
  depends_on:
  - compliance_consolidation
  - calculate_credit_score
  - calculate_debt_to_income
  description: Route to appropriate decision path based on assessments
  default_route: manual_review_path
  previous_node: compliance_consolidation
- id: final_underwriting_analysis
  name: Final Underwriting Decision Analysis
  type: ai_agent
  config:
    input_format: json
    output_format: json
    model_client_id: underwriting_analyst
  depends_on:
  - underwriting_decision_router
  - application_quality_check
  - consolidate_document_verification
  - income_assessment
  - calculate_credit_score
  - evaluate_default_risk
  - compliance_consolidation
  - calculate_debt_to_income
  description: Comprehensive AI analysis for final underwriting decision
  user_message: 'Please make the final underwriting decision based on:


    Application Quality: ${application_quality_check}

    Document Verification: ${consolidate_document_verification}

    Income Assessment: ${income_assessment}

    Credit Score: ${calculate_credit_score}

    Default Risk: ${evaluate_default_risk}

    Compliance: ${compliance_consolidation}

    Debt-to-Income: ${calculate_debt_to_income}


    Provide your final recommendation with detailed reasoning.

    '
  previous_node: underwriting_decision_router
  system_message: 'You are a senior underwriting manager making final loan decisions.


    Analyze all assessment results and provide:

    1. Final recommendation (APPROVE/CONDITIONAL/DECLINE)

    2. Loan terms and conditions

    3. Risk mitigation measures

    4. Reasoning for decision

    5. Confidence level (1-100)


    Consider all factors: income, credit, compliance, and overall risk profile.

    '
- id: generate_approval_terms
  name: Generate Loan Approval Terms
  type: script
  script: "import json\n\n# Generate loan terms based on assessment\nloan_terms =\
    \ {\n    \"decision\": \"APPROVED\",\n    \"loan_amount\": ${loan_amount},\n \
    \   \"interest_rate\": 8.5,\n    \"tenure_months\": 240,\n    \"monthly_emi\"\
    : round(${loan_amount} / 240 * 1.085, 2),\n    \"processing_fee\": ${loan_amount}\
    \ * 0.01,\n    \"conditions\": [\n        \"Property insurance required\",\n \
    \       \"Auto-debit for EMI payments\",\n        \"Annual income certificate\
    \ submission\"\n    ],\n    \"disbursement_conditions\": [\n        \"Property\
    \ registration documents\",\n        \"Insurance policy copy\",\n        \"Post-dated\
    \ cheques for EMI\"\n    ],\n    \"approval_date\": \"2024-07-15\",\n    \"offer_validity\"\
    : \"2024-08-15\"\n}\n\nprint(f\"__OUTPUTS__ {json.dumps(loan_terms)}\")\n"
  depends_on:
  - final_underwriting_analysis
  description: Generate loan terms and conditions for approved application
  previous_node: final_underwriting_analysis
  timeout_seconds: 60
  execute_on_routes:
  - approval_path
- id: generate_conditional_terms
  name: Generate Conditional Approval Terms
  type: script
  script: "import json\n\n# Generate conditional approval terms\nconditional_terms\
    \ = {\n    \"decision\": \"CONDITIONAL_APPROVAL\",\n    \"loan_amount\": ${loan_amount},\n\
    \    \"interest_rate\": 9.2,\n    \"tenure_months\": 240,\n    \"monthly_emi\"\
    : round(${loan_amount} / 240 * 1.092, 2),\n    \"processing_fee\": ${loan_amount}\
    \ * 0.015,\n    \"additional_conditions\": [\n        \"Co-applicant required\"\
    ,\n        \"Additional collateral security\",\n        \"Higher down payment\
    \ (30% instead of 20%)\",\n        \"Salary account maintenance for 2 years\"\n\
    \    ],\n    \"documents_required\": [\n        \"Co-applicant income documents\"\
    ,\n        \"Additional property papers\",\n        \"Enhanced bank statements\
    \ (12 months)\"\n    ],\n    \"approval_date\": \"2024-07-15\",\n    \"offer_validity\"\
    : \"2024-08-01\"\n}\n\nprint(f\"__OUTPUTS__ {json.dumps(conditional_terms)}\"\
    )\n"
  depends_on:
  - final_underwriting_analysis
  description: Generate conditional approval with additional requirements
  previous_node: final_underwriting_analysis
  timeout_seconds: 60
  execute_on_routes:
  - conditional_path
- id: generate_decline_notice
  name: Generate Loan Decline Notice
  type: script
  script: "import json\n\n# Generate decline notice\ndecline_notice = {\n    \"decision\"\
    : \"DECLINED\",\n    \"primary_reasons\": [\n        \"Insufficient credit score\"\
    ,\n        \"High debt-to-income ratio\",\n        \"Inadequate income documentation\"\
    \n    ],\n    \"decline_code\": \"RISK_001\",\n    \"decline_date\": \"2024-07-15\"\
    ,\n    \"appeal_process\": \"Customer can appeal within 30 days with additional\
    \ documentation\",\n    \"suggestions\": [\n        \"Improve credit score by\
    \ 6 months\",\n        \"Reduce existing debt burden\",\n        \"Consider lower\
    \ loan amount\",\n        \"Add co-applicant with good credit\"\n    ],\n    \"\
    reapplication_eligibility\": \"2024-12-15\"\n}\n\nprint(f\"__OUTPUTS__ {json.dumps(decline_notice)}\"\
    )\n"
  depends_on:
  - final_underwriting_analysis
  description: Generate loan decline notice with reasons
  previous_node: final_underwriting_analysis
  timeout_seconds: 30
  execute_on_routes:
  - decline_path
- id: flag_for_manual_review
  name: Auto-Approve Manual Review Cases
  type: script
  script: "import json\n\n# Auto-approve with specific conditions for manual review\
    \ cases\nauto_approval_result = {\n    \"decision\": \"AUTO_APPROVED_WITH_CONDITIONS\"\
    ,\n    \"loan_amount\": ${loan_amount},\n    \"interest_rate\": 9.0,  # Slightly\
    \ higher rate for complex cases\n    \"tenure_months\": 240,\n    \"monthly_emi\"\
    : round(${loan_amount} / 240 * 1.090, 2),\n    \"processing_fee\": ${loan_amount}\
    \ * 0.012,\n    \"approval_type\": \"automatic_complex_case\",\n    \"conditions\"\
    : [\n        \"Quarterly income verification for first year\",\n        \"Maintain\
    \ minimum account balance\",\n        \"No additional loans for 12 months\",\n\
    \        \"Property insurance with bank as beneficiary\"\n    ],\n    \"risk_mitigation\"\
    : [\n        \"Enhanced monitoring for first 6 months\",\n        \"Automatic\
    \ alerts for missed payments\",\n        \"Periodic credit score reviews\"\n \
    \   ],\n    \"approval_date\": \"2024-07-15\",\n    \"offer_validity\": \"2024-08-10\"\
    ,\n    \"notes\": \"Auto-approved with enhanced monitoring due to complex risk\
    \ factors\"\n}\n\nprint(f\"__OUTPUTS__ {json.dumps(auto_approval_result)}\")\n"
  depends_on:
  - final_underwriting_analysis
  description: Automatically approve applications that would have gone to manual review
  previous_node: final_underwriting_analysis
  timeout_seconds: 30
  execute_on_routes:
  - manual_review_path
- id: generate_final_output
  name: Generate Final Workflow Output
  type: script
  script: "import json\n\n# Determine final decision based on route\nfinal_output\
    \ = {\n    \"application_id\": \"${application_id}\",\n    \"processing_date\"\
    : \"2024-07-15\",\n    \"workflow_status\": \"completed\",\n    \"processing_time_minutes\"\
    : 45,\n    \"decision_summary\": {\n        \"credit_score\": ${calculate_credit_score.internal_credit_score},\n\
    \        \"debt_to_income_ratio\": ${calculate_debt_to_income.debt_to_income_ratio},\n\
    \        \"compliance_status\": \"compliant\",\n        \"risk_category\": \"\
    ${calculate_credit_score.risk_category}\"\n    },\n    \"next_steps\": [\n   \
    \     \"Customer notification\",\n        \"Document archival\",\n        \"Compliance\
    \ reporting\"\n    ]\n}\n\nprint(f\"__OUTPUTS__ {json.dumps(final_output)}\")\n"
  depends_on:
  - generate_approval_terms
  - generate_conditional_terms
  - generate_decline_notice
  - flag_for_manual_review
  - calculate_credit_score
  - calculate_debt_to_income
  description: Generate comprehensive workflow output
  previous_node:
  - generate_approval_terms
  - generate_conditional_terms
  - generate_decline_notice
  - flag_for_manual_review
  timeout_seconds: 30
- id: upload_to_supabase
  name: Upload Loan Data to Supabase
  type: script
  script: "import json\nimport os\nimport pandas as pd\nfrom datetime import datetime\n\
    import io\nfrom supabase import create_client, Client\n\n# Supabase configuration\n\
    SUPABASE_URL = \"https://mbauzgvitqvxceqanzjw.supabase.co\"\nSUPABASE_KEY = \"\
    eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Im1iYXV6Z3ZpdHF2eGNlcWFuemp3Iiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImlhdCI6MTc0Mzg2MDEwOCwiZXhwIjoyMDU5NDM2MTA4fQ.R71cWZoLuq2GNojkLSvhcXXt9rAW9PJ5O9V4g7vXvC0\"\
    \n\n# Helper function to safely extract values with defaults\ndef safe_get(expression,\
    \ default_value=\"N/A\"):\n    try:\n        return expression if expression is\
    \ not None else default_value\n    except:\n        return default_value\n\ndef\
    \ safe_get_numeric(expression, default_value=0):\n    try:\n        return float(expression)\
    \ if expression is not None else default_value\n    except:\n        return default_value\n\
    \ndef safe_get_boolean(expression, default_value=False):\n    try:\n        return\
    \ bool(expression) if expression is not None else default_value\n    except:\n\
    \        return default_value\n\ntry:\n    # Initialize Supabase client\n    supabase:\
    \ Client = create_client(SUPABASE_URL, SUPABASE_KEY)\n    \n    # Parse data from\
    \ previous nodes with safe extraction\n    try:\n        # Extract application\
    \ info\n        app_info = ${extract_application_info.customer_info}\n       \
    \ \n        # Extract document verification results\n        pan_verification\
    \ = ${verify_pan_card}\n        aadhaar_verification = ${verify_aadhaar}\n   \
    \     passport_verification = ${verify_passport}\n        address_verification\
    \ = ${verify_address_proof}\n        \n        # Extract income and financial\
    \ data\n        salary_analysis = ${analyze_salary_slips}\n        bank_analysis\
    \ = ${analyze_bank_statements}\n        tax_returns = ${verify_tax_returns}\n\
    \        employment_data = ${verify_employment}\n        debt_income_calc = ${calculate_debt_to_income}\n\
    \        \n        # Extract credit assessment\n        cibil_data = ${check_cibil_score}\n\
    \        credit_history = ${analyze_credit_history}\n        existing_loans =\
    \ ${assess_existing_loans}\n        credit_score_calc = ${calculate_credit_score}\n\
    \        \n        # Extract compliance data\n        kyc_compliance = ${kyc_compliance_check}\n\
    \        aml_screening = ${aml_screening}\n        regulatory_comp = ${regulatory_compliance}\n\
    \        policy_check = ${internal_policy_check}\n        \n        # Extract\
    \ final decision\n        final_decision = ${final_underwriting_analysis}\n  \
    \      workflow_output = ${generate_final_output}\n        \n        # Extract\
    \ individual document URLs\n        pan_card_url = os.environ.get('pan_card_url',\
    \ '').strip()\n        aadhaar_card_url = os.environ.get('aadhaar_card_url', '').strip()\n\
    \        passport_url = os.environ.get('passport_url', '').strip()\n        address_proof_url\
    \ = os.environ.get('address_proof_url', '').strip()\n        salary_slip_url =\
    \ os.environ.get('salary_slip_url', '').strip()\n        bank_statement_url =\
    \ os.environ.get('bank_statement_url', '').strip()\n        tax_return_url = os.environ.get('tax_return_url',\
    \ '').strip()\n        \n        # Extract URL validation results\n        pan_url_validation\
    \ = ${validate_pan_card_url}\n        aadhaar_url_validation = ${validate_aadhaar_card_url}\n\
    \        passport_url_validation = ${validate_passport_url}\n        address_proof_url_validation\
    \ = ${validate_address_proof_url}\n        \n        # Count provided documents\n\
    \        provided_documents = [\n            pan_card_url, aadhaar_card_url, passport_url,\
    \ address_proof_url,\n            salary_slip_url, bank_statement_url, tax_return_url\n\
    \        ]\n        total_provided_documents = len([url for url in provided_documents\
    \ if url])\n        \n        # Count valid URLs\n        valid_urls = sum([\n\
    \            pan_url_validation.get('url_valid', False),\n            aadhaar_url_validation.get('url_valid',\
    \ False),\n            passport_url_validation.get('url_valid', False),\n    \
    \        address_proof_url_validation.get('url_valid', False)\n        ])\n  \
    \      \n        print(\"Successfully parsed data from all previous nodes\")\n\
    \        print(f\"Total document URLs provided: {total_provided_documents}\")\n\
    \        print(f\"Valid URLs: {valid_urls}\")\n        print(f\"PAN card provided:\
    \ {bool(pan_card_url)}\")\n        print(f\"Aadhaar card provided: {bool(aadhaar_card_url)}\"\
    )\n        print(f\"Passport provided: {bool(passport_url)}\")\n        print(f\"\
    Address proof provided: {bool(address_proof_url)}\")\n        \n    except Exception\
    \ as e:\n        print(f\"Error parsing previous node data: {e}\")\n        #\
    \ Set fallback values\n        app_info = {}\n        pan_verification = {}\n\
    \        aadhaar_verification = {}\n        passport_verification = {}\n     \
    \   address_verification = {}\n        salary_analysis = {}\n        bank_analysis\
    \ = {}\n        tax_returns = {}\n        employment_data = {}\n        debt_income_calc\
    \ = {}\n        cibil_data = {}\n        credit_history = {}\n        existing_loans\
    \ = {}\n        credit_score_calc = {}\n        kyc_compliance = {}\n        aml_screening\
    \ = {}\n        regulatory_comp = {}\n        policy_check = {}\n        final_decision\
    \ = {}\n        workflow_output = {}\n        total_provided_documents = 0\n \
    \       pan_card_url = \"\"\n        aadhaar_card_url = \"\"\n        passport_url\
    \ = \"\"\n        address_proof_url = \"\"\n        salary_slip_url = \"\"\n \
    \       bank_statement_url = \"\"\n        tax_return_url = \"\"\n        pan_url_validation\
    \ = {}\n        aadhaar_url_validation = {}\n        passport_url_validation =\
    \ {}\n        address_proof_url_validation = {}\n        valid_urls = 0\n    \n\
    \    # Create comprehensive data structure for CSV\n    loan_data = {\n      \
    \  \"processing_timestamp\": datetime.now().isoformat(),\n        \"workflow_id\"\
    : \"loan_underwriting_workflow_real\",\n        \"workflow_version\": \"1.0\"\
    ,\n        \n        # Application Information\n        \"application_id\": safe_get(\"\
    ${application_id}\", \"UNKNOWN_APP\"),\n        \"requested_amount\": safe_get_numeric(\"\
    ${loan_amount}\", 0),\n        \"processing_date\": datetime.now().strftime(\"\
    %Y-%m-%d\"),\n        \"workflow_status\": \"completed\",\n        \n        #\
    \ Personal Information\n        \"customer_id\": safe_get(app_info.get(\"customer_id\"\
    ), \"UNKNOWN_CUSTOMER\"),\n        \"applicant_name\": safe_get(app_info.get(\"\
    full_name\"), \"Unknown Applicant\"),\n        \"email\": safe_get(app_info.get(\"\
    email\"), \"unknown@email.com\"),\n        \"phone\": safe_get(app_info.get(\"\
    phone\"), \"Unknown Phone\"),\n        \"address\": safe_get(app_info.get(\"address\"\
    ), \"Unknown Address\"),\n        \"employment_status\": safe_get(app_info.get(\"\
    employment_status\"), \"Unknown\"),\n        \"annual_income\": safe_get_numeric(app_info.get(\"\
    annual_income\"), 0),\n        \"loan_purpose\": safe_get(app_info.get(\"loan_purpose\"\
    ), \"Unknown\"),\n        \n        # Document Verification\n        \"pan_number\"\
    : safe_get(pan_verification.get(\"pan_number\"), \"UNKNOWN_PAN\"),\n        \"\
    pan_verified\": safe_get_boolean(pan_verification.get(\"is_valid\"), False),\n\
    \        \"pan_verification_score\": safe_get_numeric(pan_verification.get(\"\
    verification_score\"), 0),\n        \"pan_name_match\": safe_get_boolean(pan_verification.get(\"\
    name_match\"), False),\n        \"pan_verified_name\": safe_get(pan_verification.get(\"\
    verified_name\"), \"N/A\"),\n        \"pan_father_name\": safe_get(pan_verification.get(\"\
    father_name\"), \"N/A\"),\n        \"pan_date_of_birth\": safe_get(pan_verification.get(\"\
    date_of_birth\"), \"N/A\"),\n        \"pan_document_url\": safe_get(pan_verification.get(\"\
    document_url\"), \"N/A\"),\n        \n        \"aadhaar_masked\": safe_get(aadhaar_verification.get(\"\
    aadhaar_masked\"), \"XXXX-XXXX-XXXX\"),\n        \"aadhaar_verified\": safe_get_boolean(aadhaar_verification.get(\"\
    is_valid\"), False),\n        \"aadhaar_verification_score\": safe_get_numeric(aadhaar_verification.get(\"\
    verification_score\"), 0),\n        \"aadhaar_address_match\": safe_get_boolean(aadhaar_verification.get(\"\
    address_match\"), False),\n        \"aadhaar_name\": safe_get(aadhaar_verification.get(\"\
    name_on_aadhaar\"), \"N/A\"),\n        \"aadhaar_address\": safe_get(aadhaar_verification.get(\"\
    address_on_aadhaar\"), \"N/A\"),\n        \"aadhaar_date_of_birth\": safe_get(aadhaar_verification.get(\"\
    date_of_birth\"), \"N/A\"),\n        \"aadhaar_gender\": safe_get(aadhaar_verification.get(\"\
    gender\"), \"N/A\"),\n        \"aadhaar_document_url\": safe_get(aadhaar_verification.get(\"\
    document_url\"), \"N/A\"),\n        \n        \"passport_number\": safe_get(passport_verification.get(\"\
    passport_number\"), \"Not Provided\"),\n        \"passport_verified\": safe_get_boolean(passport_verification.get(\"\
    is_valid\"), False),\n        \"passport_verification_score\": safe_get_numeric(passport_verification.get(\"\
    verification_score\"), 0),\n        \"passport_name\": safe_get(passport_verification.get(\"\
    name_on_passport\"), \"N/A\"),\n        \"passport_nationality\": safe_get(passport_verification.get(\"\
    nationality\"), \"N/A\"),\n        \"passport_expiry_date\": safe_get(passport_verification.get(\"\
    expiry_date\"), \"N/A\"),\n        \"passport_issuing_authority\": safe_get(passport_verification.get(\"\
    issuing_authority\"), \"N/A\"),\n        \"passport_document_url\": safe_get(passport_verification.get(\"\
    document_url\"), \"N/A\"),\n        \n        \"address_proof_type\": safe_get(address_verification.get(\"\
    document_type\"), \"Unknown\"),\n        \"address_proof_verified\": safe_get_boolean(address_verification.get(\"\
    is_valid\"), False),\n        \"address_proof_verification_score\": safe_get_numeric(address_verification.get(\"\
    verification_score\"), 0),\n        \"address_proof_confirmed\": safe_get(address_verification.get(\"\
    address_confirmed\"), \"N/A\"),\n        \"address_proof_name\": safe_get(address_verification.get(\"\
    name_on_document\"), \"N/A\"),\n        \"address_proof_date\": safe_get(address_verification.get(\"\
    document_date\"), \"N/A\"),\n        \"address_proof_issuing_authority\": safe_get(address_verification.get(\"\
    issuing_authority\"), \"N/A\"),\n        \"address_proof_document_url\": safe_get(address_verification.get(\"\
    document_url\"), \"N/A\"),\n        \n        # Document Processing Information\n\
    \        \"total_provided_documents\": total_provided_documents,\n        \"pan_card_provided\"\
    : bool(pan_card_url),\n        \"aadhaar_card_provided\": bool(aadhaar_card_url),\n\
    \        \"passport_provided\": bool(passport_url),\n        \"address_proof_provided\"\
    : bool(address_proof_url),\n        \"documents_processed_successfully\": sum([\n\
    \            pan_verification.get(\"extracted_from_ocr\", False),\n          \
    \  aadhaar_verification.get(\"extracted_from_ocr\", False),\n            passport_verification.get(\"\
    extracted_from_ocr\", False),\n            address_verification.get(\"extracted_from_ocr\"\
    , False)\n        ]),\n        \"average_ocr_confidence\": safe_get_numeric((\n\
    \            pan_verification.get(\"ocr_confidence\", 0) +\n            aadhaar_verification.get(\"\
    ocr_confidence\", 0) +\n            passport_verification.get(\"ocr_confidence\"\
    , 0) +\n            address_verification.get(\"ocr_confidence\", 0)\n        )\
    \ / 4, 0),\n        \n        # Individual Document OCR Status\n        \"pan_card_ocr_extracted\"\
    : safe_get_boolean(pan_verification.get(\"extracted_from_ocr\"), False),\n   \
    \     \"pan_card_ocr_confidence\": safe_get_numeric(pan_verification.get(\"ocr_confidence\"\
    ), 0),\n        \"aadhaar_ocr_extracted\": safe_get_boolean(aadhaar_verification.get(\"\
    extracted_from_ocr\"), False),\n        \"aadhaar_ocr_confidence\": safe_get_numeric(aadhaar_verification.get(\"\
    ocr_confidence\"), 0),\n        \"passport_ocr_extracted\": safe_get_boolean(passport_verification.get(\"\
    extracted_from_ocr\"), False),\n        \"passport_ocr_confidence\": safe_get_numeric(passport_verification.get(\"\
    ocr_confidence\"), 0),\n        \"address_proof_ocr_extracted\": safe_get_boolean(address_verification.get(\"\
    extracted_from_ocr\"), False),\n        \"address_proof_ocr_confidence\": safe_get_numeric(address_verification.get(\"\
    ocr_confidence\"), 0),\n        \n        # Document URLs Provided\n        \"\
    pan_card_url_provided\": pan_card_url,\n        \"aadhaar_card_url_provided\"\
    : aadhaar_card_url,\n        \"passport_url_provided\": passport_url,\n      \
    \  \"address_proof_url_provided\": address_proof_url,\n        \"salary_slip_url_provided\"\
    : salary_slip_url,\n        \"bank_statement_url_provided\": bank_statement_url,\n\
    \        \"tax_return_url_provided\": tax_return_url,\n        \n        # URL\
    \ Validation Results\n        \"pan_url_valid\": safe_get_boolean(pan_url_validation.get('url_valid'),\
    \ False),\n        \"pan_url_accessible\": safe_get_boolean(pan_url_validation.get('url_accessible'),\
    \ False),\n        \"pan_url_content_type\": safe_get(pan_url_validation.get('content_type'),\
    \ 'N/A'),\n        \"pan_url_file_size\": safe_get_numeric(pan_url_validation.get('file_size'),\
    \ 0),\n        \"pan_url_status_code\": safe_get_numeric(pan_url_validation.get('status_code'),\
    \ 0),\n        \n        \"aadhaar_url_valid\": safe_get_boolean(aadhaar_url_validation.get('url_valid'),\
    \ False),\n        \"aadhaar_url_accessible\": safe_get_boolean(aadhaar_url_validation.get('url_accessible'),\
    \ False),\n        \"aadhaar_url_content_type\": safe_get(aadhaar_url_validation.get('content_type'),\
    \ 'N/A'),\n        \"aadhaar_url_file_size\": safe_get_numeric(aadhaar_url_validation.get('file_size'),\
    \ 0),\n        \"aadhaar_url_status_code\": safe_get_numeric(aadhaar_url_validation.get('status_code'),\
    \ 0),\n        \n        \"passport_url_valid\": safe_get_boolean(passport_url_validation.get('url_valid'),\
    \ False),\n        \"passport_url_accessible\": safe_get_boolean(passport_url_validation.get('url_accessible'),\
    \ False),\n        \"passport_url_content_type\": safe_get(passport_url_validation.get('content_type'),\
    \ 'N/A'),\n        \"passport_url_file_size\": safe_get_numeric(passport_url_validation.get('file_size'),\
    \ 0),\n        \"passport_url_status_code\": safe_get_numeric(passport_url_validation.get('status_code'),\
    \ 0),\n        \n        \"address_proof_url_valid\": safe_get_boolean(address_proof_url_validation.get('url_valid'),\
    \ False),\n        \"address_proof_url_accessible\": safe_get_boolean(address_proof_url_validation.get('url_accessible'),\
    \ False),\n        \"address_proof_url_content_type\": safe_get(address_proof_url_validation.get('content_type'),\
    \ 'N/A'),\n        \"address_proof_url_file_size\": safe_get_numeric(address_proof_url_validation.get('file_size'),\
    \ 0),\n        \"address_proof_url_status_code\": safe_get_numeric(address_proof_url_validation.get('status_code'),\
    \ 0),\n        \n        \"total_valid_urls\": valid_urls,\n        \n       \
    \ # Income and Employment Information\n        \"monthly_gross_salary\": safe_get_numeric(salary_analysis.get(\"\
    monthly_gross\"), 0),\n        \"monthly_net_salary\": safe_get_numeric(salary_analysis.get(\"\
    monthly_net\"), 0),\n        \"annual_gross_income\": safe_get_numeric(salary_analysis.get(\"\
    annual_gross\"), 0),\n        \"annual_net_income\": safe_get_numeric(salary_analysis.get(\"\
    annual_net\"), 0),\n        \"employer_name\": safe_get(salary_analysis.get(\"\
    employer\"), \"Unknown Company\"),\n        \"employment_duration\": safe_get(salary_analysis.get(\"\
    employment_duration\"), \"Unknown\"),\n        \"salary_consistency\": safe_get_boolean(salary_analysis.get(\"\
    salary_consistency\"), False),\n        \"recent_increment\": safe_get_boolean(salary_analysis.get(\"\
    recent_increment\"), False),\n        \"employment_confirmed\": safe_get_boolean(employment_data.get(\"\
    employment_confirmed\"), False),\n        \"designation\": safe_get(employment_data.get(\"\
    designation\"), \"Unknown Position\"),\n        \"employment_type\": safe_get(employment_data.get(\"\
    employment_type\"), \"Unknown\"),\n        \"job_stability_score\": safe_get_numeric(employment_data.get(\"\
    job_stability_score\"), 0),\n        \n        # Financial Analysis\n        \"\
    average_monthly_credits\": safe_get_numeric(bank_analysis.get(\"average_monthly_credits\"\
    ), 0),\n        \"account_balance_trend\": safe_get(bank_analysis.get(\"account_balance_trend\"\
    ), \"Unknown\"),\n        \"minimum_balance\": safe_get_numeric(bank_analysis.get(\"\
    minimum_balance\"), 0),\n        \"maximum_balance\": safe_get_numeric(bank_analysis.get(\"\
    maximum_balance\"), 0),\n        \"bounced_transactions\": safe_get_numeric(bank_analysis.get(\"\
    bounced_transactions\"), 0),\n        \"loan_emis_detected\": safe_get_numeric(bank_analysis.get(\"\
    loan_emis_detected\"), 0),\n        \"financial_discipline_score\": safe_get_numeric(bank_analysis.get(\"\
    financial_discipline_score\"), 0),\n        \"declared_tax_income\": safe_get_numeric(tax_returns.get(\"\
    declared_income\"), 0),\n        \"tax_paid\": safe_get_numeric(tax_returns.get(\"\
    tax_paid\"), 0),\n        \"tax_compliance_score\": safe_get_numeric(tax_returns.get(\"\
    tax_compliance_score\"), 0),\n        \"debt_to_income_ratio\": safe_get_numeric(debt_income_calc.get(\"\
    debt_to_income_ratio\"), 0),\n        \"existing_debt_payments\": safe_get_numeric(debt_income_calc.get(\"\
    existing_debt_payments\"), 0),\n        \"proposed_emi\": safe_get_numeric(debt_income_calc.get(\"\
    proposed_emi\"), 0),\n        \"total_debt_payments\": safe_get_numeric(debt_income_calc.get(\"\
    total_debt_payments\"), 0),\n        \"ratio_acceptable\": safe_get_boolean(debt_income_calc.get(\"\
    ratio_acceptable\"), False),\n        \n        # Credit Information\n       \
    \ \"cibil_score\": safe_get_numeric(cibil_data.get(\"credit_score\"), 0),\n  \
    \      \"credit_score_range\": safe_get(cibil_data.get(\"score_range\"), \"Unknown\"\
    ),\n        \"credit_history_length\": safe_get(cibil_data.get(\"credit_history_length\"\
    ), \"Unknown\"),\n        \"total_accounts\": safe_get_numeric(cibil_data.get(\"\
    total_accounts\"), 0),\n        \"active_accounts\": safe_get_numeric(cibil_data.get(\"\
    active_accounts\"), 0),\n        \"credit_utilization\": safe_get_numeric(cibil_data.get(\"\
    credit_utilization\"), 0),\n        \"payment_history\": safe_get(cibil_data.get(\"\
    payment_history\"), \"Unknown\"),\n        \"recent_inquiries\": safe_get_numeric(cibil_data.get(\"\
    recent_inquiries\"), 0),\n        \"internal_credit_score\": safe_get_numeric(credit_score_calc.get(\"\
    internal_credit_score\"), 0),\n        \"score_acceptable\": safe_get_boolean(credit_score_calc.get(\"\
    score_acceptable\"), False),\n        \"risk_category\": safe_get(credit_score_calc.get(\"\
    risk_category\"), \"unknown\"),\n        \"total_outstanding_loans\": safe_get_numeric(existing_loans.get(\"\
    total_outstanding\"), 0),\n        \"number_of_existing_loans\": safe_get_numeric(existing_loans.get(\"\
    number_of_loans\"), 0),\n        \"total_monthly_emi\": safe_get_numeric(existing_loans.get(\"\
    total_monthly_emi\"), 0),\n        \"loan_burden_ratio\": safe_get_numeric(existing_loans.get(\"\
    loan_burden_ratio\"), 0),\n        \"repayment_track_record\": safe_get(existing_loans.get(\"\
    repayment_track_record\"), \"Unknown\"),\n        \n        # Compliance Status\n\
    \        \"kyc_status\": safe_get(kyc_compliance.get(\"kyc_status\"), \"unknown\"\
    ),\n        \"kyc_compliance_score\": safe_get_numeric(kyc_compliance.get(\"compliance_score\"\
    ), 0),\n        \"identity_verified\": safe_get_boolean(kyc_compliance.get(\"\
    identity_verified\"), False),\n        \"address_verified\": safe_get_boolean(kyc_compliance.get(\"\
    address_verified\"), False),\n        \"income_verified\": safe_get_boolean(kyc_compliance.get(\"\
    income_verified\"), False),\n        \"documents_complete\": safe_get_boolean(kyc_compliance.get(\"\
    documents_complete\"), False),\n        \"aml_status\": safe_get(aml_screening.get(\"\
    aml_status\"), \"unknown\"),\n        \"aml_compliance_score\": safe_get_numeric(aml_screening.get(\"\
    compliance_score\"), 0),\n        \"watchlist_check\": safe_get(aml_screening.get(\"\
    watchlist_check\"), \"unknown\"),\n        \"pep_screening\": safe_get(aml_screening.get(\"\
    pep_screening\"), \"unknown\"),\n        \"sanctions_check\": safe_get(aml_screening.get(\"\
    sanctions_check\"), \"unknown\"),\n        \"manual_review_required\": safe_get_boolean(aml_screening.get(\"\
    manual_review_required\"), True),\n        \"rbi_guidelines_met\": safe_get_boolean(regulatory_comp.get(\"\
    rbi_guidelines_met\"), False),\n        \"lending_norms_compliant\": safe_get_boolean(regulatory_comp.get(\"\
    lending_norms_compliant\"), False),\n        \"regulatory_compliance_score\":\
    \ safe_get_numeric(regulatory_comp.get(\"compliance_score\"), 0),\n        \"\
    internal_policy_compliance_score\": safe_get_numeric(policy_check.get(\"policy_compliance_score\"\
    ), 0),\n        \n        # Final Decision\n        \"final_decision\": safe_get(\"\
    PENDING\", \"PENDING\"),  # Default to PENDING for safety\n        \"decision_confidence\"\
    : safe_get_numeric(90.0, 0),\n        \"overall_processing_status\": safe_get(workflow_output.get(\"\
    workflow_status\"), \"completed\"),\n        \"processing_time_minutes\": safe_get_numeric(workflow_output.get(\"\
    processing_time_minutes\"), 0),\n        \n        # Metadata\n        \"processed_by\"\
    : \"automated_workflow\",\n        \"processing_node\": \"supabase_upload\"\n\
    \    }\n    \n    # Create DataFrame\n    df = pd.DataFrame([loan_data])\n   \
    \ \n    # Generate CSV content\n    csv_buffer = io.StringIO()\n    df.to_csv(csv_buffer,\
    \ index=False)\n    csv_content = csv_buffer.getvalue()\n    \n    # Generate\
    \ unique filename\n    timestamp = datetime.now().strftime(\"%Y%m%d_%H%M%S\")\n\
    \    app_id = loan_data[\"application_id\"].replace(\"/\", \"_\").replace(\"\\\
    \\\", \"_\")\n    filename = f\"loan_underwriting_{app_id}_{timestamp}.csv\"\n\
    \    \n    print(f\"Generated CSV file: {filename}\")\n    print(f\"CSV content\
    \ size: {len(csv_content)} characters\")\n    print(f\"Data rows: {len(df)}\"\
    )\n    print(f\"Columns: {len(df.columns)}\")\n    \n    # Upload to Supabase\
    \ Storage\n    try:\n        # Upload file to the loan-approval bucket\n     \
    \   storage_response = supabase.storage.from_(\"loan-approval\").upload(\n   \
    \         filename, \n            csv_content.encode('utf-8'),\n            file_options={\n\
    \                \"content-type\": \"text/csv\",\n                \"cache-control\"\
    : \"3600\"\n            }\n        )\n        \n        print(f\"File uploaded\
    \ successfully to Supabase\")\n        \n        # Get public URL\n        public_url\
    \ = supabase.storage.from_(\"loan-approval\").get_public_url(filename)\n     \
    \   \n        upload_result = {\n            \"upload_status\": \"success\",\n\
    \            \"bucket_name\": \"loan-approval\",\n            \"filename\": filename,\n\
    \            \"file_size_bytes\": len(csv_content.encode('utf-8')),\n        \
    \    \"public_url\": public_url,\n            \"upload_timestamp\": datetime.now().isoformat(),\n\
    \            \"records_uploaded\": len(df),\n            \"columns_count\": len(df.columns),\n\
    \            \"data_summary\": {\n                \"application_id\": loan_data[\"\
    application_id\"],\n                \"applicant_name\": loan_data[\"applicant_name\"\
    ],\n                \"requested_amount\": loan_data[\"requested_amount\"],\n \
    \               \"internal_credit_score\": loan_data[\"internal_credit_score\"\
    ],\n                \"debt_to_income_ratio\": loan_data[\"debt_to_income_ratio\"\
    ],\n                \"final_decision\": loan_data[\"final_decision\"],\n     \
    \           \"cibil_score\": loan_data[\"cibil_score\"],\n                \"risk_category\"\
    : loan_data[\"risk_category\"],\n                \"total_provided_documents\"\
    : loan_data[\"total_provided_documents\"],\n                \"documents_processed_successfully\"\
    : loan_data[\"documents_processed_successfully\"],\n                \"average_ocr_confidence\"\
    : loan_data[\"average_ocr_confidence\"],\n                \"pan_card_provided\"\
    : loan_data[\"pan_card_provided\"],\n                \"aadhaar_card_provided\"\
    : loan_data[\"aadhaar_card_provided\"],\n                \"passport_provided\"\
    : loan_data[\"passport_provided\"],\n                \"address_proof_provided\"\
    : loan_data[\"address_proof_provided\"],\n                \"pan_verified\": loan_data[\"\
    pan_verified\"],\n                \"aadhaar_verified\": loan_data[\"aadhaar_verified\"\
    ],\n                \"passport_verified\": loan_data[\"passport_verified\"],\n\
    \                \"address_proof_verified\": loan_data[\"address_proof_verified\"\
    ]\n            },\n            \"csv_structure\": {\n                \"columns\"\
    : list(df.columns),\n                \"column_count\": len(df.columns),\n    \
    \            \"row_count\": len(df),\n                \"key_metrics\": {\n   \
    \                 \"credit_score\": loan_data[\"internal_credit_score\"],\n  \
    \                  \"cibil_score\": loan_data[\"cibil_score\"],\n            \
    \        \"risk_category\": loan_data[\"risk_category\"],\n                  \
    \  \"kyc_compliant\": loan_data[\"kyc_status\"] == \"compliant\",\n          \
    \          \"aml_cleared\": loan_data[\"aml_status\"] == \"cleared\",\n      \
    \              \"ratio_acceptable\": loan_data[\"ratio_acceptable\"],\n      \
    \              \"documents_provided\": loan_data[\"total_provided_documents\"\
    ],\n                    \"documents_processed\": loan_data[\"documents_processed_successfully\"\
    ],\n                    \"avg_ocr_confidence\": loan_data[\"average_ocr_confidence\"\
    ],\n                    \"pan_provided\": loan_data[\"pan_card_provided\"],\n\
    \                    \"aadhaar_provided\": loan_data[\"aadhaar_card_provided\"\
    ]\n                }\n            }\n        }\n        \n        print(f\"Upload\
    \ completed successfully\")\n        print(f\"Public URL: {public_url}\")\n  \
    \      print(f\"Records uploaded: {len(df)}\")\n        \n    except Exception\
    \ as upload_error:\n        print(f\"Error uploading to Supabase: {upload_error}\"\
    )\n        \n        # Return error result\n        upload_result = {\n      \
    \      \"upload_status\": \"failed\",\n            \"error_message\": str(upload_error),\n\
    \            \"bucket_name\": \"loan-approval\",\n            \"filename\": filename,\n\
    \            \"attempted_upload_timestamp\": datetime.now().isoformat(),\n   \
    \         \"records_prepared\": len(df),\n            \"data_summary\": {\n  \
    \              \"application_id\": loan_data[\"application_id\"],\n          \
    \      \"applicant_name\": loan_data[\"applicant_name\"],\n                \"\
    requested_amount\": loan_data[\"requested_amount\"],\n                \"final_decision\"\
    : loan_data[\"final_decision\"],\n                \"total_provided_documents\"\
    : loan_data[\"total_provided_documents\"],\n                \"documents_processed_successfully\"\
    : loan_data[\"documents_processed_successfully\"],\n                \"pan_card_provided\"\
    : loan_data[\"pan_card_provided\"],\n                \"aadhaar_card_provided\"\
    : loan_data[\"aadhaar_card_provided\"]\n            }\n        }\n\nexcept Exception\
    \ as e:\n    print(f\"Critical error in Supabase upload process: {e}\")\n    \n\
    \    # Return critical error result\n    upload_result = {\n        \"upload_status\"\
    : \"critical_error\",\n        \"error_message\": str(e),\n        \"bucket_name\"\
    : \"loan-approval\",\n        \"error_timestamp\": datetime.now().isoformat(),\n\
    \        \"workflow_id\": \"loan_underwriting_workflow_real\",\n        \"application_id\"\
    : \"${application_id}\"\n    }\n\nprint(f\"__OUTPUTS__ {json.dumps(upload_result)}\"\
    )\n"
  packages:
  - supabase==2.4.4
  - pandas==2.0.3
  - python-dotenv==1.0.0
  depends_on:
  - generate_final_output
  - final_underwriting_analysis
  - calculate_credit_score
  - calculate_debt_to_income
  - compliance_consolidation
  - consolidate_document_verification
  - income_assessment
  - verify_pan_card
  - verify_aadhaar
  - verify_passport
  - verify_address_proof
  - analyze_salary_slips
  - analyze_bank_statements
  - verify_tax_returns
  - verify_employment
  - check_cibil_score
  - analyze_credit_history
  - assess_existing_loans
  - kyc_compliance_check
  - aml_screening
  - regulatory_compliance
  - internal_policy_check
  description: Upload comprehensive loan underwriting data to Supabase storage bucket
    as CSV
  previous_node: generate_final_output
  timeout_seconds: 120
inputs:
- name: application_id
  type: string
  required: true
  description: Unique loan application identifier
- name: loan_amount
  type: integer
  required: true
  description: Requested loan amount
- name: applicant_data
  type: object
  required: true
  description: Applicant personal information
- name: pan_card_url
  type: string
  required: false
  description: URL of PAN card document (optional)
- name: aadhaar_card_url
  type: string
  required: false
  description: URL of Aadhaar card document (optional)
- name: passport_url
  type: string
  required: false
  description: URL of passport document (optional)
- name: address_proof_url
  type: string
  required: false
  description: URL of address proof document (optional)
- name: salary_slip_url
  type: string
  required: false
  description: URL of salary slip document (optional)
- name: bank_statement_url
  type: string
  required: false
  description: URL of bank statement document (optional)
- name: tax_return_url
  type: string
  required: false
  description: URL of tax return document (optional)
- name: min_credit_score
  type: integer
  default: 650
  required: false
  description: Minimum acceptable credit score
- name: max_debt_to_income_ratio
  type: float
  default: 0.4
  required: false
  description: Maximum debt-to-income ratio allowed
- name: min_income_multiplier
  type: float
  default: 3.0
  required: false
  description: Minimum income multiplier for loan approval
- name: compliance_threshold
  type: float
  default: 0.95
  required: false
  description: Minimum compliance score threshold
outputs:
  final_decision:
    type: object
    source: final_underwriting_analysis
    description: Final underwriting decision with terms
  compliance_status:
    type: object
    source: compliance_consolidation
    description: Overall compliance assessment
  credit_assessment:
    type: object
    source: calculate_credit_score
    description: Comprehensive credit assessment results
  processing_summary:
    type: object
    source: generate_final_output
    description: Complete workflow processing summary
  supabase_upload_result:
    type: object
    source: upload_to_supabase
    description: Result of CSV upload to Supabase storage bucket
version: '1.0'
metadata:
  author: Financial Services Team
  version: '1.0'
  environment: production
  last_updated: '2024-07-15'
  compliance_certified: true
namespace: financial
description: End-to-end loan underwriting process with document verification, income
  analysis, credit assessment, and compliance checks
model_clients:
- id: underwriting_analyst
  config:
    model: gpt-4o-mini
    api_key: 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
    max_tokens: 2000
    temperature: 0.1
  provider: openai
- id: compliance_reviewer
  config:
    model: gpt-4o-mini
    api_key: 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
    max_tokens: 1500
    temperature: 0.0
  provider: openai
- id: risk_assessor
  config:
    model: gpt-4o-mini
    api_key: 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
    max_tokens: 1000
    temperature: 0.2
  provider: openai
timeout_seconds: 3600
Execution ID Status Started Duration Actions
c8dbdc92... COMPLETED 2025-07-16
08:08:51
N/A View
d8ff0b68... COMPLETED 2025-07-16
07:57:05
N/A View
50412097... COMPLETED 2025-07-16
07:34:40
N/A View
3d4eb0f8... COMPLETED 2025-07-16
07:33:30
N/A View
50d98b6d... COMPLETED 2025-07-16
07:32:34
N/A View
21bf605f... COMPLETED 2025-07-16
07:24:36
N/A View