DEV Community

Cover image for You Have Sixty Minutes. Nobody Tells You What To Do With Them.
Ashutosh Kumar
Ashutosh Kumar

Posted on

You Have Sixty Minutes. Nobody Tells You What To Do With Them.

TL;DR — India's cybercrime portal gives a victim a complaint number and then silence. But the complaint is only one of ten separate obligations, most of them on a statutory clock, and nobody hands the victim that list. Kavach is the list — running as a live case file, with every document written for you, in your language. Code: github.com/ashusnapx/hackathon

The Kavach case file, showing the recovery window: a 52% chance of freezing the funds in the first hour, decaying steeply across the first 48

The first screen of a case file. The curve is the whole product in one image — reporting inside the first hour is roughly an even chance, and it collapses from there.


The build I threw away

The obvious project here was a nicer version of the form on cybercrime.gov.in.

That form deserves it. If you search for it, you will find years of people describing the same failure in the same words: the session dies mid-form, the OTP arrives after it expires, refreshing loses everything.

"I tried four times to register a complaint. After five minutes the portal threw me out. No complaint means no fraud, as far as the government is concerned."

"After the first page the site stopped responding. Refreshing asked for OTPs again. Eight attempts, still nothing filed."

So I started there. Auto-save, resume-later, a clean multi-step wizard. It took about a day, and it was fine, and then I went and read what actually happens to a fraud victim after the form is submitted — and I deleted it.

Because the form is not the problem. The form is one percent of the problem.


What is actually happening to a victim

Here is the part almost nothing written about Indian cyber fraud says out loud. When money leaves your account, you do not have one obligation. You have ten. Most are on a clock. They run in parallel. They are owed to four different institutions, none of which will tell you about the other three.

# What has to happen Deadline Why it exists
1 Call 1930 first hour The only mechanism that can freeze the money in transit
2 File on the NCRP portal 24 hours Creates the record, routes to the state cyber unit
3 Notify your own bank, in writing 3 working days RBI's circular on unauthorised electronic transactions — your liability becomes zero
4 Get an FIR registered as soon as possible A portal complaint is not an FIR (BNSS s.173)
5 Report the number on Chakshu any time Disconnects the fraudster's SIM before the next victim
6 Provisional credit from the bank 10 working days after (3) Same RBI circular — the money goes back while they investigate
7 Money Restoration Module after the freeze A freeze is not a refund. This is how held money is actually released
8 RBI Ombudsman 30 days after (3) Free, online. Banks settle a lot of cases the moment it is filed
9 The bank's 90-day outer limit 90 days after (3) Past this, compensation for delay becomes payable
10 Free legal aid — call 15100 any time A statutory entitlement, not charity

Track 3 is the one that matters most and the one nobody is told about.

Notify your bank in writing within three working days and, where a third party caused the loss, your liability is zero. Miss it and you personally absorb a loss the bank was obliged to carry. A phone call is not notification. It has to be in writing, with a dated acknowledgement.

Track 7 is the one that surprised me. A freeze is not a refund — money stopped in a mule account used to sit there indefinitely. The Home Ministry's Money Restoration Module changed that, and where the amount held in any single account is under ₹50,000, no FIR and no court order are needed to get it back. Almost nobody knows the module exists.

Now layer this on top:

  • 88% of Indians online prefer a language other than English.
  • Two in five people in rural India search by voice, because typing is not realistic.
  • A police station wants a typed English narrative. The NCRP description box rejects most punctuation and demands 200+ characters.

Today, the victim absorbs that gap. At the worst moment of their year, in a language they do not write, under a clock they have not been told about.

That gap is the product.

The problem section of the Kavach landing page: 22.5 lakh complaints in a year, 78% financial fraud, Rs 22,800 crore reported lost, roughly 1 in 8 rupees recovered


What Kavach does

Four things, in the order a citizen meets them.

1. Check something before you pay

Everything else in the tool happens after the loss. /check happens before it.

You paste the message, the link, the UPI ID, the number that just called. It names the tells — not generic advice, the specific things in your text:

  • a link that goes to a bare IP address
  • a domain wearing a bank's name that is not on the bank's domain
  • punycode look-alike letters
  • a company mentioned in the message but a personal UPI ID given to pay
  • the digital-arrest script: parcel, narcotics, CBI, "stay on the call, do not tell anyone"

Two rules govern that module, and they are worth stating because they are the difference between a safety tool and a liability:

Every signal must be decidable from the text itself. Nothing is inferred from a database I do not have.

It never returns "safe." A clean result means only that these particular tells were absent. The authoritative check is I4C's Suspect Repository, and the tool sends you there rather than pretending to hold a copy of it.

The Kavach check page, headed 'Is this a fraud?', with a box to paste a suspicious SMS, link or UPI ID

The only screen in the tool that runs before the money moves.

2. Say what happened — however you speak

Press the microphone and talk. Or type. No login, no OTP, no captcha, no account.

3. Triage

The model reads the statement in whatever language it arrived in, and:

  • classifies it against the real NCRP category tree — seven categories, forty-plus sub-categories, the same tree the portal uses, so nothing has to be re-classified later under pressure
  • extracts every UPI ID, UTR, account, phone number and amount
  • works out when it happened from "yesterday evening" or "do ghante pehle"
  • rewrites the account as formal English suitable for a police application — faithfully rendered, not summarised
  • returns its genuine confidence, because a low number makes the citizen check it, which is the correct outcome

The triage result screen: category 'Online financial fraud', type 'OTP or KYC fraud', the inferred incident time, and a confidence label reading 'Needs your check'

Low confidence is rendered as “Needs your check”, not hidden. The line at the bottom — “Written by AI from what you told us. Read it before you send it.” — is on every generated document in the product.

4. The case file

A reference, ten clocks running from the incident time, and every document written.

  • All seven documents: the NCRP description (already inside the portal's character rules), a 1930 call script, a bank dispute letter citing the RBI circular, an FIR application citing IT Act ss.66C/66D and BNS s.318, a Chakshu report, an MRM restoration worksheet, and an Ombudsman complaint.
  • An evidence vault — twelve checklist items across transaction, communication and complaint evidence, each with a line explaining why an investigating officer wants it.
  • A readiness score, weighted by what actually moves a case. A UTR number is worth more than an email address.
  • Your state's Nodal and Grievance Officer, by name and mailbox — because the single most common thing that happens after filing is nothing, and there is an escalation path that essentially no victim is told exists.
  • Ask — a grounded assistant that answers only from your own case file. Ask about the Ombudsman before your bank has been notified and it will tell you that, rather than reciting the general rule.

The citizen reads all of it in their language. The authorities get English.


The engineering

The deadline engine, and why the calendar matters

"Three working days" is not three days. Scheduled commercial banks in India are shut on Sundays and on the second and fourth Saturday of each month. Approximating this is precisely the error that costs someone their zero-liability claim, so it is computed:

export function isBankHoliday(d: Date): boolean {
  const day = d.getDay();
  if (day === 0) return true;            // Sunday
  if (day !== 6) return false;
  const nth = Math.floor((d.getDate() - 1) / 7) + 1;  // which Saturday
  return nth === 2 || nth === 4;
}

export function addWorkingDays(from: Date, days: number): Date {
  const d = new Date(from.getTime());
  let left = days;
  while (left > 0) {
    d.setDate(d.getDate() + 1);
    if (!isBankHoliday(d)) left -= 1;
  }
  d.setHours(17, 0, 0, 0);  // deadlines land at close of business
  return d;
}
Enter fullscreen mode Exit fullscreen mode

Each track is a declarative definition with a deadline function, an optional blockedBy, and a flag for whether it applies to non-financial cases at all:

{
  id: "bank-notice",
  index: 3,
  doc: "bank",
  financialOnly: true,
  deadline: (c) => addWorkingDays(alertDate(c), 3),
}
Enter fullscreen mode Exit fullscreen mode

Three details in there took real thought:

The clock runs from the bank's alert, not from when you noticed. Falling back to the incident time is the conservative reading — it can only ever make the deadline earlier, never later.

Dependencies are modelled, not assumed. You cannot claim provisional credit before you have notified the bank. You cannot raise a restoration request without the 14-digit NCRP acknowledgement. Those tracks stay visibly locked, with the reason shown, rather than appearing as things you have already failed at.

A stressed person can hold one instruction, not ten. So nextAction() collapses the whole board into one card: overdue-but-still-worth-doing beats not-yet-due, and among equals the nearest deadline wins.

There are 119 unit tests over this engine — every working-day boundary, every dependency transition, every state. Not because deadline math is hard, but because it is the one part of this product where being quietly wrong is worse than crashing.

The AI layer

Eight routes. Three models. The split is deliberate:

Route Model Job
/api/ai/triage gpt-5 Classify, extract, infer time, render into English
/api/ai/extract gpt-5 Separate the fraudster's identifiers from the victim's
/api/ai/draft gpt-5 Write all seven documents
/api/ai/translate gpt-5 Render a document into the citizen's language
/api/ai/check gpt-5 The pre-loss second opinion
/api/ai/transcribe gpt-4o-transcribe Speech to text when the browser cannot
/api/ai/ask gpt-5-mini Answer questions grounded in the case file

The client is written against the OpenAI HTTP API rather than the SDK, so the same code path runs against any OpenAI-compatible endpoint. OPENAI_BASE_URL is the only knob — which matters, because the voice side of this product is meant to sit on an Indian-language speech provider while the reasoning stays on OpenAI.

Every call uses json_schema with strict: true. A response either matches the TypeScript types or it does not come back. There is no defensive parsing of half-formed JSON anywhere in the request path.

Regex runs first, and independently

This is the design decision I would defend hardest.

A UTR number is a twelve-digit string. A regular expression gets that right every single time, and a language model occasionally does not. So the deterministic pass runs before the model and separately from it — UPI IDs against a table of forty real bank handles, phones, accounts, references, URLs, handles, and amounts written the way Indians actually write them: 85,000, ₹1.4L, 2 lakh, eighty five thousand, दो लाख. Indic digit forms are normalised first, so a statement typed in Devanagari still yields numbers.

The model's job is explicitly what regex cannot do:

A number in "credited to A/c XX4471" is usually the victim's; a number in "sent to 9876543210@ybl" is usually the fraudster's. Put only the fraudster's identifiers in the suspect fields.

Return empty arrays rather than guesses. A wrong account number sends police to an innocent person.

Then the two are merged. Neither is trusted alone.

The prompts are a specification, not a vibe

Every prompt inherits one preamble, and it does most of the safety work:

Never invent a fact. If a name, amount, account number or date is not in the input, leave the placeholder in square brackets exactly as given to you. A fabricated UTR number in a police application is worse than a blank one.

Never ask for, repeat, or store an Aadhaar number, PAN, card PIN, CVV, password or OTP. If the input contains one, omit it from every output.

Never claim to be a government body, and never promise that money will be recovered.

The drafting prompt then specifies each document by its reader, because getting the register wrong gets a citizen dismissed at the counter. The NCRP description is told which characters the portal rejects (# $ @ ^ * ' ~ | !) and to spell out "at" inside email addresses. The bank letter is told it must survive a bank's legal team, and told not to cite a circular number or date it is not certain of — refer to it by name instead. The 1930 script is told it will be read aloud by a frightened person, so: numbered steps, exact sentences in quotation marks, blank lines to write the acknowledgement number in.

The fallback is the product, not a stub

Kavach works with no API key at all.

Every AI route falls back to a deterministic rules engine — keyword classification against the same category tree, regex extraction, real document templates filled from the case file. The interface says "demo mode" rather than implying a model ran.

This is not demo scaffolding. It is the degraded mode. Somebody filing at 2am on a patchy connection still walks away with a complete case pack. Every call also has a timeout and an abort controller, and every caller must handle null:

A victim of fraud at 2am is not a good moment to show an error screen.

Twenty-three languages, honestly

The picker offers all 22 languages of the Eighth Schedule plus English — correct scripts, correct endonyms (never show someone the English name of a language they cannot read), and RTL for Urdu, Kashmiri and Sindhi. One Noto family per script, preload: false, so a citizen reading in Tamil never downloads the Malayalam font.

The interface itself is hand-translated into four: English, Hindi, Marathi, Kannada. The other nineteen fall back to English and say so, marked EN in the picker.

That badge was a real argument with myself. Twenty-three is a better number to put on a slide. But quietly rendering English under a Santali label is a lie about coverage, and this is a tool asking fraud victims to trust it. Generated documents can still be translated into any of the 23 at runtime.

Dictionaries are code-split — one language file downloads, never twenty-three — typed against the English one, so a missing key is a compile error and a partial dictionary falls through key by key rather than rendering blanks.

Storage: the browser, and nothing else

The case file lives in localStorage and nowhere else. No server, no database, no account.

For a prototype handling fraud narratives that is the right trade: there is nothing to breach. The cost — no cross-device access, no SMS reminders, deadlines that only count while you have the tab — is real, and it is exactly what the "if this were real" section addresses rather than hides.

The PDF case pack is deliberately English-only. It exists to be read across a counter by an officer or a bank manager, and jsPDF's core fonts cannot render Indic scripts without embedding a megabyte of font per language. The vernacular copy lives on screen, where the citizen reads it before they sign.

Stack

Next.js 16 (App Router), React 19, TypeScript strict, Tailwind v4, jsPDF. No component library, no animation library, no smooth-scroll library. The design system is ~280 lines of CSS.

That was not minimalism for its own sake. The landing page claims the tool works on a slow phone, and that claim has to survive contact with the bundle.

src/
  app/
    page.tsx              landing
    check/                the pre-loss check
    start/                voice + text triage
    case/[id]/            the case file
    api/ai/               eight routes, each with a rules-engine fallback
  lib/
    ai/        provider, prompts, deterministic extraction, rules engine
    case/      types, ten tracks, banking-calendar math, store, PDF pack
    check/     text signals
    i18n/      23 languages, lazy dictionaries, script-aware fonts
Enter fullscreen mode Exit fullscreen mode

What is real, and what is pretend

A tool that asks fraud victims to trust it does not get to be coy about what it fakes. This is on the landing page itself, not buried in a README.

Real: voice and text intake in 23 languages · AI classification against the official category tree · identifier extraction · all ten deadline clocks computed from real RBI and BNSS timelines against the Indian banking calendar · all seven generated documents · the state officer directory · the downloadable PDF pack · case files surviving a closed browser.

Mocked, on purpose: nothing is submitted to cybercrime.gov.in — you copy the text across yourself · complaint status and police-station routing are simulated · there is no OTP, login, Aadhaar or PAN anywhere · reference numbers are generated locally and are not government numbers · there is no server database.

Kavach is not affiliated with, endorsed by, or connected to any government body.


If this were real

Kavach is deliberately shaped as a layer beside the portal, not a replacement for it. That shape is the scaling plan:

  • Submit through an authenticated I4C integration rather than the clipboard.
  • Encrypted case store with the citizen holding the key — the reason it is in localStorage today is that I would rather hold nothing than hold fraud narratives badly.
  • Run the deadline engine as a scheduled job that sends SMS, so the clock does not depend on a tab staying open. This is the single highest-value change: the whole product is a reminder system wearing a case file.
  • Hand police a structured, machine-readable case instead of a paragraph.

Nothing in the design assumes the citizen has a fast phone or a stable connection.


Three things I would tell another builder

Find the ninety-nine percent. Every visible problem has a queue behind it. The form is what people complain about; the ten obligations are what actually costs them money. The interesting build is almost never the one being complained about.

Let the model do only what the model is uniquely good at. Regex owns twelve-digit strings. The model owns "eighty-five thousand" and "whose account is this?" Running both and merging is not belt-and-braces — it is knowing which tool is deterministic and refusing to give that ground away.

Ship the honest version of the number. Nineteen EN badges look worse on a slide than "23 languages" and are worth more than the slide.


Try it

Built for Build What Moves India by Ashutosh Kumar and Kaustubh Tripathi. Audit structure and the deadline test harness were reviewed with Codex.

If you have been through this process — as a victim, in banking operations, or in cyber policing — I would genuinely like to know where this model breaks. That is the most useful thing anyone can send me.


Helplines: 1930 cyber fraud, 24×7 · 1091 women's helpline · 112 emergency · 15100 free legal aid · 14416 Tele-MANAS

Top comments (0)