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

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

A claims processing agent in retail banking takes an incoming claim, extracts the facts, checks policy and account context, routes exceptions, and drafts a decision-ready summary for a human reviewer. It matters because claims work is high-volume, rules-heavy, and audit-sensitive: if you can reduce manual triage without losing traceability, you cut turnaround time and keep compliance teams happy.

Architecture

  • Claim intake layer

    • Receives structured payloads from web forms, branch systems, or case management APIs.
    • Normalizes fields like customer ID, claim type, amount, incident date, and supporting documents.
  • Policy and product retrieval

    • Pulls the relevant retail banking product rules.
    • Uses approved internal sources only: policy docs, fee schedules, dispute rules, and escalation thresholds.
  • Claims triage agent

    • Classifies the claim into straight-through processing vs. manual review.
    • Detects missing data, contradictions, fraud flags, and regulatory edge cases.
  • Compliance checker

    • Enforces KYC/AML-adjacent constraints where applicable.
    • Validates audit requirements, retention rules, and jurisdiction-specific handling.
  • Decision summarizer

    • Produces a concise case note for operations staff.
    • Includes reasons for routing decisions and references to source documents.
  • Audit logging layer

    • Stores inputs, tool calls, outputs, timestamps, model version, and reviewer actions.
    • This is non-negotiable in retail banking.

Implementation

1) Install CrewAI and define the claim schema

For TypeScript projects, keep your claim payload explicit. Banking workflows fail when teams let free-form JSON drift across services.

npm install @crew-ai/crewai zod
import { z } from "zod";

export const ClaimSchema = z.object({
  claimId: z.string(),
  customerId: z.string(),
  productType: z.enum(["current_account", "credit_card", "personal_loan"]),
  claimType: z.enum(["unauthorized_transaction", "fee_dispute", "cashback_missing"]),
  amount: z.number().positive(),
  currency: z.string().length(3),
  incidentDate: z.string(),
  submittedAt: z.string(),
  evidenceUrls: z.array(z.string().url()).default([]),
  jurisdiction: z.string(),
});

export type Claim = z.infer<typeof ClaimSchema>;

This schema gives you a hard boundary before the agent sees anything. In banking systems, that boundary is part of your control environment.

2) Build tools for policy lookup and case logging

CrewAI agents should not hallucinate policy. Give them tools that read from approved sources and write audit records.

import { tool } from "@crew-ai/crewai";
import { promises as fs } from "node:fs";

export const getPolicyTool = tool({
  name: "get_policy",
  description: "Fetch approved claims policy text by product type and jurisdiction.",
  parameters: {
    type: "object",
    properties: {
      productType: { type: "string" },
      jurisdiction: { type: "string" }
    },
    required: ["productType", "jurisdiction"]
  },
  execute: async ({ productType, jurisdiction }) => {
    const path = `./policies/${jurisdiction}/${productType}.md`;
    return await fs.readFile(path, "utf8");
  }
});

export const writeAuditTool = tool({
  name: "write_audit_record",
  description: "Persist a tamper-evident audit record for the claim decision.",
  parameters: {
    type: "object",
    properties: {
      claimId: { type: "string" },
      decision: { type: "string" },
      rationale: { type: "string" }
    },
    required: ["claimId", "decision", "rationale"]
  },
  execute: async (payload) => {
    await fs.appendFile("./audit/claims.log", JSON.stringify({
      ...payload,
      timestamp: new Date().toISOString()
    }) + "\n");
    return { ok: true };
  }
});

The key pattern here is simple:

  • read-only access for policy
  • append-only writes for audit
  • no direct database mutation from the model

3) Define agents and tasks with CrewAI

Use one agent for triage and one for compliance review. That separation makes it easier to explain decisions to ops teams and auditors.

import { Agent, Task, Crew } from "@crew-ai/crewai";
import { ClaimSchema } from "./schema";
import { getPolicyTool, writeAuditTool } from "./tools";

const triageAgent = new Agent({
  role: "Claims Triage Analyst",
  goal: "Classify retail banking claims and identify missing or risky cases.",
  backstory:
    "You process retail banking claims with strict attention to policy text, evidence quality, and escalation thresholds.",
  tools: [getPolicyTool],
});

const complianceAgent = new Agent({
  role: "Compliance Reviewer",
  goal:
    "Validate that the proposed claim handling aligns with bank policy, audit requirements, and jurisdictional controls.",
  backstory:
    "You review claims for regulatory risk, data residency concerns, retention obligations, and exception handling.",
  tools: [writeAuditTool],
});

export function buildCrew(rawClaimData: unknown) {
  const claim = ClaimSchema.parse(rawClaimData);

  const triageTask = new Task({
    description:
      `Review this retail banking claim and decide whether it can be auto-processed or needs manual review:\n${JSON.stringify(claim)}`,
    expectedOutput:
      "A structured triage result with decision, reasons, missing fields if any, and next action.",
    agent: triageAgent,
  });

  const complianceTask = new Task({
    description:
      `Validate the triage result against bank controls for claim ${claim.claimId}. Write an audit record after review.`,
    expectedOutput:
      "A compliance verdict with control checks passed/failed and an audit entry confirmation.",
    agent: complianceAgent,
    contextTasks: [triageTask],
  });

  

Continue the crew execution pattern:

import { Process } from "@crew-ai/crewai";

export async function processClaim(rawClaimData: unknown) {
const crew = buildCrew(rawClaimData);

return await crew.kickoff({
processType : Process.sequential,
inputs : rawClaimData
});
}

In practice:

  • Task handles each stage of work
  • contextTasks passes upstream output into downstream review
  • Crew.kickoff() starts execution
  • Process.sequential keeps the workflow deterministic enough for regulated operations

What this gives you

LayerResponsibilityBanking concern
SchemaValidate incoming claim dataPrevent malformed or incomplete cases
Triage agentClassify the caseReduce manual queue load
Compliance agentReview controlsEnforce policy and jurisdiction rules
Audit toolPersist decision trailSupport internal/external audits

Production Considerations

  • Deploy inside your data boundary

Only run the agent in regions approved for customer data. If your bank has residency requirements for EU or APAC customers, keep prompts, logs, embeddings, and artifacts in-region.

  • Log every model interaction

Capture prompt inputs, retrieved policy snippets, task outputs, tool invocations, latency, and final decisions. Store these logs in immutable storage with retention aligned to banking recordkeeping rules.

  • Add hard guardrails before execution

Reject claims without required identifiers or evidence metadata before they hit the agent. Also block unsupported actions like refunds above threshold amounts without human approval.

  • Monitor decision quality

Track auto-processing rate, false positives on fraud flags, escalation rate by product type، and average time to resolution. Review drift weekly because policy changes in retail banking are constant.

Common Pitfalls

  1. Letting the model decide outside policy text

    Avoid free-form “use your judgment” prompts. Force every decision to cite retrieved policy content or route to manual review.

  2. Skipping schema validation

    If you pass raw webhook payloads directly into the crew, you will get brittle behavior. Validate with Zod first so bad inputs fail fast.

  3. Mixing audit writes with business logic

    Don’t let the same task both decide the outcome and mutate records in your core system. Keep decisioning separate from persistence so auditors can replay exactly what happened.

  4. Ignoring jurisdiction-specific handling

    A chargeback-style dispute in one region may have different timelines or disclosure rules elsewhere. Model jurisdiction as a first-class input; do not bury it in prompt text.


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