Skip to content

YeboVerify — Product Requirements Document ​

B2B Identity Verification API for Africa Version 1.0 | March 2026


Table of Contents ​

  1. Executive Summary
  2. Vision & Mission
  3. Problem Statement
  4. Solution
  5. Features
  6. User Journeys
  7. Data Models
  8. API Reference
  9. Service Architecture
  10. SDKs
  11. Authentication
  12. Billing & Plans
  13. Technical Stack
  14. Security
  15. Gaps & Missing Features

1. Executive Summary ​

YeboVerify is a B2B identity verification API designed for African businesses. It provides automated KYC (Know Your Customer) verification by comparing a user's selfie against their government-issued ID document and extracting personal information via OCR.

Key Capabilities ​

  • Face Comparison: AWS Rekognition-powered face matching between ID document photo and live selfie
  • Document OCR: Google Gemini AI extraction of personal data from ID documents
  • Multi-Platform SDKs: Node.js, React, React Native, Flutter, Kotlin (Android), Swift (iOS)
  • Async Processing: Non-blocking verification with webhook notifications
  • Multi-Tenant: API key authentication for business accounts

Primary Use Cases ​

  • Customer onboarding for fintech/banking apps
  • Age/identity verification for e-commerce
  • Employee identity verification for HR platforms
  • Compliance with KYC regulations

2. Vision & Mission ​

Vision ​

Become the leading identity verification infrastructure for Africa, enabling businesses to onboard customers securely and compliantly with world-class technology.

Mission ​

Provide simple, fast, and accurate identity verification that works with African ID documents, at a price point accessible to startups and enterprises alike.

Guiding Principles ​

  1. Security First: Documents encrypted at rest and in transit; never stored publicly
  2. Developer Experience: Simple integration via RESTful API and native SDKs
  3. African Focus: Optimized for African ID document formats (National IDs, passports)
  4. Speed: Sub-5-second verification times with async webhooks

3. Problem Statement ​

The Challenge ​

African businesses face significant hurdles implementing KYC:

  1. Global providers are expensive — per-verification pricing often exceeds $1-2 USD, prohibitive for high-volume African businesses
  2. Poor support for African documents — OCR models trained on Western documents fail on African ID formats
  3. Complex integration — most providers require weeks of integration work
  4. No local data residency — compliance concerns with data leaving the continent

Market Gap ​

Existing solutions (Jumio, Onfido, Veriff) are priced for Western markets and lack optimization for African ID documents. Local alternatives are fragmented and lack modern developer tooling.

Target Customers ​

SegmentUse CaseVolume
Fintech startupsUser onboarding100-10,000/month
BanksAccount opening10,000-100,000/month
E-commerceAge verification1,000-50,000/month
HR platformsEmployee verification100-5,000/month
GovernmentCitizen services50,000+/month

4. Solution ​

Overview ​

YeboVerify provides a single API endpoint that accepts:

  1. ID Front Image — photo of the front of government-issued ID
  2. Selfie — live photo of the person
  3. ID Back Image (optional) — back of ID for additional data

Within seconds, YeboVerify:

  1. Compares the face on the ID to the selfie using AWS Rekognition
  2. Extracts personal data (name, DOB, ID number) via Gemini OCR
  3. Returns a decision (APPROVED / REJECTED / NEEDS_REVIEW)
  4. Sends results to configured webhook

Architecture Overview ​

┌─────────────┐     ┌──────────────────┐     ┌─────────────────┐
│   Client    │────▶│  YeboVerify API  │────▶│ Cloudflare R2   │
│   (SDK)     │     │  (Cloud Run)     │     │ (Storage)       │
└─────────────┘     └────────┬─────────┘     └─────────────────┘
                             │
        ┌────────────────────┼────────────────────┐
        │                    │                    │
        ▼                    ▼                    ▼
┌───────────────┐   ┌────────────────┐   ┌───────────────┐
│ AWS Rekognition│   │ Google Gemini  │   │ PostgreSQL    │
│ (Face Match)  │   │ (OCR)          │   │ (Neon)        │
└───────────────┘   └────────────────┘   └───────────────┘

5. Features ​

5.1 Core Features (Implemented) ​

FeatureDescriptionStatus
Face ComparisonAWS Rekognition CompareFaces API✅ Live
Document OCRGemini 2.5 Flash multimodal extraction✅ Live
Async ProcessingBackground processing with setImmediate✅ Live
Webhook NotificationsHMAC-signed POST to business webhook✅ Live
Multi-tenancyAPI key per business account✅ Live
Business DashboardStats endpoint with verification counts✅ Live
Verification HistoryPaginated list with status filtering✅ Live
Image StorageCloudflare R2 (private, presigned URLs)✅ Live

5.2 Decision Logic ​

The verification decision is computed based on face match score and OCR confidence:

Face ScoreOCR ConfidenceDecisionConfidence Level
≥ 85%≥ 70%APPROVEDHigh
≥ 80%≥ 60%APPROVEDMedium
≥ 70%≥ 50%NEEDS_REVIEWMedium
< 60%(skipped)REJECTEDLow
< 70%< 50%REJECTEDLow

5.3 OCR Extracted Fields ​

FieldDescriptionExample
surnameFamily/last nameDLAMINI
namesGiven namesSIBUSISO JOHN
dateOfBirthDOB in YYYY-MM-DD1990-05-15
idNumberNational ID number9005155555083
sexGenderMale / Female
chiefCodeChief/regional code12345
issueDateDocument issue date2020-01-15
expiryDateDocument expiry date2030-01-15
documentTypeType of documentNational ID

5.4 Supported File Types ​

  • image/jpeg
  • image/png
  • image/webp
  • image/heic
  • image/heif

Max file size: 10MB per image


6. User Journeys ​

6.1 Business Integrator Journey ​

Persona: Developer at a fintech company implementing KYC

Steps ​

  1. Registration

    • Admin creates business account via admin API
    • Receives API key (yv_live_xxx...)
  2. Integration

    • Installs SDK for their platform (Node/React/Flutter/etc.)
    • Configures webhook URL for async results
  3. Testing

    • Submits test verifications
    • Verifies webhook signatures
  4. Production

    • Deploys SDK widget to production app
    • Monitors verification stats via /v1/account
javascript
// Node.js Integration Example
import { YeboVerify } from '@yeboverify/node';

const client = new YeboVerify({ apiKey: 'yv_live_xxx' });

// Configure webhook
await client.setWebhook('https://api.myapp.com/webhooks/kyc');

// Submit verification
const { verificationId } = await client.verify({
  idFront: '/path/to/id.jpg',
  selfie: '/path/to/selfie.jpg',
  externalRef: 'user_123',
});

// Results arrive via webhook...

6.2 End User Journey ​

Persona: Customer signing up for a fintech app

Steps ​

  1. Prompt

    • App displays "Verify your identity" screen
    • YeboVerify widget appears
  2. ID Front

    • User photographs front of their National ID
    • Widget validates image quality
  3. ID Back (optional)

    • User photographs back of ID
    • Can skip if not required
  4. Selfie

    • User takes a selfie facing camera
    • Instructions: good lighting, no glasses
  5. Review

    • User reviews all captured photos
    • Can retake any photo
  6. Submit

    • Widget uploads to YeboVerify API
    • User sees "Processing..." state
  7. Result

    • App receives webhook notification
    • User sees approval or rejection

6.3 Admin Journey ​

Persona: YeboVerify platform administrator

Steps ​

  1. Create Business

    bash
    POST /v1/businesses/register
    Authorization: Bearer ADMIN_SECRET
  2. Monitor

    • View business verification stats
    • Review flagged verifications (NEEDS_REVIEW)
  3. Support

    • Lookup verification by ID
    • Investigate failed verifications

7. Data Models ​

7.1 Business Model ​

Represents a registered business customer.

prisma
model Business {
  id            String         @id @default(cuid())
  name          String                              // Company name
  email         String         @unique              // Contact email
  apiKey        String         @unique @default(cuid())  // API key (yv_live_xxx)
  webhookUrl    String?                             // Webhook endpoint URL
  webhookSecret String?                             // HMAC signing secret (whsec_xxx)
  active        Boolean        @default(true)       // Account active/disabled
  plan          String         @default("free")     // Subscription plan
  createdAt     DateTime       @default(now())
  updatedAt     DateTime       @updatedAt
  verifications Verification[]
}
FieldTypeDescription
idcuidPrimary key
nameStringBusiness display name
emailStringUnique contact email
apiKeyStringAPI key (format: yv_live_<32chars>)
webhookUrlString?HTTPS URL for webhook delivery
webhookSecretString?HMAC secret (format: whsec_<32chars>)
activeBooleanAccount status
planStringPlan tier: free, basic, pro, enterprise
createdAtDateTimeAccount creation timestamp
updatedAtDateTimeLast update timestamp

7.2 Verification Model ​

Represents a single identity verification request.

prisma
model Verification {
  id             String             @id @default(cuid())
  verificationId String             @unique         // Public ID (vrf_xxx)
  businessId     String                             // FK to Business
  externalRef    String?                            // Client's reference ID
  status         VerificationStatus @default(PENDING)
  idFrontImage   String                             // R2 storage key
  idBackImage    String?                            // R2 storage key (optional)
  selfieImage    String                             // R2 storage key
  extractedData  Json?                              // OCR results
  ocrConfidence  Float?                             // 0-100 OCR confidence
  faceScore      Float?                             // 0-100 face similarity
  faceDecision   String?                            // approved/rejected/needs_review
  decision       Decision?                          // Final decision
  decisionReason String?                            // Human-readable reason
  confidence     String?                            // high/medium/low
  processingMs   Int?                               // Processing time in ms
  webhookSent    Boolean            @default(false)
  webhookSentAt  DateTime?
  webhookError   String?
  createdAt      DateTime           @default(now())
  updatedAt      DateTime           @updatedAt
  completedAt    DateTime?
  business       Business           @relation(...)

  @@index([businessId])
  @@index([status])
}
FieldTypeDescription
idcuidInternal database ID
verificationIdStringPublic ID (format: vrf_<16chars>)
businessIdStringForeign key to Business
externalRefString?Client's user/order ID
statusVerificationStatusProcessing status
idFrontImageStringR2 object key for ID front
idBackImageString?R2 object key for ID back
selfieImageStringR2 object key for selfie
extractedDataJson?Structured OCR output
ocrConfidenceFloat?OCR confidence percentage (0-100)
faceScoreFloat?Face similarity percentage (0-100)
faceDecisionString?Face comparison decision
decisionDecision?Final verification decision
decisionReasonString?Explanation of decision
confidenceString?Confidence level: high, medium, low
processingMsInt?Total processing time
webhookSentBooleanWebhook delivery status
webhookSentAtDateTime?Webhook delivery timestamp
webhookErrorString?Webhook error message if failed
createdAtDateTimeVerification creation time
updatedAtDateTimeLast update time
completedAtDateTime?Processing completion time

7.3 Enums ​

VerificationStatus ​

typescript
enum VerificationStatus {
  PENDING       // Just submitted, not yet processing
  PROCESSING    // Currently being verified
  COMPLETED     // Successfully processed
  FAILED        // Processing failed (error)
  NEEDS_REVIEW  // Requires manual review
}

Decision ​

typescript
enum Decision {
  APPROVED      // Identity verified successfully
  REJECTED      // Identity verification failed
  NEEDS_REVIEW  // Manual review required
}

8. API Reference ​

Base URL ​

https://api.yeboverify.com

8.1 Health Check ​

GET /health ​

Returns API health status.

Response

json
{
  "status": "ok",
  "timestamp": "2026-03-19T10:00:00.000Z",
  "service": "yeboverify-api",
  "version": "1.0.0"
}

8.2 Admin Endpoints ​

POST /v1/businesses/register ​

Create a new business account.

Authentication: Bearer token (ADMIN_SECRET)

Request Body

json
{
  "name": "Acme Corp",
  "email": "admin@acme.com",
  "plan": "pro"
}
FieldTypeRequiredDescription
namestring✅Business name (1-255 chars)
emailstring✅Valid email address
planstring❌free, basic, pro, enterprise (default: free)

Response (201 Created)

json
{
  "success": true,
  "data": {
    "businessId": "clx...",
    "apiKey": "yv_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ123456",
    "name": "Acme Corp",
    "email": "admin@acme.com",
    "plan": "pro"
  }
}

8.3 Business Account Endpoints ​

All endpoints require X-API-Key header.

GET /v1/account ​

Get current business account info and stats.

Response

json
{
  "success": true,
  "data": {
    "id": "clx...",
    "name": "Acme Corp",
    "email": "admin@acme.com",
    "webhookUrl": "https://api.acme.com/webhooks/kyc",
    "plan": "pro",
    "createdAt": "2026-01-01T00:00:00.000Z",
    "stats": {
      "total": 1234,
      "pending": 5,
      "completed": 1200,
      "approved": 1100,
      "rejected": 80,
      "needsReview": 20
    }
  }
}

PUT /v1/account/webhook ​

Configure webhook URL for receiving verification results.

Request Body

json
{
  "webhookUrl": "https://api.acme.com/webhooks/kyc",
  "webhookSecret": "my-custom-secret"
}
FieldTypeRequiredDescription
webhookUrlstring✅Valid HTTPS URL
webhookSecretstring❌Custom secret (auto-generated if omitted)

Response

json
{
  "success": true,
  "data": {
    "webhookUrl": "https://api.acme.com/webhooks/kyc",
    "webhookSecret": "whsec_aBcDeFgHiJkLmNoPqRsTuVwXyZ123456",
    "message": "Webhook configured successfully"
  }
}

8.4 Verification Endpoints ​

POST /v1/verify ​

Submit a new identity verification.

Content-Type: multipart/form-data

Form Fields

FieldTypeRequiredDescription
idFrontfile✅Front of ID document (JPEG, PNG, WebP, HEIC)
selfiefile✅User's selfie
idBackfile❌Back of ID document
externalRefstring❌Your reference ID (e.g., user ID)
documentTypestring❌Document type hint

Response (202 Accepted)

json
{
  "success": true,
  "data": {
    "verificationId": "vrf_abc123xyz456789",
    "status": "pending",
    "message": "Verification submitted successfully. Results will be sent via webhook."
  }
}

GET /v1/verifications/:verificationId ​

Get a specific verification by ID.

Response

json
{
  "success": true,
  "data": {
    "verificationId": "vrf_abc123xyz456789",
    "externalRef": "user_123",
    "status": "COMPLETED",
    "decision": "APPROVED",
    "decisionReason": "High face match and OCR confidence",
    "confidence": "high",
    "faceScore": 92.5,
    "ocrConfidence": 85.0,
    "extractedData": {
      "surname": "DLAMINI",
      "names": "SIBUSISO JOHN",
      "dateOfBirth": "1990-05-15",
      "idNumber": "9005155555083",
      "documentType": "National ID"
    },
    "processingMs": 3200,
    "createdAt": "2026-03-19T10:00:00.000Z",
    "completedAt": "2026-03-19T10:00:03.200Z"
  }
}

GET /v1/verifications ​

List verifications with pagination and filtering.

Query Parameters

ParameterTypeDefaultDescription
pageint1Page number
limitint20Items per page (max 100)
statusstring-Filter by status

Response

json
{
  "success": true,
  "data": {
    "verifications": [
      {
        "verificationId": "vrf_abc123xyz456789",
        "externalRef": "user_123",
        "status": "COMPLETED",
        "decision": "APPROVED",
        "confidence": "high",
        "createdAt": "2026-03-19T10:00:00.000Z",
        "completedAt": "2026-03-19T10:00:03.200Z"
      }
    ],
    "pagination": {
      "page": 1,
      "limit": 20,
      "total": 1234,
      "totalPages": 62
    }
  }
}

8.5 Webhook Payload ​

When verification completes, YeboVerify POSTs to your webhook URL:

json
{
  "event": "verification.completed",
  "verificationId": "vrf_abc123xyz456789",
  "externalRef": "user_123",
  "status": "completed",
  "decision": "approved",
  "confidence": "high",
  "faceScore": 92.5,
  "ocrConfidence": 85.0,
  "extractedData": {
    "surname": "DLAMINI",
    "names": "SIBUSISO JOHN",
    "dateOfBirth": "1990-05-15",
    "idNumber": "9005155555083",
    "documentType": "National ID"
  },
  "timestamp": "2026-03-19T10:00:03.200Z"
}

Headers

HeaderDescription
Content-Typeapplication/json
X-YeboVerify-SignatureHMAC-SHA256 signature

Signature Verification

javascript
const crypto = require('crypto');

function verifySignature(rawBody, signature, secret) {
  const expected = 'sha256=' + crypto
    .createHmac('sha256', secret)
    .update(rawBody)
    .digest('hex');
  
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

8.6 Error Responses ​

All errors follow this format:

json
{
  "success": false,
  "error": {
    "code": "ERROR_CODE",
    "message": "Human readable error message"
  }
}
CodeHTTP StatusDescription
MISSING_API_KEY401X-API-Key header not provided
INVALID_API_KEY401API key not found or invalid
ACCOUNT_DISABLED403Business account is disabled
MISSING_AUTH401Admin Bearer token required
INVALID_TOKEN401Invalid admin token
MISSING_FILES400No files uploaded
MISSING_ID_FRONT400ID front image required
MISSING_SELFIE400Selfie image required
INVALID_FILE_TYPE400Unsupported file type
FILE_TOO_LARGE400File exceeds 10MB limit
NOT_FOUND404Resource not found
VALIDATION_ERROR400Request validation failed
EMAIL_EXISTS409Email already registered
VERIFICATION_ERROR500Verification processing error
INTERNAL_ERROR500Unexpected server error

9. Service Architecture ​

9.1 Face Comparison Service (AWS Rekognition) ​

File: src/services/aws-rekognition.service.ts

Uses AWS Rekognition CompareFaces API to match the face on an ID document with a selfie.

Configuration ​

env
AWS_ACCESS_KEY_ID=AKIA...
AWS_SECRET_ACCESS_KEY=...
AWS_REGION=us-east-1

Key Methods ​

MethodDescription
compareFaces(sourceBuffer, targetBuffer)Compare two face images
getVerificationDecision(result)Convert score to decision
isEnabled()Check if AWS credentials are configured

Face Comparison Result ​

typescript
interface FaceComparisonResult {
  similarity: number;           // 0-100 match score
  confidence: number;           // Source face detection confidence
  sourceImageFace: {
    boundingBox: { width, height, left, top };
    confidence: number;
  };
  faceMatches: Array<{
    similarity: number;
    face: { boundingBox, confidence };
  }>;
  unmatchedFaces: unknown[];
}

Decision Thresholds ​

SimilarityDecisionConfidence
≥ 90%approvedhigh
≥ 80%needs_reviewmedium
≥ 70%needs_reviewlow
< 70%rejectedlow

9.2 OCR Service (Google Gemini) ​

File: src/services/gemini-ocr.service.ts

Uses Google Gemini 2.5 Flash multimodal model to extract personal information from ID document images.

Configuration ​

env
GEMINI_API_KEY=AIzaSy...

Key Methods ​

MethodDescription
extractPersonalInfo(imageBuffer)Extract data from ID image
validatePersonalInfo(info)Validate extracted data completeness
isEnabled()Check if API key is configured

OCR Result ​

typescript
interface OCRResult {
  personalInfo: {
    surname?: string;
    names?: string;
    dateOfBirth?: string;
    sex?: string;
    idNumber?: string;
    chiefCode?: string;
    issueDate?: string;
    expiryDate?: string;
    documentType?: string;
    confidence: number;
  };
  extractedText: string[];
  confidence: number;
  success: boolean;
  error?: string;
}

Prompt Engineering ​

The service uses a structured prompt requesting JSON output:

Analyze this ID document image and extract the following personal information in JSON format.
Be very precise and only extract information that is clearly visible and readable.
...
Return ONLY a JSON object with the fields above.

9.3 Storage Service (Cloudflare R2) ​

File: src/services/storage.service.ts

Uses Cloudflare R2 (S3-compatible) for storing document images. Images are stored privately — never exposed publicly.

Configuration ​

env
R2_ACCOUNT_ID=9f15f12d867a59b212ed2ae3cf4615ca
R2_BUCKET=yeboverify-documents
R2_ACCESS_KEY_ID=...
R2_SECRET_ACCESS_KEY=...

Key Methods ​

MethodDescription
uploadFile(buffer, key, mimeType)Upload file to R2
downloadBuffer(key)Download file as Buffer
getSignedUrl(key, expiresIn)Generate presigned URL (15 min default)
deleteFile(key)Delete file from R2

Storage Structure ​

yeboverify-documents/
├── verifications/
│   ├── vrf_abc123xyz/
│   │   ├── id-front-1710844800000
│   │   ├── id-back-1710844800001
│   │   └── selfie-1710844800002
│   └── vrf_def456uvw/
│       └── ...

9.4 Webhook Service ​

File: src/services/webhook.service.ts

Handles webhook delivery with retry logic and HMAC signing.

Key Features ​

  • Retry: 3 attempts with exponential backoff (1s, 2s, 3s)
  • Signing: HMAC-SHA256 signature in X-YeboVerify-Signature header
  • Tracking: Records delivery status and errors in database

Webhook Payload ​

typescript
interface WebhookPayload {
  event: 'verification.completed';
  verificationId: string;
  externalRef: string | null;
  status: 'completed' | 'failed' | 'needs_review';
  decision: 'approved' | 'rejected' | 'needs_review';
  confidence: 'high' | 'medium' | 'low';
  faceScore: number;
  ocrConfidence: number;
  extractedData: {
    surname?: string;
    names?: string;
    dateOfBirth?: string;
    idNumber?: string;
    documentType?: string;
  };
  timestamp: string;
}

9.5 Verification Flow ​

1. Client submits POST /v1/verify with images
                    │
                    ▼
2. Upload images to R2 (private bucket)
                    │
                    ▼
3. Create Verification record (status: PENDING)
                    │
                    ▼
4. Return 202 Accepted with verificationId
                    │
                    ▼
5. Background: setImmediate → processVerification()
                    │
                    ├── Update status to PROCESSING
                    │
                    ├── Download images from R2
                    │
                    ├── AWS Rekognition: compareFaces()
                    │   │
                    │   ├── If faceScore < 60%: REJECT (skip OCR)
                    │   │
                    │   └── Continue...
                    │
                    ├── Gemini OCR: extractPersonalInfo()
                    │
                    ├── Calculate final decision
                    │
                    ├── Update Verification record
                    │
                    └── Send webhook (if configured)

10. SDKs ​

YeboVerify provides official SDKs for all major platforms.

10.1 Node.js SDK ​

Package: @yeboverify/node

Installation ​

bash
npm install @yeboverify/node

Usage ​

typescript
import { YeboVerify } from '@yeboverify/node';

const client = new YeboVerify({ apiKey: 'yv_live_xxx' });

// Submit verification
const { verificationId } = await client.verify({
  idFront: '/path/to/id.jpg',     // File path, Buffer, or base64
  selfie: '/path/to/selfie.jpg',
  externalRef: 'user_123',
});

// Get verification
const verification = await client.getVerification(verificationId);

// Poll until complete (for testing)
const result = await client.pollVerification(verificationId, {
  intervalMs: 2000,
  timeoutMs: 60000,
});

// Verify webhook signature
const isValid = client.verifyWebhook(rawBody, signature, webhookSecret);

Types ​

typescript
interface YeboVerifyConfig {
  apiKey: string;
  baseUrl?: string;  // defaults to https://api.yeboverify.com
}

interface VerifyOptions {
  idFront: Buffer | string;
  selfie: Buffer | string;
  idBack?: Buffer | string;
  externalRef?: string;
  documentType?: string;
}

interface Verification {
  verificationId: string;
  externalRef?: string | null;
  status: VerificationStatus;
  decision?: VerificationDecision | null;
  decisionReason?: string | null;
  confidence?: VerificationConfidence | null;
  faceScore?: number | null;
  ocrConfidence?: number | null;
  extractedData?: ExtractedData | null;
  processingMs?: number | null;
  createdAt: string;
  completedAt?: string | null;
}

10.2 React SDK ​

Package: @yeboverify/react

Installation ​

bash
npm install @yeboverify/react

Usage ​

tsx
import { YeboVerifyWidget } from '@yeboverify/react';

function KYCPage() {
  return (
    <YeboVerifyWidget
      apiKey="yv_live_xxx"
      externalRef={currentUser.id}
      documentType="National ID"
      requireIdBack={false}
      primaryColor="#16a34a"
      onSubmit={(verificationId) => {
        console.log('Submitted:', verificationId);
      }}
      onError={(error) => {
        console.error('Error:', error);
      }}
    />
  );
}

Props ​

PropTypeRequiredDescription
apiKeystring✅Your YeboVerify API key
baseUrlstring❌API base URL
externalRefstring❌Your reference ID
documentTypestring❌Document type hint
requireIdBackboolean❌Require back of ID
primaryColorstring❌Brand color (default: #16a34a)
onSubmitfunction❌Called on successful submission
onResultfunction❌Called when result received
onErrorfunction❌Called on error
classNamestring❌Custom CSS class

10.3 React Native SDK ​

Package: @yeboverify/react-native

Installation ​

bash
npm install @yeboverify/react-native
npx expo install expo-image-picker

Usage ​

tsx
import { YeboVerifyScreen } from '@yeboverify/react-native';

function KYCScreen() {
  return (
    <YeboVerifyScreen
      apiKey="yv_live_xxx"
      externalRef={user.id}
      documentType="National ID"
      requireIdBack={false}
      primaryColor="#16a34a"
      onSubmit={(verificationId) => {
        navigation.navigate('KYCComplete', { verificationId });
      }}
      onError={(error) => {
        Alert.alert('Error', error.message);
      }}
    />
  );
}

10.4 Flutter SDK ​

Package: yeboverify

Installation ​

yaml
# pubspec.yaml
dependencies:
  yeboverify: ^1.0.0
  image_picker: ^1.0.0

Usage ​

dart
import 'package:yeboverify/yeboverify.dart';

// Programmatic API
final client = YeboVerify(apiKey: 'yv_live_xxx');

final verificationId = await client.verify(
  idFront: File('/path/to/id.jpg'),
  selfie: File('/path/to/selfie.jpg'),
  externalRef: user.id,
);

// Widget
YeboVerifyWidget(
  apiKey: 'yv_live_xxx',
  externalRef: user.id,
  requireIdBack: false,
  primaryColor: Color(0xFF16a34a),
  onSubmit: (verificationId) {
    Navigator.pushNamed(context, '/kyc-complete');
  },
  onError: (error) {
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text(error.message)),
    );
  },
)

Models ​

dart
enum VerificationStatus { pending, processing, completed, failed, needsReview }
enum VerificationDecision { approved, rejected, needsReview }
enum VerificationConfidence { high, medium, low }

class Verification {
  final String verificationId;
  final String? externalRef;
  final VerificationStatus status;
  final VerificationDecision? decision;
  final String? decisionReason;
  final VerificationConfidence? confidence;
  final double? faceScore;
  final double? ocrConfidence;
  final ExtractedData? extractedData;
  final int? processingMs;
  final DateTime createdAt;
  final DateTime? completedAt;
  
  bool get isApproved => decision == VerificationDecision.approved;
  bool get isPending => status == VerificationStatus.pending || status == VerificationStatus.processing;
}

10.5 Kotlin SDK (Android) ​

Package: com.yeboverify

Installation ​

gradle
// build.gradle
dependencies {
    implementation 'com.yeboverify:yeboverify:1.0.0'
    implementation 'com.squareup.okhttp3:okhttp:4.12.0'
}

Usage ​

kotlin
// Programmatic API
val client = YeboVerify(apiKey = "yv_live_xxx")

val verificationId = client.verify(
    idFront = File("/path/to/id.jpg"),
    selfie = File("/path/to/selfie.jpg"),
    externalRef = user.id,
)

// Launch built-in UI
val intent = client.launchVerificationFlow(
    context = this,
    externalRef = user.id,
    documentType = "National ID",
    requireIdBack = false,
)
startActivityForResult(intent, REQUEST_CODE_VERIFY)

// Handle result
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    if (requestCode == REQUEST_CODE_VERIFY && resultCode == RESULT_OK) {
        val verificationId = data?.getStringExtra(YeboVerifyActivity.EXTRA_VERIFICATION_ID)
        // Handle success
    }
}

10.6 Swift SDK (iOS) ​

Package: YeboVerify (Swift Package)

Installation ​

swift
// Package.swift
dependencies: [
    .package(url: "https://github.com/yeboverify/swift-sdk.git", from: "1.0.0")
]

Usage ​

swift
// Programmatic API
let client = YeboVerify(apiKey: "yv_live_xxx")

let verificationId = try await client.verify(
    idFront: idFrontImageData,
    selfie: selfieImageData,
    externalRef: user.id
)

// SwiftUI Widget
YeboVerifyView(
    apiKey: "yv_live_xxx",
    externalRef: user.id,
    documentType: "National ID",
    requireIdBack: false,
    primaryColor: Color.green
) { verificationId in
    print("Submitted:", verificationId)
} onError: { error in
    print("Error:", error.message)
}

11. Authentication ​

11.1 Business API Key Authentication ​

All verification and account endpoints use API key authentication:

X-API-Key: yv_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ123456

API Key Format: yv_live_<32 alphanumeric characters>

Generated by: customAlphabet('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', 32)

11.2 Admin Bearer Token ​

Admin endpoints (business registration) use Bearer token:

Authorization: Bearer YOUR_ADMIN_SECRET

Configured via: ADMIN_SECRET environment variable

11.3 Webhook Signature ​

Webhooks are signed with HMAC-SHA256:

X-YeboVerify-Signature: sha256=abc123...

Secret Format: whsec_<32 alphanumeric characters>


12. Billing & Plans ​

12.1 Current Plan Structure ​

PlanDescriptionDefault
freeTrial tier✅
basicStarter businesses
proGrowing businesses
enterpriseHigh-volume

Note: Billing/payments are not yet implemented. Plans are stored but not enforced.

12.2 Planned Pricing Model ​

PlanVerifications/monthPrice/verificationFeatures
Free100$0Basic OCR
Basic1,000$0.30+ Webhooks
Pro10,000$0.20+ Priority processing
EnterpriseUnlimitedCustom+ SLA, support

13. Technical Stack ​

13.1 Backend ​

ComponentTechnology
RuntimeNode.js 22+
LanguageTypeScript 5.7
FrameworkExpress.js 4.21
DatabasePostgreSQL (Neon serverless)
ORMPrisma 6.3
ValidationZod 3.24
LoggingWinston 3.17

13.2 External Services ​

ServiceProviderPurpose
Face RecognitionAWS RekognitionFace comparison
OCRGoogle Gemini 2.5 FlashDocument data extraction
StorageCloudflare R2Image storage
DatabaseNeonPostgreSQL
HostingGoogle Cloud RunContainer hosting
CI/CDGoogle Cloud BuildBuild & deploy

13.3 Dependencies ​

json
{
  "dependencies": {
    "@aws-sdk/client-rekognition": "^3.637.0",
    "@aws-sdk/client-s3": "^3.1006.0",
    "@aws-sdk/s3-request-presigner": "^3.1006.0",
    "@google/generative-ai": "^0.21.0",
    "@prisma/client": "^6.3.1",
    "cors": "^2.8.5",
    "dotenv": "^16.4.5",
    "express": "^4.21.1",
    "multer": "^1.4.5-lts.1",
    "nanoid": "^3.3.7",
    "winston": "^3.17.0",
    "zod": "^3.24.1"
  }
}

13.4 Deployment ​

  • Region: europe-west1 (Belgium)
  • Platform: Google Cloud Run
  • Build: Cloud Build with Docker
  • Deploy Script: deploy.sh (required for secrets)

14. Security ​

14.1 Data Protection ​

MeasureImplementation
Encryption at RestR2 server-side encryption
Encryption in TransitTLS 1.3
No Public URLsImages only accessible via presigned URLs (15 min expiry)
API Key HashingKeys stored as plain text (TODO: hash)
Webhook SigningHMAC-SHA256 with constant-time comparison

14.2 Access Control ​

LayerControl
Admin APIsBearer token (ADMIN_SECRET)
Business APIsAPI key (X-API-Key)
WebhooksHMAC signature verification
StoragePrivate bucket + presigned URLs

14.3 Input Validation ​

  • File types: Allowlist (JPEG, PNG, WebP, HEIC, HEIF)
  • File size: Max 10MB per image
  • Request body: Zod schema validation
  • ID format: Regex validation

15. Gaps & Missing Features ​

15.1 Critical Gaps ​

GapPriorityDescription
No rate limiting🔴 HighAPI has no rate limits — vulnerable to abuse
No billing/payments🔴 HighPlans exist but not enforced
No Stripe integration🔴 HighNo payment processing
No API key rotation🔴 HighCannot rotate compromised keys
API keys in plaintext🔴 HighShould be hashed

15.2 Important Missing Features ​

FeaturePriorityDescription
Admin dashboard🟡 MediumWeb UI for managing businesses
Manual review UI🟡 MediumInterface for reviewing NEEDS_REVIEW cases
Usage analytics🟡 MediumDetailed verification metrics
Document type detection🟡 MediumAuto-detect ID type (passport vs national ID)
Liveness detection🟡 MediumEnsure selfie is live, not a photo
Email notifications🟡 MediumNotify business on verification completion
Retry webhook endpoint🟡 MediumAPI to retry failed webhooks

15.3 Nice-to-Have ​

FeaturePriorityDescription
Batch verification🟢 LowSubmit multiple verifications at once
Image quality scoring🟢 LowReject blurry/dark images upfront
Document expiry check🟢 LowFlag expired IDs
Multi-language support🟢 LowSDK widgets in local languages
Sandbox mode🟢 LowTest mode with mock responses
GraphQL API🟢 LowAlternative to REST

15.4 SDK Gaps ​

SDKGap
Node.jsNo retry logic, no streaming upload
ReactNo camera capture (gallery only)
React NativeRequires expo-image-picker (not bare RN)
FlutterNo liveness detection integration
KotlinNo Jetpack Compose UI (Activity only)
SwiftiOS 16+ only, no UIKit support

15.5 Technical Debt ​

ItemDescription
setImmediate for background jobsShould use proper job queue (Bull/BullMQ)
Single-file uploadsShould support streaming for large files
No retry mechanismFailed processing doesn't retry automatically
No dead letter queueFailed webhooks lost after 3 attempts
No idempotency keysDuplicate submissions not prevented

Appendix A: Environment Variables ​

env
# Server
PORT=3010
NODE_ENV=production

# Database (Neon PostgreSQL)
DATABASE_URL=postgresql://user:pass@host:5432/yeboverify?sslmode=require

# Cloudflare R2 Storage
R2_ACCOUNT_ID=9f15f12d867a59b212ed2ae3cf4615ca
R2_BUCKET=yeboverify-documents
R2_ACCESS_KEY_ID=
R2_SECRET_ACCESS_KEY=

# AWS Rekognition
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_REGION=us-east-1

# Google Gemini OCR
GEMINI_API_KEY=

# Admin API
ADMIN_SECRET=change-this-secret

Appendix B: API Response Codes ​

HTTP CodeMeaning
200Success
201Created
202Accepted (async processing)
400Bad Request (validation error)
401Unauthorized (missing/invalid auth)
403Forbidden (account disabled)
404Not Found
409Conflict (duplicate)
500Internal Server Error

Appendix C: Glossary ​

TermDefinition
KYCKnow Your Customer — identity verification requirements
OCROptical Character Recognition — extracting text from images
Face Score0-100 percentage indicating face similarity
WebhookHTTP POST callback when verification completes
HMACHash-based Message Authentication Code
R2Cloudflare's S3-compatible object storage
Presigned URLTemporary, signed URL for private object access

Document generated: March 2026Last updated: March 19, 2026

One chat. Everything done.