Legacy Verification Modules

This page documents FaceSign's legacy modules system. For new implementations, we recommend using the node-based flow system which provides more flexibility and control over verification processes.

Available node types

FaceSign provides several node types that you can combine in your flows:

Start Node

Entry point for every verification flow
Learn more →

Email Verification

Verify user email addresses with OTP codes
Learn more →

SMS Verification

Send verification codes via SMS
Learn more →

Liveness Detection

Biometric liveness verification with face scanning
Learn more →

Document Scan

Scan and authenticate government-issued documents
Learn more →

Conversation

AI-powered conversational interactions
Learn more →

End Node

Terminal point for flow completion
Learn more →

Node types

Each node has a specific type identifier that you use when creating flows:

  • Name
    start
    Type
    node
    Description

    Entry point node - every flow must begin with this

  • Name
    end
    Type
    node
    Description

    Terminal node - marks successful completion of the flow

  • Name
    conversation
    Type
    node
    Description

    AI-powered conversational interaction node

  • Name
    email_verification
    Type
    node
    Description

    Email OTP verification node

  • Name
    sms_verification
    Type
    node
    Description

    SMS OTP verification node

  • Name
    liveness_detection
    Type
    node
    Description

    Biometric liveness verification node

  • Name
    document_scan
    Type
    node
    Description

    Government document scanning and authentication node

  • Name
    age_estimation
    Type
    node
    Description

    AI-powered age verification and estimation node


Creating flows

Basic flow configuration

Create flows by defining nodes and edges that connect them:

Basic flow setup

const { session, clientSecret } = await client.sessions.create({
  clientReferenceId: 'user_123',
  flow: {
    nodes: [
      { id: 'start', type: 'start' },
      { id: 'email', type: 'email_verification' },
      { id: 'liveness', type: 'liveness_detection' },
      { id: 'document', type: 'document_scan' },
      { id: 'end', type: 'end' }
    ],
    edges: [
      { id: 'e1', source: 'start', target: 'email' },
      { id: 'e2', source: 'email', target: 'liveness' },
      { id: 'e3', source: 'liveness', target: 'document' },
      { id: 'e4', source: 'document', target: 'end' }
    ]
  }
});

Advanced flow configuration

Nodes can accept configuration options and define conditional outcomes:

Advanced flow configuration

const { session, clientSecret } = await client.sessions.create({
  clientReferenceId: 'user_123',
  flow: {
    nodes: [
      { id: 'start', type: 'start' },
      { 
        id: 'email', 
        type: 'email_verification',
        config: {
          email: 'user@example.com', // Pre-fill email
          name: 'John Doe' // Personalize email
        }
      },
      { 
        id: 'liveness', 
        type: 'liveness_detection',
        outcomes: {
          livenessDetected: 'document',
          deepfakeDetected: 'end_failure'
        }
      },
      { 
        id: 'document', 
        type: 'document_scan',
        outcomes: {
          documentVerified: 'end_success',
          documentRejected: 'end_failure'
        }
      },
      { id: 'end_success', type: 'end' },
      { id: 'end_failure', type: 'end' }
    ],
    edges: [
      { id: 'e1', source: 'start', target: 'email' },
      { id: 'e2', source: 'email', target: 'liveness' },
      { id: 'e3', source: 'liveness', target: 'document', condition: 'livenessDetected' },
      { id: 'e4', source: 'liveness', target: 'end_failure', condition: 'deepfakeDetected' },
      { id: 'e5', source: 'document', target: 'end_success', condition: 'documentVerified' },
      { id: 'e6', source: 'document', target: 'end_failure', condition: 'documentRejected' }
    ]
  }
});

Common module combinations

Basic identity verification

Perfect for user onboarding and account verification:

Basic identity flow

const basicIdentityModules = [
  { type: 'emailVerification' },
  { type: 'identityVerification' }
];

Financial services KYC

Comprehensive verification for financial applications:

Financial KYC flow

const kycModules = [
  { type: 'emailVerification' },
  { type: 'smsVerification' },
  { type: 'identityVerification' },
  { type: 'documentAuthentication' },
  { 
    type: 'proofOfIntent',
    requestedData: [
      {
        key: 'kyc_consent',
        isRequired: true,
        description: 'I consent to identity verification as required by law'
      }
    ]
  }
];

Age-restricted services

For platforms requiring age verification:

Age verification flow

const ageVerificationModules = [
  { type: 'emailVerification' },
  {
    type: 'ageEstimation',
    age: 21 // Must be 21 or older
  },
  { type: 'documentAuthentication' }, // Backup verification
  {
    type: 'proofOfIntent',
    requestedData: [
      {
        key: 'age_verification_consent',
        isRequired: true,
        description: 'I confirm that I am of legal age to use this service'
      }
    ]
  }
];

High-security applications

Maximum security for sensitive applications:

High-security flow

const highSecurityModules = [
  { type: 'emailVerification' },
  { type: 'smsVerification' },
  { type: 'identityVerification' },
  { type: 'documentAuthentication' },
  { type: 'knowledgeVerify' },
  {
    type: 'proofOfIntent',
    requestedData: [
      {
        key: 'security_acknowledgment',
        isRequired: true,
        description: 'I understand this is a high-security verification process'
      }
    ]
  }
];

Quick social verification

Lightweight verification for social platforms:

Social platform flow

const socialVerificationModules = [
  { type: 'emailVerification' },
  {
    type: 'ageEstimation',
    age: 13 // COPPA compliance
  }
];

Dynamic module selection

User-based module selection

Choose modules based on user characteristics:

Dynamic module selection

async function selectModulesForUser(userId: string, verificationLevel: 'basic' | 'standard' | 'premium') {
  const user = await db.users.findUnique({ where: { id: userId } });
  
  const baseModules = [{ type: 'emailVerification' }];
  
  switch (verificationLevel) {
    case 'basic':
      return baseModules;
      
    case 'standard':
      return [
        ...baseModules,
        { type: 'identityVerification' }
      ];
      
    case 'premium':
      return [
        ...baseModules,
        { type: 'smsVerification' },
        { type: 'identityVerification' },
        { type: 'documentAuthentication' }
      ];
  }
}

async function createAdaptiveSession(userId: string, verificationLevel: string) {
  const modules = await selectModulesForUser(userId, verificationLevel);
  
  return client.sessions.create({
    clientReferenceId: userId,
    modules,
    metadata: {
      verificationLevel,
      userId
    }
  });
}

Risk-based module selection

Adjust verification requirements based on risk assessment:

Risk-based verification

interface RiskAssessment {
  score: number; // 0-100, higher = more risky
  factors: string[];
  recommendation: 'low' | 'medium' | 'high';
}

async function assessUserRisk(userId: string, ipAddress: string): Promise<RiskAssessment> {
  // Implement your risk assessment logic
  const factors = [];
  let score = 0;
  
  // Check if new user
  const user = await db.users.findUnique({ where: { id: userId } });
  if (!user.emailVerified) {
    score += 20;
    factors.push('unverified_email');
  }
  
  // Check IP reputation
  const ipRisk = await checkIPReputation(ipAddress);
  if (ipRisk.isVPN || ipRisk.isTor) {
    score += 30;
    factors.push('suspicious_ip');
  }
  
  // Check device fingerprint
  const deviceRisk = await checkDeviceRisk(userId);
  if (deviceRisk.isNewDevice) {
    score += 15;
    factors.push('new_device');
  }
  
  let recommendation: 'low' | 'medium' | 'high';
  if (score < 25) recommendation = 'low';
  else if (score < 60) recommendation = 'medium';
  else recommendation = 'high';
  
  return { score, factors, recommendation };
}

async function createRiskBasedSession(userId: string, ipAddress: string) {
  const riskAssessment = await assessUserRisk(userId, ipAddress);
  
  let modules;
  
  switch (riskAssessment.recommendation) {
    case 'low':
      modules = [
        { type: 'emailVerification' }
      ];
      break;
      
    case 'medium':
      modules = [
        { type: 'emailVerification' },
        { type: 'identityVerification' }
      ];
      break;
      
    case 'high':
      modules = [
        { type: 'emailVerification' },
        { type: 'smsVerification' },
        { type: 'identityVerification' },
        { type: 'documentAuthentication' },
        { type: 'knowledgeVerify' }
      ];
      break;
  }
  
  return client.sessions.create({
    clientReferenceId: userId,
    modules,
    metadata: {
      riskScore: riskAssessment.score,
      riskFactors: riskAssessment.factors,
      ipAddress
    }
  });
}

Module sequencing

Sequential vs. parallel verification

FaceSign automatically handles module sequencing for optimal user experience:

Module flow patterns

// Sequential flow - one module at a time
const sequentialModules = [
  { type: 'emailVerification' },    // Step 1: Verify email first
  { type: 'smsVerification' },      // Step 2: Then SMS
  { type: 'identityVerification' }  // Step 3: Finally biometric
];

// Parallel flow - multiple modules can be completed in any order
const flexibleModules = [
  { type: 'emailVerification' },
  { type: 'smsVerification' },
  // These can be completed in any order
];

Conditional module activation

Use custom flows to activate modules based on previous results:

Conditional module flows

// Example: Only require document verification if biometric confidence is low
const conditionalFlow = {
  nodes: [
    {
      id: 'email_verification',
      type: 'email',
      outcomes: { success: 'identity_check', failure: 'end_failure' }
    },
    {
      id: 'identity_check',
      type: 'identity',
      outcomes: { 
        high_confidence: 'end_success',
        low_confidence: 'document_verification',
        failure: 'end_failure'
      }
    },
    {
      id: 'document_verification',
      type: 'document',
      outcomes: { success: 'end_success', failure: 'end_failure' }
    }
  ],
  edges: [
    { source: 'email_verification', target: 'identity_check', condition: 'success' },
    { source: 'identity_check', target: 'document_verification', condition: 'low_confidence' },
    // ... other edges
  ]
};

const { session, clientSecret } = await client.sessions.create({
  clientReferenceId: 'user_123',
  flow: conditionalFlow, // Use custom flow instead of simple modules array
  modules: [] // Empty when using custom flow
});

Module results and data

Accessing verification results

Each module provides specific data in the session report:

Module result handling

// Handle webhook with module-specific results
app.post('/webhooks/facesign', async (req, res) => {
  const { type, data } = req.body;
  
  if (type === 'session.completed') {
    const { session } = data;
    const { report } = session;
    
    // Check overall verification status
    if (report.isVerified) {
      // Extract data from different modules
      const extractedData = report.extractedData || {};
      
      // Email verification results
      if (extractedData.email) {
        await updateUserEmail(session.settings.clientReferenceId, extractedData.email);
      }
      
      // SMS verification results
      if (extractedData.phone) {
        await updateUserPhone(session.settings.clientReferenceId, extractedData.phone);
      }
      
      // Document authentication results
      if (extractedData.firstName && extractedData.lastName) {
        await updateUserName(
          session.settings.clientReferenceId,
          extractedData.firstName,
          extractedData.lastName
        );
      }
      
      // Age estimation results
      if (report.aiAnalysis?.ageMin && report.aiAnalysis?.ageMax) {
        await updateUserAgeRange(
          session.settings.clientReferenceId,
          report.aiAnalysis.ageMin,
          report.aiAnalysis.ageMax
        );
      }
      
      // Proof of intent results
      if (extractedData.terms_acceptance) {
        await recordUserConsent(
          session.settings.clientReferenceId,
          'terms_of_service',
          extractedData.terms_acceptance === 'true'
        );
      }
    }
  }
  
  res.status(200).send('OK');
});

Best practices

1. Start simple, add complexity gradually

Begin with basic modules and add more based on your needs:

Progressive module adoption

// Phase 1: Basic email verification
const phase1Modules = [{ type: 'emailVerification' }];

// Phase 2: Add biometric verification
const phase2Modules = [
  { type: 'emailVerification' },
  { type: 'identityVerification' }
];

// Phase 3: Full verification suite
const phase3Modules = [
  { type: 'emailVerification' },
  { type: 'smsVerification' },
  { type: 'identityVerification' },
  { type: 'documentAuthentication' }
];

2. Consider user experience

Balance security with user experience:

UX-optimized module selection

// Good: Progressive verification
const userFriendlyFlow = [
  { type: 'emailVerification' },     // Quick and familiar
  { type: 'identityVerification' }   // Fast biometric check
];

// Avoid: Too many verification steps at once
const overwhelmingFlow = [
  { type: 'emailVerification' },
  { type: 'smsVerification' },
  { type: 'identityVerification' },
  { type: 'documentAuthentication' },
  { type: 'knowledgeVerify' },
  { type: 'ageEstimation' }
]; // This may cause user abandonment

3. Match modules to use case

Choose modules that make sense for your application:

Use case specific modules

const USE_CASE_MODULES = {
  social_media: [
    { type: 'emailVerification' },
    { type: 'ageEstimation', age: 13 }
  ],
  
  dating_app: [
    { type: 'emailVerification' },
    { type: 'identityVerification' }, // Prevent catfishing
    { type: 'ageEstimation', age: 18 }
  ],
  
  financial_services: [
    { type: 'emailVerification' },
    { type: 'smsVerification' },
    { type: 'identityVerification' },
    { type: 'documentAuthentication' },
    { type: 'proofOfIntent' }
  ],
  
  gaming: [
    { type: 'emailVerification' },
    { type: 'ageEstimation', age: 13 }
  ]
};

4. Handle module failures gracefully

Provide fallback options when modules fail:

Module fallback strategies

// If biometric verification fails, fall back to document verification
const fallbackFlow = {
  primary: [
    { type: 'emailVerification' },
    { type: 'identityVerification' }
  ],
  fallback: [
    { type: 'emailVerification' },
    { type: 'documentAuthentication' }, // Alternative to biometric
    { type: 'knowledgeVerify' }         // Additional security
  ]
};

Next steps

Explore individual module documentation for detailed configuration options: