Developer's Guide to XRechnung: EN 16931 Compliance via API
A hands-on guide to generating and validating XRechnung invoices using the Postscale API, with code examples and common pitfalls.
XRechnung is Germany's implementation of the EN 16931 European e-invoicing standard. This guide walks through the practical steps of generating and validating XRechnung invoices with the Postscale API.
Understanding XRechnung
XRechnung is a Core Invoice Usage Specification (CIUS) — it takes the broad EN 16931 standard and adds Germany-specific constraints. Think of EN 16931 as the interface and XRechnung as the implementation.
Key differences from a plain EN 16931 invoice:
- Seller contact is mandatory — name, phone, and email (BR-DE-02 through BR-DE-04)
- Buyer reference required — Leitweg-ID for government invoices (BR-DE-15)
- Payment terms in text — human-readable payment conditions (BR-DE-18)
- Restricted type codes — only 380, 381, 384, 389 are allowed (BR-DE-17)
Validation First
Before generating invoices, validate your existing ones:
curl -X POST https://api.postscale.io/v1/invoices/validate \
-H "Authorization: Bearer ps_live_..." \
-H "Content-Type: application/xml" \
-d @your-invoice.xml
The response tells you exactly what's wrong:
{
"valid": false,
"format": "xrechnung-ubl",
"errors": [
{
"rule": "BR-DE-02",
"path": "/Invoice/AccountingSupplierParty/Party/Contact/Name",
"message": "The element 'Seller contact point' (BT-41) is required"
}
]
}
Each error includes the XRechnung rule ID, the XPath where the violation occurs, and a description referencing the EN 16931 business term ID (BT-*). This makes it straightforward to map errors back to your data model.
Generating Invoices from JSON
Rather than constructing UBL XML by hand, pass structured JSON to the send endpoint:
curl -X POST https://api.postscale.io/v1/invoices/send \
-H "Authorization: Bearer ps_live_..." \
-H "Content-Type: application/json" \
-d '{
"from": "billing@yourdomain.com",
"to": "invoices@customer.de",
"format": "xrechnung-ubl",
"invoice": {
"number": "2026-0089",
"issue_date": "2026-03-18",
"due_date": "2026-04-17",
"currency": "EUR",
"seller": {
"name": "Your Company GmbH",
"street": "Hauptstr. 42",
"city": "Munich",
"postal_code": "80331",
"country": "DE",
"tax_id": "DE987654321",
"contact_name": "Billing Team",
"contact_email": "billing@yourdomain.com",
"contact_phone": "+49 89 1234567"
},
"buyer": {
"name": "Customer AG",
"street": "Berliner Str. 10",
"city": "Berlin",
"postal_code": "10115",
"country": "DE"
},
"payment": {
"means_code": "58",
"iban": "DE89370400440532013000",
"terms": "Net 30 days"
},
"line_items": [
{
"description": "Consulting — March 2026",
"quantity": 40,
"unit": "HUR",
"unit_price": "120.00",
"tax_category": "S",
"tax_rate": "19.00"
}
]
}
}'
The API generates valid UBL 2.1 XML, validates it, and sends it as an email with the XML attached.
Common Pitfalls
Missing seller contact — XRechnung requires all three: contact name (BT-41), phone (BT-42), and email (BT-43). EN 16931 makes these optional, so invoices valid under the base standard may fail XRechnung validation.
Wrong payment means code — Code 58 (SEPA credit transfer) is the most common for German B2B. Don't use 1 (not defined) or ZZZ (mutually agreed).
Tax rounding — Calculate tax per line item, then sum. Don't calculate tax on the total net amount. XRechnung expects line-level tax calculation matching the invoice total.
Date format — Always use ISO 8601 (YYYY-MM-DD). The API accepts this format in JSON and produces the correct XML date representations.
Testing
Use the validation endpoint to test your invoices before going live. Common test scenarios:
- Standard B2B invoice with 19% VAT
- Mixed VAT rates (19% and 7%)
- Credit note (type code 381)
- B2G invoice with Leitweg-ID
- Reverse charge / intra-community supply
The validation endpoint is free and doesn't generate or send anything — use it as part of your CI/CD pipeline or pre-send checks.