Fintech KYC Analytics: Track Onboarding Drop-Off Without Storing PII
GuidesJuly 27, 202612 min read

Fintech KYC Analytics: Track Onboarding Drop-Off Without Storing PII

Fintech KYC funnels lose 40-60% of users. Track drop-off without storing PII.

Last March I sat in a war room at a neobank watching their growth team stare at a dashboard full of question marks. Literally. The KYC funnel showed "Step 1: 12,847 started" and then "Step 5: 4,192 completed." Steps 2, 3, and 4? Blank.

Compliance had killed analytics on those screens. Document uploads. SSN entry. Understandable, but painful.

Forty percent of their signups were vanishing into a black hole. Nobody could tell whether users rage-quit at the selfie step or bounced because the document scanner crashed on Android. I've been in this exact meeting five times this year with different fintechs. It's depressingly common.

The fix isn't complicated, but it requires thinking about analytics differently. You don't need to capture what's on the screen. Just know when users move between screens. Or don't.

What We're Building

By the end of this, you'll have funnel analytics for a fintech KYC flow that tracks step-to-step conversion without capturing any personally identifiable information. No SSNs, no passport photos, no addresses. Just event counts: X users reached document upload, Y users completed it, Z users made it to the approval screen.

The setup works for any KYC provider — Onfido, Jumio, Persona, or your own in-house flow. I'll use a typical neobank onboarding sequence as the example.

Prerequisites

  • A JustAnalytics account (free tier handles 100K events/month — plenty for testing)
  • Your onboarding flow running in any web framework or native app (React, React Native, Flutter — all work)
  • Access to fire events from your KYC provider's callbacks or webhooks
  • About 45 minutes for initial setup, plus time for compliance review

The Core Problem: KYC Steps Are PII Minefields

A typical neobank onboarding flow looks something like this:

  1. Email and phone signup
  2. Personal information (name, DOB, address)
  3. SSN or tax ID entry
  4. Document upload (passport, driver's license, or ID card)
  5. Selfie capture for liveness check
  6. KYC verification (async, happens server-side)
  7. Approval or manual review

Steps 2-5 are where compliance panics. They're not wrong.

Most analytics tools — GA4, Amplitude, Mixpanel — capture form field values by default. Or they make it dangerously easy to accidentally capture them. One misconfigured event property and suddenly you're storing Social Security numbers in a third-party analytics platform. If you're still on GA4, check out our GA4 to JustAnalytics migration guide for a safer alternative.

That's not a GDPR fine. That's incident response, regulatory notification, and possibly a very uncomfortable conversation with the OCC or your state banking regulator. I've seen this happen. It's not fun to watch.

So compliance says "no analytics on those screens." Engineering complies. And now you can't tell why 35% of users disappear at step 4.

Step 1: Define Funnel Events (Not Screen Content)

The mental shift: track transitions, not content. You don't care what users type. You care whether they successfully moved from step 3 to step 4.

Write out your funnel events like this:

kyc_step_email_completed
kyc_step_personal_info_completed
kyc_step_ssn_completed
kyc_step_document_uploaded
kyc_step_selfie_captured
kyc_step_verification_submitted
kyc_step_approved
kyc_step_rejected
kyc_step_manual_review

Notice what's NOT in the event names: any actual data. No kyc_ssn_entered_123456789. No kyc_document_type_passport. Just the fact that the step happened.

If you need to segment later (document type, device, etc.), you can add non-PII properties. But start with bare events. You can always add properties later; you can't un-capture PII.

Step 2: Instrument Browser Events with PII Exclusion

Here's where most teams mess up. (I've made this mistake myself, so no judgment.) They add analytics to their KYC flow and accidentally capture form fields because that's what their analytics SDK does by default.

With JustAnalytics, the default is safe — no form field capture, no text capture. But you still need to be intentional about where you fire events.

For a React-based onboarding flow (see our Next.js 15 analytics guide for framework-specific details):

// components/KYCSteps/SSNEntry.tsx
import { useCallback } from 'react';

export function SSNEntry({ onComplete }: { onComplete: () => void }) {
  const handleSubmit = useCallback((e: React.FormEvent) => {
    e.preventDefault();

    // Fire AFTER validation, but BEFORE sending to server
    // Event contains NO form data — just the fact that it happened
    window.ja?.('event', 'kyc_step_ssn_completed', {
      attempt_number: attemptCount,
      device_type: /mobile/i.test(navigator.userAgent) ? 'mobile' : 'desktop',
    });

    onComplete();
  }, [onComplete, attemptCount]);

  return (
    <form onSubmit={handleSubmit}>
      {/* SSN input fields — analytics never touches these */}
      <input type="password" name="ssn" autoComplete="off" />
      <button type="submit">Continue</button>
    </form>
  );
}

The key detail: the event fires on successful submission, not on every keystroke. You're tracking the transition, not the interaction.

For document upload steps, fire the event from the KYC provider's success callback:

// components/KYCSteps/DocumentUpload.tsx
import { useOnfido } from '@onfido/react-sdk';

export function DocumentUpload({ onComplete }: { onComplete: () => void }) {
  const handleComplete = useCallback((data: OnfidoResult) => {
    // Fire ONLY on success — no document data in the event
    window.ja?.('event', 'kyc_step_document_uploaded', {
      document_side: data.side, // 'front' or 'back', not the actual image
      attempt_number: attemptCount,
    });

    onComplete();
  }, [onComplete, attemptCount]);

  // Onfido SDK handles the actual capture
  return <OnfidoCapture onComplete={handleComplete} />;
}

What we're NOT doing: capturing the document image, the extracted text, or any biometric data. The analytics event just says "document was uploaded." That's it.

Step 3: Handle Server-Side KYC Outcomes

The final KYC decision happens server-side — your identity verification provider runs their checks and sends you a webhook. This is where you track approval, rejection, and manual review outcomes.

Node.js example for an Onfido webhook:

// api/webhooks/onfido.js
export async function POST(req) {
  const event = await req.json();

  // Verify webhook signature (omitted for brevity)

  let analyticsEvent;
  let properties = {};

  switch (event.payload.action) {
    case 'check.completed':
      const result = event.payload.object.result;
      if (result === 'clear') {
        analyticsEvent = 'kyc_step_approved';
      } else if (result === 'consider') {
        analyticsEvent = 'kyc_step_manual_review';
      } else {
        analyticsEvent = 'kyc_step_rejected';
        // Do NOT log the rejection reason — it contains PII
        properties.rejection_category = 'document_issue'; // generic bucket only
      }
      break;
    default:
      return new Response('OK', { status: 200 });
  }

  await fetch('https://api.justanalytics.app/v1/events', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.JA_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      site_id: process.env.JA_SITE_ID,
      event: analyticsEvent,
      properties,
    }),
  });

  return new Response('OK', { status: 200 });
}

Critical point: don't log the specific rejection reason. Onfido might tell you "SSN mismatch" or "document expired" — useful for customer support, but it doesn't belong in analytics. Use generic buckets: document_issue, identity_mismatch, manual_review_required.

(Yes, this means your analytics won't tell you why users fail. That's the trade-off. Your KYC provider's dashboard already shows failure reasons — use that for debugging. Analytics is for volume and trends.)

Step 4: Disable Replay on Sensitive Screens

If you're using session replay anywhere in your onboarding flow, you need to exclude KYC screens entirely. Not just mask them — exclude them.

The problem: even with input masking enabled, document upload screens can leak PII through:

  • Thumbnail previews of uploaded documents
  • OCR text rendered in the DOM by your KYC provider
  • Error messages that include extracted data ("The name on your document doesn't match: JOHN DOE")

Here's how to exclude specific routes from replay:

<script
  defer
  data-site="your-site-id"
  data-replay="true"
  data-replay-exclude="/onboarding/ssn,/onboarding/document,/onboarding/selfie"
  src="https://cdn.justanalytics.app/script.js">
</script>

On excluded routes, replay stops entirely — no DOM capture, no mouse tracking, nothing. The funnel events still fire, so you know users visited those pages. You just can't watch the recording.

This is the same principle we covered in the GDPR session replay guide: when in doubt, don't capture.

Step 5: Build the Funnel Dashboard

With events flowing, create your KYC funnel in JustAnalytics:

  1. Open Funnels → New Funnel
  2. Add steps in order:
    • kyc_step_email_completed
    • kyc_step_personal_info_completed
    • kyc_step_ssn_completed
    • kyc_step_document_uploaded
    • kyc_step_selfie_captured
    • kyc_step_verification_submitted
    • kyc_step_approved

You'll immediately see where users drop. In my experience, the two worst steps are usually:

  • Document upload (25-35% drop-off): Camera permissions, bad lighting, or — my favorite — users don't have their passport handy and never come back
  • Selfie/liveness (15-25% drop-off): Face detection fails, or users just really don't want to take a selfie on a Tuesday afternoon

Now you know which step needs work.

That neobank from the beginning of this article? Their document upload step had a 42% drop-off. Brutal. Turned out the Onfido SDK was crashing on older Android devices — a bug they'd never have found without step-level funnel data. Three weeks of engineering time wasted before they had visibility.

Common Errors and Fixes

Events fire but funnel shows zero users.

Check that the site_id in your events matches your dashboard. Also verify the events are reaching the API — open Network tab, fire a test event, confirm you see a 200 response. If you're behind a corporate proxy or VPN, the events might be blocked.

Document upload shows more completions than SSN entry.

You've got an event ordering bug. Either your SSN event isn't firing reliably, or your document event fires multiple times (once per document side). Add deduplication logic:

const firedRef = useRef(false);

const handleComplete = useCallback(() => {
  if (firedRef.current) return;
  firedRef.current = true;
  window.ja?.('event', 'kyc_step_document_uploaded');
}, []);

Webhook events don't appear.

Server-side events need API authentication. Make sure your JA_API_KEY environment variable is set and the key has write permissions. Test with a curl request:

curl -X POST https://api.justanalytics.app/v1/events \
  -H "Authorization: Bearer $JA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"site_id":"your-site","event":"test_event"}'

Mobile web events are undercounted.

Safari on iOS aggressively terminates background scripts. If users switch apps during document capture (to open their photos app, for example), the analytics script might not survive. This drove me crazy for a week until I figured it out. Move critical events server-side where possible, or use the sendBeacon API for client-side events. For teams hosting their own infrastructure, consider our Railway vs Vercel deployment comparison when choosing where to run backend event handlers.

What This Won't Tell You

Funnel analytics show you where users drop. They don't show you why.

For the "why," you have options that don't involve storing PII:

  • Error tracking — if users hit JavaScript errors during document upload, you'll see the stack trace (minus any PII). We've got a guide on correlating errors with funnel drop-off that covers this pattern.
  • KYC provider dashboards — Onfido, Jumio, and Persona all have their own analytics showing failure reasons. They're already compliant for storing identity data. Use their tools for what they're good at.
  • User interviews — sometimes you just need to ask. Recruit users who abandoned and offer them a gift card to explain what happened. Feels old-school. Works surprisingly well.

For broader context on how all-in-one observability helps fintechs monitor both funnel performance and system health, the case against running five separate tools covers the integration benefits.

If you're also running paid acquisition, coordinating your analytics with click fraud protection ensures you're not optimizing a funnel that's 20% bot traffic. For teams running multi-account testing with antidetect browsers, funnel events help you validate that browser fingerprints aren't skewing conversion data.

Frequently Asked Questions

Can I use session replay on KYC document upload screens?

Only with aggressive masking that blocks the document preview entirely. When users upload a passport or driver's license, many browsers render a thumbnail preview in the DOM. If your replay tool captures that thumbnail, you've just stored identity documents in a third-party server — a massive compliance violation. JustAnalytics masks all image elements by default, including dynamically rendered previews. But the safer approach is to disable replay entirely on document upload steps and rely on funnel events to track drop-off.

How do I track KYC failures without logging the failure reason?

Fire a generic failure event with the step name but no PII. Instead of logging "SSN mismatch for John Smith," log kyc_step_failed with properties like step: 'identity_verification' and attempt_number: 2. You know where users are failing and how many times they retry without capturing what specifically failed. For debugging specific cases, work directly with your KYC provider's dashboard — they're already compliant for storing that data.

What's the typical KYC funnel drop-off rate I should expect?

Industry benchmarks from Onfido and Jumio research show 40-60% abandonment across the full KYC flow, with document upload being the worst step (often 25-35% drop-off just there). If you're seeing numbers worse than that, you've got a UX problem on top of the baseline friction. If you're seeing better numbers, either your flow is unusually smooth or you're undercounting — check that events fire on mobile.

It depends on your jurisdiction and what you're tracking. Under GDPR, cookieless analytics tracking funnel steps (without capturing PII) can typically run under legitimate interest. But some financial regulators have stricter rules about any tracking during identity verification. Check with your compliance team — they probably have opinions. (Strong ones.) The safe default: treat KYC pages like payment pages. Minimal tracking, no replay, no form field capture.


Try JustAnalytics

All-in-one observability in one under-5KB script: cookieless analytics + error tracking + APM + session replay + uptime + structured logs. Replaces GA4 + Sentry + Datadog + Pingdom + LogRocket. Free tier (100K events/mo), Pro $49/month ($39 annual).

Start free → · AI Command Center MCP

JP
JustAnalytics Platform TeamContributor

Author at JustAnalytics.

Related posts