How to Build a compliance checking Agent Using AutoGen in TypeScript for payments
A compliance checking agent for payments reviews transaction context, policy rules, and supporting evidence before a payment is approved, flagged, or escalated. It matters because payment flows are full of regulatory and operational constraints: sanctions screening, KYC status, country restrictions, transaction limits, and auditability all need to be enforced consistently.
Architecture
- •Policy source
- •A deterministic rules layer for hard checks like sanctioned countries, blocked MCCs, amount thresholds, and required fields.
- •Compliance agent
- •An
AssistantAgentthat interprets the case, explains violations, and returns a structured decision.
- •An
- •Evidence collector
- •A function layer that fetches customer profile, KYC state, merchant metadata, and transaction history from internal services.
- •Review orchestrator
- •A
GroupChatwith a compliance agent plus a tool executor so the agent can inspect evidence before deciding.
- •A
- •Audit sink
- •Persistent logging of prompts, tool calls, decisions, and rule versions for regulator-facing traceability.
- •Human escalation path
- •A manual review queue for ambiguous or high-risk cases that should not be auto-approved.
Implementation
1) Install AutoGen and define the payment case shape
For TypeScript, use the AutoGen agent chat package and keep your payment payload explicit. The agent should never infer missing compliance data when it can fetch or reject.
npm install @autogen-agentchat/agentchat @autogen-agentchat/openai
type PaymentCase = {
paymentId: string;
customerId: string;
merchantId: string;
amount: number;
currency: string;
country: string;
mcc: string;
kycStatus: "verified" | "pending" | "failed";
sanctionsHit: boolean;
};
type ComplianceDecision =
| { action: "approve"; reason: string }
| { action: "reject"; reason: string; ruleId: string }
| { action: "manual_review"; reason: string; riskFlags: string[] };
2) Build deterministic checks first
Payments compliance should not depend on free-form model reasoning for obvious violations. Put hard rules in code and let the model explain edge cases.
const blockedCountries = new Set(["IR", "KP", "SY"]);
const blockedMcc = new Set(["4829", "6012"]); // example only
function evaluateHardRules(pmt: PaymentCase): ComplianceDecision | null {
if (pmt.sanctionsHit) {
return {
action: "reject",
reason: "Sanctions screening returned a positive hit.",
ruleId: "SANCTIONS_001",
};
}
if (blockedCountries.has(pmt.country)) {
return {
action: "reject",
reason: `Payments to ${pmt.country} are not allowed.`,
ruleId: "COUNTRY_002",
};
}
if (blockedMcc.has(pmt.mcc)) {
return {
action: "reject",
reason: `Merchant category ${pmt.mcc} is restricted.`,
ruleId: "MCC_004",
};
}
if (pmt.kycStatus !== "verified") {
return {
action: "manual_review",
reason: `KYC status is ${pmt.kycStatus}.`,
riskFlags: ["KYC_NOT_VERIFIED"],
};
}
return null;
}
3) Create an AutoGen assistant that returns structured compliance output
Use AssistantAgent for reasoning and keep the prompt narrow. The model should assess only the evidence you provide and produce one of three outcomes.
import { AssistantAgent } from "@autogen-agentchat/agentchat";
import { OpenAIChatCompletionClient } from "@autogen-agentchat/openai";
const modelClient = new OpenAIChatCompletionClient({
model: process.env.OPENAI_MODEL ?? "gpt-4o-mini",
});
const complianceAgent = new AssistantAgent({
name: "compliance_agent",
modelClient,
systemMessage:
[
"You are a payments compliance reviewer.",
"Only use the provided facts.",
"Return one of: approve, reject, manual_review.",
"If information is missing for a safe decision, choose manual_review.",
"Be concise and include the exact policy concern.",
].join(" "),
});
4) Orchestrate review with a single execution path
The pattern below runs hard rules first. If no deterministic violation exists, the agent reviews the case and returns a final decision that you can store in your audit log.
async function reviewPayment(pmt: PaymentCase): Promise<ComplianceDecision> {
const hardRuleDecision = evaluateHardRules(pmt);
if (hardRuleDecision) return hardRuleDecision;
const prompt = `
Payment case:
${JSON.stringify(pmt, null, 2)}
Decide whether this payment is compliant.
Return JSON with one of:
{"action":"approve","reason":"..."}
{"action":"reject","reason":"...","ruleId":"..."}
{"action":"manual_review","reason":"...","riskFlags":["..."]}
`;
const result = await complianceAgent.run(prompt);
const text = result.messages.at(-1)?.content ?? "";
try {
return JSON.parse(text) as ComplianceDecision;
} catch {
return {
action: "manual_review",
reason: `Non-JSON response from agent: ${text.slice(0, 200)}`,
riskFlags: ["INVALID_AGENT_OUTPUT"],
};
}
}
If you need tool access for evidence lookup, wrap internal APIs as functions and expose them through AutoGen tool calling. In payments systems I prefer tools for KYC status and merchant profile reads only; write actions should stay outside the agent.
Production Considerations
- •Keep residency boundaries explicit
- •Run the model in-region if your payment data cannot leave a jurisdiction. If you must redact PII before inference, do it before constructing the prompt.
- •Log every decision with versioned policy inputs
- •Store the prompt hash, rule version, model version, tool outputs, and final decision. That is what makes an audit trail useful during disputes.
- •Use guardrails around auto-approval
- •Only auto-approve low-risk cases with verified KYC and no sanctions or country restrictions. Everything else goes to manual review.
- •Monitor false positives by segment
- •Track reject rates by country, MCC, amount band, and customer tier. Compliance models often drift into overblocking specific corridors.
Common Pitfalls
- •
Letting the LLM decide obvious policy violations
Hard violations like sanctions hits or blocked jurisdictions should never be left to model interpretation. Handle them in code first.
- •
Sending raw payment data into prompts
PANs, account numbers, names, addresses, and other PII should be minimized or tokenized. Keep prompts scoped to what the decision actually needs.
- •
No audit trail for reviewer actions
If you cannot reconstruct why a payment was rejected or escalated, your controls are weak. Persist inputs, outputs, timestamps, policy versions, and operator overrides.
Keep learning
- •The complete AI Agents Roadmap — my full 8-step breakdown
- •Free: The AI Agent Starter Kit — PDF checklist + starter code
- •Work with me — I build AI for banks and insurance companies
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