Validation & rulesGuide

From PDF invoice to e-invoice: what actually fails when converting legacy invoices

A PDF invoice from Word or Excel can be converted into an e-invoice with rules alone — the text layer is there and the layout repeats. What the PDF lacks must come from master data: bank account, seller contact, the buyer's e-mail address and Leitweg-ID. Most rejections do not happen while reading, but while mapping to EN 16931: a wrong document-type value, a missing due date, negative unit prices on credit notes, payment details without a payment method.

Also:
Bestandsrechnung umwandeln · Word-Rechnung E-Rechnung · PDF zu ZUGFeRD
Reviewed on 2026-08-29.

Starting point: the invoice already exists, just in the wrong format

Many companies have written their invoices in Word or Excel for years and send them as PDF. The obligation to send electronic invoices does not change what is on the invoice — only the form in which the buyer accepts it. The obvious route is therefore not to replace the invoicing process but to put a pipeline behind it: read the PDF, rebuild it as [[en-16931|EN 16931]] data, check it, generate [[zugferd|ZUGFeRD]] or [[xrechnung|XRechnung]].

This works because PDFs from templates have a text layer and are uniform. Regular expressions read document number, date, line items and totals reliably — no language model, no cost per document, no external service. The real work is elsewhere: mapping what was read onto the standard. This guide describes where that fails in practice.

What is not on the PDF

Before the first e-invoice exists, four pieces of information are missing that a classic invoice never had to carry:

InformationWhere it comes fromRule
Seller's bank accountMaster data per departmentBR-DE-1 requires payment details (BG-16)
Contact person with phone and e-mailMaster dataBR-DE-2, plus BR-DE-6 for XRechnung
Buyer's e-mail addressCustomer tableno sending without it
Leitweg-ID / buyer referenceCustomer tableBT-10, BR-DE-15 — for Germany in both formats

🔴 The customer table is not optional. Neither the e-mail address nor the [[leitweg-id|Leitweg-ID]] is printed on an incoming document — neither was ever needed. A buyer without an entry must stop the document, not wave it through: better a note back to the department than an undeliverable e-invoice.

The seven mapping errors

These errors do not show up while reading the PDF and not in your own schema check. They show up only when the data runs against the real API.

1. The document type is credit_note, not creditnote

An enum value with one letter off is a schema error, not a business-rule violation. The valid values are invoice, credit_note, proforma, correction.

2. The due date is mandatory

If the document carries no due date, the issue date is the right fallback — not leaving it out. A missing dueDate is rejected.

3. The country of the country rules has to be named

German extra fields such as the buyer reference live in their own group, and that group needs the country code: countrySpecific.countryCode: "DE". Without it the group is silently dropped — and the Leitweg-ID with it.

4. There is no paymentMeans

The bank account belongs to the seller (seller.bankAccount), not to a separate payment-means object. Anyone coming from another implementation of the standard looks for the field in the wrong place.

5. BR-DE-2: the contact person is missing

It is usually printed on the letterhead. It is missing from the data because it was never a field on any invoice form. Name plus phone or e-mail — as seller.contact.

6. BR-DE-1: a bank account alone is not enough

Payment details additionally need a payment method: paymentMethods: [{ type: "bank_transfer" }]. IBAN and BIC without a method are incomplete. And a payment-terms element without text violates PEPPOL-EN16931-R008 — paymentTerms.description must not be empty.

7. BR-27: negative unit prices are rejected

On the PDF the credit note shows minus amounts. In EN 16931 the document type carries the sign: type: "credit_note", all amounts positive, quantity and unit price positive. Passing the numbers through from the PDF produces a document that looks structurally clean and fails BR-27.

Recompute before the API does

The line items of a legacy invoice do not always add up to the stated net amount — rounding, attachments, hand-corrected totals. A pipeline that does not recompute forwards the error and gets it back as BR-CO-10. Three checks with a tolerance of two cents are enough: items against net, net plus tax against gross, tax amount against tax rate.

A fourth comparison costs nothing and catches a different mistake: if the document carries a different VAT ID than the department that sent it, the mail went to the wrong mailbox.

Validation means two things

The [[validierung|validation]] returns two answers, and they are not the same:

  • valid answers the object-level rules — mandatory fields, totals, identifier formats.
  • results[] holds, per target format, the generated XML checked against XSD and [[schematron|Schematron]] — the rules a recipient actually rejects on.

An approval should require both. Anyone checking valid alone sees format violations only at generation time — or not at all.

Two documents in one file

From practice: one PDF file, two invoices, one per page. A pipeline that assumes "one PDF = one invoice" processes the first and loses the second without an error. So read page by page and run every page through the parser on its own; pages without a document — attachments, terms — yield nothing and do no harm. A parser that cannot find a field returns null and does not guess: what is missing surfaces in the downstream check, by name.

What does not pass goes back

If a document fails, nothing may reach the buyer. Instead: a line in the log, the source PDF unchanged in the archive, and a mail back to the department that sent it — with file name, document number and every reason in plain language. Correct, resend. The buyer never notices.

One detail: the hash the API returns with the generated document is the hash of the output bytes. A PDF carries a creation timestamp, so this value changes on every call — it is useless as a duplicate marker in the file name. A fingerprint of your own over the invoice data with a stable key order is not: the same document received twice yields the same file name, the same document with changed content a different one.

Ready-made pipeline

The setup described here — one mailbox per department, rule-based reading, master data plus customer table, three validation stages, an error path back to the sender — is available as an n8n workflow template: PDF invoices from mailbox → ZUGFeRD / XRechnung. The result is a [[pdf-a-3|PDF/A-3]] with embedded factur-x.xml (EN 16931 profile) or an XRechnung — whichever the buyer accepts.

Frequently asked questions