<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Kuveco · Blog</title>
    <link>https://kuveco.com/en/blog/</link>
    <atom:link href="https://kuveco.com/en/blog/feed.xml" rel="self" type="application/rss+xml"/>
    <description>Kuveco&#39;s notes on n8n, Kubernetes, OpenShift and AI automation in production.</description>
    <language>en</language>
    <lastBuildDate>Sun, 01 Nov 2026 01:00:00 +0100</lastBuildDate>
    <item>
      <title>Invoices that file themselves: our own invoice flow in n8n</title>
      <link>https://kuveco.com/en/blog/invoices-that-file-themselves/</link>
      <guid isPermaLink="true">https://kuveco.com/en/blog/invoices-that-file-themselves/</guid>
      <pubDate>Sun, 01 Nov 2026 01:00:00 +0100</pubDate>
      <description>&lt;p&gt;We still get some invoices on paper too. Scanning them takes no time. The annoying part comes after: the file is called something like &lt;code&gt;scan_0047.pdf&lt;/code&gt;, and before you know it there&#39;s a folder full of scans and nobody remembers which invoice is which.&lt;/p&gt;
&lt;p&gt;So we leave that to a small flow in n8n. We scan the invoice, and within half an hour it sits in the right folder, under a name that tells you who it&#39;s from and when.&lt;/p&gt;
&lt;h2 id=&quot;from-paper-to-folder-in-five-steps&quot; tabindex=&quot;-1&quot;&gt;From paper to folder in five steps&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;The printer scans the invoice and emails the PDF to our mailbox.&lt;/li&gt;
&lt;li&gt;Every half hour, n8n checks for a new scan.&lt;/li&gt;
&lt;li&gt;Tesseract turns the scan into plain text.&lt;/li&gt;
&lt;li&gt;A language model, Google&#39;s Gemini Flash, reads the supplier, the invoice number and the date from that text.&lt;/li&gt;
&lt;li&gt;The file gets a new name and moves to the folder for processed invoices. A Telegram message tells us it worked.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;the-scan-arrives-by-email&quot; tabindex=&quot;-1&quot;&gt;The scan arrives by email&lt;/h2&gt;
&lt;p&gt;Our Epson printer can email a scan directly. That is all the connection to the printer there is. In n8n, the flow starts with a &lt;em&gt;Gmail Trigger&lt;/em&gt; that checks for new mail every thirty minutes, but only from the address the printer sends its scans from. It leaves all other mail alone, and downloads the attachment straight away.&lt;/p&gt;
&lt;p&gt;First, the flow checks whether it has seen this scan before. It searches the scans folder in Google Drive for a file with the same name. If there is one, it stops and sends a short message saying the invoice was already processed. If not, it saves the scan to that folder and carries on.&lt;/p&gt;
&lt;h2 id=&quot;text-first-then-the-language-model&quot; tabindex=&quot;-1&quot;&gt;Text first, then the language model&lt;/h2&gt;
&lt;p&gt;A scan is an image, and a computer can&#39;t search an image. Tesseract is a free, open-source program for text recognition (OCR) that runs in n8n through an extra node. It turns the scan into plain text, with all the mess that comes with it: columns that run into each other, a logo that comes back as a string of odd characters.&lt;/p&gt;
&lt;p&gt;Only that text goes to the language model. For that we use n8n&#39;s &lt;em&gt;Information Extractor&lt;/em&gt; node with Gemini Flash. You tell the node in plain words which fields you&#39;re looking for, and the model fills them in. We ask for three:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the supplier, described as the name of the company issuing the invoice, &amp;quot;often at the top, sometimes in capitals&amp;quot;;&lt;/li&gt;
&lt;li&gt;the invoice number, recognisable by the word &amp;quot;invoice&amp;quot; or &amp;quot;factuur&amp;quot;;&lt;/li&gt;
&lt;li&gt;the invoice date, recognisable by &amp;quot;invoice date&amp;quot; or &amp;quot;datum&amp;quot;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All three are required fields: the model always has to fill them in. A language model is good at this because every invoice looks different. Fixed rules (&amp;quot;the number is always top right&amp;quot;) won&#39;t get you far; a language model understands that &amp;quot;Factuurnr.&amp;quot; and &amp;quot;Invoice no.&amp;quot; mean the same thing.&lt;/p&gt;
&lt;h2 id=&quot;a-name-you-can-find-again&quot; tabindex=&quot;-1&quot;&gt;A name you can find again&lt;/h2&gt;
&lt;p&gt;From the supplier and the date, the flow builds a new file name: the supplier&#39;s name, an underscore and the date in digits. Dates on invoices come in every possible format, so a bit of code first turns Dutch and English month names into numbers (&amp;quot;November&amp;quot; becomes 11) and then keeps only the digits.&lt;/p&gt;
&lt;p&gt;The flow then makes a copy of the scan under the new name and moves that copy to the folder for processed invoices. The original scan stays in the scans folder, so if something goes wrong with the name, nothing is lost.&lt;/p&gt;
&lt;p&gt;As a last step, the flow posts a Telegram message with the new file name and the invoice date. You read a message like that in a few seconds. If the model ever reads a name wrong, we see it straight away on our phone, not weeks later in the books.&lt;/p&gt;
&lt;h2 id=&quot;what-the-flow-doesnt-do&quot; tabindex=&quot;-1&quot;&gt;What the flow doesn&#39;t do&lt;/h2&gt;
&lt;p&gt;It doesn&#39;t book anything and it doesn&#39;t pay anything. All it does is make sure every paper invoice ends up in the same place digitally, under a name you can find. Invoices that already arrive in digital form, for example as e-invoices through Peppol, don&#39;t go through this flow; they&#39;re readable without a scan.&lt;/p&gt;
&lt;h2 id=&quot;the-tools-at-a-glance&quot; tabindex=&quot;-1&quot;&gt;The tools at a glance&lt;/h2&gt;
&lt;div class=&quot;table-wrap&quot; role=&quot;region&quot; tabindex=&quot;0&quot; aria-label=&quot;Table&quot;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Part&lt;/th&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Scanning&lt;/td&gt;
&lt;td&gt;Epson printer with scan to email&lt;/td&gt;
&lt;td&gt;Emails the PDF to our mailbox.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mailbox&lt;/td&gt;
&lt;td&gt;Gmail, with n8n&#39;s Gmail Trigger&lt;/td&gt;
&lt;td&gt;Supplies new scans every thirty minutes.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Text recognition&lt;/td&gt;
&lt;td&gt;Tesseract (OCR), as an extra n8n node&lt;/td&gt;
&lt;td&gt;Turns the scan into text.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reading&lt;/td&gt;
&lt;td&gt;Gemini Flash (Google), in the Information Extractor node&lt;/td&gt;
&lt;td&gt;Pulls the supplier, invoice number and date from the text.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Archive&lt;/td&gt;
&lt;td&gt;Google Drive&lt;/td&gt;
&lt;td&gt;Keeps the original scan and the renamed copy.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Alerts&lt;/td&gt;
&lt;td&gt;Telegram&lt;/td&gt;
&lt;td&gt;Confirms each invoice and flags duplicate scans.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h2 id=&quot;is-this-something-for-your-business&quot; tabindex=&quot;-1&quot;&gt;Is this something for your business?&lt;/h2&gt;
&lt;p&gt;It&#39;s a small flow, fourteen nodes, and it uses things a lot of small businesses already have: a printer that can scan, a mailbox and somewhere in the cloud to keep files. If paper invoices still land on your desk regularly, or you spend too much time searching a folder full of files called &lt;code&gt;scan_&lt;/code&gt;, this is a good first automation.&lt;/p&gt;
</description>
    </item>
    <item>
      <title>Quotes that write themselves: how the AI automation for a print shop works</title>
      <link>https://kuveco.com/en/blog/quotes-that-write-themselves/</link>
      <guid isPermaLink="true">https://kuveco.com/en/blog/quotes-that-write-themselves/</guid>
      <pubDate>Sun, 01 Nov 2026 01:00:00 +0100</pubDate>
      <description>&lt;p&gt;Our client, a print shop, gets a lot of its quote requests by email. No two emails are alike. One customer might write &amp;quot;twelve copies, 160 pages, 24 of them in colour&amp;quot;. Another starts with the story behind the project and only mentions the numbers in the last sentence. Someone had to read every email, work out the price and type a reply.&lt;/p&gt;
&lt;p&gt;We built a flow in n8n that takes this over. It runs every hour, reads the new requests, calculates the price and sends the quote, in Dutch or in English.&lt;/p&gt;
&lt;h2 id=&quot;what-happens-every-hour&quot; tabindex=&quot;-1&quot;&gt;What happens every hour&lt;/h2&gt;
&lt;p&gt;n8n is a tool in which you build an automation as a chain of blocks, called &lt;em&gt;nodes&lt;/em&gt;. Each block does one thing and hands its result to the next. This flow starts by itself every hour and then goes through these steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Fetch the new emails from the mailbox.&lt;/li&gt;
&lt;li&gt;Clean up each email until only the text the customer wrote is left.&lt;/li&gt;
&lt;li&gt;Check whether this customer has already had a quote.&lt;/li&gt;
&lt;li&gt;Have Claude, Anthropic&#39;s language model, read out the details.&lt;/li&gt;
&lt;li&gt;Check that there is enough information to put a price on it.&lt;/li&gt;
&lt;li&gt;Calculate the price.&lt;/li&gt;
&lt;li&gt;Email the quote in the customer&#39;s language.&lt;/li&gt;
&lt;li&gt;Let a member of staff know if the email contained another question, or if something went wrong.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;fetching-and-cleaning-up-the-emails&quot; tabindex=&quot;-1&quot;&gt;Fetching and cleaning up the emails&lt;/h2&gt;
&lt;p&gt;The print shop uses Microsoft 365. n8n fetches the emails through the Microsoft Graph API, with a search that only returns messages from the last two days. Because the flow looks back two days, no request gets lost if it doesn&#39;t run for a few hours.&lt;/p&gt;
&lt;p&gt;An email arrives as HTML. A short piece of code strips the HTML tags, converts special characters and keeps only the relevant text. Less text also costs less: you pay a language model for every bit of text it reads.&lt;/p&gt;
&lt;h2 id=&quot;one-request-one-quote&quot; tabindex=&quot;-1&quot;&gt;One request, one quote&lt;/h2&gt;
&lt;p&gt;n8n has built-in tables, called &lt;em&gt;Data Tables&lt;/em&gt;. For each contact, the table holds the email address, whether a quote has gone out and when. The flow looks up the sender of every email and only carries on if that address is in the table and hasn&#39;t had a quote yet. If the same customer writes again later, they don&#39;t get a second quote.&lt;/p&gt;
&lt;p&gt;If no quote can be made, that goes into the table too. That way the print shop can see at a glance which requests still need a person to look at them.&lt;/p&gt;
&lt;h2 id=&quot;claude-reads-the-request&quot; tabindex=&quot;-1&quot;&gt;Claude reads the request&lt;/h2&gt;
&lt;p&gt;The reading happens in n8n&#39;s &lt;em&gt;AI Agent&lt;/em&gt; node, with Claude Sonnet as the language model. The instruction to the model is short: you&#39;ll get a raw email in Dutch or English, pull out these six things.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the number of copies;&lt;/li&gt;
&lt;li&gt;the number of black-and-white pages;&lt;/li&gt;
&lt;li&gt;the number of colour pages;&lt;/li&gt;
&lt;li&gt;the language of the email;&lt;/li&gt;
&lt;li&gt;whether the customer wants a laminated cover;&lt;/li&gt;
&lt;li&gt;whether the email asks anything else.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A &lt;em&gt;Structured Output Parser&lt;/em&gt; is attached to the AI Agent node. It fixes the shape of the answer: a JSON object with exactly those six fields, numbers where numbers belong. For an email like &amp;quot;I&#39;d like 12 copies printed, 160 pages in black and white and 24 in colour, with a laminated cover. Could you post them to me as well?&amp;quot;, this is what comes back:&lt;/p&gt;
&lt;pre class=&quot;language-json&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;quantity&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;blackAndWhite&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;160&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;color&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;24&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;language&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;E&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;coverPage&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Y&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;extraQuestion&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Y&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;why-the-price-doesnt-come-from-the-language-model&quot; tabindex=&quot;-1&quot;&gt;Why the price doesn&#39;t come from the language model&lt;/h2&gt;
&lt;p&gt;A language model is good at reading messy text. Working through a price list is a different matter: it might round differently or forget a discount, and asking the same question twice doesn&#39;t always get you the same answer twice. For a quote, that won&#39;t do.&lt;/p&gt;
&lt;p&gt;So Claude&#39;s job ends once it has read the email. First, a plain check makes sure there is at least a number of copies and a number of black-and-white pages. If there isn&#39;t, the flow makes no quote and records that in the table. Then a &lt;em&gt;Code&lt;/em&gt; node works out the price with the print shop&#39;s own rules: a price per page for black and white and for colour, a fixed cost per book, a surcharge for a laminated cover and a discount above a certain number of copies.&lt;/p&gt;
&lt;p&gt;If a rate changes, someone edits one number. Nobody has to rewrite a prompt and hope the model remembers the new price.&lt;/p&gt;
&lt;h2 id=&quot;the-quote-goes-out&quot; tabindex=&quot;-1&quot;&gt;The quote goes out&lt;/h2&gt;
&lt;p&gt;The language Claude picked up decides which text the customer gets: Dutch or English.&lt;/p&gt;
&lt;p&gt;The quote also explains what happens next: the customer sends their files as a PDF, the print shop checks the layout, prints a proof and lets them know when the first copy is ready. That part stays human work, as it should.&lt;/p&gt;
&lt;p&gt;Sending goes through SMTP2GO, a service that sends email through a simple API.&lt;/p&gt;
&lt;h2 id=&quot;what-the-flow-leaves-to-people&quot; tabindex=&quot;-1&quot;&gt;What the flow leaves to people&lt;/h2&gt;
&lt;p&gt;A good automation knows when to stop. This one stops in three cases.&lt;/p&gt;
&lt;p&gt;If the number of copies or the number of pages is missing, no quote goes out.&lt;/p&gt;
&lt;p&gt;If the customer asks something else in the same email, about delivery for example, the quote still goes out. The print shop also gets an internal email asking someone to reply to the customer personally. It opens with &amp;quot;Hello, this is your AI bot.&amp;quot;&lt;/p&gt;
&lt;p&gt;And if something goes wrong, because the language model gives no usable answer or an email fails to send, the team hears about it straight away in an internal chat message.&lt;/p&gt;
&lt;h2 id=&quot;the-tools-at-a-glance&quot; tabindex=&quot;-1&quot;&gt;The tools at a glance&lt;/h2&gt;
&lt;div class=&quot;table-wrap&quot; role=&quot;region&quot; tabindex=&quot;0&quot; aria-label=&quot;Table&quot;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Part&lt;/th&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Automation&lt;/td&gt;
&lt;td&gt;n8n&lt;/td&gt;
&lt;td&gt;Runs the flow every hour and connects all the steps.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mailbox&lt;/td&gt;
&lt;td&gt;Microsoft 365, through the Microsoft Graph API&lt;/td&gt;
&lt;td&gt;Supplies the new requests.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reading&lt;/td&gt;
&lt;td&gt;Claude Sonnet (Anthropic), in the AI Agent node&lt;/td&gt;
&lt;td&gt;Pulls the six details out of the email.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fixed format&lt;/td&gt;
&lt;td&gt;Structured Output Parser&lt;/td&gt;
&lt;td&gt;Forces the answer into a JSON schema.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Memory&lt;/td&gt;
&lt;td&gt;n8n Data Tables&lt;/td&gt;
&lt;td&gt;Keeps track of who has had a quote, and when.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cleaning up and calculating&lt;/td&gt;
&lt;td&gt;Code nodes in JavaScript&lt;/td&gt;
&lt;td&gt;Cleans up the email and works out the price.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sending&lt;/td&gt;
&lt;td&gt;SMTP2GO&lt;/td&gt;
&lt;td&gt;Sends the quote.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Alerts&lt;/td&gt;
&lt;td&gt;Telegram&lt;/td&gt;
&lt;td&gt;Says when something goes wrong.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h2 id=&quot;is-this-something-for-your-business&quot; tabindex=&quot;-1&quot;&gt;Is this something for your business?&lt;/h2&gt;
&lt;p&gt;The same setup works outside printing too. It pays off most when:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;requests come in as free text, by email or through a form;&lt;/li&gt;
&lt;li&gt;your price follows from a handful of details and fixed rules, even if those rules are complicated;&lt;/li&gt;
&lt;li&gt;someone on your team spends hours on this every week;&lt;/li&gt;
&lt;li&gt;a quick answer makes a difference to your customers.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You don&#39;t need to buy a new software package. We build on the mailbox and the software you already have, and we start with one type of request. Once that works, we move on to the next.&lt;/p&gt;
</description>
    </item>
  </channel>
</rss>
