Jesús Martínez
ES
← House-made
In daily use since May 2026 · v2

Receipts

Weekly client reports that come with receipts.

I built Receipts to write the weekly status reports my clients actually read. Every claim in a report points to something I logged, so nothing gets inflated and nothing gets lost.

Status
In daily use, version 2
Built
May to Sep 2026, solo
Runs on
Next.js, Neon, OpenRouter, Telegram, Claude Code

Why I built it

My commits only told half the story.

My clients aren’t technical. Every week they need to know what got done, what’s live and what I need from them. Writing that by hand was slow, and my commit history missed the parts they care about most: the call where we changed the plan, the decision that unblocked a launch, the deploy that fixed their checkout.

The pivot

Version 1 lied.

The first version wrote reports straight from GitHub: merged pull requests in, client-friendly prose out. The model filled every gap with confidence. Work looked more finished than it was, and a week with two client meetings and a production deploy, but no commits, came out as a quiet week.

v1 · written from git activity
We shipped the new booking flow to production this week.
The code was merged. Nothing had been deployed.
v2 · written from the ledger
The new booking flow is complete and waiting for release.E-31
Every claim traces to a ledger entry I can point to.

So I flipped it. The ledger is the product.

Now the model is a clerk, not an author. It turns what I tell it into structured facts, the report is built only from those facts, and before anything ships I check that every sentence traces back to one.

How it works

From a voice note to a report.

01

Capture

During the week I text or send voice notes to a Telegram bot, in English or Spanish. GitHub and Linear sync on their own every three hours.

02

Structure

A model turns each message into ledger entries: work, deploy, meeting, decision, blocker. If I say “actually that was staging,” it corrects the entry instead of adding a new one.

03

Check in

At 6:30 pm the bot asks about each client: here’s what synced today, anything to add? One tap if there’s nothing.

04

Write

On report day, Claude Code drafts the report from the week’s ledger in a session I supervise. The first gate is an evidence map that ties every claim to an entry.

05

Deliver

I send a clean PDF in plain language: what was delivered, what is live, the decisions we made, and what I need from you.

How it works, in 30 seconds

The idea, in miniature.

Pick a message for the bot and watch it become a ledger entry, then a line in the report draft with the entry behind it. Try the correction after the deploy.

Telegram

Ledger · Northwind Dental

Nothing logged yet. Send the bot a message.

Report draft, with its evidence

Weekly status · Week 40Northwind Dental

What changed this week

0delivered
0now live
0decisions
0needed
What was delivered

Nothing yet.

What is now live

Nothing went live this week.

Decisions and conversations

Nothing yet.

What I need from you

Nothing yet.

A simplified explainer on invented data, not the real interface.

Under the hood

Small system, strict rules.

Telegram · voice · UIGitHub · Linear syncIntake · model extractsThe ledger · PostgresClaude Code · supervisedPDF report
01

A clerk, not an author.

The only unattended model call turns what I said into structure. Nothing a client reads is written without me in the loop.

Chose extraction over generation
02

Only I can say “deploy.”

The report can’t say live, shipped or in production unless a deploy entry in that week names the environment. Staging is said as staging.

Chose a vocabulary rule over trusting the prose
03

Memory is rows, not processes.

Every call rebuilds its context from the messages and entries tables. No paused workflows, and every scheduled job catches up after a miss.

Chose stateless jobs over long-running workflows

The rules it follows

Extract, don’t write.

The model only turns my words into structured facts. It never writes for the client.

Correct, don’t duplicate.

“Actually that was staging” updates the existing entry instead of adding a second one.

Staging is staging.

“Live” and “shipped” need a deploy entry that names the environment.

Every claim has an entry.

Before a report ships, each sentence maps to the ledger. If it can’t, it goes.

Plain language, first person.

No pull requests, branches or tickets. What the client can now do, and what problem is gone.

No promises, no dates.

Reports cover what happened, never what might.

Next.js 15TypeScriptNeon PostgresNeon FunctionsDrizzleOpenRouterTelegramClaude CodePuppeteerVercelSentry

Build log

Three versions, one lesson each.

v0 · before May 2026

A cron script on a server.

Python on a VM: pull GitHub activity, ask Claude for a narrative, render a PDF, drop it in Gmail as a draft. It proved the idea and nothing else.

v1 · May 2026

A real app, reading the wrong source.

Next.js, background jobs, reports written from git activity. Fast and polished, and wrong in a way that took months to see.

v2 · September 2026

The ledger.

Telegram and voice capture, evening check-ins, supervised report writing. Moved everything onto Neon (database, functions, storage) and deleted the old pipeline and the layers built for tests that were never written.

What I learned

Don’t let a model write about what it can’t see.

It will fill the gap, and it will sound sure. Give it the facts, or make it ask.

Make strong words earn their place.

“Live” is a claim. Tie each strong claim to evidence and the whole report becomes trustworthy.

Delete what you built for later.

Version 2 got simpler by removing two platforms and an abstraction layer nobody used.

Want one like this? Let’s build it.

Next for Receipts: Slack and a chat widget as new ways to log, on the same intake path.