<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Alberto Landa</title>
    <description>The latest articles on DEV Community by Alberto Landa (@sirko007).</description>
    <link>https://dev.to/sirko007</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4034338%2F469d4567-3049-4194-b376-311d5d0a11bc.jpg</url>
      <title>DEV Community: Alberto Landa</title>
      <link>https://dev.to/sirko007</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sirko007"/>
    <language>en</language>
    <item>
      <title>I built a 100% local invoice reader with Ollama + n8n — the real trick was teaching it NOT to guess</title>
      <dc:creator>Alberto Landa</dc:creator>
      <pubDate>Sat, 18 Jul 2026 14:44:15 +0000</pubDate>
      <link>https://dev.to/sirko007/i-built-a-100-local-invoice-reader-with-ollama-n8n-the-real-trick-was-teaching-it-not-to-guess-3113</link>
      <guid>https://dev.to/sirko007/i-built-a-100-local-invoice-reader-with-ollama-n8n-the-real-trick-was-teaching-it-not-to-guess-3113</guid>
      <description>&lt;p&gt;I'm not a professional developer — I run a small business and I'm self-taught (HTML/CSS/JS, a bit of Python). Every month I had the same chore: a pile of PDF invoices and me copying supplier, date, concept and total into a spreadsheet by hand. Boring, and full of typos.&lt;/p&gt;

&lt;p&gt;I automated it with &lt;strong&gt;n8n&lt;/strong&gt;, but with two hard rules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Invoices never leave my computer.&lt;/strong&gt; They're supplier and business data — I didn't want to push them to any cloud API.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No monthly subscription&lt;/strong&gt; per model call.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So I ran it on &lt;strong&gt;Ollama locally&lt;/strong&gt; (&lt;code&gt;qwen2.5-coder:7b&lt;/code&gt;) instead of a paid API. It works on a normal laptop — a Ryzen 7, 16 GB RAM, no pro GPU — at ~12 s per invoice and €0 cost per run.&lt;/p&gt;

&lt;p&gt;The flow is 7 nodes: a trigger (Gmail label "Invoices" in production) → a Code node with 3 sample invoices embedded so anyone can test it with zero setup → an HTTP Request to Ollama asking for JSON (supplier, date, concept, total) → a classifier → a summary → a Telegram ping → a slot to dump into Google Sheets.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part that actually mattered
&lt;/h2&gt;

&lt;p&gt;At first the model &lt;em&gt;invented&lt;/em&gt; data when an invoice was blurry or badly scanned — a disaster for accounting. The fix was to ask the prompt for an extra &lt;code&gt;confidence&lt;/code&gt; field (0 to 1) and add one simple rule:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if confidence &amp;gt;= 0.8 AND supplier AND total  -&amp;gt;  OK
else                                          -&amp;gt;  REVIEW
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now dubious invoices don't slip through: they're flagged and I check them in 10 seconds. I'd rather that than a silent €300 error. The prompt uses &lt;code&gt;temperature: 0&lt;/code&gt; and &lt;code&gt;format: json&lt;/code&gt; so it doesn't ramble.&lt;/p&gt;

&lt;h2&gt;
  
  
  A few gotchas that cost me time
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;If n8n runs in Docker, the Ollama node must point to &lt;code&gt;http://host.docker.internal:11434&lt;/code&gt;, not &lt;code&gt;localhost&lt;/code&gt; — that's the classic "connection refused".&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;first&lt;/strong&gt; invoice is much slower (~1 min on my laptop) because Ollama loads the model into memory the first time. From the second one it's ~8–10 s. First one is the toll, then it flies.&lt;/li&gt;
&lt;li&gt;If everything comes back "REVIEW", your confidence threshold is too high — drop it to 0.6.&lt;/li&gt;
&lt;li&gt;If the PDF is a scan with no text layer, the model gets nothing — you need an OCR step first.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Two questions for anyone who's done this
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;How do you handle the "REVIEW" bucket — a second pass with another model, or straight to human review like me?&lt;/li&gt;
&lt;li&gt;For production: Gmail Trigger, a watched folder, or a webhook?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Happy to share the full workflow JSON if it's useful. Any critique welcome — I'm more bar-counter than code. 🙂&lt;/p&gt;

</description>
      <category>n8n</category>
      <category>ollama</category>
      <category>automation</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
