Cash Flow Cop
The finance team, living in a Slack DM.
A Slack finance bot I DM in plain English โ what's overdue, total outstanding, due in the next 7 days, biggest creditor. Scans the billing inboxes twice a day, tracks every invoice from scan to payment in one database, marks things paid on a confirm, and auto-reconciles a dropped-in Revolut CSV.
// architecture
A hub-and-spoke around one Postgres database (Neon, EU) that's the single source of truth. On GCP, a set of Cloud Functions do the work: an email scanner (cron, 9am/9pm London), the Slack bot itself (HTTP-triggered by Slack events โ not an always-on process), and a renderer that mirrors the database into two read-only Google Sheets. A Next.js web app (Docker โ Cloud Run) is the visual Command Center and the Revolut CSV upload. Everything reads and writes the same Postgres.
// how it works
- 1
Scan the inboxes
Twice a day โ 9am and 9pm London โ a Cloud Function sweeps the billing inboxes over the Gmail API and pulls the invoice PDF from each new mail.
- 2
Extract with Claude
Each PDF is sent to Claude Haiku 4.5 as a document block (with a cached system prompt) to pull amount, currency, vendor and due date, then de-duped and UPSERTed into Postgres.
- 3
Ask in plain English
The Slack bot doesn't write SQL. It reads all invoices from Postgres, computes the buckets and does keyword routing in Python to build context, then hands that to Claude Haiku with your question and the last few turns โ Claude replies with strict JSON (query / edit / clarify / refresh / confirm) that the bot acts on.
- 4
Mark things paid
'mark 1042 paid' โ it asks you to confirm โ 'confirm paid 1042' โ it writes the status to Postgres, the sheet and an audit log. The confirm step is deterministic โ no LLM in the money path.
- 5
Auto-reconcile Revolut
Drop a Revolut CSV into the web app and it matches each payment to an invoice by exact reference and flips the matched ones to Paid โ idempotent, sheet-first-then-DB to avoid racing the sync job.
- 6
Mirror everywhere
A renderer Cloud Function pushes the same data into a web Command Center and two read-only Google Sheets, so everyone is looking at identical numbers.
// tool calls
gmail.messages.list / attachments.getsweeps billing inboxes and pulls the invoice PDFmessages.create (Claude Haiku 4.5)extracts invoice fields from the PDF; classifies the Slack question โ JSONchat.postMessage (Slack)the bot's replies (inbound requests HMAC-verified)Postgres UPSERT ON CONFLICTde-dupes invoices by (vendor, invoice #); writes an invoice_audit row on editssheets.values.updaterenders the mirror sheets and applies live editsRevolut CSV importexact reference match โ flips matched invoices to Paid
// slack dm
// stack
// infrastructure
- โธCompute: GCP Cloud Functions + Cloud Run
- โธDB: Neon Postgres (EU) โ single source of truth
- โธSchedule: Cloud Scheduler (scan 9am/9pm; sheet render ~5 min)
- โธModel: Claude Haiku 4.5 (extraction + NLQ)
- โธAccess: Slack-ID allowlist; Google-OAuth allowlist on the web app
// why it exists
'How much do we owe, and what's overdue?' used to be a spreadsheet archaeology dig. Now it's one Slack message, answered in seconds.