How to Build a KYC verification Agent Using LangChain in TypeScript for retail banking

By Cyprian AaronsUpdated 2026-04-21
kyc-verificationlangchaintypescriptretail-banking

A KYC verification agent helps retail banks collect customer identity data, check it against policy, flag missing evidence, and route risky cases to human review. It matters because onboarding is where fraud, compliance failures, and customer drop-off all show up at once.

Architecture

  • Channel adapter

    • Receives onboarding data from web, mobile, or branch systems.
    • Normalizes payloads into a stable internal schema.
  • KYC policy engine

    • Encodes bank rules for required fields, document types, country restrictions, and risk thresholds.
    • Keeps deterministic checks outside the LLM.
  • LangChain agent layer

    • Uses ChatOpenAI plus tools to classify documents, explain missing items, and decide next actions.
    • Handles natural-language interaction with the customer or ops staff.
  • Document extraction service

    • Pulls text from IDs, proof of address, and source-of-funds documents.
    • Can be backed by OCR or a document AI service.
  • Audit and case store

    • Persists every input, tool call, model output, and human override.
    • Required for compliance review and model governance.
  • Human escalation workflow

    • Routes high-risk or ambiguous cases to an analyst queue.
    • Prevents the agent from making final decisions on edge cases.

Implementation

1) Define the KYC data model and deterministic checks

Keep core compliance logic in plain TypeScript. The agent should explain policy outcomes, not invent them.

import { z } from "zod";

export const KycApplicationSchema = z.object({
  customerId: z.string(),
  fullName: z.string(),
  dateOfBirth: z.string(),
  countryOfResidence: z.string(),
  nationality: z.string(),
  documentType: z.enum(["passport", "national_id", "drivers_license", "utility_bill"]),
  documentText: z.string().optional(),
  pepMatch: z.boolean().default(false),
  sanctionsMatch: z.boolean().default(false),
});

export type KycApplication = z.infer<typeof KycApplicationSchema>;

export function assessKycRisk(app: KycApplication) {
  const missingFields = [
    !app.fullName && "fullName",
    !app.dateOfBirth && "dateOfBirth",
    !app.countryOfResidence && "countryOfResidence",
    !app.nationality && "nationality",
    !app.documentType && "documentType",
  ].filter(Boolean) as string[];

  const riskFlags = [
    app.pepMatch ? "pep_match" : null,
    app.sanctionsMatch ? "sanctions_match" : null,
    app.documentType === "utility_bill" ? "address_proof_only" : null,
  ].filter(Boolean) as string[];

  const requiresReview =
    missingFields.length > 0 || app.pepMatch || app.sanctionsMatch;

  return {
    missingFields,
    riskFlags,
    requiresReview,
    decision: requiresReview ? "manual_review" : "approve_for_next_step",
  };
}

2) Build a LangChain chain that explains the decision

Use the model for customer-facing or analyst-facing explanations. Do not let it override policy outputs.

import { ChatOpenAI } from "@langchain/openai";
import { ChatPromptTemplate } from "@langchain/core/prompts";
import { StringOutputParser } from "@langchain/core/output_parsers";
import { assessKycRisk, KycApplicationSchema } from "./kyc-policy";

const llm = new ChatOpenAI({
  model: "gpt-4o-mini",
  temperature: 0,
});

const prompt = ChatPromptTemplate.fromMessages([
  [
    "system",
    `You are a KYC operations assistant for a retail bank.
Only explain the provided policy result.
Do not change the decision.
Mention compliance language clearly and concisely.`,
  ],
  [
    "human",
    `Customer data:
{application}

Policy result:
{policyResult}

Write a short analyst note with next steps.`,
  ],
]);

export async function buildKycNote(rawInput: unknown) {
  const application = KycApplicationSchema.parse(rawInput);
  const policyResult = assessKycRisk(application);

  const chain = prompt.pipe(llm).pipe(new StringOutputParser());

  const note = await chain.invoke({
    application: JSON.stringify(application),
    policyResult: JSON.stringify(policyResult),
  });

  return {
    ...policyResult,
    note,
  };
}

3) Add tools for document review and case escalation

This is where LangChain becomes useful in production. The agent can call tools to fetch document metadata or create a case without embedding those side effects in prompts.

import { tool } from "@langchain/core/tools";
import { z } from "zod";

export const fetchDocumentMetadata = tool(
  async ({ documentId }) => {
    // Replace with your OCR/document service lookup
    return {
      documentId,
      extractedCountry: "GB",
      extractedName: "Jane Doe",
      confidence: 0.96,
    };
  },
  {
    name: "fetch_document_metadata",
    description: "Fetch extracted metadata for a submitted identity document.",
    schema: z.object({
      documentId: z.string(),
    }),
  }
);

export const createManualReviewCase = tool(
  async ({ customerId, reason }) => {
    // Replace with your case management API call
    return {
      caseId: `case_${customerId}`,
      status: "queued",
      reason,
    };
    
},
{
    name: "create_manual_review_case",
    description: "Create an analyst review case for high-risk KYC applications.",
schema:
      z.object({
        customerId: z.string(),
        reason: z.string(),
      }),
});

Details that matter

Use tool results to enrich the application before you score it. Keep the final approval decision outside the LLM so you can prove how it was made during audit.

Production Considerations

  • Data residency

    • Keep PII in-region and choose model endpoints that satisfy your banking residency requirements.
    • If you cannot guarantee residency, redact before sending any text to the model.
  • Auditability

    • Store raw inputs, normalized fields, policy outputs, tool calls, prompts, completions, timestamps, and analyst overrides.
    • You need this trail for AML/KYC audit requests and internal model risk reviews.
  • Guardrails


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