Unified KYC specification v1
Reference document for product, compliance and technical teams at partner SGIs. Architecture, levels, public API, cross-SGI portability and cryptographic audit.
1. Founding principles
- Portability: a KYC validated within one SGI can, with investor consent, be reused by any other partner SGI without redoing the documents.
- Cryptographic proof: every validated artefact is sealed with an Inopay Ed25519 signature plus qualified timestamp. The consuming SGI verifies authenticity without depending on Inopay.
- Tamper-evident audit trail: every action (submission, validation, access, consent revocation) is recorded in an append-only log, consultable for 5 years.
- Data non-custodial: Inopay acts as a processor under the UEMOA Personal Data Protection Act. Data remain owned by the SGI and the investor.
- Standardised levels: 3 levels (KYC1/2/3) aligned on the anticipated CREPMF grid and CIMA best practices.
2. The 3 levels
| Level | Required items | Allowed ticket | 1st validation SLA |
|---|---|---|---|
| KYC1 | ID document (national ID / passport) + selfie liveness + GDPR/UEMOA consent | < 100,000 FCFA (~€150) | < 24 business hours |
| KYC2 | KYC1 + proof of address (< 3 months) + source of income declaration + investment profile (AMF-UMOA questionnaire) | < 5,000,000 FCFA (~€7,500) | < 48 business hours |
| KYC3 | KYC2 + PEP screening + detailed source of funds + bank reference letter or tax statement | Unlimited / institutional | < 5 business days |
3. KYC lifecycle
NEW— journey initiated by the investor, draft saved automatically.PENDING— submitted, under validation (AI + conditional human review).REQUIRES_COMPLETION— missing or non-compliant document, targeted request sent to the investor.VALIDE— accepted, cryptographically sealed, usable. Valid for 12 months.REJECTED— rejected after 3 attempts or proven fraud. Investor notified with clear reason.EXPIRED— beyond 12 months without renewal.REVOKED— revoked after incident (post-validation fraud, investor request, CREPMF order).
4. Technical architecture
Components
- REST API (
api.getinopay.com/v1/kyc/*) — SGI key authentication (inopay_live_*/inopay_test_*), HMAC-SHA256 signatures on sensitive request bodies. - Validation pipeline — magic bytes MIME → OCR → multimodal vision (primary: Gemini; fallback: DeepSeek) → anti-fraud scoring → conditional human review queue.
- Document storage — MinIO S3-compatible, AES-256-GCM encryption at rest, key rotation every 90 days.
- Signed registry —
kyc_attestationsappend-only table, every validation produces an Ed25519 attestation with SHA-256 hashes of documents. - Signed webhooks — real-time events pushed to SGIs, HMAC-SHA256 signature in the
X-Inopay-Signatureheader.
Cross-SGI portability flow
1. Existing investor at SGI A (KYC VALIDE) → registers at SGI B
2. SGI B calls: GET /v1/kyc/by-email/{email}
→ Inopay replies: { exists: true, level: "KYC2", validated_at: "...", ... }
3. SGI B initiates: POST /v1/kyc/{id}/request-portability
→ Inopay sends an SMS/email to the investor:
"SGI B wishes to access your KYC. Do you authorise?"
4. Investor clicks → consent recorded (logged, signed)
5. SGI B calls: GET /v1/kyc/{id}
→ Inopay delivers the signed attestation + documents (1h pre-signed URLs)
6. SGI B verifies the Ed25519 signature locally → accepted without redoing the docs
7. Inopay bills 300 FCFA reuse to SGI B
5. Main API endpoints
| Method | Endpoint | Description |
|---|---|---|
| POST | /v1/kyc/sessions | Create a KYC session for a new investor. Returns a unique URL to send to the investor. |
| GET | /v1/kyc/:id | Retrieve current state + signed attestation (if VALIDE). |
| GET | /v1/kyc/by-email/:email | Check existence of a reusable KYC (minimal response for privacy). |
| POST | /v1/kyc/:id/request-portability | Request access to an existing KYC from another SGI. |
| POST | /v1/kyc/:id/complement | Trigger a complement request (missing document, data to refresh). |
| POST | /v1/kyc/:id/revoke | Revoke the KYC (fraud, investor request, regulatory order). |
| GET | /v1/kyc/:id/audit-trail | Retrieve the complete signed history for CREPMF audit. |
| POST | /v1/kyc/verify-attestation | Verify an Ed25519 attestation offline without a network call (optional). |
6. Webhook events
kyc.submitted— new submission receivedkyc.validated— validation effective, attestation generatedkyc.rejected— rejection with reasonkyc.requires_completion— complement requested from investorkyc.portability_requested— another SGI requests accesskyc.portability_consented— investor granted accesskyc.revoked— revocation performedkyc.expired— expiration without renewal
7. Security & compliance
- Encryption in transit: TLS 1.3 only
- Encryption at rest: AES-256-GCM, keys managed via HashiCorp Vault (roadmap Q2 2026)
- SGI API authentication: distinct test/prod keys, rotation available on request
- Rate limiting: 1,000 req/min per SGI key, 100 KYC/day/SGI by default (adjustable)
- Anti-fraud: modified document detection (EXIF + perceptual hash), selfie liveness, VPN blockers detected
- GDPR / UEMOA Act: rights to access, rectification, erasure (after legal retention), portability
- CREPMF: PSP-KYC licence application filing within 18 months
- ISO 27001: certification targeted Q4 2026
8. Quality commitments
| Indicator | Target |
|---|---|
| Auto-validation rate (without human review) | ≥ 75 % KYC1, ≥ 50 % KYC2 |
| False positive rate (valid KYC rejected) | < 2 % |
| False negative rate (fraudulent KYC validated) | < 0.1 % (target: 0) |
| API availability | 99.5 % monthly |
| Median API response time | < 300 ms |
9. Interoperability
The Inopay API follows the OpenAPI 3.1 specification, documented and versioned. SGIs can generate clients in their language of choice (Node, Python, Java, Go, PHP).
The spec is exportable to a PAS-CIMA format should CIMA publish a regional KYC standard (work in progress with the Regional Council).
Version v1 — frozen on 22/04/2026. v1.x changes backward-compatible. Major evolutions (v2) with 180 days' notice + v1 support for 12 months after v2 release.