Automatically Check That Base + VAT − Withholding Matches When You Extract an Invoice
Automatically Check That Base + VAT − Withholding Matches When You Extract an Invoice
If you automate a single validation rule after OCR, make it this one: taxable base + VAT − withholding = invoice total, verified to the cent with an explicit tolerance of one cent per tax-rate block. It is the only check that catches the most expensive kind of extraction error — a number that is *plausible* but wrong. A mangled supplier name is obvious at a glance. A total that is €31.50 off is not, and it will sit in your ledger until a bank reconciliation fails three weeks later.
That €31.50 is not a made-up figure, it is the single most common arithmetic mistake in the whole document: the withholding is always computed on the taxable base, never on the VAT-inclusive amount. On a €1,000 professional invoice with 21% VAT and 15% Spanish IRPF withholding, the correct total is 1,000 + 210 − 150 = €1,060.00. Apply the 15% to the €1,210 VAT-inclusive figure instead and you get €181.50 of withholding and a total of €1,028.50 — a difference of €31.50 that reconciles against nothing, and that you will also be under-reporting on your withholding return. The calculators that rank for this topic will hand you the formula; what follows is how to turn it into an automatic gate that runs on every extracted document.
Why bother automating it at all: the IOFM 2025 benchmark puts the average manual data-entry error rate at around 3.6%, and industry AP surveys put the average cost of resolving a single invoice error at roughly $53. At 400 invoices a month, a 3.6% error rate is about 14 errors — the arithmetic check does not need to catch all of them to pay for itself.
The rule, written the way a machine can run it
For each extracted invoice, the check is:
- `sum(base_i) + sum(base_i × vat_rate_i) + sum(base_i × surcharge_i) − withholding − |total| ≤ tolerance`
And, separately, per rate block:
- `vat_amount_i ≈ base_i × vat_rate_i` (this is essentially the rule Spain's SII applies when it validates submissions: the reported VAT amount must match base × rate, with only a couple of cents of slack before the invoice is rejected or accepted-with-errors)
- `withholding ≈ sum(base_i) × withholding_rate` — computed on the base, with VAT excluded
Three things make this harder than it looks, and they are exactly where naive implementations break.
1. Never hardcode the rate
Derive the rate from the numbers, then check it against the set of legal rates. Spain runs 21 / 10 / 4% VAT and 15% professional IRPF (7% for new self-employed in their first three years and for certain listed activities). Italy runs 22% IVA with a 20% ritenuta d'acconto. Portugal's category B withholding is not a single number either: 25%, 16.5% for intellectual property income and 11.5% for activities outside the article 151 table, plus a reduced general rate introduced in 2025. Any checker that assumes "15%" or "25%" will fire false alarms on half your suppliers. Let the extraction return the rate the supplier printed, recompute it from base and amount, and flag only when the two disagree.
2. Multiple rate blocks in one invoice
A supplier that bills you goods at 21% and a delivery service at 10% produces two blocks. Check each block on its own, then sum. Example: 800 at 21% (=168.00) plus 200 at 10% (=20.00), 15% withholding on the full 1,000 base (=150.00), total 1,038.00. If you only read the largest VAT line — a very common single-field extraction shortcut — you get 1,000 + 210 − 150 = 1,060.00 and a phantom €22 gap.
3. Lines that are in the total but not in the base
Some amounts belong to the total without belonging to the taxable base or to the withholding base:
- Disbursements paid in the client's name (suplidos in Spain, spese anticipate in Italy): outside the base, outside VAT, outside the withholding.
- The equivalence surcharge on Spanish retail invoices: 5.2% on top of 21% VAT, 1.4% on 10% and 0.5% on 4%. It adds to the total and never touches the base.
- Pension-fund contributions on Italian invoices: for professionals in the INPS gestione separata, the 4% rivalsa is part of the withholding base; for those with their own cassa, the 4% integrativo is not. Same-looking line, different arithmetic.
If your check simply sums "everything that looks like a number", these three will produce constant false positives and your team will start ignoring the alerts — which is worse than not having them.
How much difference is acceptable
Use ±0.01 € per rate block, plus ±0.01 € for the withholding line. So a single-rate invoice tolerates 0.02 €, a two-rate invoice 0.03 €. This is not laxness, it is arithmetic: suppliers legitimately differ on whether they round VAT per line or on the block subtotal, and the two methods diverge by a cent or two on long invoices. Anything above that tolerance is not rounding — it is either a misread digit or a genuinely wrong invoice.
Split the outcome into three states, not two:
- Green — within tolerance. Post it. Nothing to review.
- Amber — off by a few cents beyond tolerance. Almost always a rounding policy difference or an OCR confusion between a comma and a period in a decimal. Queue for a five-second human glance.
- Red — off by an amount that matches a known pattern. Withholding computed on the VAT-inclusive total, a missing rate block, a transposed digit (7↔1, 3↔8, 6↔5 are the classic OCR confusions on low-quality scans). Send back for re-extraction or manual entry.
The difference *itself* is diagnostic. If the gap equals `withholding × vat_rate` exactly, you have found error number one. If it equals a whole rate block, you missed a block. If it is a clean power of ten, someone dropped or added a digit.
Where this belongs in the flow
The check has to run at extraction time, in the same second the data is produced. A validation that runs weekly inside your accounting software is a report; a validation that runs at extraction is a gate. With WhappScan the natural place is right after the document arrives — a supplier or a colleague sends the invoice photo or PDF to your WhatsApp number, the fields come back structured, and the arithmetic check runs on the extracted fields before anything reaches your spreadsheet or ERP. If you are building this into an existing pipeline, the same fields go out over the API, so the check can live on your side: see how to send invoices to Excel or your ERP via API for the shape of that integration, and how to automate invoice extraction over WhatsApp for the capture side.
A practical detail: define the check against a document template that already declares base, VAT rate, VAT amount, withholding rate, withholding amount and total as separate fields. If your extraction returns one blob of text, you have nothing to validate. Structured fields per rate block are the prerequisite, not a nice-to-have — the same reason preparing supplier invoices properly before handing them to your accountant saves the back-and-forth at quarter end. If you work through a practice, the whole flow — capture, extraction, validation — is what WhappScan for accounting is built around.
The check you should add next
Once arithmetic validation is green across the board, the next highest-yield rule is duplicate detection: same supplier tax ID, same invoice number, same total. It costs almost nothing to compute on already-extracted fields and it catches the error class that arithmetic never will — a perfectly correct invoice booked twice. That one is covered in how to catch duplicate invoices before you book them.
Also worth noting for Spanish readers: Verifactu became a 2027 obligation (1 January 2027 for corporate income tax payers, 1 July 2027 for self-employed) after the deferral approved in Royal Decree-Law 15/2025. It governs the invoices you *issue*, not the ones you receive — but the discipline is the same, and getting your received-invoice arithmetic clean now means less to untangle later.
Try it on a real invoice
Take the messiest supplier invoice on your desk — the one with two VAT rates and a withholding line — and extract it. Then run the three-line arithmetic yourself and see whether the numbers agree.
Extract your document's data free, with no account and no install, at /en/free.
Need to extract data from a document right now?
Try it free in seconds — no account, no card. Upload an invoice or document and get the data instantly.
Try it free