Which format do you need?
An e-invoice carries the invoice as machine-readable data. The receiver can book it without typing anything. Three formats matter in Germany and France, and they share one data model, EN 16931.
| Format | Use it for | What you get |
|---|---|---|
| Factur-X (France) and ZUGFeRD (Germany) | Business customers who want a readable PDF and the data | A PDF/A-3 file with factur-x.xml inside |
| XRechnung, CII or UBL syntax | German public buyers and their portals | The XML file only |
| ZUGFeRD with the XRechnung profile | A readable copy of an XRechnung | A PDF/A-3 file with xrechnung.xml inside |
Factur-X and ZUGFeRD are the same standard under two names. Profiles set how much data the XML carries. Use en16931 unless your customer asks for another one. It is the default.
Send the invoice
Put the invoice in e_invoice.invoice. You do not need a template: the built-in invoice template renders it. You can still pass your own template_id. It then receives the same data, with every amount already computed.
curl https://api.castpdf.com/v1/pdf \
-H "Authorization: Bearer $CASTPDF_KEY" \
-H "Content-Type: application/json" \
-o RE-2026-0815.pdf \
-d '{
"e_invoice": {
"standard": "factur-x",
"profile": "en16931",
"invoice": {
"number": "RE-2026-0815",
"issue_date": "2026-10-10",
"currency": "EUR",
"seller": {
"name": "Lindqvist Software GmbH",
"vat_id": "DE123456789",
"address": { "line1": "Kastanienallee 12", "city": "Berlin", "postal_code": "10435", "country_code": "DE" }
},
"buyer": {
"name": "Brightwater Kaffee GmbH",
"address": { "city": "Hamburg", "postal_code": "20457", "country_code": "DE" }
},
"payment": { "iban": "DE89370400440532013000", "due_date": "2026-11-09" },
"lines": [
{ "name": "Software licence", "quantity": 1, "unit_price": "4800.00", "vat": { "category": "S", "rate": 19 } },
{ "name": "Printed handbook", "quantity": 3, "unit_price": "24.90", "vat": { "category": "S", "rate": 7 } }
]
}
}
}'Send amounts as strings like "24.90" if you want to avoid any doubt about decimals. Numbers work too. Unit codes follow UN/ECE Recommendation 20, for example C62 for a piece and HUR for an hour.
How the totals are computed
- Each line is quantity times net price, rounded to cents. Line discounts and charges come off or on after that.
- Document discounts and charges apply to the sum of the lines.
- VAT is computed once per category and rate on the taxable amount, then rounded to cents.
- The amount due is the total minus any prepaid amount, plus any rounding amount.
Many systems round VAT per line. That can differ by a cent from the EN 16931 result. If you send your own totals in invoice.totals, we compare each one. A difference is a 400 error that names the field, our value and the reason.
XRechnung for public buyers
Use xrechnung-cii or xrechnung-ubl. Ask the buyer which syntax their portal takes. The response is the XML file, not a PDF. XRechnung needs more data than a normal invoice:
buyer_reference: the Leitweg-ID the public buyer gave you.- A seller contact with name, phone and email.
- Electronic addresses for seller and buyer. For a public buyer this is usually the Leitweg-ID with scheme
0204. - Payment details, for example an IBAN for a bank transfer.
If something is missing, the API answers with 400 invalid_e_invoice and lists every problem with its path. Fix the fields and send the request again. Nothing is counted for a rejected request.
Credit notes, reverse charge and exempt sales
Most invoices use the standard VAT category S with a rate. A few common cases need one more field.
| Case | What to send |
|---|---|
| Credit note | type_code: "381" and preceding_invoice with the number of the original invoice |
| Reverse charge in the EU | VAT category AE on the lines and the buyer's vat_id |
| Goods to another EU country | Category K, the buyer's vat_id, a delivery date and the delivery country |
| Exempt service | Category E and a reason in tax_exemptions.E |
| Export outside the EU | Category G |
For reverse charge, intra-EU supplies and exports we add the standard exemption text and code for you. For exempt services you name the legal reason yourself, because it differs from case to case.
Reading the error response
A rejected invoice comes back with status 400 and the code invalid_e_invoice. The field details.issues holds one entry per problem. Each entry has a path and a message.
{
"error": {
"code": "invalid_e_invoice",
"message": "The e-invoice has 2 problems. The first: e_invoice.invoice.buyer_reference: is required for XRechnung: the buyer reference, which for German public buyers is the Leitweg-ID (for example 04011000-12345-67).",
"details": {
"issues": [
{ "path": "e_invoice.invoice.buyer_reference", "message": "is required for XRechnung: the buyer reference, which for German public buyers is the Leitweg-ID (for example 04011000-12345-67)." },
{ "path": "e_invoice.invoice.totals.tax_total", "message": "is 917.24, but it should be 917.23 (the VAT of each rate rounded to cents (19% of 4800.00 = 912.00; 7% of 74.70 = 5.23)). EN 16931 rounds each line to cents and the VAT once per rate, not per line." }
]
}
}
}Show the messages to the person who keeps the invoice data, or map the paths back to your own form fields. Each message says what is wrong and what a valid value looks like.
What we check, and what you should check
Before anything is rendered we validate the invoice and compute the totals. Our tests run the generated XML through the official XSD schemas and Schematron rules: EN 16931, the Factur-X profiles and XRechnung. The PDF is converted to PDF/A-3b with the XML attached as an associated file.
We also ran the two reference tools by hand on 10 October 2026. The PDF/A checker veraPDF 1.30.3 found all 16 sample PDFs compliant with PDF/A-3b. The KoSIT validator 1.6.3 with the XRechnung 3.0.2 configuration accepted all 14 EN 16931 and XRechnung files. No authority has reviewed CastPDF itself, and your receiver may still have rules of its own.
Already have an e-invoice from another system? Our free e-invoice validator reads the XML, or the XML inside a PDF, and checks the fields and the EN 16931 totals. It says plainly what it does not check.
Send a test invoice to your customer or their portal before you go live. Test keys add a watermark, so use a live key for that final check.