Pixeldev

Work/Flo

A bookkeeping assistant you talk to, instead of another dashboard you have to check.

Small businesses get their bills by email and someone types them into Xero by hand. invoiceflo is my own product that does that part: it watches Gmail, pulls the details out of each invoice with Claude, files the bill in Xero and only interrupts you when it isn't sure. The interface is a chat with an assistant called flo, not a grid of forms.

Product
invoiceflo (my own SaaS, private alpha)
Industry
Small business bookkeeping
Services
Product design, full-stack build, AI integration, hosting
Stack
Next.js, Express, Postgres, Redis and BullMQ, Claude Agent SDK
Timeline
April to July 2026, 48 commits
The invoiceflo dashboard showing invoices filed this month, items waiting for review, value processed, recent activity and a note from flo.
The dashboard: what flo filed, what's waiting on you, and where each bill was coded in Xero.
25tools flo can call from chat, from approving a bill to fixing a bad bank match
15Postgres tables behind one conversation
283Vitest tests across the API, workers and integrations

The problem

Most small businesses I talk to have the same Monday job: open the inbox, find the supplier invoices, download the PDFs, type vendor, date, amount and GST into Xero, pick an account code, repeat. Bookkeeping software has had "bill capture" for years, but it still asks you to forward emails somewhere and then review every line in yet another screen.

I wanted to see whether the whole loop could run on its own and only come to you with the exceptions, in plain language, the way a sharp junior bookkeeper would. The idea came from my own books: every month I was copying the same dozen supplier invoices from Gmail into Xero, and it was exactly the kind of job I build software to get rid of.

What I built

A pnpm monorepo with a Next.js web app (installable as a PWA, with push notifications), an Express API, and a separate worker process running BullMQ queues on Redis. Gmail and Xero connect over OAuth, with Xero tokens stored AES-256-GCM encrypted. Extraction uses Claude Haiku through the Agent SDK; the chat assistant runs on Sonnet with 25 tools it can call against the user's own data.

Every user-facing string lives in one file and was written to a voice spec: lowercase, short, no grovelling, honest when Xero is down. That sounds cosmetic, but it's the product. People trust an assistant that talks like a colleague.

  1. 1Inbox scanning: a daily poll per Gmail account, with chained back-fill jobs for history, reading attachments, linked documents and plain email receipts.
  2. 2Extraction and confidence: each field gets a confidence score, GST maths is cross-checked (10%, inclusive or exclusive), and anything under 0.9 goes to the review queue instead of Xero.
  3. 3Filing: approved bills are created in Xero with the right account code, and foreign currency bills are converted using daily ECB rates.
  4. 4Memory: correct an account code once ("310 and remember it") and flo learns the vendor, with per-vendor and master switches for auto-filing.
  5. 5Bank checks: flo matches filed bills against Xero's bank feed, proposes payments, spots bills that were paid twice, and chases spends that have no invoice behind them.
The review queue listing invoices that need a human, with confidence percentages, unassigned account codes and possible duplicate flags.
The review queue: anything flo isn't sure about waits here, with its confidence and why.

The hard part

Accounting software doesn't forgive mistakes quietly, so most of the engineering went into not making them. Xero matches supplier bills by contact and reference number: post a bill with a number that already exists and it overwrites the original without complaint, and a voided number can never be reused. Before filing, flo looks up every bill with that number. An active twin means a real duplicate, so it refuses and says why. A voided twin means it refiles under a de-conflicted number and leaves a note on the bill.

Duplicates are subtler than matching numbers. Suppliers resend corrected invoices, recurring bills look identical month to month, and two numberless receipts both read "N/A". The detector gates on Jaro-Winkler vendor similarity first, allows three days of date drift for OCR noise, ignores placeholder invoice numbers, and treats documents from the same email as siblings. When one email carries several PDFs, auto-filing waits until the whole email has been read, so a corrected re-issue later in the thread can't lose the race to Xero.

Bank matching works the same way: weighted scores on vendor text, amount and date, exact cents for local bills and a 3% tolerance for converted ones, because the bank's rate and fees never match the reference rate exactly. When a match needs a human, flo posts a card in the chat rather than guessing.

Two phones showing the invoiceflo chat: one chasing a bank spend with no invoice on file, the other offering to fix a bill that was paid twice.
On mobile: chasing a spend with no invoice behind it, and fixing a bill that was paid twice.

Where it's at

invoiceflo is in private alpha on my own infrastructure, deployed with Docker Compose behind Traefik. The core loop works end to end: scan, extract, file, reconcile, chase. 9 businesses are using it, and it has filed 1,240 invoices so far. What surprised me was how quickly people stopped opening the dashboard: once flo had learned their regular suppliers, most of them only used the chat to answer its questions.

Next up is QuickBooks Online and a multi-client view for bookkeepers, both deliberately left out of V1 so the Xero path could be made trustworthy first.

The flo's memory page listing vendors with learned account codes and auto-file toggles, plus facts flo remembers from chat.
flo's memory: learned vendor codings with per-vendor auto-file switches, and facts kept from past conversations.