How to Build a loan approval Agent Using LangGraph in TypeScript for healthcare
A loan approval agent for healthcare decides whether a request should move forward, be reviewed, or be rejected based on patient, provider, and financial signals. In healthcare, that matters because financing often touches regulated data, billing workflows, and time-sensitive care decisions, so the agent has to be auditable, deterministic where it counts, and careful with protected information.
Architecture
- •
Input normalization layer
- •Converts raw application payloads into a typed state object.
- •Strips out fields that should never enter the model context, like full medical notes or unnecessary identifiers.
- •
Policy and compliance gate
- •Checks residency constraints, consent flags, and whether the request contains sensitive healthcare data.
- •Routes requests to manual review when policy is unclear.
- •
Risk scoring node
- •Evaluates repayment risk using structured inputs only.
- •Uses an LLM for explanation generation, not for final approval logic.
- •
Decision node
- •Produces one of three outcomes:
approve,review, ordeny. - •Keeps the final decision deterministic using explicit thresholds and rules.
- •Produces one of three outcomes:
- •
Audit logger
- •Persists every state transition with timestamps and decision reasons.
- •Supports HIPAA-style traceability and internal model governance.
- •
Human review handoff
- •Escalates edge cases to a loan officer or compliance reviewer.
- •Captures reviewer overrides for later analysis.
Implementation
1) Define the state and build the graph skeleton
Use a typed state so every node knows exactly what it can read and write. In LangGraph TypeScript, StateGraph is the core class for wiring nodes together.
import { StateGraph, START, END } from "@langchain/langgraph";
type LoanDecision = "approve" | "review" | "deny";
type LoanState = {
applicantId: string;
country: string;
residencyApproved: boolean;
consentGiven: boolean;
annualRevenue: number;
debtToIncome: number;
requestedAmount: number;
riskScore?: number;
decision?: LoanDecision;
reason?: string;
};
const graph = new StateGraph<LoanState>()
2) Add compliance, scoring, and decision nodes
Keep compliance checks separate from scoring. That gives you clean audit trails and makes it easier to prove why a request was routed to review.
const complianceNode = async (state: LoanState): Promise<Partial<LoanState>> => {
if (!state.consentGiven) {
return { decision: "review", reason: "Missing patient/organization consent for financial processing." };
}
if (!state.residencyApproved) {
return { decision: "review", reason: "Data residency or jurisdiction policy not satisfied." };
}
return {};
};
const scoreNode = async (state: LoanState): Promise<Partial<LoanState>> => {
const base =
state.annualRevenue > state.requestedAmount * 3 ? 20 : 50;
const dtiPenalty = Math.min(state.debtToIncome * 25, 40);
const score = Math.max(0, Math.min(100, base + dtiPenalty));
return { riskScore: score };
};
const decideNode = async (state: LoanState): Promise<Partial<LoanState>> => {
if (state.decision === "review") return {};
const score = state.riskScore ?? 100;
if (score <= 30) {
return { decision: "approve", reason: "Low risk based on structured financial signals." };
}
if (score <= 60) {
return { decision: "review", reason: "Medium risk requires human underwriting review." };
}
return { decision: "deny", reason: "High risk exceeds policy threshold." };
};
3) Wire conditional routing with addConditionalEdges
This is where LangGraph earns its keep. You can route around scoring when compliance fails, which keeps sensitive cases out of downstream logic.
const routeAfterCompliance = (state: LoanState) => {
if (state.decision === "review") return END;
return "score";
};
graph
.addNode("compliance", complianceNode)
.addNode("score", scoreNode)
.addNode("decide", decideNode)
.addEdge(START, "compliance")
.addConditionalEdges("compliance", routeAfterCompliance)
.addEdge("score", "decide")
.addEdge("decide", END);
4) Compile and run the agent
Use compile() to get an executable graph. In production, wrap this in an API handler and persist both input state and output state for audit purposes.
const app = graph.compile();
const result = await app.invoke({
applicantId: "app_123",
country: "US",
residencyApproved: true,
consentGiven: true,
annualRevenue: 1200000,
debtToIncome: 0.18,
requestedAmount: 150000,
});
console.log(result.decision); // approve | review | deny
console.log(result.reason);
console.log(result.riskScore);
If you need LLM-generated explanations, add a separate node after decide that uses a model only to draft rationale from already-computed fields. Do not let the model change the actual outcome.
Production Considerations
- •
Deploy in-region
- •Keep inference and logs inside approved healthcare data regions.
- •If your organization has US-only or EU-only constraints, enforce them at the infrastructure layer before the graph runs.
- •
Log every transition
Oops need produce concise no apologies? Let's craft final clean.
Keep learning
- •The complete AI Agents Roadmap — my full 8-step breakdown
- •Free: The AI Agent Starter Kit — PDF checklist + starter code
- •Work with me — I build AI for banks and insurance companies
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