KSeF
Auto-assign KSeF invoices to projects — step by step
KSeFMakeClaude
Czytaj po polsku: wersja polska
Now that KSeF delivers every supplier and subcontractor invoice in one structured format, the most valuable first automation in a construction firm is also the most boring: getting each invoice onto the right project, without anyone re-typing it. Here’s how to build it.
What you’ll end up with
- Every inbound KSeF invoice lands in a sheet/board, already tagged to a project.
- ~80% are matched automatically by rules.
- The ~20% that aren’t go into an exceptions queue for a human to resolve in seconds.
You don’t need a developer. Make (or n8n) plus one AI step is enough.
Step 1 — Get the invoices out of KSeF and into one place
Pull inbound invoices via your accounting tool’s KSeF export or the API into a single table (Google Sheet, Airtable, or a Make data store). Capture the fields KSeF already gives you: seller NIP, seller name, line-item text, gross amount, issue date, invoice number.
Don’t skip this. A clean, append-only list of inbound invoices is the foundation — automate nothing until it’s reliable.
Step 2 — Write your matching rules first (on paper)
Before any automation, write down how you decide which project an invoice belongs to. It’s usually one of:
- Seller NIP → project (this concrete supplier only delivers to the Mokotów site this quarter).
- Keyword in line items → project (an address, a project code, a material tied to one build).
- Amount threshold → review (anything over X always gets eyes on it).
If you can’t write the rule, AI can’t apply it. Most firms find 5–10 rules cover the bulk.
Step 3 — Build the rule layer in Make
Create a scenario triggered on “new row” in your invoice table:
- A Router with one path per high-confidence rule (NIP match, project-code match).
- Each path sets a
projectfield and aconfidence: highflag. - A fallback path leaves
projectempty withconfidence: low.
This alone will auto-tag every invoice from your regular suppliers.
Step 4 — Add one AI step for the messy ones
For the fallback path, add an AI module (Claude/OpenAI) with a tight prompt:
You match construction invoices to projects.
Active projects: {{list of projects + their addresses/codes}}
Invoice: seller {{seller}}, items "{{line_items}}", amount {{amount}}.
Return JSON: { "project": "<name or 'unknown'>", "reason": "<short>" }.
If unsure, return "unknown".
Crucially: tell it to return unknown when unsure. A confident wrong guess is worse than an honest “I don’t know.”
Step 5 — Route the exceptions to a human
confidence: high(rules) or AI returned a project → write the tag, done.- AI returned
unknownor low confidence → push to an exceptions queue (a filtered view, a Slack message, whatever your PM already checks).
The PM now reviews a handful of invoices a week instead of sorting all of them. Over time, the patterns they keep resolving the same way become new Step-3 rules — and the queue shrinks.
Watch-outs
- Duplicates: dedupe on invoice number + NIP before tagging.
- Don’t auto-approve payment off this — it tags and routes; a human still approves money.
- Log the AI’s
reason— it’s how you debug and turn judgments into rules.
Start with rules, add the one AI step for the long tail, and keep a human on the exceptions. That’s the whole pattern — and it’s the same shape for quoting, follow-ups, and document routing.