Create ZUGFeRD Invoice: Step-by-Step Guide

Table of Contents
You send a PDF invoice. Your customer downloads it. Someone on their team types every line item into their accounting system manually. A digit gets transposed. The payment gets delayed.
A ZUGFeRD invoice solves this. It bundles a human-readable PDF with machine-readable XML into a single file. Your customer sees the invoice exactly as before. Their system extracts every data point automatically, with no manual entry and no transcription errors.
This guide walks you through how to create a ZUGFeRD invoice from scratch — profile selection through validation — in five concrete steps.
Key Takeaways:
- A ZUGFeRD invoice = PDF/A-3 container + embedded CII XML attachment.
- Six profiles exist — "EN 16931" covers the German e-invoicing mandate for most B2B cases.
- Creation paths: manual (open source), accounting software, or API.
- Validation is non-negotiable — invalid files get rejected and delay payments.
- The Complyance API handles the entire pipeline in a single API call.
Prerequisites: What You Need Before You Start
1. Pick the right profile
ZUGFeRD offers six profiles with varying levels of detail. For German e-invoicing compliance, you need at least the "EN 16931" profile.
2. Choose your creation method
You can generate ZUGFeRD invoices manually with open-source libraries, through accounting software with built-in export, or via a dedicated API.
3. Prepare your invoice data per EN 16931
The European standard EN 16931 defines every mandatory field a compliant e-invoice must contain.
Pro Tip: Map your existing invoice template fields to EN 16931 requirements before touching any code.
🖼️ Visual placeholder: Numbered steps infographic (1–5): Choose EN 16931 profile → Prepare invoice data per EN 16931 → Generate CII XML → Embed XML into PDF/A-3 as factur-x.xml → Validate (schema + Schematron + PDF/A-3).
How to Create a ZUGFeRD Invoice in 5 Steps
Step 1: Choose the Right ZUGFeRD Profile
| Profile | What It Includes | Best For |
|---|---|---|
| Minimum | Basic header data only | Internal archival, supplier-side only |
| Basic WL | Extended header data without line items | Internal archival |
| Basic | Full line-item detail | Standard B2B invoices |
| EN 16931 | Complete EN 16931 implementation | Recommended for German e-invoicing mandate |
| Extended | Additional fields beyond the standard | Industry-specific requirements |
| XRechnung | XRechnung-profile XML embedded in PDF/A-3 | B2G invoicing with human-readable layer |
Important: MINIMUM and BASIC WL are for internal use only. They do not satisfy the German e-invoicing mandate.
The bottom line: For German B2B compliance, choose EN 16931.
Step 2: Prepare Your Invoice Data (Mandatory Fields per EN 16931)
Invoice Header:
- Invoice number (unique, sequential)
- Invoice issue date
- Payment terms and due date
- Currency code (e.g., EUR)
- Invoice type code (e.g., 380 for commercial invoice)
Seller Information:
- Seller name and postal address
- VAT identification number
- Tax registration number or commercial register entry
Buyer Information:
- Buyer name and postal address
- Leitweg-ID (mandatory for German public sector recipients)
Line Items (per invoice line):
- Line item number
- Item description
- Quantity and unit of measure
- Net unit price
- Tax category and tax rate
- Line item net amount
Document Totals:
- Total net amount
- Total tax amount
- Total gross amount (amount due)
Step 3: Generate the CII XML
<?xml version="1.0" encoding="UTF-8"?>
<rsm:CrossIndustryInvoice
xmlns:rsm="urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100"
xmlns:ram="urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100"
xmlns:udt="urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100">
<rsm:ExchangedDocumentContext>
<ram:GuidelineSpecifiedDocumentContextParameter>
<ram:ID>urn:cen.eu:en16931:2017#compliant#urn:zugferd.de:2p1:en16931</ram:ID>
</ram:GuidelineSpecifiedDocumentContextParameter>
</rsm:ExchangedDocumentContext>
<rsm:ExchangedDocument>
<ram:ID>INV-2026-001234</ram:ID>
<ram:TypeCode>380</ram:TypeCode>
<ram:IssueDateTime>
<udt:DateTimeString format="102">20260421</udt:DateTimeString>
</ram:IssueDateTime>
</rsm:ExchangedDocument>
<rsm:SupplyChainTradeTransaction>
<!-- Seller, Buyer, Line Items, Totals -->
</rsm:SupplyChainTradeTransaction>
</rsm:CrossIndustryInvoice>Critical detail: The GuidelineSpecifiedDocumentContextParameter must exactly match your chosen profile.
Step 4: Embed the XML Into a PDF/A-3 Container
Three things must happen:
- Create or convert to PDF/A-3. Standard PDF or PDF/A-1 and PDF/A-2 will not work.
- Embed the XML with the correct filename. The CII XML must be attached as
factur-x.xml. - Set XMP metadata. The PDF metadata must declare the ZUGFeRD version and profile.
Pro Tip: PDF/A-3 compliance fails most often because of fonts. Every font used in the PDF must be fully embedded.
Step 5: Validate the Output
| Validation Layer | What Gets Checked | Common Failures |
|---|---|---|
| Schema validation | XML structure against the CII schema | Missing elements, wrong element order |
| Schematron rules | Business rules per EN 16931 | Tax calculation errors |
| PDF/A-3 compliance | Archival standard of the PDF container | Fonts not embedded |
Recommended validation tools:
- Complyance Validator
- KoSIT Validator (open-source)
- ZUGFeRD Community Validator at ferd-net.de
Non-negotiable: Never send a ZUGFeRD invoice without validating it first.
Method Comparison: Manual vs. Software vs. API
| Criteria | Manual (Open Source) | Accounting Software | API (Complyance) |
|---|---|---|---|
| Setup effort | High — requires XML expertise | Medium — one-time configuration | Medium — developer integration |
| Ongoing effort | High | Low | Very low |
| Scalability | Not scalable | Limited | Unlimited |
| Error rate | High | Low | Very low |
| Cost | Free | From \~10 EUR/month | Usage-based |
| Best for | Solo developers, testing | SMBs with 10-500 invoices/month | Businesses with 100+ invoices/month |
Code Example: Create a ZUGFeRD Invoice With the Complyance API
import requests
API_KEY = "your_api_key"
BASE_URL = "https://api.complyance.io/v1"
invoice_data = {
"profile": "EN16931",
"invoice_number": "INV-2026-001234",
"invoice_date": "2026-04-21",
"currency": "EUR",
"seller": {
"name": "Muster GmbH",
"address": {"street": "Musterstrasse 1", "city": "Berlin", "postal_code": "10115", "country": "DE"},
"vat_id": "DE123456789"
},
"buyer": {
"name": "Example Corp",
"address": {"street": "Example Road 42", "city": "Munich", "postal_code": "80331", "country": "DE"},
"vat_id": "DE987654321"
},
"line_items": [
{"description": "IT Consulting - April 2026", "quantity": 40, "unit": "HUR", "unit_price": 150.00, "tax_rate": 19.0, "tax_category": "S"}
],
"payment": {"means_code": "58", "iban": "DE89370400440532013000", "due_date": "2026-05-21"}
}
response = requests.post(
f"{BASE_URL}/zugferd/create",
json=invoice_data,
headers={"Authorization": f"Bearer {API_KEY}"}
)
if response.status_code == 200:
with open("invoice_INV-2026-001234.pdf", "wb") as f:
f.write(response.content)
print("ZUGFeRD invoice created successfully.")Tools for ZUGFeRD Invoice Creation
Online platforms and APIs:
- Complyance.io — API-first, built-in validation, all six profiles
- ZUGFeRD community tools at ferd-net.de
Accounting software with ZUGFeRD export:
- lexoffice, sevdesk, DATEV, SAP (select modules)
Open-source libraries:
- Mustang (Java), Factur-X (Python), ZUGFeRD-csharp (.NET)
Common Errors: Do's and Don'ts
| Do | Don't |
|---|---|
| Use PDF/A-3 as the container format | Use PDF/A-1, PDF/A-2, or standard PDF |
| Name the XML attachment factur-x.xml | Use arbitrary filenames |
| Fill every mandatory field per EN 16931 | Confuse optional fields with required ones |
| Calculate tax amounts programmatically | Round taxes manually, then hard-code them |
| Validate before sending | Assume it will "probably work" |
| Set the profile identifier to match your actual profile | Copy profile IDs from older ZUGFeRD versions |
| Fully embed all fonts in the PDF | Reference system fonts |
Pro Tip: The single most frequent production error? Rounding discrepancies in tax calculations. Calculate each line item net amount first, sum those amounts, then calculate tax on the sum.
Next Steps
Creating a ZUGFeRD invoice is a structured, repeatable process. Choose your profile. Prepare your data. Generate the XML. Embed it in PDF/A-3. Validate. Done.
The real leverage comes from automation. If you send more than a handful of invoices per month, an API-based approach eliminates manual steps and scales without friction.
Related posts
Frequently Asked Questions
Five steps: choose the right profile, prepare your invoice data per EN 16931, generate the CII XML, embed the XML into a PDF/A-3 container, and validate the output.
The ZUGFeRD standard itself is open and free. Free options include Mustang (Java) and Factur-X (Python).
For occasional invoices, a free online tool works. For regular invoicing, accounting software with ZUGFeRD export (lexoffice, sevdesk, DATEV). For automated, high-volume processing, an API like Complyance.
Both comply with EN 16931. ZUGFeRD is hybrid — PDF plus embedded XML. XRechnung is pure XML. XRechnung is mandatory for German public sector.
For the German e-invoicing mandate, you need at least the EN 16931 profile.
Technically yes, but it is not straightforward. Generating ZUGFeRD invoices directly from your source system is faster, cheaper, and more reliable.
Yes. Without validation, you risk sending files that get rejected.
Use ZUGFeRD 2.3.3 or later.





















