iLive Docs
Guides

KYC onboarding

End-to-end KYC using Liveness and Face Services.

You run a fintech. A new customer installs your app and taps Open account. Before you let them move money, you need to know three things:

  1. A real, live human is on the other side of the screen.
  2. Their face matches the government ID they uploaded.
  3. They haven't already registered under a different name.

This guide wires all three checks together using iLive's managed liveness sessions and v2 Face Services. Budget thirty minutes.

Architecture

┌─────────────┐    1. start session     ┌───────────────────┐
│             │───────────────────────▶│                   │
│  Your app   │◀──── session_url ──────│   iLive platform  │
│ (backend)   │                         │                   │
│             │                         │  • liveness       │
│             │◀──── webhook: pass ─────│  • face services  │
│             │ 3. compare vs ID photo  │                   │
│             │───────────────────────▶│                   │
│             │ 4. index + dedupe       │                   │
│             │───────────────────────▶│                   │
└─────────────┘                         └───────────────────┘

      │ stores: ID photo, user row, iLive face_id

 your database

The ID photo your customer uploads stays on your side. iLive only sees it when you send it in a compare or index call; we do not retain images by default beyond the scope of the operation.

Step 1 — Liveness verification

Create a managed session in passive mode. Passive is the right choice for onboarding: it's a three-second hold, and it still runs the full anti-spoof stack. Register a webhook so you don't have to poll.

import httpx
 
async def start_liveness(user_id: str):
    async with httpx.AsyncClient() as c:
        r = await c.post(
            "https://api.iliveauth.com/api/v1/managed-session",
            headers={"Authorization": f"Bearer {ILIVE_KEY}"},
            json={
                "mode": "passive",
                "redirect_url": f"https://your-app.com/onboard/done?uid={user_id}",
                "webhook_url":  "https://your-app.com/webhooks/ilive",
                "reference_id": user_id,
            },
        )
        r.raise_for_status()
        return r.json()["session_url"]

Redirect the user to the returned session_url. When they finish, iLive POSTs the verdict to your webhook:

@app.post("/webhooks/ilive")
async def ilive_webhook(req: Request):
    body = await req.json()
    if body["verdict"] != "pass":
        await flag_for_review(body["reference_id"], body["verdict"])
        return {"status": "ok"}
 
    # Proceed to step 2 with the ICAO photo from this session.
    await enqueue_identity_match(body["reference_id"], body["session_id"])
    return {"status": "ok"}

See Webhooks for idempotency guidance.

Step 2 — Compare the verified face to the uploaded ID

The managed session yields an ICAO-compliant photo at GET /api/v1/session/{id}/photo. Feed that (source) and the customer's ID photo (target) to /api/v2/faces:compare:

curl -X POST https://api.iliveauth.com/api/v2/faces:compare \
  -H "Authorization: Bearer iksk_..." \
  -F source_image=@icao.jpg \
  -F target_image=@id_photo.jpg

A similarity above 0.65 is a solid match for onboarding. Below that, queue the account for manual review rather than auto-rejecting — poor lighting on the ID photo is the most common cause of false negatives.

Step 3 — Index the face

Once the applicant is verified and matched, store their face in a per-tenant collection so you can search it later. Use your internal user ID as external_face_id:

curl -X POST https://api.iliveauth.com/api/v2/collections/users/faces \
  -H "Authorization: Bearer iksk_..." \
  -F image=@icao.jpg \
  -F external_face_id=user-12345

If the collection doesn't exist yet, create it once up front:

curl -X POST https://api.iliveauth.com/api/v2/collections \
  -H "Authorization: Bearer iksk_..." \
  -H "Content-Type: application/json" \
  -d '{ "external_id": "users", "description": "Verified onboarding faces" }'

Step 4 — Detect duplicates

Before finalising the account, search the same collection to see if this face already belongs to another user:

curl -X POST \
  "https://api.iliveauth.com/api/v2/collections/users/search-by-face/{face_id}?max_faces=5" \
  -H "Authorization: Bearer iksk_..."

Inspect the response:

  • If every hit shares the new user's external_face_id, you're just seeing the face you just indexed — proceed.
  • If any hit with a different external_face_id scores higher than 0.5, flag for review. That's a strong signal of a duplicate identity attempt.

See the Deduplication guide for thresholds tuned to your false-positive budget.

Pitfalls and tips

  • Photo quality matters. Passive-mode responses include a photo_quality_score from 0–1; treat anything below 0.6 as a candidate for a retry rather than a hard compare.
  • Pick thresholds deliberately. The right similarity threshold depends on your risk appetite and population. Start at 0.65 for match and 0.5 for dedupe, then tune against manual-review outcomes.
  • Handle retry gracefully. A retry verdict means the session was inconclusive, not fraudulent. Offer the user one more attempt before escalating to manual review.
  • Store the session_id. Keep it alongside the user record — it's the audit trail for the liveness check.

On this page