How to Build a claims processing Agent Using LangGraph in TypeScript for investment banking

By Cyprian AaronsUpdated 2026-04-21
claims-processinglanggraphtypescriptinvestment-banking

A claims processing agent in investment banking handles the intake, validation, routing, and resolution of operational claims tied to trades, settlements, custody breaks, fee disputes, or client reimbursement requests. It matters because every delay or manual handoff increases operational risk, weakens auditability, and creates exposure in regulated workflows where compliance, traceability, and data residency are non-negotiable.

Architecture

  • Claim intake layer

    • Accepts structured claims from internal ops tools, email ingestion, or case management systems.
    • Normalizes fields like claim ID, counterparty, trade date, product type, amount, and jurisdiction.
  • Validation node

    • Checks required fields, schema correctness, duplicate claim detection, and policy thresholds.
    • Flags missing evidence or unsupported claim types before any downstream action.
  • Policy and compliance router

    • Applies investment banking rules: KYC/AML-sensitive cases, sanctions exposure, regional handling rules, escalation thresholds.
    • Decides whether the claim can be auto-processed or must go to a human reviewer.
  • Resolution planner

    • Generates the next action: request documents, reconcile against trade records, escalate to legal/compliance, or approve payout.
    • Uses deterministic branching instead of free-form agent behavior.
  • Audit logger

    • Persists every state transition with timestamps, actor decisions, and rationale.
    • Produces an immutable trail for internal audit and regulator review.
  • Human approval checkpoint

    • Handles exceptions above a threshold or cases involving restricted jurisdictions.
    • Prevents autonomous settlement on high-risk claims.

Implementation

1) Define the claim state and graph nodes

Use a typed state so every node knows exactly what it can read and write. In LangGraph TypeScript, Annotation.Root is the cleanest way to define that contract.

import { Annotation, StateGraph } from "@langchain/langgraph";

type ClaimStatus = "received" | "validated" | "needs_review" | "approved" | "rejected";

const ClaimState = Annotation.Root({
  claimId: Annotation<string>(),
  amount: Annotation<number>(),
  jurisdiction: Annotation<string>(),
  evidenceProvided: Annotation<boolean>(),
  duplicateFound: Annotation<boolean>(),
  complianceFlag: Annotation<boolean>(),
  status: Annotation<ClaimStatus>(),
  auditTrail: Annotation<string[]>({
    default: () => [],
    reducer: (left, right) => left.concat(right),
  }),
});

const validateClaim = async (state: typeof ClaimState.State) => {
  const missingEvidence = !state.evidenceProvided;
  const duplicateFound = state.duplicateFound;

  return {
    status: "validated" as const,
    auditTrail: [
      `Validated claim ${state.claimId}`,
      missingEvidence ? "Evidence missing" : "Evidence present",
      duplicateFound ? "Duplicate detected" : "No duplicate detected",
    ],
    complianceFlag: missingEvidence || duplicateFound,
  };
};

const routeClaim = async (state: typeof ClaimState.State) => {
  const restrictedJurisdiction = ["IR", "RU", "KP"].includes(state.jurisdiction);
  const highValue = state.amount >= 100000;

  return {
    status:
      restrictedJurisdiction || highValue || state.complianceFlag
        ? ("needs_review" as const)
        : ("approved" as const),
    auditTrail: [
      restrictedJurisdiction
        ? `Jurisdiction ${state.jurisdiction} requires review`
        : `Jurisdiction ${state.jurisdiction} allowed`,
      highValue ? `Amount ${state.amount} exceeds auto-approval threshold` : "Amount within threshold",
    ],
    complianceFlag: restrictedJurisdiction || highValue || state.complianceFlag,
  };
};

2) Build the graph with conditional routing

This is where LangGraph fits better than a single LLM chain. You want explicit control flow for regulated workflows.

const workflow = new StateGraph(ClaimState)
  .addNode("validateClaim", validateClaim)
  .addNode("routeClaim", routeClaim)
  .addEdge("__start__", "validateClaim")
  .addEdge("validateClaim", "routeClaim")
  .addConditionalEdges("routeClaim", (state) => {
    if (state.status === "needs_review") return "review";
    if (state.status === "approved") return "approve";
    return "__end__";
  }, {
    review: "__end__",
    approve: "__end__",
  });

const app = workflow.compile();

3) Add a human review path for controlled escalation

For investment banking claims, the agent should never auto-resolve everything. High-value claims and anything involving restricted jurisdictions should stop at a human checkpoint.

const initialState = {
  claimId: "CLM-20491",
  amount: 250000,
  jurisdiction: "GB",
  evidenceProvided: true,
}

const result = await app.invoke({
  ...initialState,
});
console.log(result.status);
console.log(result.auditTrail);

If you want a real approval step instead of ending at review, add another node:

const humanReview = async (state: typeof ClaimState.State) => {
  const approvedByOps = false; // replace with case management callback
  return {
    status: approvedByOps ? ("approved" as const) : ("rejected" as const),
    auditTrail: [approvedByOps ? "Approved by ops reviewer" : "Rejected by ops reviewer"],
    complianceFlag: !approvedByOps,
  };
};

Then route "review" to humanReview before ending. That gives you a clean separation between machine validation and accountable approval.

Step-by-step pattern summary

  1. Model the claim as typed state

    • Include business fields plus audit metadata.
    • Keep every field explicit; do not bury decision inputs inside free text.
  2. Use deterministic nodes for validation and policy checks

    • Avoid letting the model decide whether a claim is eligible.
    • Reserve LLM usage for extraction or summarization only if needed.
  3. Route by policy thresholds

    • Amount limits.
    • Restricted jurisdictions.
    • Missing evidence.
    • Duplicate detection.
  4. Persist the audit trail

    • Store each node output in your case system or event log.
    • Make sure reviewers can reconstruct why a claim moved forward or stopped.

Production Considerations

  • Deployment isolation

    • Run the agent in-region if claims contain client data subject to residency controls.
    • Keep EU claims in EU infrastructure; do not ship payloads cross-border just because your model endpoint is elsewhere.
  • Monitoring

    • Track approval rate by desk, jurisdiction, product type, and amount band.
    • Alert on spikes in needs_review, because that usually means broken upstream data or a policy regression.
  • Guardrails

    • Hard-code thresholds for auto-approval and escalation.
    • Never allow the model to override sanctions checks, AML flags, or legal holds.
  • Auditability


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