How we built the Invoice Processing demo
Pull structured data — vendor, line items, totals, due date — out of an invoice in any format a finance team actually receives one in. Not a formatted sample PDF; whatever gets forwarded to AP.
The problem
Invoices arrive as PDFs, scanned images saved as PDFs, Word docs, or plain email text pasted into a form. Most invoice-AI demos quietly assume the first case. Real AP inboxes get all of them, plus the occasional file that isn't actually an invoice at all.
The pipeline
File upload hits a shared /api/extract-text route before the invoice-specific logic ever runs:
- 01Detect file type by extension, not MIME type — browsers report MIME types inconsistently across OS/browser combinations, extensions don't.
- 02PDF → unpdf (serverless-compatible; pdf-parse and pdfjs-dist both hit Node API assumptions that break under Vercel's runtime — this took two failed attempts to learn).
- 03DOCX → mammoth, extracting raw text only.
- 04TXT/MD/CSV → read directly, no parsing needed.
- 05Text truncated to a 10,000-character ceiling before it ever reaches a model call.
The extraction call
The prompt asks for one JSON object — vendor, invoice number, dates, PO number, line items, subtotal, tax, total, payment instructions, and a self-reported confidence score — and nothing else. The response is regex-matched for the outermost{...}block before JSON.parse, since models occasionally wrap output in a sentence even when told not to.
Why Haiku, not Sonnet
This is field extraction against text that's already been parsed out of the source file — there's no multi-step reasoning, no judgment call, no ambiguity to weigh. Claude Haiku 4.5 handles it at a fraction of the latency and cost of a larger model. The Migration Assessment demo, which does require judgment, runs on Sonnet 5 instead — matching model size to the actual reasoning load is a cost decision we apply to client work, not just this site.