Project 002 · Lending · Document AI

Loan income verificationthat shows its working.

Payslips, a bank statement and Form 16 in. Verified income, FOIR, fraud signals and a verdict out, in about two minutes. An AI model reads; Python decides.

11 / 11planted fraud cases caught, across 8 kinds of edit
0 of 19honest applicants wrongly flagged
98.9%of transactions read right on real-layout scans it never saw
$0.05per applicant, about two minutes

v2 · held-out · 30 synthetic applicants + 30 real-layout scanned statements · synthetic data only

Demo · 1:44

The challenge

Before a salaried borrower gets a loan, an underwriter checks their income by hand. Do the payslips add up? Did that salary reach the bank? Which EMIs are already running? Does Form 16 agree?

It takes hours per file, and edited documents still get through: net pay changed in a PDF editor, a large transfer two days before the statement ends, a salary that never arrives.

A complete verification service, from upload to signed-off verdict, that a lender could run on their own server.

How it works

  1. 01

    Read

    OCR on every page, including scans and phone photos.

    A model copies each value exactly as printed and never calculates.

    Mistral OCR · OpenAI
  2. 02

    Check

    Payslip maths, PF, ESI and professional tax by state.

    Salary credits, names and Form 16 matched across documents.

    Python
  3. 03

    Catch edits

    Edited PDFs, odd fonts, late large credits, money in and straight out.

    Every point lost has a reason and a page.

    Python
  4. 04

    Review

    The original page beside the values; click one to see it boxed.

    Corrections are logged and the file is checked again.

    React · pdf.js

How a request flows

Animated flow diagram of IncomeVerify

Architecture

Screens

Applications and verdicts
Applications and verdicts
A flagged applicant, with reasons
A flagged applicant, with reasons
Click a value, see it boxed on the page
Click a value, see it boxed on the page
The eval, inside the app
The eval, inside the app

What I learned

  1. "Couldn't read" isn't "tampered". v1 flagged 2 honest files whose phone-photo payslips lost their totals in OCR. Treating blanks as a reason for a person to look, and re-reading them from the image, took that to 0.
  2. Real documents find what synthetic ones can't. Statements that print a time next to each date broke date parsing. Only the real-layout set showed it.
  3. Still open: when many files arrive at once, everyone waits for the whole batch. The next fix is finishing one file before starting the next.

Stack

Reactpdf.jsNodeExpressPythonRabbitMQPostgresMistral OCROpenAILangfuseDocker