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

By Cyprian AaronsUpdated 2026-04-21
kyc-verificationcrewaitypescriptbanking

A KYC verification agent automates the first pass of customer due diligence: it collects identity data, checks it against policy, flags mismatches, and prepares an auditable decision trail for compliance teams. In banking, that matters because onboarding speed is useless if you cannot prove why a customer was approved, rejected, or escalated.

Architecture

A production KYC agent in CrewAI needs a small set of focused components:

  • Customer intake layer

    • Normalizes inputs from onboarding forms, uploaded documents, and CRM records.
    • Validates required fields before any agent work starts.
  • Document extraction tool

    • Pulls structured data from passports, national IDs, utility bills, and proof-of-address documents.
    • Should return machine-readable JSON with confidence scores.
  • Policy evaluation agent

    • Compares extracted identity data against bank KYC rules.
    • Checks name consistency, DOB match, address validity, and document freshness.
  • Sanctions / PEP screening tool

    • Queries approved screening providers or internal watchlists.
    • Returns hits with match rationale, not just a boolean.
  • Audit logging layer

    • Stores prompts, tool outputs, timestamps, and final decisions.
    • Must be immutable enough for compliance review.
  • Escalation workflow

    • Routes low-confidence or high-risk cases to human reviewers.
    • Keeps the agent out of final decision-making when policy requires manual approval.

Implementation

1) Install CrewAI for TypeScript and define your data model

CrewAI’s TypeScript SDK gives you the same core abstractions you need in Python: Agent, Task, and Crew. For banking use cases, keep your input and output contracts strict so every downstream system can validate them.

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

export const KycInputSchema = z.object({
  customerId: z.string(),
  fullName: z.string(),
  dateOfBirth: z.string(),
  address: z.string(),
  documentType: z.enum(["passport", "national_id", "drivers_license"]),
  documentNumber: z.string(),
});

export const KycResultSchema = z.object({
  riskLevel: z.enum(["low", "medium", "high"]),
  decision: z.enum(["approve", "reject", "manual_review"]),
  reasons: z.array(z.string()),
});

2) Create tools for document checks and screening

In banking, the agent should not “guess” from raw text alone. Wrap external services as tools so the model calls deterministic systems for extraction and screening.

import { Tool } from "@crewai/crewai";

export const extractDocumentTool = new Tool({
  name: "extract_document",
  description: "Extract structured identity fields from a government ID or proof-of-address document.",
  func: async (input: string) => {
    // Replace with OCR / IDP provider call
    const parsed = JSON.parse(input);
    return JSON.stringify({
      fullName: parsed.fullName,
      dateOfBirth: parsed.dateOfBirth,
      documentNumber: parsed.documentNumber,
      confidence: 0.97,
    });
  },
});

export const sanctionsScreenTool = new Tool({
  name: "sanctions_screen",
  description: "Screen a customer against sanctions and PEP lists.",
  func: async (name: string) => {
    // Replace with vendor API call
    return JSON.stringify({
      hit: false,
      matches: [],
      confidence: 0.91,
    });
  },
});

3) Define the KYC agent and task

Use one agent for policy reasoning and one task for producing an auditable outcome. Keep the prompt explicit about escalation thresholds and prohibited behavior.

import { Agent, Task } from "@crewai/crewai";

export const kycAgent = new Agent({
  role: "KYC Verification Analyst",
  goal:
    "Verify customer identity using bank policy, flag inconsistencies, and produce an audit-ready recommendation.",
  backstory:
    "You review onboarding cases for a regulated financial institution. You never approve cases with unresolved sanctions hits or insufficient evidence.",
});

export const kycTask = new Task({
  description:
    "Review the customer record, compare it with extracted document data, run sanctions screening if needed, and return a KYC decision with reasons.",
});

4) Run the crew and enforce schema validation

The key pattern is simple: let CrewAI reason over the case, but validate the result before any workflow continues. That keeps bad outputs from reaching onboarding systems.

import { Crew } from "@crewai/crewai";
import { KycInputSchema, KycResultSchema } from "./schemas";
import { kycAgent, kycTask } from "./kyc-agent";
import { extractDocumentTool, sanctionsScreenTool } from "./tools";

async function runKycCase(rawInput: unknown) {
  const input = KycInputSchema.parse(rawInput);

  const crew = new Crew({
    agents: [kycAgent],
    tasks: [kycTask],
    verbose: true,
    tools: [extractDocumentTool, sanctionsScreenTool],
  });

  const result = await crew.kickoff({
    customerId: input.customerId,
    fullName: input.fullName,
    dateOfBirth: input.dateOfBirth,
    address: input.address,
    documentType: input.documentType,
    documentNumber: input.documentNumber,
  });

  
Here is how to think about the output contract:

```ts
const parsed = KycResultSchema.parse(JSON.parse(result.toString()));

if (parsed.decision === "manual_review") {
  
}

For a real implementation, persist both the raw result and the validated parsed object to your audit store. That gives compliance teams traceability without trusting free-form text.

Production Considerations

  • Data residency

    • Keep OCR payloads, PII, and screening results inside approved regions.
    • If your bank operates across jurisdictions, route EU customer data to EU-hosted infrastructure only.
  • Auditability

Create immutable logs for:

  • input payload hash

  • tool calls

  • model version

  • final decision

  • human override events

  • Guardrails

Block auto-approval when any of these are true:

  • sanctions hit confidence exceeds threshold

  • address mismatch is unresolved

  • document authenticity score is below policy minimum

  • required fields are missing

  • Monitoring

Track:

  • manual review rate
  • false positive screening rate
  • average time-to-decision
  • tool failure rate

If those numbers drift after a model or vendor change, freeze rollout until compliance signs off.

Common Pitfalls

  1. Letting the LLM make final compliance decisions

    • Avoid this by making the agent recommend outcomes while policy code enforces thresholds.
    • In banking workflows, “approve” should be blocked by deterministic checks when required evidence is missing.
  2. Skipping schema validation on tool outputs

    • OCR vendors and screening APIs fail in messy ways.
    • Validate every response with zod before passing it into CrewAI or writing it to your case management system.
  3. Ignoring jurisdiction-specific storage rules

    • A single S3 bucket or shared database is usually not acceptable for regulated identity data.
    • Partition storage by region and retain evidence according to local retention policy.
  4. Not designing for human escalation

    • Some cases must go to a reviewer no matter how confident the model sounds.
    • Build an explicit manual_review path into the task output and make it first-class in your orchestration layer.

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