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

By Cyprian AaronsUpdated 2026-04-21
claims-processingcrewaitypescriptpayments

A claims processing agent for payments takes an incoming claim, extracts the relevant facts, checks policy and transaction context, validates supporting documents, and routes the case for approval, rejection, or manual review. It matters because claims are where money moves with risk attached: if you get classification, evidence handling, or auditability wrong, you create payout leakage, compliance exposure, and slow customer resolution.

Architecture

Build this agent as a small workflow, not a single prompt.

  • Claim intake layer

    • Accepts claim payloads from your API or queue.
    • Normalizes fields like claim ID, merchant ID, amount, currency, timestamp, and attachments.
  • Evidence extraction tool

    • Pulls structured data from PDFs, images, receipts, chargeback forms, or email threads.
    • Returns only the fields the agent needs to decide.
  • Policy validation tool

    • Checks claim against payment rules: refund window, card network rules, KYC/AML flags, fraud thresholds, and jurisdiction constraints.
  • Decisioning crew

    • Uses one agent for classification and another for final review.
    • Produces a decision plus a reason code that can be audited later.
  • Case persistence and audit log

    • Stores every input, tool result, intermediate decision, and final output.
    • Required for disputes, compliance reviews, and model governance.
  • Human escalation path

    • Sends borderline cases to an operations queue when confidence is low or policy conflicts exist.

Implementation

1) Install CrewAI for TypeScript and define your claim schema

Start by modeling the exact data shape you expect. In payments systems, loose objects become audit problems fast.

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

export const ClaimSchema = z.object({
  claimId: z.string(),
  merchantId: z.string(),
  customerId: z.string(),
  amount: z.number().positive(),
  currency: z.string().length(3),
  transactionId: z.string(),
  reasonCode: z.string(),
  submittedAt: z.string(),
  attachments: z.array(z.string()).default([]),
});

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

That schema is your first guardrail. Everything downstream should validate against it before the agent sees it.

2) Build tools for policy checks and evidence lookup

CrewAI agents work best when they call narrow tools instead of hallucinating around business rules. For payments claims, the tools should return deterministic outputs.

import { Tool } from "@crewai/crewai";

export const checkPolicyTool = new Tool({
  name: "check_policy",
  description: "Validate a payment claim against refund windows and compliance rules.",
  func: async (input: string) => {
    const claim = JSON.parse(input);
    const withinRefundWindow = true; // replace with real policy lookup
    const amlFlagged = false; // replace with sanctions/AML service call

    return JSON.stringify({
      eligible: withinRefundWindow && !amlFlagged,
      withinRefundWindow,
      amlFlagged,
      reason:
        !withinRefundWindow ? "OUTSIDE_REFUND_WINDOW" :
        amlFlagged ? "COMPLIANCE_FLAG" : "OK",
    });
  },
});

export const fetchEvidenceTool = new Tool({
  name: "fetch_evidence",
  description: "Fetch supporting documents and metadata for a payment claim.",
  func: async (input: string) => {
    const { claimId } = JSON.parse(input);

    return JSON.stringify({
      claimId,
      documentsFound: true,
      extractedFields: {
        disputeType: "fraudulent_transaction",
        receiptMatch: true,
      },
    });
  },
});

Keep these tools side-effect free. In production they should read from approved internal services only.

3) Create the agents and crew

Use one agent to assess the case and another to approve or escalate. This keeps reasoning separated from final action selection.

import { Agent, Crew } from "@crewai/crewai";
import { checkPolicyTool, fetchEvidenceTool } from "./tools";
import { ClaimSchema } from "./schema";

const triageAgent = new Agent({
  role: "Claims Triage Analyst",
  goal: "Classify payment claims using policy rules and evidence.",
  backstory:
    "You process payment claims with strict attention to compliance, auditability, and reason codes.",
  tools: [checkPolicyTool, fetchEvidenceTool],
});

const decisionAgent = new Agent({
  role: "Claims Decision Maker",
  goal: "Produce a final decision for payment claims with a clear audit trail.",
  backstory:
    "You only approve claims that satisfy policy checks and have sufficient evidence.",
});

export function buildClaimsCrew() {
  return new Crew({
    agents: [triageAgent, decisionAgent],
    tasks: [
      {
        description:
          "Validate the claim payload. Call policy and evidence tools. Summarize eligibility and risk.",
        expectedOutput:
          "A structured triage summary containing eligibility status, key evidence signals, and risk notes.",
        agent: triageAgent,
      },
      {
        description:
          "Decide approve, reject, or escalate based on the triage summary. Include reason code.",
        expectedOutput:
          "A final decision object with status, reasonCode, confidenceScore, and audit notes.",
        agent: decisionAgent,
      },
    ],
    verbose: true,
  });
}

The important part here is not the number of agents. It’s that each step has one job and emits something you can log.

4) Run the crew behind an API boundary

Validate input first. Then run the crew with a bounded prompt surface so you do not pass raw junk into the reasoning layer.

import { buildClaimsCrew } from "./crew";
import { ClaimSchema } from "./schema";

export async function processClaim(rawClaim: unknown) {
  const claim = ClaimSchema.parse(rawClaim);
  const crew = buildClaimsCrew();

  const result = await crew.kickoff({
    inputs: {
      claimId: claim.claimId,
      merchantId: claim.merchantId,
      customerId: claim.customerId,
      amount: claim.amount.toFixed(2),
      currency: claim.currency,
      transactionId: claim.transactionId,
      reasonCode: claim.reasonCode,
      submittedAt: claim.submittedAt,
      attachmentsCount: String(claim.attachments.length),
    },
  });

  return {
    claimId: claim.claimId,
    result,
    processedAt: new Date().toISOString(),
  };
}

If you need manual review routing, add a post-processing rule outside the model:

  • confidence below threshold → queue for human review
  • compliance flag present → reject or escalate
  • missing evidence → request more documents

Production Considerations

  • Deploy in-region

    • Keep PII and payment data in approved regions.
    • If your team operates across jurisdictions, pin storage and inference to the correct residency boundary.
  • Log every decision artifact

    • Store input hashes, tool outputs, model version, prompt version, timestamps, final status.
    • That gives you traceability during chargeback disputes and regulatory reviews.
  • Add deterministic guardrails

    • Never let the model override hard rules like sanctions hits or expired refund windows.
  • Use rule engines before final approval.

  • Treat LLM output as advisory unless all controls pass.

  • Monitor drift by reason code

    • Track approval rate by merchant category, region, amount band, and dispute type.
  • Sudden shifts usually mean upstream data quality issues or prompt regressions.

Common Pitfalls

  1. Letting the agent make financial decisions without hard rules

    • Fix it by enforcing policy checks outside the model.
    • The agent can explain; your rule engine decides eligibility boundaries.
  2. Passing raw documents directly into prompts

    • Fix it by extracting structured fields first.
    • Keep attachments in object storage and send only normalized evidence summaries to the crew.
  3. Skipping audit metadata

    • Fix it by persisting every task output with timestamps and model/version identifiers.
    • If you cannot reconstruct why a payout was approved six months later, your design is incomplete.

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