Skip to main content

Complete KYB Verification Workflow

gu1’s KYB workflow automates the entire business verification process from initial data collection to ongoing monitoring. This guide walks through each step with technical implementation details.

Workflow Overview

Step 1: Initial Data Collection

What to Collect

Gather basic business information from your application form:
  • Company Name: Legal name and trade name
  • Registration Number: Government-issued business registration
  • Tax ID: EIN (US), CNPJ (Brazil), RFC (Mexico), etc.
  • Incorporation Date: When the business was formed
  • Country: Jurisdiction of incorporation
  • Business Address: Registered office location
  • Industry: Type of business activity
  • Expected Transaction Volume: Monthly/annual estimates

Example Application Form

const businessApplication = {
  legalName: "Tech Solutions Sociedade AnΓ΄nima",
  tradeName: "Tech Solutions",
  registrationNumber: "12.345.678/0001-90",
  taxId: "12.345.678/0001-90",
  incorporationDate: "2020-06-15",
  country: "BR",
  address: {
    street: "Av. Paulista, 1000",
    city: "SΓ£o Paulo",
    state: "SP",
    postalCode: "01310-100"
  },
  industry: "Software Development",
  website: "https://techsolutions.com.br",
  expectedMonthlyVolume: 250000,
  businessDescription: "B2B SaaS platform for enterprise clients"
};

Step 2: Create Business Entity

Use the Entities API to create a company entity in gu1:
curl -X POST http://api.gu1.ai/entities \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "company",
    "externalId": "business_12345",
    "name": "Tech Solutions Sociedade AnΓ΄nima",
    "taxId": "12.345.678/0001-90",
    "countryCode": "BR",
    "entityData": {
      "company": {
        "legalName": "Tech Solutions Sociedade AnΓ΄nima",
        "tradeName": "Tech Solutions",
        "incorporationDate": "2020-06-15",
        "industry": "Software Development",
        "employeeCount": 50,
        "revenue": 5000000
      }
    },
    "attributes": {
      "registrationNumber": "12.345.678/0001-90",
      "website": "https://techsolutions.com.br",
      "registeredAddress": "Av. Paulista, 1000, SΓ£o Paulo",
      "expectedMonthlyVolume": 250000,
      "applicationDate": "2024-10-03T10:00:00Z"
    }
  }'
Response:
{
  "success": true,
  "entity": {
    "id": "660e9511-f39c-52e5-b827-557766551111",
    "externalId": "business_12345",
    "type": "company",
    "name": "Tech Solutions Sociedade AnΓ΄nima",
    "riskScore": 0,
    "status": "under_review",
    "createdAt": "2024-10-03T10:00:00Z"
  }
}

Step 3: Document Upload & Verification

Upload required business documents for verification:

Required Documents

  1. Articles of Incorporation - Founding document
  2. Business License - Government-issued operating permit
  3. Tax Certificate - Proof of tax registration
  4. Proof of Address - Utility bill or bank statement
  5. Beneficial Ownership Declaration - UBO information

Upload Documents

async function uploadBusinessDocuments(entityId, documents) {
  const documentUploads = [];

  for (const doc of documents) {
    const formData = new FormData();
    formData.append('file', doc.file);
    formData.append('documentType', doc.type);
    formData.append('entityId', entityId);

    const response = await fetch('http://api.gu1.ai/documents', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${API_KEY}`
      },
      body: formData
    });

    documentUploads.push(await response.json());
  }

  return documentUploads;
}

// Usage
const documents = [
  { file: articlesOfIncorporation, type: 'articles_of_incorporation' },
  { file: businessLicense, type: 'business_license' },
  { file: taxCertificate, type: 'tax_certificate' },
  { file: proofOfAddress, type: 'proof_of_address' }
];

const uploadedDocs = await uploadBusinessDocuments(entityId, documents);

Step 4: AI Analysis & Risk Assessment

Request AI-powered analysis of the business entity:
async function requestAIAnalysis(entityId) {
  const response = await fetch(
    `http://api.gu1.ai/entities/${entityId}/ai-analysis`,
    {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${API_KEY}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        analysisType: 'kyb_verification',
        includeDocuments: true,
        checkSanctions: true,
        checkAdverseMedia: true
      })
    }
  );

  return await response.json();
}
AI Analysis Response:
{
  "analysisId": "analysis_abc123",
  "entityId": "660e9511-f39c-52e5-b827-557766551111",
  "riskScore": 35,
  "riskLevel": "medium",
  "findings": {
    "documentVerification": {
      "status": "verified",
      "confidence": 0.95,
      "articlesOfIncorporation": "valid",
      "businessLicense": "valid",
      "taxCertificate": "valid"
    },
    "businessLegitimacy": {
      "status": "verified",
      "registrationVerified": true,
      "taxIdValid": true,
      "addressConfirmed": true
    },
    "sanctionsScreening": {
      "status": "clear",
      "listsChecked": ["OFAC", "UN", "EU", "UK"],
      "matches": []
    },
    "adverseMedia": {
      "status": "minor_flags",
      "findings": [
        {
          "source": "news_article",
          "date": "2023-05-15",
          "summary": "Small tax dispute resolved",
          "severity": "low"
        }
      ]
    },
    "riskFactors": [
      {
        "factor": "new_business",
        "impact": 20,
        "description": "Company incorporated less than 5 years ago"
      },
      {
        "factor": "high_growth_industry",
        "impact": 15,
        "description": "Software industry has elevated fraud risk"
      },
      {
        "factor": "clean_compliance_history",
        "impact": -10,
        "description": "No regulatory violations found"
      }
    ]
  },
  "recommendation": "approve_with_monitoring",
  "nextSteps": [
    "Verify beneficial ownership",
    "Set transaction monitoring thresholds",
    "Schedule 6-month review"
  ]
}

Step 5: Risk-Based Decision

Based on the risk score, route to appropriate workflow:
async function processKYBDecision(analysisResult) {
  const { riskScore, riskLevel, recommendation } = analysisResult;

  if (riskScore < 30) {
    // Low Risk - Auto-approve
    return await autoApprove(analysisResult.entityId);

  } else if (riskScore < 70) {
    // Medium Risk - Enhanced Due Diligence
    return await enhancedDueDiligence(analysisResult.entityId);

  } else {
    // High Risk - Manual Review Required
    return await flagForManualReview(analysisResult.entityId, analysisResult.findings);
  }
}

async function autoApprove(entityId) {
  // Update entity status to approved
  await fetch(`http://api.gu1.ai/entities/${entityId}`, {
    method: 'PATCH',
    headers: {
      'Authorization': `Bearer ${API_KEY}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      attributes: {
        kybStatus: 'approved',
        approvedAt: new Date().toISOString(),
        approvedBy: 'automated_system',
        reviewNotes: 'Low risk - auto-approved'
      }
    })
  });

  // Send approval notification
  await sendApprovalEmail(entityId);

  // Enable monitoring
  await enableOngoingMonitoring(entityId);

  return { status: 'approved', action: 'auto_approved' };
}

async function enhancedDueDiligence(entityId) {
  // Request additional information
  await requestAdditionalDocs(entityId, [
    'financial_statements',
    'ownership_structure',
    'source_of_funds'
  ]);

  // Update status
  await updateEntityStatus(entityId, 'enhanced_due_diligence');

  return { status: 'pending', action: 'edd_required' };
}

async function flagForManualReview(entityId, findings) {
  // Create review task
  await createReviewTask({
    entityId,
    priority: 'high',
    assignTo: 'compliance_team',
    findings,
    dueDate: new Date(Date.now() + 24 * 60 * 60 * 1000) // 24 hours
  });

  return { status: 'manual_review', action: 'flagged_for_review' };
}

Step 6: Beneficial Ownership Verification

Identify and verify Ultimate Beneficial Owners (UBOs):
async function verifyBeneficialOwners(entityId, owners) {
  const verifiedOwners = [];

  for (const owner of owners) {
    // Create person entity for each UBO
    const uboEntity = await fetch('http://api.gu1.ai/entities', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${API_KEY}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        type: 'person',
        externalId: `ubo_${owner.id}`,
        name: `${owner.firstName} ${owner.lastName}`,
        countryCode: owner.nationality,
        entityData: {
          person: {
            firstName: owner.firstName,
            lastName: owner.lastName,
            dateOfBirth: owner.dateOfBirth,
            nationality: owner.nationality
          }
        },
        attributes: {
          ownershipPercentage: owner.ownership,
          isPEP: owner.isPEP,
          relationshipToCompany: 'beneficial_owner'
        }
      })
    });

    const ubo = await uboEntity.json();

    // Run KYC checks on UBO
    const kycResult = await runKYCCheck(ubo.entity.id);

    // Create relationship between company and UBO
    await createRelationship({
      fromEntityId: entityId,
      toEntityId: ubo.entity.id,
      relationshipType: 'beneficial_owner',
      ownershipPercentage: owner.ownership
    });

    verifiedOwners.push({
      ...ubo.entity,
      kycStatus: kycResult.status,
      riskScore: kycResult.riskScore
    });
  }

  return verifiedOwners;
}

Step 7: Setup Ongoing Monitoring

Enable continuous monitoring for approved businesses:
async function setupOngoingMonitoring(entityId) {
  // Configure monitoring rules
  await fetch('http://api.gu1.ai/rules', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${API_KEY}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      name: `KYB Monitoring - ${entityId}`,
      entityId: entityId,
      rules: [
        {
          type: 'sanctions_screening',
          frequency: 'daily',
          action: 'alert_compliance_team'
        },
        {
          type: 'adverse_media',
          frequency: 'weekly',
          action: 'create_review_task'
        },
        {
          type: 'ownership_changes',
          frequency: 'realtime',
          action: 'trigger_reverification'
        },
        {
          type: 'transaction_anomalies',
          threshold: 'high',
          action: 'flag_for_review'
        }
      ]
    })
  });

  // Setup webhook notifications
  await configureWebhook({
    entityId,
    events: [
      'sanctions_match',
      'adverse_media_found',
      'ownership_change',
      'transaction_anomaly'
    ],
    url: 'https://your-app.com/webhooks/kyb-alerts'
  });
}

Step 8: Status Updates & Notifications

Keep the business informed throughout the process:
async function sendStatusUpdate(entityId, status, details) {
  // Get entity details
  const entity = await getEntity(entityId);

  // Send email notification
  await sendEmail({
    to: entity.attributes.contactEmail,
    subject: `KYB Verification Update - ${status}`,
    template: 'kyb_status_update',
    data: {
      companyName: entity.name,
      status: status,
      details: details,
      nextSteps: getNextSteps(status),
      dashboardUrl: `https://app.gu1.ai/onboarding/${entityId}`
    }
  });

  // Send webhook to your system
  await sendWebhook({
    event: 'kyb_status_changed',
    entityId: entityId,
    status: status,
    timestamp: new Date().toISOString(),
    details: details
  });
}

// Status update examples
await sendStatusUpdate(entityId, 'documents_received', {
  message: 'We have received your documents and are processing them.'
});

await sendStatusUpdate(entityId, 'approved', {
  message: 'Your business has been verified and approved!',
  approvalDate: new Date().toISOString()
});

await sendStatusUpdate(entityId, 'additional_info_required', {
  message: 'We need additional information to complete your verification.',
  requiredDocuments: ['financial_statements', 'ownership_structure']
});

Complete Workflow Implementation

Here’s a complete function that orchestrates the entire KYB workflow:
async function executeKYBWorkflow(applicationData) {
  try {
    // Step 1 & 2: Create entity
    console.log('Creating business entity...');
    const entity = await createBusinessEntity(applicationData);

    // Step 3: Upload documents
    console.log('Uploading verification documents...');
    await uploadBusinessDocuments(entity.id, applicationData.documents);

    // Step 4: Request AI analysis
    console.log('Running AI analysis...');
    const analysis = await requestAIAnalysis(entity.id);

    // Step 5: Make risk-based decision
    console.log('Processing KYB decision...');
    const decision = await processKYBDecision(analysis);

    if (decision.status === 'approved') {
      // Step 6: Verify UBOs (for approved entities)
      console.log('Verifying beneficial owners...');
      await verifyBeneficialOwners(entity.id, applicationData.beneficialOwners);

      // Step 7: Setup monitoring
      console.log('Setting up ongoing monitoring...');
      await setupOngoingMonitoring(entity.id);
    }

    // Step 8: Send status update
    console.log('Sending status notification...');
    await sendStatusUpdate(entity.id, decision.status, analysis.findings);

    return {
      success: true,
      entityId: entity.id,
      status: decision.status,
      riskScore: analysis.riskScore,
      decision: decision
    };

  } catch (error) {
    console.error('KYB workflow error:', error);
    return {
      success: false,
      error: error.message
    };
  }
}

Timeline Estimates

Risk LevelAutomated ProcessingWith Manual ReviewTraditional Process
Low Risk2-5 minutes30 minutes2-3 days
Medium Risk15-30 minutes2-4 hours3-5 days
High Risk30-60 minutes1-2 days5-10 days

Next Steps