Skip to content

Create Factur-X and ZUGFeRD e-invoices

A Factur-X or ZUGFeRD invoice is one file for two readers: a PDF people read, and the same invoice as XML inside it, which accounting software reads instead of the page. Typefold builds the XML from the data you already render the invoice with, checks it against the official rules and embeds it in a PDF/A-3 file.

  • One format, two names. ZUGFeRD is the German name and Factur-X the French one. Since ZUGFeRD 2.1 (Factur-X 1.0) they are technically identical.
  • The XML is the invoice in the UN/CEFACT Cross Industry Invoice (CII) syntax, following the European standard EN 16931. It travels as the file factur-x.xml.
  • The PDF is a PDF/A-3 file, the archive format that allows embedded files, with an XMP description that names the XML and its profile.
  • Profiles set how much the XML holds, from MINIMUM to EXTENDED. Only BASIC, EN 16931 and EXTENDED are full invoices; see profiles.

These rules change, and this page is not legal or tax advice. Check the current rules with your tax adviser or the sources linked here.

  • Germany: businesses must be able to receive e-invoices for domestic B2B sales since 1 January 2025. Issuing them is phased in: from 1 January 2027 for businesses whose turnover in the previous year was more than EUR 800,000, and from 1 January 2028 for all others. An e-invoice must meet EN 16931: ZUGFeRD from version 2.0.1 qualifies, except the MINIMUM and BASIC WL profiles, and so does XRechnung. A plain PDF no longer counts as an e-invoice, and no special network is needed: e-mail works. See the Federal Ministry of Finance’s e-invoice FAQ and the IHK summary of its guidance.
  • France: all VAT-registered businesses must be able to receive e-invoices from 1 September 2026. Large and mid-sized businesses issue them from that date, small and micro businesses from 1 September 2027, and French invoices gain new required details such as the buyer’s SIREN number. Factur-X is one of the three formats every accredited platform (plateforme agréée) must accept, with UBL and CII. In France, invoices travel through such a platform: Typefold makes the Factur-X file, and your platform sends it. To add the French details, see invoices for France. See Service Public Entreprendre and FNFE-MPE.
  • German public authorities often ask for XRechnung, an XML file without a PDF. Typefold renders PDFs, so it does not produce plain XRechnung files.

The quickest way to a working e-invoice is the Invoice starter: it already embeds an EN 16931 invoice built from its sample data.

  1. In Studio, open Editor in the sidebar and choose Add template. Enter a Template name, set Start with to Ready-made starter, pick Invoice and click Create template.
  2. Change the page and the sample data to fit your invoices, as in get started with the visual editor.
  3. Click the Factur-X tab in the side panel and check that each field points at your data. The Problems tab lists any rule the sample invoice breaks.
  4. Click Publish, then render the template with your data like any other, for example with the Reports API.

Every render now returns a PDF/A-3b file with factur-x.xml inside. To add Factur-X to a template you already have, follow the next section.

  1. Open the invoice template in the visual editor and click the Factur-X tab in its side panel.
  2. Turn on Embed a Factur-X invoice and pick a profile. EN 16931 suits most invoices.
  3. Map each field to your data, such as {{invoice.number}} for the invoice number. The panel suggests the paths the template already shows. Text around a placeholder is kept, and a field without a placeholder is a fixed value, such as EUR.
  4. Under Lines, enter the path of the invoice’s items, such as invoice.items, and map each line’s fields from an item: {{name}}, {{quantity}}, {{price}}.
  5. Click Save, then publish the template.

Studio writes the mapping into page.json. In the code editor, write it there yourself:

{
"pdfa": "3b",
"facturX": {
"profile": "EN 16931",
"fields": {
"invoiceNumber": "{{invoice.number}}",
"issueDate": "{{invoice.date}}",
"currency": "EUR",
"sellerName": "Example Ltd",
"sellerVatId": "DE123456789",
"sellerCountry": "DE",
"buyerName": "{{invoice.customer.name}}",
"buyerCountry": "{{invoice.customer.country}}"
},
"lines": {
"source": "invoice.items",
"fields": {
"name": "{{name}}",
"quantity": "{{quantity}}",
"netPrice": "{{price}}",
"vatRate": "{{vat}}"
}
}
}
}

The API reference lists every field with its EN 16931 business term (BT-1 and so on). Renders then need nothing more: each one builds the XML from its data, checks it and embeds it with its XMP description.

  • Dates take 2026-10-22, a date and time, or 20261022. Numbers take 19.5 or "19.5".
  • Fields left empty are left out. The type code is 380 (invoice) unless you map it; map 381 for a credit note. Lines have VAT category S, quantity 1 and unit C62 (one piece) unless you map them.
  • Totals and the VAT breakdown are added up from the lines. Map a total to keep your own figure: the check then tells you if it disagrees with the lines.
  • An invoice with lines (BASIC and up) can have up to 500 of them.
  • Without a PDF/A level, the template writes PDF/A-3b. Levels other than 3b, 3u and 3a fail.

French invoices sent through an accredited platform carry details other invoices leave out. Map them in page.json beside the other fields:

{
"facturX": {
"fields": {
"businessProcess": "S1",
"sellerLegalId": "{{seller.siren}}",
"sellerLegalIdScheme": "0002",
"buyerLegalId": "{{buyer.siren}}",
"buyerLegalIdScheme": "0002",
"latePaymentNote": "Late payment penalty: three times the legal interest rate.",
"recoveryFeeNote": "Fixed recovery fee for late payment: EUR 40.",
"discountNote": "No discount for early payment."
}
}
}
Field What it holds
businessProcess The billing framework: B1 for goods, S1 for services, M1 for both.
sellerLegalId, buyerLegalId The SIREN numbers, with sellerLegalIdScheme and buyerLegalIdScheme 0002.
deliverToName, deliverToStreet, deliverToCity and the other deliverTo fields Where goods go, when that is not the buyer’s address.
vatPointDateCode 5 when the seller pays VAT on debits.
latePaymentNote, recoveryFeeNote, discountNote The payment notes, written with subject codes PMD, PMT and AAB.

Use the EN 16931 or EXTENDED profile. Each render checks the EN 16931 rules, not the French platform rules, so your platform may still reject an invoice they do not cover; its rules are in the AFNOR XP Z12-012 specification.

The profiles, from smallest to largest:

Profile What the XML holds
MINIMUM Header and totals. Not a full invoice under EN 16931.
BASIC WL Header, totals and VAT breakdown, without lines. Not a full invoice.
BASIC A full invoice with lines, limited to the core fields.
EN 16931 The European standard invoice (default).
EXTENDED EN 16931 with room for more detail.

Use EN 16931 unless your buyer asks for another. In Germany, MINIMUM and BASIC WL do not count as e-invoices.

Every render checks its XML against the profile’s official rules (the EN 16931 rules from CEN, or the Factur-X rules for MINIMUM and BASIC WL) on the server that renders it, without sending it anywhere. A render whose XML breaks a rule fails, and the error lists each rule, such as [BR-06]-An Invoice shall contain the Seller name (BT-27). A value the XML cannot take, such as a date it cannot read, fails the render with 400 BAD_REQUEST. The visual editor lists both under Problems, on the block that shows the value.

To build the XML yourself, leave the Factur-X setting off. Set the invoice template to PDF/A-3b, save it and publish it. Then send the XML file factur-x.xml with each render as an attachment. The examples render one template version with the Renders API:

This example uses jq to put the file into the JSON body:

Terminal window
jq -n --rawfile xml factur-x.xml '{
templateVersionId: "0b7d5c1e-8f3a-4d2b-9e61-5a4c3b2a1f00",
data: { invoice: { number: "2026-0042", total: 1190.00 } },
waitMs: 10000,
attachments: [
{
name: "factur-x.xml",
content: $xml,
mimeType: "text/xml",
description: "Factur-X invoice",
relationship: "alternative"
}
]
}' | curl -X POST "$PDF_URL/v1/renders" \
-H "x-api-key: $API_KEY" \
-H "content-type: application/json" \
--data-binary @-
  • A render can embed up to 10 files, 5 MiB in total. Send text as content or bytes as contentBase64.
  • In a PDF/A file, every attachment needs mimeType and description. Only the levels 3b, 3u, 3a and 4f can embed files.

Factur-X also expects its XMP extension schema. Add it to the template’s page.json, with the Version and ConformanceLevel of your XML’s profile:

{
"metadata": {
"xmp": [
{
"name": "Factur-X PDF/A Extension Schema",
"prefix": "fx",
"namespace": "urn:factur-x:pdfa:CrossIndustryDocument:invoice:1p0#",
"properties": [
{ "name": "DocumentType", "value": "INVOICE", "description": "Document type" },
{
"name": "DocumentFileName",
"value": "factur-x.xml",
"description": "Attached XML file"
},
{ "name": "Version", "value": "1.0", "description": "Factur-X version" },
{ "name": "ConformanceLevel", "value": "EN 16931", "description": "Factur-X profile" }
]
}
]
}
}

Open the PDF in a reader that lists attachments to see factur-x.xml. To check a file the way your buyer’s software will, run it through a Factur-X validator such as Mustang, and a PDF/A checker such as veraPDF.