Try it
- Public demo (recorded live run, no keys, free): https://papertrail-demo-iota.vercel.app
- Live mode for judges (real Nemotron + Tavily calls on your own letter): https://server.tail113e81.ts.net:10000 — the judge access code is in the file attached to this submission for judges (JUDGE_ACCESS.txt); enter it in the "Judge access code" field and press Unlock.
- Code (MIT): https://github.com/LevAdamenko/papertrail
Inspiration
Foreigners in Poland and Austria get letters in a language they barely read, citing laws they've never seen, with deadlines that end their case if missed. People paste them into a chatbot and get a confident answer with no source. I wanted the opposite: an assistant that only says "confirmed" when an official page says so.
What it does
- Reads a photo of an official letter (Polish residence permit — karta pobytu; one Austrian Vienna MA 35 case).
- Extracts deadlines, required documents, fees and legal bases; every item must quote the letter verbatim or it is dropped.
- Checks each item on official sites (
gov.pl,gv.at) through Tavily. A badge per item:VERIFIEDwith the page link and the exact sentence,UNVERIFIEDwith a plain-language reason, orFROM LETTERfor dates only the letter can state. - Drafts the reply in Polish or German from proofread templates, with an English translation.
- Exports deadlines to the calendar (
.ics) with statutory holiday rules (KPA art. 57 § 4, AVG § 33 Abs. 2) and remembers the case across letters, showing what changed.
How I built it
- NVIDIA Nemotron 3 Super (
nvidia/nemotron-3-super-120b-a12b) on Nebius Token Factory, called at run time: extraction to JSON, search queries, and judging official pages. - Qwen3.8-27B (open weights, on Token Factory) for OCR, because Token Factory has no vision-capable Nemotron.
- Tavily search restricted to allow-listed government domains; one fixed query per letter for the procedure's official checklist page plus one search per item.
- Code over the model where code can decide: the URL must be an official host, the quote must be on the page, the amount or number of days must be in it. Polish law text is cut out of the act on ISAP by code; the KPA 7-day minimum is compared with the letter's term by code; the stamp-duty receipt line is quoted by code. A model-reported "contradiction" is never shown as a finding.
- Python standard library only; 222 offline tests on recorded API responses; a live evaluation script that logs every model call, token count and verdict.
Results
- Live pipeline on three Polish letters, two of them new and not in the demo: 19 of 24 checkable items
VERIFIED, 0 false; OCR 0.99, extraction 100 %. - Recorded demo (4 letters): 11 of 26
VERIFIED, 0 false. Every verified item was checked by hand. - One letter scores 0/5 on purpose: it does not say which kind of permit it is about, so purpose-specific official pages are not allowed to confirm it.
Challenges I ran into
- Nemotron as judge sometimes returned malformed JSON or "not found" for loosely worded items; retries didn't change verdicts, so I moved the checks that code can do out of the model.
- Searches for a single document drifted to unrelated pages (tenancy law, ID cards); one search for the procedure's own checklist page fixed it.
- The judge "contradicted" letters using pages on other topics; I decided a model-reported conflict is only ever a reason to check with the office.
- Model-written Polish replies had spelling mistakes and invented appointments, so replies use fixed templates.
Accomplishments I'm proud of
Zero false VERIFIED across every measured run, and a measurement tool that shows exactly where an item is lost (OCR, extraction, search or judging).
What I learned
For a user who can lose their residence case, an honest "not confirmed — check with the office" is worth more than a confident guess. The biggest gains came from code checks, not from prompt tuning.
What's next
More procedures and countries, more official sources per office, active reminders, and a check of the user's documents against the verified list.
Log in or sign up for Devpost to join the conversation.