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

EndpointBodyDoes
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/verifiersthe 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.

On this page