BMI KYC API Documentation
API Live
Sign In
📖 Overview

BMI KYC API

The BMI KYC API lets you verify user identities in real time using AI-powered face matching, document OCR, and liveness detection. Integrate with a single REST API call.

💡 Base URL: https://kyc.bm-e-industrie.com/api/v1 — All requests must use HTTPS.
Instant Results
Under 2 seconds per verification, no polling required
🤖
AI-Powered
FaceNet + PaddleOCR + liveness detection
🌍
FR & EN
Supports French and English documents
🔒
Private
Data never leaves our servers
🚀 Quick Start

Get running in 5 minutes

Follow these steps to submit your first KYC verification.

1

Get your credentials

Contact BMI to receive your email and password. You'll use these to get a JWT token for API calls.

2

Login to get a token

bash
curl -X POST https://kyc.bm-e-industrie.com/api/v1/auth/login \
  -d "username=you@company.com&password=yourpassword"

# Response:
{ "access_token": "eyJhbGci...", "token_type": "bearer" }
3

Submit your first verification

bash
curl -X POST https://kyc.bm-e-industrie.com/api/v1/verify/upload \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -F "id_document=@id_card.jpg" \
  -F "selfie_video=@selfie.jpg"
4

Read the decision

{ "decision": "approved", "face_match_score": 0.924, "document_valid": true, "document_confidence": 0.87, "extracted_data": { "name": "Jean Dupont", "date_of_birth": "15/03/1990", "document_number": "GA123456" }, "liveness_score": 0.78, "session_id": "4a04e359-...", "processing_time": 1.24 }
When decision is approved — activate the user. No human review needed.
🔑 Authentication

Authentication

All API requests require a JWT Bearer token obtained by logging in.

Login endpoint

FieldValue
MethodPOST
URL/auth/login
Content-Typeapplication/x-www-form-urlencoded
Bodyusername=you@email.com&password=yourpassword
⚠️ Tokens expire after 8 days. Store the token securely and refresh it before expiry by calling /auth/login again.

Using the token

bash
# Add this header to every request
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
📤 Verify Identity

Submit a Verification

MethodURLAuth
POST/verify/uploadRequired

Required Fields

FieldTypeDescription
id_documentimage filePhoto of ID card, passport, or any government ID
selfie_videoimage/videoSelfie photo or short video (3–5 sec)

Optional Fields

FieldTypeDescription
vehicle_documentimage fileVehicle registration card / Carte grise
insurance_documentimage fileInsurance certificate / Assurance
driver_licenseimage fileDriver's license / Permis de conduire
📥 Response Format

Response Fields

FieldTypeDescription
session_idstringUnique ID for this verification. Save in your DB.
statusstringcompleted, failed, processing
decisionstringapproved, rejected, manual_review
decision_reasonstringHuman-readable explanation of the decision
face_match_scorefloat 0–1Similarity between selfie and ID photo. ≥0.75 = good match
document_validbooleanWhether the document was successfully read and validated
document_confidencefloat 0–1Confidence in document OCR extraction
document_typestringid_card, passport, driver_license, etc.
extracted_dataobjectName, date of birth, document number, expiry date extracted by OCR
liveness_scorefloat 0–1Anti-spoofing score. ≥0.7 = real person
processing_timefloatTime in seconds to process the verification
⚖️ Decisions

Understanding Decisions

DecisionMeaningWhat to do
approvedAll checks passed✅ Activate user / unlock feature
rejectedVerification failed❌ Ask user to retry with better photos
manual_reviewBorderline case⏳ Show "under review" — admin will decide

Decision Reasons & User Messages

ReasonMessage to show user
All checks passed✅ Your identity has been verified
Face match score too lowYour selfie doesn't match your ID. Retake in good lighting looking straight at camera.
No face detected in ID documentWe couldn't read your ID photo. Place it flat on a dark surface with no glare.
No face detected in selfieYour face wasn't detected. Ensure your face is fully visible and well-lit.
Document could not be validatedYour document is unreadable. Retake with better lighting and no blur.
Partial match - manual review recommended⏳ Your verification is under review. You'll be notified shortly.

Score Interpretation

Face Match Score

≥ 0.85 — Excellent

0.75–0.84 — Good

0.60–0.74 — Review

< 0.60 — Reject

Document Confidence

≥ 0.70 — Clearly readable

0.30–0.69 — Partially readable

< 0.30 — Unreadable

Liveness Score

≥ 0.70 — Real person

0.50–0.69 — Uncertain

< 0.50 — Possible fake

🔍 Check Status

Check Verification Status

Use the session_id returned from the upload endpoint to check the status of a verification later.

bash
curl https://kyc.bm-e-industrie.com/api/v1/verify/status/{session_id} \
  -H "Authorization: Bearer YOUR_TOKEN"
🔄 Retry Logic

Implementing Retry Logic

When a verification is rejected, your app should determine if the user can retry or if it's a hard rejection.

💡 Retryable: bad photo quality, face not detected. Non-retryable: face mismatch (wrong person). Max 3 retries then escalate to manual review.
PHP
function handleKycResult($result, $attempt = 1): array {
  $decision = $result['decision'] ?? 'error';
  $reason   = $result['decision_reason'] ?? '';

  if ($decision === 'approved') {
    return ['action' => 'activate', 'message' => '✅ Verified!'];
  }

  // Retryable: image quality issues
  $retryable = ['face', 'document', 'detected', 'unreadable'];
  $canRetry = $attempt < 3 && array_filter(
    $retryable, fn($w) => stripos($reason, $w) !== false
  );

  if ($canRetry) {
    return ['action' => 'retry', 'message' => getUserMessage($reason)];
  }

  if ($decision === 'manual_review' || $attempt >= 3) {
    return ['action' => 'review', 'message' => '⏳ Under review'];
  }

  return ['action' => 'reject', 'message' => '❌ Verification failed'];
}
📸 Image Requirements

Image Requirements

📄
ID Document

• Place flat on dark surface
• Good lighting, no flash glare
• All 4 corners visible
• Min 800×600 pixels
• JPG, PNG, HEIC accepted

🤳
Selfie / Video

• Face fully visible, looking straight
• Well-lit, no backlighting
• No sunglasses or mask
• Min 400×400 pixels
• JPG, PNG, MP4 accepted

⚠️ Error Codes

HTTP Error Codes

CodeMeaningAction
200SuccessRead the decision field
401UnauthorizedToken expired or invalid — re-login
422Validation ErrorMissing required fields (id_document or selfie_video)
500Server ErrorRetry after 30 seconds
💻 Full Examples

Complete Integration Examples

PHP
// KYC Client Class
class BMIKycClient {
  private string $base = 'https://kyc.bm-e-industrie.com/api/v1';
  private string $token;

  public function __construct(string $email, string $password) {
    $ch = curl_init($this->base . '/auth/login');
    curl_setopt_array($ch, [
      CURLOPT_POST => true,
      CURLOPT_POSTFIELDS => "username=$email&password=$password",
      CURLOPT_RETURNTRANSFER => true,
    ]);
    $this->token = json_decode(curl_exec($ch), true)['access_token'];
  }

  public function verify(string $idPath, string $selfiePath, array $extra = []): array {
    $fields = [
      'id_document'  => new CURLFile($idPath),
      'selfie_video' => new CURLFile($selfiePath),
    ];
    foreach (['vehicle_document','insurance_document','driver_license'] as $k) {
      if (!empty($extra[$k])) $fields[$k] = new CURLFile($extra[$k]);
    }
    $ch = curl_init($this->base . '/verify/upload');
    curl_setopt_array($ch, [
      CURLOPT_POST => true,
      CURLOPT_POSTFIELDS => $fields,
      CURLOPT_RETURNTRANSFER => true,
      CURLOPT_TIMEOUT => 60,
      CURLOPT_HTTPHEADER => ["Authorization: Bearer {$this->token}"],
    ]);
    return json_decode(curl_exec($ch), true);
  }
}

// Usage
$kyc = new BMIKycClient('you@company.com', 'yourpassword');
$result = $kyc->verify('id.jpg', 'selfie.jpg');

match($result['decision']) {
  'approved'      => activateUser(),
  'manual_review' => showPending(),
  default         => showError($result['decision_reason']),
};
Python
import requests

class BMIKycClient:
    BASE = 'https://kyc.bm-e-industrie.com/api/v1'

    def __init__(self, email, password):
        res = requests.post(
            f'{self.BASE}/auth/login',
            data={'username': email, 'password': password}
        )
        self.token = res.json()['access_token']

    def verify(self, id_path, selfie_path, **extra):
        files = {
            'id_document':  open(id_path, 'rb'),
            'selfie_video': open(selfie_path, 'rb'),
        }
        for key, path in extra.items():
            if path: files[key] = open(path, 'rb')

        return requests.post(
            f'{self.BASE}/verify/upload',
            headers={'Authorization': f'Bearer {self.token}'},
            files=files, timeout=60
        ).json()

# Usage
kyc = BMIKycClient('you@company.com', 'yourpassword')
result = kyc.verify('id.jpg', 'selfie.jpg',
    vehicle_document='car.jpg',
    insurance_document='insurance.jpg'
)

if result['decision'] == 'approved':
    activate_user()
elif result['decision'] == 'manual_review':
    show_pending()
else:
    show_error(result['decision_reason'])
JavaScript (Node.js)
const FormData = require('form-data');
const fs = require('fs');
const axios = require('axios');

const BASE = 'https://kyc.bm-e-industrie.com/api/v1';

async function getToken(email, password) {
  const { data } = await axios.post(`${BASE}/auth/login`,
    `username=${email}&password=${password}`,
    { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }
  );
  return data.access_token;
}

async function verify(token, idPath, selfiePath) {
  const form = new FormData();
  form.append('id_document',  fs.createReadStream(idPath));
  form.append('selfie_video', fs.createReadStream(selfiePath));

  const { data } = await axios.post(`${BASE}/verify/upload`, form, {
    headers: { 'Authorization': `Bearer ${token}`, ...form.getHeaders() },
    timeout: 60000
  });
  return data;
}

// Usage
const token = await getToken('you@company.com', 'yourpassword');
const result = await verify(token, 'id.jpg', 'selfie.jpg');
console.log(result.decision); // 'approved' | 'rejected' | 'manual_review'
cURL (bash)
#!/bin/bash

# Step 1: Get token
TOKEN=$(curl -s -X POST \
  https://kyc.bm-e-industrie.com/api/v1/auth/login \
  -d "username=you@company.com&password=yourpassword" \
  | python3 -c "import sys,json;print(json.load(sys.stdin)['access_token'])")

# Step 2: Submit verification
curl -s -X POST \
  https://kyc.bm-e-industrie.com/api/v1/verify/upload \
  -H "Authorization: Bearer $TOKEN" \
  -F "id_document=@id_card.jpg" \
  -F "selfie_video=@selfie.jpg" \
  | python3 -m json.tool

Need help? Contact us at

jude.takwan@bm-e-industrie.com

Back to home