Problem
Nester's core value proposition is getting yield-bearing savings out to fiat in Nigeria and other African markets. In every one of those markets, processing a fiat payout to a bank account is a regulated activity. Without KYC (Know Your Customer) and basic AML (Anti-Money Laundering) checks:
- Payment providers (Flutterwave, Paystack, etc.) will reject settlements above micro-transaction thresholds
- The product is not legally operable at scale in NG/GH/KE
- Fraudulent offramp attempts have no friction
This is a prerequisite for issue #100 (real fiat settlement integration) working at any meaningful volume.
Scope
This issue covers the minimum viable KYC layer needed to unblock offramp. Full regulatory compliance is beyond scope here — the goal is to gate settlement behind a basic identity check and pass verified user data to the payment provider.
What's Needed
Backend
New domain: apps/api/internal/domain/kyc/
model.go // KYCSubmission, KYCStatus (pending/approved/rejected/expired)
service.go // Submit, GetStatus, Verify
repository.go // Interface
New DB migration:
CREATE TABLE kyc_submissions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES users(id),
status TEXT NOT NULL DEFAULT 'pending', -- pending | approved | rejected | expired
full_name TEXT NOT NULL,
date_of_birth DATE NOT NULL,
country TEXT NOT NULL, -- ISO 3166-1 alpha-2
id_type TEXT NOT NULL, -- nin | bvn | passport | drivers_license
id_number TEXT NOT NULL, -- encrypted at rest
submitted_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
reviewed_at TIMESTAMPTZ,
rejection_reason TEXT
);
Endpoints:
POST /api/v1/users/{id}/kyc — submit KYC details
GET /api/v1/users/{id}/kyc — get current KYC status
Settlement guard:
POST /api/v1/settlements must check kyc_submissions.status = 'approved' before creating
- Return
403 + { code: "KYC_REQUIRED", message: "..." } if not verified
For MVP: verification can be manual (admin reviews and approves via PATCH /api/v1/admin/kyc/{id}) with a future path to automated NIN/BVN lookup APIs.
Frontend
New settings page section: "Identity Verification"
- Show current KYC status badge (Not Started / Pending Review / Verified / Rejected)
- Form to submit: full name, date of birth, country, ID type, ID number
- On rejection: show reason and allow resubmission
- Lock settlement UI with an inline prompt to complete KYC if status is not
approved
Admin Panel
GET /api/v1/admin/kyc — list pending submissions
PATCH /api/v1/admin/kyc/{id} — approve or reject with reason
Acceptance Criteria
Notes
- NIN (National Identification Number) and BVN (Bank Verification Number) are the two most important ID types for Nigeria
- Do NOT store the raw ID number in plaintext — encrypt with the app secret or a separate KMS key
- Future iteration: automate BVN lookup via NIBSS or a licensed data broker
Problem
Nester's core value proposition is getting yield-bearing savings out to fiat in Nigeria and other African markets. In every one of those markets, processing a fiat payout to a bank account is a regulated activity. Without KYC (Know Your Customer) and basic AML (Anti-Money Laundering) checks:
This is a prerequisite for issue #100 (real fiat settlement integration) working at any meaningful volume.
Scope
This issue covers the minimum viable KYC layer needed to unblock offramp. Full regulatory compliance is beyond scope here — the goal is to gate settlement behind a basic identity check and pass verified user data to the payment provider.
What's Needed
Backend
New domain:
apps/api/internal/domain/kyc/New DB migration:
Endpoints:
Settlement guard:
POST /api/v1/settlementsmust checkkyc_submissions.status = 'approved'before creating403 + { code: "KYC_REQUIRED", message: "..." }if not verifiedFor MVP: verification can be manual (admin reviews and approves via
PATCH /api/v1/admin/kyc/{id}) with a future path to automated NIN/BVN lookup APIs.Frontend
New settings page section: "Identity Verification"
approvedAdmin Panel
GET /api/v1/admin/kyc— list pending submissionsPATCH /api/v1/admin/kyc/{id}— approve or reject with reasonAcceptance Criteria
403 KYC_REQUIREDif user has no approved KYCNotes