KYC
Verify identity documents, match a live face to them, and review the results.
The KYC service checks a customer's identity document, captures their face, matches the two, and keeps a record your team can review.
Use the REST API for now
The aice-kyc / @aiceafrica/kyc clients don't match the current service: they leave out the /api/v1/kyc path and the tenant_id every request needs, send files where the service expects JSON, and authenticate with the tenant key where the service expects a service key. Until they're updated, call the endpoints below directly.
Base URL and access
Every route is under /api/v1/kyc, so locally the base is http://localhost:6700/api/v1/kyc.
Requests carry a service key rather than a project key:
X-Service-Key: <service key>The service skips this check when it runs in development. Anywhere else, a missing or wrong key is a 401. Call it only from your backend.
Every request names the organization in its body or query as tenant_id (your organization's UUID).
Verify a customer
Start verification
curl -X POST "$AICE_KYC_URL/verify" \
-H "X-Service-Key: $KYC_SERVICE_KEY" -H "Content-Type: application/json" \
-d '{
"tenant_id": "'"$TENANT_ID"'",
"customer_id": "cust_123",
"doc_type": "national_id",
"doc_number": "12345678",
"country": "KE"
}'Optional fields: metadata, triggered_by, triggered_by_user_id.
Analyse the document
The service reads the image from storage, so upload it there first and pass its ID:
curl -X POST "$AICE_KYC_URL/documents/analyze" \
-H "X-Service-Key: $KYC_SERVICE_KEY" -H "Content-Type: application/json" \
-d '{
"tenant_id": "'"$TENANT_ID"'",
"customer_id": "cust_123",
"storage_object_id": "obj_front",
"doc_type": "national_id",
"country": "KE",
"side": "front"
}'Capture and match the face
Open a capture session for the analysed document, then send the captured frames:
curl -X POST "$AICE_KYC_URL/face/capture-session" \
-H "X-Service-Key: $KYC_SERVICE_KEY" -H "Content-Type: application/json" \
-d '{"tenant_id": "'"$TENANT_ID"'", "customer_id": "cust_123", "analysis_id": "an_1"}'
curl -X POST "$AICE_KYC_URL/face/match" \
-H "X-Service-Key: $KYC_SERVICE_KEY" -H "Content-Type: application/json" \
-d '{
"tenant_id": "'"$TENANT_ID"'",
"customer_id": "cust_123",
"analysis_id": "an_1",
"capture_token": "…",
"frames": ["<base64 jpeg>", "<base64 jpeg>"]
}'Send storage_object_ids instead of frames if the frames are already in storage.
Check the result
curl "$AICE_KYC_URL/status/cust_123?tenant_id=$TENANT_ID" -H "X-Service-Key: $KYC_SERVICE_KEY"Review
| Endpoint | Body | Does |
|---|---|---|
GET /admin/stats?tenant_id= | verification counts | |
POST /admin/accept | {tenant_id, customer_id, kyc_document_id} | accept a verification by hand |
POST /admin/recheck | {tenant_id, customer_id, kyc_document_id?} | run the checks again |
GET /admin/verifiers | the verification providers configured | |
POST /admin/face-review | {tenant_id, customer_id, kyc_document_id, decision, reviewer_user_id, note} | record a person's face-match decision |
Accepting and re-checking are compliance actions: don't retry them blindly after a timeout. Check the status first.
GET /healthz and GET /healthz/face report the service's health.