ParseHQ
A solo-built SaaS product, not a workflow. Independent bookkeepers process 20 to 100 supplier invoices a week by hand, retyping line items into spreadsheets. ParseHQ automates that read-and-transcribe work with an AI vision model, then does the part most AI tools skip: it makes the extraction verifiable. Every value traces to its source in the PDF. Every confidence score comes from real arithmetic checks, not the model grading its own work. Built with the same discipline as the rest of this portfolio: tenant isolation, server-side quota enforcement, validation at the AI boundary, automated end-to-end tests. Just applied to a full application instead of a workflow canvas.

The premise
Every other project on this page is an n8n workflow. This one isn't, and that's worth saying plainly instead of hoping nobody notices. ParseHQ is a full Next.js application with its own database, its own auth, its own AI pipeline. What it shares with the rest of this portfolio is the actual instinct behind the work: find a repetitive, error-prone manual process and automate it properly, with AI doing the perception work a rules-based tool never could. This one needed a real application instead of a workflow canvas, because the core feature, showing a person exactly where an extracted number came from, needed a UI a workflow tool doesn't give you.
Same constraint as everything else here though: it has to survive real use, not just a demo run. Tenant isolation. A usage gate that can't be skipped by a fast client. An AI layer that gets checked, not trusted.
The starting point. A real supplier invoice, discount line and all.The problem
An independent bookkeeper managing a handful of client accounts spends hours every week doing the same thing. Open a PDF invoice. Read the line items. Type them into a spreadsheet. The tools built to solve this mostly solve half of it: header fields extract fine, line items get butchered or cost double.
There's a deeper problem underneath that one, and it's the part that actually matters for a bookkeeper's job. Even when extraction works, she has no way to check it without doing the manual read-through anyway. A spreadsheet full of AI-extracted numbers is a leap of faith. In professional bookkeeping, a leap of faith is a liability.
The goal
The goal isn't automation for its own sake. It's automation a bookkeeper can actually stand behind with a client's name on it. Every extraction tool on the market already claims to save time. Almost none of them let the person using it check the work without redoing it by hand. That's the gap I'm building ParseHQ to close: a workflow honest enough that a confidence score always means something specific, and a highlight always points at something real, never a guess dressed up to look certain.
That's still in progress, not finished. The plan is to get the core promise fully true, real source tracing for every field, not just the numbers, before building out the business layer around it: payments, a real export, the rest of the surface area a shipped product needs. Proving the trust claim holds comes first. Everything else is sequenced after it on purpose.
What it does
Upload a batch of invoice PDFs. Extraction runs automatically, no templates, no per-vendor setup. Results land in a review queue with a confidence flag on anything that needs a second look before it's even opened.


Opening an invoice puts the original PDF and the extracted data side by side. Hover a line item and the exact region it came from lights up on the source document. Click it and the highlight sticks. Click the PDF itself and it jumps back to the matching cell, in both directions. Confidence is color-coded per field, but it isn't the model grading its own homework. It's calculated: do the line items actually sum to the subtotal, does subtotal minus discount plus tax actually equal the grand total. When the math doesn't check out, that field gets flagged, no matter how confident the AI claimed to be.


How it's built
Extraction runs through a provider-agnostic adapter, a single contract every vision model has to satisfy. Gemini is the default, OpenAI sits behind the same interface as a fallback. The model version is pinned explicitly in config, never a "latest" alias, because letting a financial pipeline's reader change silently is asking for an accuracy regression nobody notices until a client complains.
The AI's raw response is never trusted directly. It gets parsed through a full schema at the boundary: quantities coerced and checked for sanity, malformed line items dropped and the invoice flagged for review instead of writing garbage into a numeric database column. An LLM's JSON output is untrusted input, same as a public form submission.
Coordinates for the highlighting feature come from a separate OCR pass run against the same PDF, matched back to each extracted value by text. If a value can't be located in the OCR output, no box gets drawn. A missing highlight is honest. A wrong one, pointing at the wrong line, is worse than nothing and undermines the one thing this product exists to prove.
Treating it like production
The usage gate is enforced where a client can't skip it. The free trial caps at 50 processed invoices. That limit is checked inside a database transaction with the account row locked, counting invoices the moment they're accepted for processing, not after extraction finishes. Counting on completion instead of acceptance left a real gap: upload fast enough and a second batch could sneak through before the first batch's count ever registered. Found it by reasoning through the timing, then proved it by reproducing the race directly: uploaded 50, then immediately uploaded 50 more before the first batch finished. The second batch is now correctly blocked before extraction even starts.
Every account is isolated at the query level. Every request re-resolves the account server-side, every resource lookup checks ownership before returning anything. Verified with a real cross-account test: one account explicitly forbidden from reading another account's invoice.

Automated coverage end to end. Playwright tests run the full path, sign up, upload, extraction, review, against real invoice PDFs, with expected values read straight from the source documents instead of assumed. One of those tests exists because a dependency version mismatch once broke the PDF viewer in production. It's a caught regression now instead of a user-reported one.
What I didn't build, and why that's in the document
Payment processing doesn't exist yet. The trial gate is real and correctly enforced, at 50 invoices uploading genuinely stops, but there's currently no way for a user to pay and continue past it. Sequencing choice: prove the extraction and verification core is trustworthy before building the billing plumbing around it.
Google Sheets export is a UI mockup right now, not a real integration. The interaction is fully designed. The OAuth flow and the actual API call aren't wired up.
Confidence scoring is honest for numbers and not yet extended to text. Vendor names, dates, and PO numbers carry no confidence signal at all, because arithmetic can't check whether a name was read correctly. That needs the same OCR text-matching mechanism already built for the highlighting feature, just applied to confidence instead of coordinates. Built, not extended there yet.
None of this is hidden. Knowing exactly what's real versus planned, and being able to say so precisely, is its own kind of work.
Two things that cost the most time
The AI was confidently wrong about its own confidence, and I nearly shipped it that way. Early on, the model self-reported a confidence score for every field it extracted, in the same call that produced the value. It looked fine in testing. The problem only became obvious once I thought through what that number actually meant: a model that misreads a total has no way of knowing it misread it, so it happily reports high confidence on a number that's wrong. The bounding boxes had the same flaw. The model was asked to guess pixel coordinates for where a value sat on the page, and it guessed, because vision models don't have reliable spatial grounding for arbitrary documents. Both signals were fabricated, and both sat right at the center of the product's entire pitch. I rebuilt both from scratch. Confidence from arithmetic reconciliation on the actual extracted numbers. Coordinates from a real OCR pass matched against the extracted text. If a value can't be verified either way, the interface says nothing instead of lying convincingly.
A locking transaction that has to survive its own race condition. Getting the 50-invoice trial limit to hold under concurrent uploads took more than an if-check. The fix, lock the account row, count invoices as accepted rather than completed, do both inside one transaction, is a handful of lines. Proving it actually closes the gap took deliberately trying to break it: two near-simultaneous uploads at the boundary, watching the database counts in real time, confirming the second one gets rejected before any extraction work even starts. The lesson that stuck: a fix that looks obviously correct on paper still needs to survive the exact attack that exposed the bug in the first place.
The result
A bookkeeper can process a week's invoices without retyping a single line item, and unlike a black box extraction tool, she can actually check the AI's work before putting her name behind it. Every number traces to its source. Every confidence flag means something specific, not a vibe the model generated about itself.
More projects

AI CV Screener
Reads every CV in a Google Drive folder, scores each one against the job criteria, and outputs a ranked list to Google Sheets. No manual shortlisting.

Overdue Invoice Chaser
Checks QuickBooks daily for overdue invoices, sends escalating reminders at day 1, 3 and 7, escalates to Slack at day 14, and logs every chase attempt to Airtable.