← All work

LedgerDrop

Invoice processing with a human in the loop. An async FastAPI pipeline extracts and validates invoices, and a Next.js review screen handles the ones it isn't sure about.

Role
Full stack developer
Team
Solo
Status
Live
Links
Live app ↗ · Code ↗
LedgerDrop invoice workspace
The invoice workspace: upload area with format limits on the left, recent uploads and their processing state on the right, review queue one click away.

Problem

  • Invoices arrive as PDFs and scans in no fixed format, and the data that matters (supplier, dates, line items, totals) has to end up in a database that can be trusted.
  • Fully automatic extraction is never fully correct, so the app is built around review rather than pretending the model is always right.

What I built

  • FastAPI service with an async upload endpoint: the file is stored, a processing job is queued, and the client polls job status instead of blocking on the request.
  • Extraction through the OpenAI API, then validation; invoices the pipeline is uncertain about are routed to a human review queue instead of being saved as-is.
  • Next.js and TypeScript review screen to approve, reject or correct extracted fields next to the source document, with a full audit trail.
  • PostgreSQL through SQLAlchemy with Alembic migrations. Deployed with Docker on Render, with uploaded files stored in Cloudflare R2.

Key decisions

  • A status-driven job model instead of one long request, so a slow or failed extraction never takes the upload down with it.
  • Validation rules run before a human sees a document, so review time goes to genuine ambiguity rather than arithmetic.
  • Every extracted field keeps its origin, so a correction is traceable instead of silently overwriting the model's output.

Stack

  • FastAPI
  • SQLAlchemy
  • PostgreSQL
  • Alembic
  • Next.js
  • TypeScript
  • Docker
  • Render
  • Cloudflare R2
  • OpenAI API