Guide

Invoice with ZUGFeRD

Make a German invoice as a PDF/A-3b file that also carries the invoice as EN 16931 data, so the buyer's software can book it without typing.

On this page

Community

ZUGFeRD and Factur-X are one format with two names. The file is a PDF/A-3b PDF (the PDF standard for long-term storage that allows attached files). A person reads the page. Inside the PDF sits the file factur-x.xml. It holds the same invoice as data in the standard EN 16931. The buyer's software reads that file.

Since 1 January 2025, every German company must be able to receive such an invoice. A company with a turnover above 800,000 EUR in the year before must send one from 1 January 2027. All other companies must send one from 1 January 2028.

Start from the template

The template rechnung-zugferd is a full invoice on A4 for a window envelope, to DIN 5008 form B. Its folder holds five files:

File What it is
rechnung-zugferd.html The page. It links ../din5008/din5008.css, so keep the folder din5008/ next to it.
rechnung-zugferd.css The settings for form B.
rechnung-zugferd.schema.json The fields and what each one takes.
sample.json An invoice with 3 lines at 19 % and 7 % VAT (Umsatzsteuer, the sales tax). Start from it.
rechnung-zugferd.pdf The sample, rendered with --einvoice.

The table of lines uses data-brevier-uebertrag, so on a second page the running total (Übertrag) is printed at the break. See Tables.

Fill in the data

Put your invoice in a JSON file shaped like sample.json.

Field Meaning
number, issueDate, deliveryDate, currency Invoice number, date, date of the service (YYYY-MM-DD) and a currency code such as EUR. All four are required.
dueDate or paymentTerms The date payment is due, or a sentence about it. One of the two is required.
seller, buyer name and address. Only country is required in the address, as a code such as DE. The seller also gives vatId.
lines At least one. Each has position, name, quantity, unitCode, unitPrice, netAmount and vatRate.
vat One entry for each VAT rate: rate, taxableAmount, taxAmount.
totals lineNet, taxBasis, tax, gross, due.

unitCode is a UN/ECE code: HUR is hours, DAY is days, C62 is pieces. The XML always gets the code. The page prints unitLabel (for example Std.) and falls back to the code.

Write the PDF

brevier pdf rechnung-zugferd.html --data my-invoice.json --einvoice -o rechnung.pdf

Without --einvoice you get the same page as a plain PDF/A-3b file. All options are on the CLI reference.

What Brevier checks

Before it writes the file, Brevier checks your data against the schema. With --einvoice it also checks that your amounts add up. It does not calculate them for you.

Check Rule
A line netAmount is quantity times unitPrice, rounded to cents.
A VAT entry taxableAmount is the sum of the lines with that rate. taxAmount is that sum times the rate, rounded to cents.
The totals lineNet is the sum of the lines. taxBasis equals lineNet. tax is the sum of the VAT entries. gross is taxBasis plus tax. due equals gross.

If a rule fails, the command stops, writes no file and names the field:

Error: E-invoice amounts do not add up: BT-112 Invoice total with VAT: is 3610.54 but BT-109 plus BT-110 is 3610.53

Check the file

List the attached files. A good invoice has one, factur-x.xml:

pdfdetach -list rechnung.pdf

To validate the XML and the PDF/A part, use Mustang and veraPDF:

java -jar Mustang-CLI-2.26.0.jar --action validate --no-notices --source rechnung.pdf
verapdf --flavour 3b rechnung.pdf

Mustang ends with exit code 0 even for an invalid file. Read its report for status="invalid".

What it cannot do yet

  • Only the standard VAT category S with a rate above zero. Zero rate, tax exempt and reverse charge are not in the schema.
  • Only invoices (type 380). No credit notes.
  • No allowances, charges or prepayments.