Skip to main content
Open Source · AI

docu-ai-flow

A .NET 10 service that watches a folder, extracts the data from PDF invoices, files them and sends the quarter to your tax advisor. Two ideas carry it: extraction lives behind a single port — local templates today, Google Document AI yesterday — and no invoice is filed unseen until its supplier has earned it. Two commands to run, and not one credential.

The problem

Managing supplier invoices is repetitive manual work: downloading PDFs, opening them one by one, copying data into a spreadsheet, sending everything to the advisor each quarter. One mistake at any step means incorrect tax filings. But full automation has a trap of its own: no extraction is right 100% of the time, and a misread figure nobody looks at ends up in a filing.

The solution

  • Folder watcher

    Detects PDFs in ./data/inbox/ in real time. A filesystem watcher plus a polling fallback guarantee no invoice is ever missed. Duplicates are caught by content hash and by natural key (number + tax id) — the latter is what catches a re-sent invoice whose bytes differ, before the same VAT is declared twice.

  • Swappable extraction

    One port, IInvoiceDataExtractor, with two live adapters: local templates over PdfPig (the default — no network, no credentials, €0 per invoice) and Google Document AI. Which one is bound is a configuration value, not a code change.

  • OCR for scans

    A scan has no text layer, so there is nothing to read. With the fallback on, poppler rasterises the page and Tesseract reads it; the recovered text goes through the same template pipeline. With it off, a Null Object returns an empty string and no branch of the extractor has to ask whether OCR exists.

  • Review, PDF alongside

    Invoices that are not filed automatically wait in a queue. The review screen puts the PDF on the left and the extracted fields on the right, each carrying the confidence it was captured with. Anything the extractor could not read becomes a blank form, with its own net + VAT = total check.

  • Excel export

    ClosedXML generates a quarterly Excel file with native types — real dates and numbers ready to sort, filter and sum. A cumulative master spreadsheet is regenerated on every confirmed invoice.

  • Advisor dispatch

    Resend packages the quarter's PDFs into a ZIP and emails them to the tax advisor. If the ZIP exceeds Resend's 40 MB limit, it splits automatically by month and sends one email per part.

The review screen: the PDF on the left, the extracted fields on the right with the confidence of each one, and how far the supplier is from filing on its own.

The review screen: the PDF on the left, the extracted fields on the right with the confidence of each one, and how far the supplier is from filing on its own.

The review screen: the PDF on the left, the extracted fields on the right with the confidence of each one, and how far the supplier is from filing on its own.

Try it

No clone, no .NET install. The image is published to GHCR from CI for amd64 and arm64, and boots on the local extractor: no accounts, no keys.

  1. 1 Write eight fictional invoices into the inbox
    docker run --rm -v "$PWD/data:/app/data" \
      ghcr.io/aitorevi/docu-ai-flow:latest make-samples /app/data/inbox
  2. 2 Start the watcher and the panel
    docker run --rm -p 8080:8080 -v "$PWD/data:/app/data" \
      ghcr.io/aitorevi/docu-ai-flow:latest

Open localhost:8080. Three of those invoices come from the same supplier: confirm them without changing anything and watch Suministros Aurora cross from "In review" to "Automatic" — from then on its invoices are filed on their own. That is the idea the whole project rests on, and it takes a minute to see.

The panel: what is waiting for review, what is already filed, and a trust meter per supplier — Suministros Aurora already automatic.

The panel: what is waiting for review, what is already filed, and a trust meter per supplier — Suministros Aurora already automatic.

The panel: what is waiting for review, what is already filed, and a trust meter per supplier — Suministros Aurora already automatic.

Tech stack

Backend

  • .NET 10
  • C#
  • Hexagonal
  • Result/Error
  • SQLite
  • PdfPig
  • Google Document AI
  • Tesseract
  • Resend
  • Polly

Frontend & deployment

  • Astro 6
  • TypeScript
  • Docker
  • GHCR
  • GitHub Actions

Tests

  • xUnit
  • NSubstitute
  • WireMock.Net
  • NetArchTest
  • WebApplicationFactory

Technical decisions

  • Hexagonal architecture

    The extractor, SQLite, the file archiver and Resend are swappable adapters behind interfaces (ports). Application use cases know no implementation detail. A NetArchTest architecture test enforces these boundaries on every build.

  • The hexagon, demonstrated

    The project started on LlamaParse, moved to Google Document AI when its accuracy fell short, and ended up on a local template extractor that beats both on the invoices it actually sees. Across all three swaps the domain, the use cases and the pipeline tests were untouched: they depend on the port, not the provider. That is the whole return on the hexagon, and it is why the default today is the free one.

  • Trust that is earned

    The interesting question is not whether a human reviews, but which invoices earn the right to skip review — and that is decided per supplier, by track record. After N consecutive confirmations in which nobody changed anything, that supplier starts being filed automatically. A single correction resets the counter and revokes it. Autonomy is earned, and it can be lost.

  • Result/Error for business failures

    A low-confidence extraction, incoherent total or missing invoice number is an expected business case, not an exception. Result<TValue, TError> expresses it as a return value. Exceptions are reserved for transient failures — API down, disk full — which Polly retries automatically.

  • Runnable with no credentials

    A portfolio project you can only read is worth half as much. The image is published to GHCR from CI, ships poppler and Tesseract inside, and boots on the local extractor: two Docker commands, no accounts and no keys, and eight fictional invoices that walk every path — including one from a supplier nobody wrote a template for, and a scan with no text at all.

  • Built with Claude Code agents

    Every feature followed the same cycle: the orchestrator explored and debated the approach, the planner designed the architecture, the coder implemented in TDD, and the reviewer audited against the approved plan. The result: consistent code and a commit history that tells the story of every decision.

Built with Claude Code

The project is also an experiment in how to organise AI-assisted development. The agents and skills live in .claude/ and are part of the public repo.

  • Orchestrator

    Coordinates the full flow: explore, debate, plan, implement, review. No phase starts without explicit human agreement. The plan lives in workflow/ and moves through plans/ → in-progress/ → reviewing/ → done/.

  • Specialised agents

    planner-backend designs the architecture and contracts; coder-backend implements in strict TDD; reviewer audits against the approved plan. Each agent has its own system prompt under .claude/agents/.

  • Reusable skills

    Markdown skills loaded at session start: commit.md defines the commit format, tdd.md encodes the Red-Green-Refactor cycle, orchestrate.md the full orchestration flow.

  • AI-assisted TDD

    Every feature starts with a failing test. 262 tests across three layers: Domain (pure), Application (use cases with NSubstitute doubles) and Integration — real SQLite and real files, the whole API hosted in-process through WebApplicationFactory, and a pipeline test that walks a supplier from its first review to filing itself.

Interested in the AI workflow?

The repo is public and runs with no credentials. The agents, skills and orchestration flow are in .claude/ alongside the source code. Use them as a template for your own projects.