How to Build a fraud detection Agent Using LlamaIndex in TypeScript for fintech

By Cyprian AaronsUpdated 2026-04-21
fraud-detectionllamaindextypescriptfintech

A fraud detection agent in fintech is not a classifier sitting in isolation. It is a retrieval-backed decision layer that can inspect transactions, pull in customer history, compare against policy rules, and return an explainable risk assessment fast enough for operational use.

That matters because fraud teams need more than a score. They need traceability for auditors, consistent handling of edge cases, and a way to route suspicious activity without leaking sensitive data or making opaque decisions.

Architecture

  • Transaction intake layer

    • Receives events from card swipes, ACH transfers, wallet top-ups, or account logins.
    • Normalizes fields like amount, merchant, device fingerprint, IP geolocation, and account age.
  • Risk context retriever

    • Uses LlamaIndex to fetch relevant historical signals from indexed transaction notes, case outcomes, KYC summaries, and policy documents.
    • Keeps the agent grounded in internal facts instead of hallucinating risk reasons.
  • Fraud reasoning agent

    • Runs on ReActAgent or a similar LlamaIndex agent abstraction.
    • Produces a structured verdict: approve, review, or block.
  • Policy and compliance guardrail

    • Enforces rules around PII exposure, regional data residency, and explainability.
    • Ensures the output includes evidence references for audit trails.
  • Case management integration

    • Sends high-risk cases to a queue or case platform.
    • Stores the final reasoning bundle for investigators and auditors.

Implementation

1) Install the core packages

You need LlamaIndex for TypeScript plus an LLM provider. The example below uses OpenAI through LlamaIndex’s OpenAI class.

npm install llamaindex zod dotenv

Set your environment variables:

export OPENAI_API_KEY="your-key"

2) Build indexes for policy and historical fraud cases

For fintech, keep policy docs and case notes separate. That lets you control access and apply different retention rules later.

import "dotenv/config";
import {
  Document,
  VectorStoreIndex,
  Settings,
  OpenAI,
} from "llamaindex";

Settings.llm = new OpenAI({
  model: "gpt-4o-mini",
});

async function buildIndexes() {
  const policyDocs = [
    new Document({
      text: `
Fraud policy:
- Block transactions over $10,000 when device is new and IP country mismatches KYC country.
- Review transactions with velocity > 5 attempts in 10 minutes.
- Always provide evidence references in analyst notes.
      `,
      metadata: { type: "policy", region: "us-east-1" },
    }),
  ];

  const caseDocs = [
    new Document({
      text: `
Case #FRD-1021:
Outcome: blocked
Reason: multiple failed login attempts followed by high-value transfer from new device.
      `,
      metadata: { type: "case", region: "us-east-1" },
    }),
    new Document({
      text: `
Case #FRD-1044:
Outcome: reviewed
Reason: merchant category was unusual but customer travel history explained location mismatch.
      `,
      metadata: { type: "case", region: "us-east-1" },
    }),
  ];

  const policyIndex = await VectorStoreIndex.fromDocuments(policyDocs);
  const caseIndex = await VectorStoreIndex.fromDocuments(caseDocs);

  return { policyIndex, caseIndex };
}

3) Create the fraud agent with retrieval tools

The pattern here is simple: turn each index into a query engine tool with asQueryEngine() and wrap them in QueryEngineTool. Then let ReActAgent decide which source to query before it returns a decision.

import {
  ReActAgent,
  QueryEngineTool,
} from "llamaindex";

type TransactionInput = {
  transactionId: string;
  amountUsd: number;
  merchantCategory: string;
  deviceNew: boolean;
  ipCountryMismatch: boolean;
  velocityLast10Min: number;
};

async function createFraudAgent() {
  const { policyIndex, caseIndex } = await buildIndexes();

  const policyTool = QueryEngineTool.from(
    {
      queryEngine: policyIndex.asQueryEngine(),
      metadata: {
        name: "fraud_policy_lookup",
        description:
          "Lookup internal fraud policy rules and compliance requirements.",
      },
    }
  );

  const caseTool = QueryEngineTool.from(
    {
      queryEngine: caseIndex.asQueryEngine(),
      metadata: {
        name: "historical_case_lookup",
        description:
          "Lookup prior fraud cases and analyst outcomes.",
      },
    }
  );

  return ReActAgent.fromTools({
    tools: [policyTool, caseTool],
    llm: Settings.llm!,
    systemPrompt:
      "You are a fraud detection agent for a fintech platform. Return only concise decisions with evidence. Never expose raw PII. Use internal policies and prior cases when relevant.",
  });
}

4) Run the decision flow and return an auditable result

Keep the output structured enough for downstream systems. In production I’d wrap this with Zod validation before writing to your case queue.

async function evaluateTransaction(txn: TransactionInput) {
  const agent = await createFraudAgent();

  const prompt = `
Evaluate this transaction:
transactionId=${txn.transactionId}
amountUsd=${txn.amountUsd}
merchantCategory=${txn.merchantCategory}
deviceNew=${txn.deviceNew}
ipCountryMismatch=${txn.ipCountryMismatch}
velocityLast10Min=${txn.velocityLast10Min}

Return:
- decision (approve|review|block)
- reason
- evidence
`;

const response = await agent.chat(prompt);
return response.toString();
}

evaluateTransaction({
transactionId:"txn_9001",
amountUsd:12500,
merchantCategory:"money_transfer",
deviceNew:true,
ipCountryMismatch:true,
velocityLast10Min:7,
}).then(console.log);

Production Considerations

  • Deploy close to your regulated data

    If your customer data must stay in-region, host the vector store and inference path in that region too. For EU customers, don’t ship KYC summaries to an out-of-region model endpoint just because it is convenient.

  • Log every decision with evidence IDs

    Store the transaction payload hash, retrieved document IDs, model version, prompt version, and final decision. That gives compliance teams an audit trail without persisting unnecessary raw PII in logs.

  • Add hard guardrails before auto-blocking

    Use deterministic rules for severe actions like account freezes. Let the agent recommend “block” or “review,” but require a rules engine or human approval for irreversible actions.

  • Monitor drift by segment

    Track false positives by merchant category, geography, device type, and customer tenure. Fraud patterns shift quickly; if review rates spike on one corridor or payment rail, retrain your policies or adjust thresholds.

Common Pitfalls

  • Using the agent as the only control

    Don’t let an LLM make final enforcement decisions alone. Combine it with deterministic rules for known fraud patterns like velocity abuse or sanctioned geographies.

  • Mixing policy docs with raw customer records

    Keep sensitive customer data out of broad retrieval indexes unless access controls are enforced at query time. Separate indexes by purpose so analysts do not accidentally retrieve unrelated PII during investigation.

  • Ignoring explainability requirements

    A score without evidence is useless in fintech operations. Make sure every decision includes which policy rule fired and which prior cases were retrieved so investigators can defend the outcome internally and externally.


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