Skip to main content

KYC Verification

This guide explains how to implement the KYC (Know Your Customer) verification process for members in your application. KYC verification is required before members can receive commission payouts.

Overview

The MLM Platform supports two KYC providers:
ProviderDescriptionUse Case
ManualDocument upload with admin reviewDefault option, full control over verification
SumsubAutomated verification via Sumsub SDKFaster verification, reduced admin workload
The KYC provider is configured at the tenant level. Contact support to switch providers.

KYC Verification Flow

The following flowchart illustrates the complete KYC verification process:

Implementation Guide

Step 1: Start KYC Verification

Begin the KYC process by calling the start endpoint:
POST /api/v1/members/{userId}/kyc/start
Response for Manual KYC:
{
  "kycRecordId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
  "provider": "manual",
  "status": "not_started",
  "uploadUrl": "https://api.mlm-platform.com/api/v1/members/{userId}/kyc/documents",
  "requiredDocuments": [
    {
      "documentType": "government_id_front",
      "label": "Government ID (Front)",
      "description": "Front side of your government-issued ID",
      "required": true,
      "acceptedFormats": ["image/jpeg", "image/png", "application/pdf"]
    },
    {
      "documentType": "government_id_back",
      "label": "Government ID (Back)",
      "description": "Back side of your government-issued ID",
      "required": true,
      "acceptedFormats": ["image/jpeg", "image/png", "application/pdf"]
    },
    {
      "documentType": "selfie",
      "label": "Selfie with ID",
      "description": "A clear photo of yourself holding your ID",
      "required": true,
      "acceptedFormats": ["image/jpeg", "image/png"]
    }
  ],
  "message": "Please upload the required documents to complete verification."
}
Response for Sumsub KYC:
{
  "kycRecordId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
  "provider": "sumsub",
  "status": "not_started",
  "sdkConfig": {
    "accessToken": "sbx:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "expiresAt": "2024-01-15T12:30:00Z",
    "flowName": "basic-kyc-flow",
    "applicantId": "65a1b2c3d4e5f6g7h8i9j0"
  },
  "message": "Complete verification using the Sumsub widget."
}

Step 2: Upload Documents (Manual KYC)

For manual KYC, upload each required document using a two-step process:

2a. Get a Signed Upload URL

POST /api/v1/members/{userId}/kyc/documents/upload-url
Content-Type: application/json

{
  "documentType": "government_id_front",
  "fileName": "drivers-license-front.jpg",
  "mimeType": "image/jpeg"
}
Response:
{
  "uploadUrl": "https://storage.supabase.co/...",
  "filePath": "tenant-123/user-456/government_id_front_abc123.jpg",
  "expiresInSeconds": 3600
}

2b. Upload the File

Upload the file directly to the signed URL:
PUT {uploadUrl}
Content-Type: image/jpeg

[binary file data]

2c. Record the Document

After successful upload, record the document metadata:
POST /api/v1/members/{userId}/kyc/documents
Content-Type: application/json

{
  "documentType": "government_id_front",
  "fileName": "drivers-license-front.jpg",
  "mimeType": "image/jpeg",
  "fileSize": 245678,
  "filePath": "tenant-123/user-456/government_id_front_abc123.jpg"
}
Repeat steps 2a-2c for each document in the requiredDocuments array.

Step 3: Check KYC Status

Poll the status endpoint to track verification progress:
GET /api/v1/members/{userId}/kyc
Response:
{
  "id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
  "status": "pending",
  "provider": "manual",
  "documents": [
    {
      "id": "doc-1",
      "documentType": "government_id_front",
      "fileName": "drivers-license-front.jpg",
      "status": "pending",
      "createdAt": "2024-01-15T10:00:00Z"
    },
    {
      "id": "doc-2",
      "documentType": "government_id_back",
      "fileName": "drivers-license-back.jpg",
      "status": "pending",
      "createdAt": "2024-01-15T10:01:00Z"
    }
  ],
  "createdAt": "2024-01-15T10:00:00Z"
}

KYC Status Values

StatusDescriptionNext Action
not_startedKYC record created, no documents uploadedUpload required documents
pendingDocuments submitted, awaiting admin reviewWait for admin decision
approvedKYC verification approvedMember can receive payouts
rejectedKYC verification rejectedReview rejection reason
resubmit_requiredAdditional documents or corrections neededUpload corrected documents

Admin Review Process

Administrators review KYC submissions through the Admin Dashboard or API.

List Pending Reviews

GET /api/v1/admin/kyc/pending

View KYC Details

GET /api/v1/admin/kyc/{kycId}

Download Document for Review

GET /api/v1/admin/kyc/documents/{documentId}

Approve KYC

POST /api/v1/admin/kyc/{kycId}/approve
Content-Type: application/json

{
  "notes": "All documents verified successfully"
}

Reject KYC

POST /api/v1/admin/kyc/{kycId}/reject
Content-Type: application/json

{
  "reason": "ID document is expired. Please upload a valid, non-expired ID."
}

Request Resubmission

POST /api/v1/admin/kyc/{kycId}/request-resubmit
Content-Type: application/json

{
  "reason": "Selfie photo is blurry. Please upload a clearer photo.",
  "requiredDocuments": ["selfie"]
}

OCR-Extracted Data

For supported document types, the platform automatically extracts data using OCR:
Document TypeExtracted Fields
government_id_frontFull name, date of birth, address
government_id_backAddress (if present)
passportFull name, date of birth, nationality
proof_of_addressAddress, document date
Extracted data is available in the admin review interface and can be edited before approval.

Best Practices

Validate Before Upload

Check file size and format on the client before uploading to avoid rejected uploads.

Show Progress

Display upload progress and status updates to keep members informed.

Handle Errors Gracefully

Provide clear error messages when uploads fail or documents are rejected.

Secure File Handling

Never store KYC documents on your own servers. Use the signed URLs provided.

Error Handling

Error CodeDescriptionResolution
400Invalid request (missing fields, invalid format)Check request body matches schema
401UnauthorizedVerify API key is valid
403ForbiddenEnsure user belongs to your tenant
404KYC record or document not foundVerify the ID is correct
409KYC already in progress or completedCheck current status before starting
413File too largeReduce file size (max 10MB)

Webhooks

Configure webhooks to receive real-time notifications when KYC status changes:
EventDescription
kyc.submittedMember submitted all required documents
kyc.approvedAdmin approved the KYC verification
kyc.rejectedAdmin rejected the KYC verification
kyc.resubmit_requiredAdmin requested document resubmission
See the Webhooks Guide for configuration details.

Complete Code Example

Here’s a complete example of implementing KYC document upload in JavaScript:
async function uploadKycDocuments(userId, files) {
  const apiKey = process.env.MLM_API_KEY;
  const baseUrl = 'https://api.mlm-platform.com/api/v1';
  
  // Step 1: Start KYC
  const startResponse = await fetch(
    `${baseUrl}/members/${userId}/kyc/start`,
    {
      method: 'POST',
      headers: { 'x-tenant-api-key': apiKey }
    }
  );
  const { kycRecordId, requiredDocuments } = await startResponse.json();
  
  // Step 2: Upload each document
  for (const doc of requiredDocuments) {
    const file = files[doc.documentType];
    if (!file) continue;
    
    // 2a: Get signed URL
    const urlResponse = await fetch(
      `${baseUrl}/members/${userId}/kyc/documents/upload-url`,
      {
        method: 'POST',
        headers: {
          'x-tenant-api-key': apiKey,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          documentType: doc.documentType,
          fileName: file.name,
          mimeType: file.type
        })
      }
    );
    const { uploadUrl, filePath } = await urlResponse.json();
    
    // 2b: Upload file
    await fetch(uploadUrl, {
      method: 'PUT',
      headers: { 'Content-Type': file.type },
      body: file
    });
    
    // 2c: Record document
    await fetch(
      `${baseUrl}/members/${userId}/kyc/documents`,
      {
        method: 'POST',
        headers: {
          'x-tenant-api-key': apiKey,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          documentType: doc.documentType,
          fileName: file.name,
          mimeType: file.type,
          fileSize: file.size,
          filePath
        })
      }
    );
  }
  
  return kycRecordId;
}

Next Steps