KYC Service
Subject to change
API reference for the Know Your Customer (KYC) verification process for users.
VERTEX ENGINE
Table of Contents
- Introduction to KYC
- KYC States and User Verification
- Document Upload to NATS Object Storage
- Submitting KYC Documents
- Querying User Information
1. Introduction to KYC
Know Your Customer (KYC) is a critical process for verifying the identity of users. The KYC process is integrated into the user aggregate and affects the user's verified
status.
KYC Process Flow
- User Registration: Personal entity user created - KYC state is
Invalid
- Document Upload: User uploads documents to NATS object storage
- Document Submission: User submits document references - KYC state transitions to
Pending
- Review Process: Admin reviews submitted documents
- Approval/Rejection: Admin approves or rejects KYC - KYC state becomes
Approved
orRejected
- User Verification: Upon KYC approval, the user's
verified
field is set totrue
2. KYC States and User Verification
KYC States
The system tracks KYC progress through the following states:
- Invalid (0): Initial state, no KYC documents uploaded
- Pending (1): KYC documents uploaded, awaiting review
- Approved (2): KYC documents reviewed and approved
- Rejected (3): KYC documents reviewed and rejected
User Verification
The verified
field on the user aggregate indicates whether a user has completed KYC verification:
verified: false
- User has not completed KYC verificationverified: true
- User has successfully completed KYC verification (KYC state isApproved
)
The verifiedAt
timestamp is set when KYC is approved.
3. Document Upload to NATS Object Storage
KYC documents should be uploaded to NATS object storage before submitting the verification request. The API is storage-agnostic and accepts document storage keys from any storage solution, but NATS object storage is recommended for consistency across the platform.
Document Naming Convention
Documents should follow these naming patterns for consistency:
Identity Documents:
kyc_id_front_<user_id>_<timestamp>
- Front of ID documentkyc_id_back_<user_id>_<timestamp>
- Back of ID document (if applicable)kyc_passport_<user_id>_<timestamp>
- Passport (if using passport instead of ID)
Proof of Residence:
kyc_proof_of_residence_<user_id>_<timestamp>
- Utility bill, bank statement, etc.
Command Line Upload Method
# Example: Upload multiple KYC documents
xargs -I {} nats obj put --name={} kyc_dropbox_<user_id> ./document.pdf << EOF
kyc_id_front_<user_id>_<timestamp>
kyc_id_back_<user_id>_<timestamp>
kyc_proof_of_residence_<user_id>_<timestamp>
EOF
Programmatic Access (JavaScript)
// 1. Connect to NATS and get object store const userId = "550e8400-e29b-41d4-a716-446655440000"; const bucketName = `kyc_dropbox_${userId}`; const os = await natsConn?.jetstream().views.os(bucketName);
2. Upload document with headers
import { MsgHdrsImpl } from "nats.ws";
async function uploadKYCDocument(os, userId, documentType, file) {
const timestamp = Math.floor(Date.now() / 1000);
let documentName = '';
switch(documentType) {
case 'ID_FRONT':
documentName = `kyc_id_front_${userId}_${timestamp}`;
break;
case 'ID_BACK':
documentName = `kyc_id_back_${userId}_${timestamp}`;
break;
case 'PASSPORT':
documentName = `kyc_passport_${userId}_${timestamp}`;
break;
case 'PROOF_OF_RESIDENCE':
documentName = `kyc_proof_of_residence_${userId}_${timestamp}`;
break;
default:
throw new Error('Invalid document type');
}
const headers = new MsgHdrsImpl();
headers.set("original_filename", file.name);
try {
const info = await os?.put(
{
name: documentName,
headers,
},
fileToReadableStream(file)
);
console.log(`Uploaded ${file.name} as ${documentName}`);
return documentName;
} catch (err) {
console.error("Upload error:", err);
throw err;
}
}
// Helper function to convert File to ReadableStream
function fileToReadableStream(file) {
return new ReadableStream({
async start(controller) {
const reader = file.stream().getReader();
try {
while (true) {
const { done, value } = await reader.read();
if (done) break;
controller.enqueue(value);
}
} finally {
controller.close();
}
}
});
}
4. Submitting KYC Documents
After uploading documents to your storage solution, submit the document references to initiate the verification process.
Subject: svc.user.<partner_id>.upload_kyc_documents
Permissions Required: write
Request
{
"user_id": "550e8400-e29b-41d4-a716-446655440000", // Required - UUID of the user
"id_document": "string", // Optional - Storage key for ID document
"proof_of_residence": "string" // Optional - Storage key for proof of residence
}
Important Notes
- At least one document must be provided (either
id_document
orproof_of_residence
) - Both documents can be provided, but only one is required
- The storage keys should reference documents already uploaded to your storage solution
- Documents can be re-uploaded if KYC is in
Pending
orRejected
state
Example Request:
{
"user_id": "550e8400-e29b-41d4-a716-446655440000",
"id_document": "kyc_id_front_550e8400-e29b-41d4-a716-446655440000_1726220326",
"proof_of_residence": "kyc_proof_of_residence_550e8400-e29b-41d4-a716-446655440000_1726220276"
}
Response
{
"success": true,
"message": "KYC documents uploaded and submitted for verification"
}
Effects
- KYC state transitions from
Invalid
toPending
kycSubmittedAt
timestamp is set- Emits
KYCDocumentsUploaded
event - Documents are stored in the user aggregate's
kycDocuments
map
Possible Errors
400 Bad Request - Missing Documents
{
"error": {
"code": 400,
"message": "missing documents"
}
}
Returned when no documents are provided.
400 Bad Request - Already Verified
{
"error": {
"code": 400,
"message": "user already verified"
}
}
Returned when the user is already verified or KYC is already approved.
5. Querying User Information
List Users
Get a list of users with their KYC status and verification details.
Subject: svc.user.<partner_id>.list
Permissions Required: read
Request
{
"entity_id": "550e8400-e29b-41d4-a716-446655440000" // Required - Entity ID to filter users
}
Response
{
"users": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"first_name": "John",
"last_name": "Doe",
"email": "john.doe@example.com",
"phone_number": "+27123456789",
"verified": true,
"verified_at": "2024-08-01T15:30:45Z",
"kyc_status": "approved", // "pending", "approved", "rejected", or empty
"kyc_submitted_at": "2024-08-01T14:00:00Z",
"kyc_reviewed_at": "2024-08-01T15:30:45Z",
"kyc_rejection_reason": "", // Only populated if rejected
"entity_id": "550e8400-e29b-41d4-a716-446655440000",
"created_at": "2024-08-01T10:00:00Z",
// ... other user fields
}
]
}
Note
The kyc_status
field returns string values ("pending", "approved", "rejected") for better readability, mapped from the internal numeric KYC state.