How to Build a underwriting Agent Using CrewAI in TypeScript for retail banking

By Cyprian AaronsUpdated 2026-04-21
underwritingcrewaitypescriptretail-banking

An underwriting agent in retail banking takes a loan application, gathers the right facts, checks them against policy, and produces a decision package for a human underwriter or an automated approval flow. It matters because underwriting is where you control credit risk, regulatory exposure, and turnaround time; if you get the workflow wrong, you either lose good customers or approve bad risk.

Architecture

  • Application intake layer

    • Normalizes applicant data from CRM, LOS, PDF statements, and bureau responses.
    • Validates required fields before any agent work starts.
  • Policy retrieval component

    • Pulls product rules, eligibility thresholds, and exception policies from a controlled source.
    • Keeps the agent aligned with current underwriting policy.
  • CrewAI orchestration

    • Uses Agent, Task, and Crew to split work into analysis steps.
    • One agent can assess income stability, another can evaluate risk flags, and a final one can compile the decision memo.
  • Tooling layer

    • Exposes bank-approved tools for bureau lookup, affordability calculation, KYC checks, and document extraction.
    • Keeps external access auditable and constrained.
  • Decision output service

    • Produces a structured underwriting recommendation: approve, refer, or decline.
    • Stores rationale for audit and model governance.
  • Compliance logging

    • Captures inputs, tool calls, outputs, timestamps, and policy version.
    • Supports audit trails, explainability reviews, and dispute handling.

Implementation

  1. Install CrewAI and define your banking tools

    In TypeScript projects, keep your tools explicit. For retail banking, each tool should map to an approved internal service or a read-only external dependency.

    import { Tool } from "@crewai/core";
    
    export const calculateDTITool = new Tool({
      name: "calculate_dti",
      description: "Calculate debt-to-income ratio from verified income and obligations",
      execute: async ({ monthlyIncome, monthlyDebtPayments }: { monthlyIncome: number; monthlyDebtPayments: number }) => {
        if (monthlyIncome <= 0) throw new Error("monthlyIncome must be > 0");
        return {
          dti: monthlyDebtPayments / monthlyIncome,
        };
      },
    });
    
    export const kycCheckTool = new Tool({
      name: "kyc_check",
      description: "Check KYC status using internal compliance service",
      execute: async ({ customerId }: { customerId: string }) => {
        // Replace with internal API call
        return {
          customerId,
          kycStatus: "passed",
          sanctionsHit: false,
          pepHit: false,
        };
      },
    });
    
    export const bureauFetchTool = new Tool({
      name: "fetch_credit_bureau",
      description: "Fetch credit bureau summary for underwriting",
      execute: async ({ ssnLast4 }: { ssnLast4: string }) => {
        return {
          score: 721,
          delinquencies12M: 0,
          utilization: 0.28,
          inquiries6M: 2,
        };
      },
    });
    
  2. Create agents for analysis and decisioning

    Use separate agents for separation of concerns. That gives you better traceability than one giant prompt that does everything.

    import { Agent } from "@crewai/core";
    import { calculateDTITool, kycCheckTool, bureauFetchTool } from "./tools";
    
    export const riskAnalyst = new Agent({
      role: "Retail Banking Risk Analyst",
      goal: "Assess applicant risk using verified financial data and bank policy",
      backstory:
        "You are a conservative retail lending analyst who prioritizes policy adherence, affordability, and fraud signals.",
      tools: [calculateDTITool, bureauFetchTool],
      verbose: true,
    });
    
    export const complianceReviewer = new Agent({
      role: "Bank Compliance Reviewer",
      goal: "Validate KYC/AML status and identify policy exceptions",
      backstory:
        "You ensure every recommendation is compliant with retail banking rules and auditable.",
      tools: [kycCheckTool],
      verbose: true,
    });
    
    export const decisionWriter = new Agent({
      role: "Underwriting Decision Writer",
      goal: "Summarize findings into an underwriting recommendation with rationale",
      backstory:
        "You produce concise decision memos for human underwriters and audit teams.",
      verbose: true,
    });
    
  3. Define tasks with structured outputs

    Keep task outputs structured enough to store in your loan origination system. For banking workflows, free-form text alone is not enough.

    import { Task } from "@crewai/core";
    import { riskAnalyst, complianceReviewer, decisionWriter } from "./agents";
    
    export const assessRiskTask = new Task({
      description:
        "Analyze applicant affordability and credit quality using verified income of $8500/month and debt obligations of $2450/month. Return DTI analysis plus key risk flags.",
      expectedOutput:
        "A JSON-like summary containing dti, credit observations, and recommendation inputs.",
      agent: riskAnalyst,
    });
    
    export const complianceTask = new Task({
      description:
        "Verify KYC status for customerId CUST-10492. Identify any sanctions or PEP concerns.",
      expectedOutput:
        "A JSON-like summary containing kycStatus, sanctionsHit, pepHit, and compliance notes.",
      agent: complianceReviewer,
    });
    
    export const memoTask = new Task({
      description:
        "Write the final underwriting memo using prior findings. Recommend approve, refer, or decline with reasons.",
      expectedOutput:
        "A concise underwriting memo suitable for audit review.",
      agent: decisionWriter,
      context: [assessRiskTask as any, complianceTask as any],
    });
    
  4. Run the crew and persist the result

    The core pattern is Crew -> kickoff() -> persist output. In production you should wrap this behind an API endpoint or queue consumer.

    import { Crew } from "@crewai/core";
    import { assessRiskTask, complianceTask, memoTask } from "./tasks";
    
    async function main() {
      const crew = new Crew({
        agents: [],
        tasks: [assessRiskTask, complianceTask, memoTask],
        verbose: true,
        processType: "sequential",
      });
    
       const result = await crew.kickoff();
    
       console.log("Underwriting result:", result);
       // Persist result + policy version + request metadata to your audit store here
     }
    
     main().catch((err) => {
       console.error(err);
       process.exit(1);
     });
    

Production Considerations

  • Data residency

    • Keep applicant PII inside your approved region.
    • If CrewAI calls external services or LLMs are involved downstream, ensure model hosting stays within your regulatory boundary.
  • Auditability

    • Log every task input/output pair with timestamps and policy version.
    • Store tool invocation metadata so reviewers can reconstruct how a decision was reached.
  • Guardrails

    • Hard-block unsupported decisions like “approve despite failed KYC.”
    • Add deterministic rules outside the agent for minimum score thresholds, max DTI limits, blacklist checks, and exception routing.
  • Monitoring

    • Track approval rate drift by product segment.
    • Alert on abnormal referral spikes because they usually indicate policy mismatch or upstream data quality issues.

Common Pitfalls

  • Letting the agent make final credit decisions without hard rules

    • Avoid this by enforcing deterministic policy checks before any recommendation is accepted.
    • The agent should recommend; your rules engine should decide whether that recommendation is admissible.
  • Passing raw sensitive data into prompts unnecessarily

    • Avoid sending full account numbers or unmasked identifiers.
    • Use tokenized customer IDs and only the minimum attributes required for underwriting.
  • Skipping versioned policy context

    • If you do not stamp each run with a policy version, audit becomes messy fast.
    • Store the exact eligibility rules used for every request so disputes can be traced later.

Keep learning

By Cyprian Aarons, AI Consultant at Topiax.

Want the complete 8-step roadmap?

Grab the free AI Agent Starter Kit — architecture templates, compliance checklists, and a 7-email deep-dive course.

Get the Starter Kit

Related Guides