How to Build a fraud detection Agent Using AutoGen in TypeScript for banking

By Cyprian AaronsUpdated 2026-04-21
fraud-detectionautogentypescriptbanking

A fraud detection agent in banking watches transaction streams, enriches them with customer and account context, and decides when to flag, block, or escalate suspicious activity. The point is not just catching fraud faster; it is reducing false positives, preserving customer trust, and creating an auditable decision trail that compliance teams can defend.

Architecture

  • Transaction ingest layer

    • Pulls payment events from Kafka, a queue, or an API gateway.
    • Normalizes fields like amount, merchant, country, deviceId, and accountId.
  • Context enrichment service

    • Fetches customer profile, KYC tier, historical spend patterns, device reputation, and recent login activity.
    • Keeps sensitive data out of the model prompt unless it is strictly needed.
  • AutoGen agent layer

    • Uses a AssistantAgent for reasoning over the event and policy context.
    • Uses a UserProxyAgent or orchestrator process to execute deterministic checks and approve actions.
  • Rules and policy engine

    • Applies hard banking controls before any model-driven decision.
    • Handles things like sanctions hits, velocity limits, country restrictions, and high-risk merchant categories.
  • Decision logger

    • Persists every input, tool call, model output, and final action.
    • Produces an audit record for investigators and regulators.
  • Action dispatcher

    • Sends alerts to case management.
    • Optionally triggers step-up auth, temporary holds, or manual review queues.

Implementation

1. Install AutoGen for TypeScript and define the event shape

Use the TypeScript AutoGen package you are already standardizing on in your agent runtime. Keep the event contract strict; fraud systems fail when payloads drift.

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

export const TransactionSchema = z.object({
  transactionId: z.string(),
  accountId: z.string(),
  amount: z.number().positive(),
  currency: z.string().length(3),
  merchantName: z.string(),
  merchantCategory: z.string(),
  country: z.string().length(2),
  deviceId: z.string(),
  timestamp: z.string(),
});

export type TransactionEvent = z.infer<typeof TransactionSchema>;

2. Create an assistant agent that classifies risk with explicit output

The key pattern is to force structured output. In banking, free-form text is useless if you need to route a case or justify a hold.

import { AssistantAgent } from "@autogen/agentchat";
import { OpenAIChatCompletionClient } from "@autogen/agentchat";

const modelClient = new OpenAIChatCompletionClient({
  model: "gpt-4o-mini",
  apiKey: process.env.OPENAI_API_KEY!,
});

export const fraudAgent = new AssistantAgent({
  name: "fraud_detector",
  modelClient,
  systemMessage: `
You are a banking fraud detection assistant.
Return only valid JSON with keys:
riskScore (0-100), decision ("approve"|"review"|"block"), reasons (string[]), complianceFlags (string[]).
Use conservative thresholds.
`,
});

3. Orchestrate deterministic checks before the agent runs

Do not let the model make first contact with raw transactions. Run policy checks first, then ask the agent to reason over the reduced feature set.

type EnrichedTransaction = TransactionEvent & {
  customerTenureDays: number;
  avgDailySpend30d: number;
  recentFailedLogins24h: number;
};

function applyHardRules(txn: EnrichedTransaction) {
  const flags: string[] = [];

  if (txn.country === "IR" || txn.country === "KP") {
    flags.push("sanctions_country");
    return { decision: "block" as const, flags };
    }

  if (txn.amount > txn.avgDailySpend30d * 8) {
    flags.push("velocity_spike");
    return { decision: "review" as const, flags };
  }

  return { decision: "allow" as const, flags };
}

async function assessFraud(txn: EnrichedTransaction) {
  const ruleResult = applyHardRules(txn);
  if (ruleResult.decision !== "allow") return ruleResult;

const prompt = `
Assess this transaction for fraud risk.
Transaction:
${JSON.stringify(txn)}

Output JSON only.
`;

const result = await fraudAgent.run(prompt);
return JSON.parse(result.content as string);
}

4. Wrap the agent in an auditable service boundary

This is where production banking systems differ from demos. Every request needs correlation IDs, immutable logs, and a replay path for investigators.

import crypto from "crypto";

async function handleTransaction(rawTxn: unknown) {
const txn = TransactionSchema.parse(rawTxn);
const requestId = crypto.randomUUID();

const assessment = await assessFraud({
...txn,
customerTenureDays: 420,
avgDailySpend30d: 180,
recentFailedLogins24h: 0,
});

await auditLog({
requestId,
transactionId: txn.transactionId,
inputHash: crypto.createHash("sha256").update(JSON.stringify(txn)).digest("hex"),
assessment,
timestamp: new Date().toISOString(),
});

return {
requestId,
transactionId: txn.transactionId,
...assessment,
};
}

async function auditLog(entry: unknown) {
console.log(JSON.stringify(entry));
}

Production Considerations

  • Data residency

    • Keep prompts and logs inside your approved region.
    • If your bank requires EU-only or country-specific processing, pin both model endpoints and telemetry storage accordingly.
  • Compliance and auditability

    • Store the exact prompt context used for each decision. Include rule-engine outputs separately from model outputs so auditors can see what was deterministic versus probabilistic.
  • Monitoring

    Track false positive rate, manual review rate, block rate by segment, and time-to-decision. If those metrics drift after a model change, roll back immediately.

  • Guardrails

    Never allow the agent to directly move money or close accounts. Limit actions to approve, review, or block, then require downstream policy approval for any customer-impacting step.

Common Pitfalls

  1. Letting the model see too much PII

    • Avoid sending full account statements or identity documents into the prompt unless they are required.
    • Redact names where possible and pass derived features instead of raw records.
  2. Using the agent as the only control

    • A fraud agent should assist decisions, not replace rules.
    • Keep sanctions screening, threshold checks, and device reputation in deterministic code before LLM inference.
  3. Skipping replayable audit logs

    • If you cannot reconstruct why a transaction was blocked, you will have trouble with compliance reviews and dispute handling.
    • Log inputs, outputs, model version, prompt template version, and policy version on every run.
  4. Treating all regions the same

    • Banking controls vary by jurisdiction.
    • Build region-aware policies for retention windows, cross-border processing limits, and escalation paths.

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