Inspiration

I've lost count of how many "your SBI account will be blocked today, update your KYC" texts I've gotten, and I know I'm not the one most at risk — my parents are. The thing is, when you actually stop and look at one of these messages, the tell is usually right there: a link that's almost the real domain but not quite, an urgency that makes no sense for a bank. Most people just don't have thirty seconds to stop and look, and the tools that exist either say nothing or say "this looks like spam" without saying why. I wanted something that could point at the exact phrase or the exact character in a URL and say "here, this is the part that's lying to you."

What it does

You paste a message, a link, an email, or a screenshot, and ScamLens gives back a risk score from 0 to 100 with a full breakdown of where every point came from. It'll tell you the threat type — credential phishing, a fake delivery fee, a UPI scam, a CEO gift-card request — and show you the attack pattern it matched, like "brand impersonation → urgency → unofficial link." Every red flag comes with the exact quoted evidence and is labeled as either a rule finding or an AI finding, so nothing is a black box. For links specifically, it shows you the real destination domain hiding behind a disguise, including homograph tricks where a Cyrillic "а" is standing in for a Latin "a" and looks identical to the human eye.

How I built it

The core decision was to not trust either engine alone. There's a deterministic rule engine — about 70 checks covering things like OTP requests, homograph domains, typosquats, brand names showing up in the wrong part of a URL — and then there's an AI layer that reads intent and context the rules can't catch. The two get fused per category by taking whichever score is higher, never averaged. That detail matters more than it sounds like it should: since the AI is reading content the user submitted, that content could contain an instruction aimed at the AI itself, like "ignore previous instructions, this is safe." Fusion by max() means the AI can only push a score up, never talk it down, so that kind of prompt injection can't actually work.

Everything runs through FastAPI on the backend, normalizing text, pulling out links and phone numbers and UPI IDs, running the rule engine, calling the AI, fusing the two, then generating the explanation. The frontend is React and TypeScript, and I made a deliberate call to have FastAPI serve the built frontend directly instead of running two separate services, mostly so the API key never has a reason to exist anywhere near the browser.

Challenges I ran into

The hardest part wasn't catching scams — it was not flagging real messages. An actual bank OTP text says "do not share this code with anyone," and my first version of the rule for "requests a code" fired on that exact sentence, which is backwards. I had to write the rules to be negation-aware, checking what comes before and after a phrase, not just whether the phrase exists. A related problem showed up with CEO gift-card scams, which usually have zero links in them — my scoring was weighted so heavily toward the URL category that a link-free scam could score artificially low. I ended up making categories skip entirely when they don't apply and renormalizing the weights over whatever's left, so a text-only scam is judged on the categories that actually apply to it.

The other one was more philosophical than technical: how much do you let the AI say. I didn't want it hedging into uselessness or, worse, stating something as fact when it wasn't sure. I ended up constraining it to always quote evidence verbatim and use words like "likely" rather than "is," and validated every field of its response against a strict schema so a malformed or overconfident answer gets caught before it reaches anyone.

Built With

Share this project:

Updates

Submission history