Inspiration

As an international student in the Bay Area, I worked with Larkin Street Youth Services, San Francisco's largest nonprofit provider of housing for young people experiencing homelessness. I sat down with their lead grants accountant to understand how the money actually moves. His team pays small bills on behalf of the young people they serve: a month of rent, a utility bill, a phone line, a bus pass. Every payment has to follow the rules of whichever grant is paying for it, and if one doesn't, the organization can be asked to pay that money back out of funds meant for other people.

That pressure exists across the whole sector. Organizations that pay out emergency aid handle a high volume of small payments, the rules are spread across grant agreements and manuals, and the software most of them use records the case but doesn't check the payment. When there isn't time to verify everything, the final sign-off risks becoming a rubber stamp, and that's where the costly mistakes slip through: the same receipt submitted twice, a reimbursement for a bill that was never paid, or a payee that doesn't exist.

I built Signet for the people who sign off on those payments: the program managers and finance staff at nonprofits and county agencies that pay emergency aid.

What it does

Signet checks every payment request before any money moves, and shows exactly why it's fine or why it isn't.

  1. It reads the paperwork. A caseworker uploads a receipt or bill. Claude reads it and pulls out who, how much, and when, with a confidence score for each.
  2. It checks the facts. Signet compares the request against the grant's rules, the organization's bank account, the vendor's identity, the vendor's public records, and how staff handled similar cases before.
  3. It flags or clears. Clean requests are marked ready. Anything off is flagged with the reason and the evidence. Signet can never approve, deny, or pay anything by itself.
  4. Two different people sign. One person reviews and certifies a batch; a different person releases the money. Every step is recorded in a log that can't be quietly edited.
  5. It learns, carefully. When a person approves something unusual, like five identical phone bills from one family plan, Signet suggests a rule so it won't flag that pattern again. A second person has to switch the rule on. And no rule can ever approve the same receipt twice. Where did the $50 go? A client asks to be reimbursed $50 for a utility bill they say they paid. The receipt looks fine. But the money never shows up leaving the bank, so Signet stops it and asks a person to check before a cent goes out.

How I built it

  • Claude reads receipts and bills into structured fields and writes a plain-English summary of each case. It only reads; it never decides.
  • Rule checks in regular code, not AI, for everything that touches money: grant rules, spending limits, duplicates, and amounts. The same request always gets the same answer, and it's covered by tests.
  • Capital One Nessie for the bank account: checking whether money actually left, and sending the real payment at release.
  • CRS to check vendor identity, using CRS's sandbox test identities only.
  • Bright Data to look up a vendor's public footprint: San Francisco business registrations and web presence, with the date each lookup was made.
  • Moss for memory: the procedures manual, grant rules, and how staff resolved similar cases.
  • Next.js, React, TypeScript, Tailwind and SQLite, hosted on Railway.

What's real and what's simulated

  • Real: the calls to Claude, Nessie (sandbox money), CRS (sandbox test identities), Bright Data (public web data), and Moss. Each one's request and response is visible on the /integrations page.
  • Simulated: the agency, its clients, caseworkers, funders and documents. No real person's data is used anywhere, and no Larkin Street data was used.
  • How I know it works: a test suite plants known problems, like a duplicate receipt, a banned expense, or an ineligible client, and checks that every one is caught.

Challenges I ran into

Keeping AI out of the decision while still making it useful. Letting the system learn from people without letting it switch off the checks that matter. Matching messy vendor names against bank records. And at 1 AM, a database corrupted by two programs writing to it at once, which I recovered from a backup.

Accomplishments that I'm proud of

Signet has no way to move money on its own; that's enforced in the code, not just promised. It learns from the people using it without being able to override the important checks. Every outside service shows its real work. I built it solo in one weekend.

What I learned

The hardest part of grant-funded aid isn't paying the bill; it's proving afterward that every dollar followed the rules. Designing for the auditor, not just the person clicking the button, changed almost every screen I built.

I also learned where AI belongs. It's genuinely good at reading messy paperwork, and it's the wrong thing to put in charge of money. Drawing that line made Signet simpler and easier to trust. And a system that learns from people needs brakes: my first version turned a supervisor's approval into a rule that flagged more requests, which taught me to decide up front which checks can never be switched off.

What's next for Signet

  1. A pilot with a Bay Area nonprofit, running alongside their current process on past (anonymized) payments, to measure what it catches and how much time it saves.
  2. Connecting to the organization's real bank instead of a sandbox.
  3. Reading grant rules straight from award documents, with a person approving each one.
  4. One-click reports for funders and auditors, generated from the log.

What I learned

  • Every outside data source fails sometimes: a sandbox goes down, credits run out, a web search returns generic pages that look like a match. Showing honestly whether data is live, cached, or a fallback mattered more than having more of it.
  • Talking to someone who does this job every day changed what I built.
  • Two programs writing to one database file at 1 AM corrupted it. Simple architecture and a fresh backup beat clever fixes.

Built With

  • anthropic
  • bright-data
  • capital-one-nessie
  • claude
  • crs-credit-api
  • drizzle
  • moss
  • nextjs
  • railway
  • react
  • sqlite
  • tailwindcss
  • typescript
Share this project:

Updates

Submission history