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

By Cyprian AaronsUpdated 2026-04-21
kyc-verificationcrewaitypescriptinsurance

A KYC verification agent for insurance takes customer identity data, checks it against policy and regulatory rules, and returns a decision with evidence. In practice, that means faster onboarding, fewer manual reviews, and a clean audit trail for compliance teams when a policyholder is flagged or approved.

Architecture

  • Input normalization layer

    • Accepts raw applicant data from CRM, broker portal, or underwriting forms.
    • Normalizes names, addresses, dates of birth, and document metadata before any agent work starts.
  • KYC verification agent

    • Uses CrewAI to orchestrate verification tasks.
    • Decides whether the applicant passes, needs manual review, or is rejected based on evidence.
  • Specialist tools

    • Document validation tool for passports, driver’s licenses, or national IDs.
    • Sanctions/PEP screening tool.
    • Address and phone verification tool.
    • Optional fraud/risk scoring tool.
  • Evidence store

    • Persists every check result, timestamps, source system responses, and model outputs.
    • Needed for auditability and regulator review.
  • Decision service

    • Converts agent output into insurance workflow states like approved, review_required, or rejected.
  • Compliance controls

    • Enforces data residency, retention windows, redaction, and access control.
    • Keeps PII handling aligned with local insurance regulations.

Implementation

1. Install the TypeScript dependencies

CrewAI’s TypeScript support is typically used alongside your own tools and API clients. Keep the agent thin; push external checks into explicit tool functions so every call is traceable.

npm install @crewai/core zod dotenv
npm install @azure/identity # if you use Azure-hosted identity/document services

2. Define the KYC tools

Use tools for each external verification step. In insurance, this matters because you want deterministic calls to sanctions providers, document services, and internal policy rules rather than one opaque prompt doing everything.

import "dotenv/config";
import { z } from "zod";
import { Agent } from "@crewai/core";

type KycInput = {
  fullName: string;
  dateOfBirth: string;
  country: string;
  documentId: string;
};

const sanctionsCheckTool = {
  name: "sanctions_check",
  description: "Checks the applicant against sanctions/PEP lists.",
  schema: z.object({
    fullName: z.string(),
    dateOfBirth: z.string(),
    country: z.string()
  }),
  async execute(input: { fullName: string; dateOfBirth: string; country: string }) {
    // Replace with real provider call
    return {
      matched: false,
      provider: "internal-screening-service",
      checkedAt: new Date().toISOString()
    };
  }
};

const documentValidationTool = {
  name: "document_validation",
  description: "Validates government-issued identity documents.",
  schema: z.object({
    documentId: z.string()
  }),
  async execute(input: { documentId: string }) {
    // Replace with real OCR/document verification API
    return {
      valid: true,
      confidence: 0.98,
      provider: "doc-verification-api"
    };
  }
};

const addressRiskTool = {
  name: "address_risk",
  description: "Checks whether the address is high-risk or inconsistent.",
  schema: z.object({
    country: z.string()
  }),
  async execute(input: { country: string }) {
    return {
      riskLevel: input.country === "IR" ? "high" : "low"
    };
  }
};

3. Create the CrewAI agent and run a structured task

The important pattern here is to make the agent produce a strict JSON decision. That gives underwriting systems something reliable to consume and makes audit logging straightforward.

import { Crew } from "@crewai/core";

const kycAgent = new Agent({
  role: "KYC Verification Agent",
  goal:
    "Verify insurance applicants using sanctions screening, document validation, and risk checks.",
  backstory:
    "You are a compliance-focused verifier for insurance onboarding. You must be strict about evidence and produce an auditable decision.",
});

async function verifyApplicant(applicant: KycInput) {
  const crew = new Crew({
    agents: [kycAgent],
    tasks: [
      {
        description:
          `Verify this applicant for insurance onboarding:\n` +
          `Name: ${applicant.fullName}\n` +
          `DOB: ${applicant.dateOfBirth}\n` +
          `Country: ${applicant.country}\n` +
          `Document ID: ${applicant.documentId}\n\n` +
          `Use only the available tools. Return JSON with fields:\n` +
          `{ decision, reasons[], evidence[], manualReviewRequired }`,
        expectedOutput:
          'Strict JSON object with decision = approved|review_required|rejected'
      }
    ],
    tools: [sanctionsCheckTool, documentValidationTool, addressRiskTool],
    verboseOutputLoggingEnabled: true,
  });

  const result = await crew.kickoff();
  return result;
}

verifyApplicant({
  fullName: "Amina Diallo",
  dateOfBirth: "1990-04-12",
  country: "KE",
  documentId: "ID-8839201"
}).then(console.log);

4. Add a decision wrapper for your insurance workflow

Do not let the agent directly mutate policy state. Wrap it in a service that maps outputs to workflow actions and stores evidence separately.

type KycDecision = {
0; // placeholder removed in your implementation
};

Use this pattern instead:

type KycResult = {
decision:
    | "approved"
    | "review_required"
    | "rejected";
reasons?: string[];
evidence?: unknown[];
manualReviewRequired?: boolean;
};

function mapDecision(resultTextOrObject): KycResult {
if (typeof resultTextOrObject === "string") {
return JSON.parse(resultTextOrObject) as KycResult;
}
return resultTextOrObject as KycResult;
}

In production, keep this mapping strict and validate the shape with Zod before writing to your case management system.

Production Considerations

  • Data residency

    • Keep applicant PII in-region if your insurer operates under local residency rules.
  • Audit logging

    • Store every tool call, response payload hash, timestamp, and final decision.
    • Regulators care more about traceability than clever prompts.
  • Guardrails

    • Force structured output with JSON schemas.
    • Block free-form decisions when required fields are missing or screening providers fail.
  • Monitoring

    • Track false positives on sanctions hits, manual review rates, latency per provider, and rejection rates by geography.
    • Sudden spikes usually mean upstream data quality issues or a broken screening integration.

Common Pitfalls

  • Using one prompt for all checks

    • Don’t ask the model to “figure out” identity verification from raw text alone.
    • Split work into explicit tools so each step is testable and auditable.
  • Ignoring partial failures

    • If sanctions screening times out but document validation passes, do not auto-approve.
    • Route to manual review with a reason like screening_provider_unavailable.
  • Storing raw PII in logs

    • Mask names, IDs, and DOBs in application logs.
    • Keep full values only in encrypted storage with tight access controls.
  • Letting the agent decide policy outcomes directly

    • The agent should recommend a KYC outcome.
    • Your underwriting service should own approval logic so compliance can change rules without retraining prompts.

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