How to Build a fraud detection Agent Using CrewAI in TypeScript for insurance

By Cyprian AaronsUpdated 2026-04-21
fraud-detectioncrewaitypescriptinsurance

A fraud detection agent for insurance reviews claims, policy history, documents, and behavioral signals to flag suspicious cases before payout. It matters because false positives create friction for legitimate customers, while false negatives leak money through inflated or fabricated claims.

Architecture

  • Claim intake layer

    • Pulls claim metadata from your core system or claims API.
    • Normalizes fields like policy number, loss date, amount, claimant identity, and adjuster notes.
  • Evidence retrieval layer

    • Loads supporting documents: PDFs, images, call transcripts, repair estimates, and prior claim history.
    • Feeds the agent only the minimum necessary data for the decision.
  • Fraud analysis agent

    • Uses CrewAI Agent with a focused role: detect fraud indicators, explain why a case is suspicious, and assign a risk score.
    • Produces structured output that downstream systems can route to SIU or human review.
  • Verification tools

    • Custom tools for policy lookup, duplicate claim search, address/identity matching, and document metadata extraction.
    • These are what make the agent useful in production instead of just summarizing text.
  • Audit and case logging

    • Persists prompts, tool calls, model outputs, scores, and final recommendations.
    • Required for compliance review and internal model governance.

Implementation

  1. Install CrewAI and define your data contract

    Keep the input shape strict. Insurance workflows fail when the agent receives half-baked JSON with missing claim IDs or untyped free text.

    npm install crewai zod
    
    import { z } from "zod";
    
    export const ClaimSchema = z.object({
      claimId: z.string(),
      policyNumber: z.string(),
      claimantName: z.string(),
      lossDate: z.string(),
      reportedDate: z.string(),
      amount: z.number(),
      state: z.string(),
      description: z.string(),
      documents: z.array(z.string()).default([]),
    });
    
    export type ClaimInput = z.infer<typeof ClaimSchema>;
    
  2. Create tools that check real insurance signals

    The fraud agent should not guess. It should call tools for policy status, duplicate claims, and document checks. CrewAI’s Tool abstraction is the right place to put those lookups.

    import { Tool } from "crewai";
    
    export const policyLookupTool = new Tool({
      name: "policy_lookup",
      description: "Fetch policy status and coverage details by policy number",
      func: async (policyNumber: string) => {
        // Replace with real API call
        return JSON.stringify({
          policyNumber,
          active: true,
          coverageType: "Auto Comprehensive",
          inceptionDate: "2023-01-01",
        });
      },
    });
    
    export const duplicateClaimTool = new Tool({
      name: "duplicate_claim_search",
      description: "Search for duplicate or related claims using claimant and loss details",
      func: async (input: string) => {
        // Replace with real search across claims warehouse
        return JSON.stringify({
          matches: [
            { claimId: "CLM-10021", similarity: 0.87, reason: "Same claimant and same loss date" },
          ],
        });
      },
    });
    
    export const docMetadataTool = new Tool({
      name: "document_metadata_check",
      description: "Inspect uploaded documents for inconsistencies in metadata",
      func: async (docIds: string[]) => {
        return JSON.stringify({
          anomalies: [
            { docId: docIds[0], issue: "Created after reported submission time" },
          ],
        });
      },
    });
    
  3. Wire up the CrewAI agent and run the analysis

    Use one agent for fraud triage and one task for structured output. The key is to force a clear recommendation with reasons that an SIU analyst can verify later.

    import { Agent, Task, Crew } from "crewai";
    import { ClaimSchema } from "./claim-schema";
    import { policyLookupTool, duplicateClaimTool, docMetadataTool } from "./tools";
    
    const fraudAgent = new Agent({
      role: "Insurance Fraud Triage Analyst",
      goal:
        "Assess insurance claims for fraud indicators using policy data, duplicate claim signals, and document anomalies.",
      backstory:
        "You work in an insurer's special investigations workflow. You produce concise findings with evidence.",
      tools: [policyLookupTool, duplicateClaimTool, docMetadataTool],
      verbose: true,
    });
    
    export async function assessClaim(rawClaim: unknown) {
      const claim = ClaimSchema.parse(rawClaim);
    
      const task = new Task({
        description: `
          Review this insurance claim for fraud risk.
    
          Claim:
          ${JSON.stringify(claim)}
    
          Return:
          - riskScore from 0 to 100
          - riskBand as low | medium | high
          - topSignals as an array of concrete indicators
          - recommendation as approve | manual_review | siu_escalation
        `,
        expectedOutput:
          "Structured fraud assessment with evidence-based reasoning.",
        agent: fraudAgent,
      });
    
      const crew = new Crew({
        agents: [fraudAgent],
        tasks: [task],
        verboseOutput: true,
      });
    
      const result = await crew.kickoff();
      return result;
    }
    
  4. Parse the output into a downstream decision

    Don’t let free-form text drive production routing. Convert the agent response into a typed object before you send it to claims operations or SIU.

     type FraudDecision = {
       riskScore: number;
       riskBand: "low" | "medium" | "high";
       topSignals: string[];
       recommendation: "approve" | "manual_review" | "siu_escalation";
     };
    
     export function routeFraudResult(resultText: string): FraudDecision {
       const parsed = JSON.parse(resultText) as FraudDecision;
    
       if (parsed.riskScore < 0 || parsed.riskScore > 100) {
         throw new Error("Invalid risk score");
       }
    
       return parsed;
     }
    

Production Considerations

  • Auditability

    • Store every prompt, tool input/output, model version, and final decision.
    • For insurance disputes and regulator requests, you need a traceable path from claim intake to recommendation.
  • Data residency

    • Keep PII and claim documents inside approved regions.
    • If your insurer operates under regional hosting constraints, route LLM traffic through approved endpoints only.
  • Guardrails

    • Redact SSNs, bank details, medical data where possible before sending context to the agent.
    • Restrict tools so the agent can only read approved systems; no write access to core claims systems from the LLM path.
  • Human-in-the-loop thresholds

    • Auto-escalate only above a calibrated score threshold.
    • Route borderline cases to adjusters or SIU analysts; do not let the model deny claims directly.

Common Pitfalls

  • Using the agent as a final adjudicator

    • Fraud detection is triage, not denial logic.
    • Keep final decisions with licensed staff or rule-based workflows tied to company policy.
  • Sending too much sensitive data into context

    • Insurance files contain PII, PHI-adjacent content, and financial details.
    • Minimize inputs to what is needed for signal extraction; redact before calling the model.
  • Skipping deterministic checks

    • An LLM should not be your only control for duplicate claims or coverage validation.
    • Run hard rules first; use CrewAI to interpret ambiguous cases and explain evidence.

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