⚠ MOCK · Lighthouse Briefings · figures in this spec are illustrative until labelled REAL.
← Lighthouse Briefings · analysis · 2026-06-16
analysis me.botim.pay.kyc

KYC Analysis Tables — Build Spec & Tracking TODO

Per-channel success funnel, failure reasons, and drop-off analysis — table layer + instrumentation work needed.
🪪 Interactive mockup: Lighthouse for KYC — Funnel & Bad-KYC analysis ↗
中文: 建表需求与埋点 TODO
Update · 2026-06-17 — server-side口径 confirmed. Channel = kyc.tm_kyc_apply.flow_type (UAE_KYC_FULL / UAEPASS / SIGNZY_FULL). Live 7d completion: UAE_KYC_FULL 65.5%, UAEPASS 54.9%, SIGNZY_FULL 46.3%. Daily Bad-KYC ~40% (spikes 45.5% on 06-06 & 06-14, aligned with UAEPASS incidents). UAE Pass root cause: 94% of failures = job check timeout (document-verification timeout, via outman.t_uaepass_monitor_event), only 6% user-reject. Interactive 3-tab dashboard updated with real data. Remaining open question: uid ↔ member_id join key for client-attribute filters (country / version / mp-version / platform).

00 What this doc is & why

KYC gates every Botim Pay finance feature (transfers, prepaid card, top-up). When KYC goes bad, downstream revenue is blocked. Today we have server-side success on submitted applications (~50% completion, dedup) — but were blind to the top-of-funnel drop where ~40% never even start a verification method. This doc joins the front-end funnel with server-side completion to see the whole picture, and drives three things:

Why front-end kMPH5: the KYC business events (KYC Flow Entered, KYC Verification Started, …) live inside kMPH5 with all business properties — including Entry Point (intent source). Business properties appear either at the JSON root or under mp_detail_obj (dual schema); regexp_extract reads across both. Completion & errors are reconciled with server-side KYC tables.

00·b Bad KYC — definition DRAFT · needs eng sign-off

A Bad KYC is an attempt that showed genuine intent but failed to reach a successful server-side completion — excluding pure user cancellation. A session is Bad KYC when all hold:

Excluded (not Bad KYC): user Cancel, "just exploring", never-started, "no Emirates ID right now" (will-retry).

Metric: Bad KYC rate = Bad KYC sessions / sessions reaching Verification Started, sliced by channel × client dims. (Session-level once session_id lands; user-level until then.)

00·c KYC order decomposition (MECE)

Every KYC order falls in exactly one bucket — A + B + C + D = 100%:

BucketMeaningFormula (of all orders)ObservedSource
A · No-intententered, never started verification(Entered − Started) / Entered~40%front-end
B · Bad KYCintent but KYC failed (pre-audit)(Started − KYC_passed − Cancel) / Entered~25%tm_kyc_apply
C · Risk/audit rejectKYC passed but audit rejectedAudit_reject / Entered~0.4%tm_audit_task (R)
D · Final successKYC passed + audit passed(KYC_passed − Audit_reject) / Entered~35%tm_kyc_apply

Variables: Entered = Flow Entered distinct uid (denominator) · Started = Verification Started · KYC_passed = journey_flag=1 OR step IN ('DOWN','PRE_EID_EXPIRY') · Cancel = user-reject / give-up / not-comfortable (excluded from Bad KYC) · Audit_reject = tm_audit_task status=R.

Audit funnel (30d): entered 2,016 → reject 1,120 (55.6%) / pass 886 (44%); 97% triggered by CHANNEL_HINT_REVIEW. Top reject reasons: Portrait Inspection Failed 66%, Document & ICP mismatch — expiry 17% / DOB 9%.

Insight: the two biggest losses are No-intent (~40%) and Bad KYC (~25%); manual audit (C) is a tiny slice (~2k/month). Bad KYC is dominated by UAE Pass job check timeout + UAE KYC "EID not scanned".

01 Table architecture

logs_footprint_mp (raw JSON)  +  kyc.* server tables
        │  flatten / parse
        ▼
  ① vew.kyc_event_flatten_di        DWD flatten table (one row per event)
        │  GROUP BY
        ▼
  ② ads_kyc_channel_funnel_di       per-channel success funnel
  ③ ads_kyc_failure_reason_di       failure reasons
  ④ ads_kyc_leave_reason_di         drop-off reasons
        +
  ⑤ dim_kyc_entry_point             entry-point code → business name (optional)
        +
  ⑥ dwd_kyc_apply_di                server apply mirror (TiDB tm_kyc_apply) — required for Bad KYC
#TableLayerStatus
vew.kyc_event_flatten_diDWD flattenBuilt (rerun for new schema)
vew.ads_kyc_channel_funnel_diADS resultTo build
vew.ads_kyc_failure_reason_diADS resultTo build
vew.ads_kyc_leave_reason_diADS resultTo build
vew.dim_kyc_entry_pointDIMOptional · needs dict
vew.dwd_kyc_apply_diDWD (server)To build · required for Bad KYC

Can we compute Bad KYC today? Bad KYC needs server completion (is_passed) which the front-end flatten table doesn't have — so it comes from dwd_kyc_apply_di (mirror of TiDB tm_kyc_apply). Server-only Bad KYC (per channel/day) = started − kyc_passed − cancel, computable once ⑥ is built. The full decomposition (incl. No-intent + client-attr filters) additionally needs the uid ↔ member_id join key (P0).

02 The three channels

Each channel has a different flow / pages, so funnels must be built per channel — the same event cannot be compared across channels.

ChannelFormKey trait
UAE PassJumps out of app for authorization (black box to front-end)Handoff invisible to us — rely on server-side error callback
UAE KYCIn-app flowHas its own pages: EID Details Viewed / Confirmed
signzyTBDMilestone events to be confirmed by eng

03 Result tables

② Per-channel success funnel — ads_kyc_channel_funnel_di (tall)

Grain: dt × channel × combination dims × funnel_step. Metric: users (distinct uid; distinct session once available).

stepAllUAE KYCUAE Pass
0Flow Entered (channel=ALL)
1Verification StartedVerification StartedVerification Started
2EID Details ViewedUAE Pass Continue
3EID ConfirmedSDK Return (needs tracking)
4Submitted (server)Submitted (server)
5Completed (server)Completed (server)

③ Failure reasons — ads_kyc_failure_reason_di

Grain: dt × channel × failure_source × failure_reason × dims. Sources: frontend_error (Error Type), server_callback (error code), audit_reject (tm_audit_log.memo), status_page (Page Code). Must split user cancel (Cancel) vs system error.

④ Drop-off reasons — ads_kyc_leave_reason_di

Grain: dt × channel × leave_source × leave_page × leave_reason × dims. Primary signal is Exit Bottom sheet Viewednot the Leave-click event (most users just close the app).

03·b Schemas & sample data

Full DDL in analysis/schema-ddl.sql. ②③④ sample figures are illustrative (06-01 distribution); error_code / server values pending eng.

vew.kyc_event_flatten_di — DWD flatten (partition dt · PARQUET)

Business fields: uid, event, entry_point, verification_type, kyc_flow_type, kyc_vendor, error_type, page_code, kyc_status, reason, page_dropped_from, cta_clicked, industry, renewal_flow, uri_path
Client dims: client_version, mp_version, os_version, device_brand, device_model, device_type, net_type, language, region, uid_region, ip_region, ip_isp, event_ts + partition dt (all STRING; event_ts BIGINT)

uidevententry_pointverification_typekyc_flow_typekyc_vendorerror_typeregionclient_verdt
f7bb…KYC Flow EnteredwalletNew User KYCAE4.9.120260601
f7bb…KYC Verification StartedwalletVerify with UAE PassNew User KYCUAE passAE4.9.120260601
192c…KYC UAE Pass Validation ErrorVerify with UAE PassNew User KYCCancelAE4.9.120260601
e0b0…KYC Emirates ID details ConfirmedTO_REMITTENCEScan or upload your Emirates IDNew User KYCUAE KYC SDKAE4.7.220260601

vew.ads_kyc_channel_funnel_di — per-channel funnel (tall · partition dt)

Fields: channel, entry_point, kyc_flow_type, verification_type, client_version, mp_version, os_platform, region, net_type, step_order INT, step_name, users BIGINT, events BIGINT, dt

channelentry_pointflowregionstep_orderstep_nameusersdt
ALLwalletNewAE0Flow Entered4,82020260601
UAE_KYCwalletNewAE1Verification Started1,82020260601
UAE_KYCwalletNewAE3EID Confirmed96820260601
UAE_KYCwalletNewAE5Completed (server)51220260601
UAE_PassTO_REMITTENCENewAE1Verification Started1,34020260601
UAE_PassTO_REMITTENCENewAE5Completed (server)19820260601

vew.ads_kyc_failure_reason_di — failure reasons (partition dt)

Fields: channel, failure_source, failure_reason, error_code, is_user_cancel BOOLEAN, entry_point, kyc_flow_type, verification_type, client_version, mp_version, region, users BIGINT, events BIGINT, dt

channelfailure_sourcefailure_reasonerror_codeis_user_cancelusersdt
UAE_Passfrontend_errorCanceltrue1,20820260601
UAE_Passserver_callbackUAEPASS_TIMEOUTE_UP_408false34020260601
UAE_KYCstatus_pagerenewNotMatchfalse28620260601
UAE_KYCaudit_rejectUpload clear EID (both sides)false18720260601

vew.ads_kyc_leave_reason_di — drop-off reasons (partition dt)

Fields: channel, leave_source, leave_page, leave_reason, entry_point, kyc_flow_type, client_version, mp_version, region, users BIGINT, dt

channelleave_sourceleave_pageleave_reasonusersdt
ALLexit_sheet/kyc/homeexit_sheet_viewed6,48620260601
UAE_Passmp_closeSDK auth page (needs tracking)(unknown)58020260601
UAE_KYCleave_click/kyc/document-reviewI'll do it later31220260601

vew.dim_kyc_entry_point — entry-point dictionary (no partition · optional)

Fields: entry_point_code, entry_point_name, category, is_tech_source BOOLEAN, note

codenamecategoryis_tech_sourcenote
walletWallet activationwalletfalse
TO_REMITTENCEInternational transfertransferfalsemisspelled Remittance
mp-ogold / mpogoldoGoldotherfalsemerge two spellings
uniapp(tech container)techtrueexclude from analysis

04 Counting rules

  1. Use distinct users (distinct session once available) — front-end has 10–28% double-firing; raw event counts are not usable.
  2. Completion & errors from server-side — front-end Completed does not fire on redirect flows.
  3. Per-channel funnels differ — never compare the same event across channels.
  4. Drop-off uses Exit-sheet — Leave-click count is severely under-reported.
  5. Channel is known from step 1 (Verification Started), not at Flow Entered — the top Entered→Started drop is channel-agnostic.

05 Tracking TODO (for eng)

PriorityItemWhy
P1Fix double-firing on 8 impression/click events (Exit Bottom sheet 28% highest)Event counts inflated 10–28%
P1Switch completion + errors to server-side sourceFront-end Completed not fired on redirect; UAE Pass handoff is a black box
P2Server-issued session_id carried in every front-end event; refine analysis from user-level to session-levelRemoves multi-attempt noise; makes per-attempt funnels correct
P2Add specific page name to leave events (own page vs SDK page)Locate where UAE Pass users vanish in the SDK black box
P3UAE KYC confirm page: record retry/leave click count + problem encounteredDiagnose in-app verification friction
P3Backfill Entry Point (~30% empty) + standardize codesIntent attribution coverage

06 Dependencies — need eng sign-off before build

  1. Channel rule: kyc_vendor + verification_type → 3 channels.
  2. signzy milestone events.
  3. Server-side error-callback table (channel / error_code / member_id / time).
  4. Server completion source (can tm_kyc_apply carry channel / entry_point?).
  5. session_id linkage to member_id / apply on the server.
  6. Entry Point code → business-name dictionary (merge mp-ogold/mpogold, MANDATORY_KYC/_V2).
  7. UAEPASS drop-off: does the server record it? (tr_leave_record excludes it.)