How to Build a KYC verification Agent Using AutoGen in TypeScript for lending
A KYC verification agent for lending collects identity evidence, checks it against policy, and returns a decision that a loan workflow can trust. It matters because lending teams need fast onboarding without weakening compliance, auditability, or data residency controls.
Architecture
- •Intake layer
- •Accepts borrower-submitted documents and structured fields: name, DOB, address, government ID number, employer, income docs.
- •KYC policy engine
- •Encodes lending rules: required document sets by product, jurisdiction-specific checks, sanctions/PEP screening triggers, and escalation thresholds.
- •AutoGen agent layer
- •Uses
AssistantAgentto reason over extracted evidence andUserProxyAgentto orchestrate tool calls, retries, and human escalation.
- •Uses
- •Verification tools
- •Deterministic functions for OCR extraction, document authenticity checks, watchlist lookup, address validation, and residency-aware storage access.
- •Audit trail
- •Persists every input, tool result, intermediate decision, and final recommendation for model risk review and regulator requests.
- •Case management handoff
- •Routes borderline cases to an analyst when confidence is low or policy conflicts exist.
Implementation
1) Install AutoGen and define your KYC types
For TypeScript projects, keep the agent logic thin and push all compliance-sensitive work into typed tools. The agent should never “guess” on identity data; it should only interpret verified tool output.
npm install @microsoft/autogen openai zod
// kyc-types.ts
import { z } from "zod";
export const KycInputSchema = z.object({
customerId: z.string(),
fullName: z.string(),
dateOfBirth: z.string(),
country: z.string(),
documentType: z.enum(["passport", "national_id", "drivers_license"]),
documentNumber: z.string(),
});
export type KycInput = z.infer<typeof KycInputSchema>;
export type KycResult = {
status: "approved" | "rejected" | "manual_review";
reasons: string[];
confidence: number;
};
2) Implement deterministic verification tools
These tools should be boring. That is the point. Lending workflows need repeatable checks with clear logs, not free-form model output.
// kyc-tools.ts
import { KycInput } from "./kyc-types";
export async function checkSanctions(input: KycInput) {
// Replace with your sanctions/PEP vendor integration.
const matched = input.fullName.toLowerCase().includes("test");
return {
matched,
source: "sanctions_vendor_x",
checkedAt: new Date().toISOString(),
};
}
export async function validateDocument(input: KycInput) {
// Replace with OCR + authenticity service.
const validFormat = input.documentNumber.length >= 6;
return {
validFormat,
source: "doc_verification_service",
checkedAt: new Date().toISOString(),
};
}
export async function validateResidency(input: KycInput) {
// Enforce data residency / jurisdiction routing before storage.
const allowedCountries = ["GB", "US", "CA"];
return {
allowed: allowedCountries.includes(input.country),
region: allowedCountries.includes(input.country) ? input.country : "restricted",
};
}
3) Build the AutoGen agents and register tools
The pattern here is simple: AssistantAgent reasons over policy and evidence; UserProxyAgent executes tool calls. For lending, keep the assistant constrained to producing a recommendation with citations from tool outputs.
// kyc-agent.ts
import { AssistantAgent, UserProxyAgent } from "@microsoft/autogen";
import { checkSanctions, validateDocument, validateResidency } from "./kyc-tools";
import { KycInputSchema } from "./kyc-types";
const llmConfig = {
config_list: [
{
model: process.env.OPENAI_MODEL ?? "gpt-4o-mini",
api_key: process.env.OPENAI_API_KEY,
},
],
};
export const kycAssistant = new AssistantAgent({
name: "kyc_assistant",
llm_config: llmConfig,
});
export const kycUserProxy = new UserProxyAgent({
name: "kyc_user_proxy",
human_input_mode: "NEVER",
});
kycUserProxy.register_function(
{
name: "checkSanctions",
description: "Run sanctions/PEP screening on the applicant.",
parameters: {
type: "object",
properties: { inputJson: { type: "string" } },
required: ["inputJson"],
},
func: async ({ inputJson }: { inputJson: string }) => checkSanctions(KycInputSchema.parse(JSON.parse(inputJson))),
},
);
kycUserProxy.register_function(
{
name: "validateDocument",
description: "Validate the submitted identity document.",
parameters: {
type: "object",
properties: { inputJson: { type: "string" } },
required: ["inputJson"],
},
func: async ({ inputJson }: { inputJson: string }) => validateDocument(KycInputSchema.parse(JSON.parse(inputJson))),
},
);
kycUserProxy.register_function(
{
name: "validateResidency",
description:
"Check whether the applicant's country is allowed for this lending workflow and storage region.",
parameters:
{
type:"object",
properties:{ inputJson:{ type:"string" } },
required:["inputJson"],
},
func: async ({ inputJson }: { inputJson:string }) => validateResidency(KycInputSchema.parse(JSON.parse(inputJson))),
},
);
4) Orchestrate a verification run and return a lending decision
This is where AutoGen earns its keep. The assistant gets the applicant payload plus policy instructions; the proxy executes functions; your app converts the result into an underwriting-safe decision.
// run-kyc.ts
import { kycAssistant, kycUserProxy } from "./kyc-agent";
import { KycInputSchema } from "./kyc-types";
export async function runKyc(rawInput: unknown) {
const input = KycInputSchema.parse(rawInput);
const prompt = `
You are a KYC verification agent for a lending workflow.
Use only tool outputs. Do not infer missing facts.
Return one of:
- approved
- rejected
- manual_review
Policy:
- Reject if sanctions matched.
- Reject if residency is not allowed for this product.
- Manual review if document validity is uncertain or any tool fails.
- Approve only when all checks pass.
Applicant JSON:
${JSON.stringify(input)}
`;
const result = await kycUserProxy.initiate_chat(kycAssistant, prompt);
return result;
}
If you want a stricter production shape, post-process the chat output into your own domain object:
type LendingKycDecision = {
status:"approved"|"rejected"|"manual_review";
auditId:string;
rawTranscript:string;
};
Production Considerations
- •
Deploy tools in-region
Keep PII processing inside the jurisdiction required by your lending program. If you operate in multiple regions, route applicants to region-specific workers and store transcripts separately.
- •
Log every tool call
Persist request IDs, tool inputs/outputs, timestamps, model version, and final decision. This is what you hand to compliance when they ask why an application was approved or escalated.
- •
Add hard guardrails outside the model
The agent should not be able to override sanctions hits or residency blocks. Enforce those as deterministic gates before any approval path reaches underwriting.
- •
Monitor drift in manual review rates
If manual reviews spike after a model change or vendor outage, you likely have prompt drift or brittle tool integration. Track approval rate by country, product type, and document class.
Common Pitfalls
- •
Letting the model make compliance decisions on its own
- •Avoid this by making sanctions screening, residency checks, and document validation deterministic tool outputs.
- •The model should summarize evidence, not invent it.
- •
Storing sensitive data in prompts without controls
- •Don’t send full documents unless you must.
- •Redact unnecessary fields before calling the agent and store raw PII in a compliant vault with strict retention rules.
- •
Skipping auditability because “the agent already knows”
- •That does not hold up in lending operations.
- •Save transcripts, tool responses, policy versions, and final outcomes so risk teams can reconstruct every decision end-to-end.
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