Factur-X solves an old tension. Accounts payable wants a document a human can look at and approve. The booking system wants structured data it can post without retyping. Factur-X gives both, in one file.
A PDF that carries its own data
A Factur-X file is a PDF/A-3 document with a structured XML invoice attached as an embedded file. The PDF is the visual invoice. The XML is the same invoice as data, following the EN 16931 semantic model in the UN/CEFACT CII syntax. Your reader opens the PDF; your software pulls the attachment.
The format is maintained jointly by FNFE-MPE in France and FeRD in Germany, and the same artifacts ship under two names: Factur-X in France, ZUGFeRD 2.x in Germany. They are the same format with two regional labels, not two formats you have to support separately.
The attachment has a name, and it matters
The embedded XML is found by filename, not by guessing. Three names are in circulation, and a receiving system has to accept all of them:
factur-x.xmlfor Factur-X, and what Beliq writes when it assembles a Factur-X hybridzugferd-invoice.xmlfor ZUGFeRD 2.xZUGFeRD-invoice.xmlfor ZUGFeRD 1.x, including the capital letters
A PDF whose attachment is called anything else is, as far as a Factur-X reader is concerned, a PDF with no invoice data in it. If you are generating hybrids yourself, this is the single easiest thing to get wrong, because the file looks perfectly fine when a person opens it.
Which profile are you actually sending
“Factur-X” is not one data set. The format ships five profiles, and they differ in how much of the invoice the XML is required to carry. Picking one is a real decision, because a receiver that needs line items cannot book a MINIMUM document however valid it is.
| Profile | What the XML carries |
|---|---|
| MINIMUM | Header only: invoice number, dates, totals |
| BASIC WL | Header-level data, still without invoice lines |
| BASIC | Header data plus the essential invoice lines |
| EN 16931 | Full EN 16931 compliance, and the usual choice |
| EXTENDED | EN 16931 plus the EXTENDED-only fields |
The document announces its own profile in
GuidelineSpecifiedDocumentContextParameter/ram:ID, and that URN is what a
validator keys off:
minimum urn:factur-x.eu:1p0:minimum
basicwl urn:factur-x.eu:1p0:basicwl
basic urn:cen.eu:en16931:2017#compliant#urn:factur-x.eu:1p0:basic
en16931 urn:cen.eu:en16931:2017
extended urn:cen.eu:en16931:2017#conformant#urn:factur-x.eu:1p0:extendedTwo of those are worth a second look. The EN 16931 profile declares the bare
urn:cen.eu:en16931:2017, which is exactly what a plain EN 16931 CII document
carries, so nothing in the URN says “Factur-X” at all. And France’s reform adds
a sixth value on top of EXTENDED,
urn:cen.eu:en16931:2017#conformant#urn.cpro.gouv.fr:1p0:extended-ctc-fr,
which is the reference profile for the French B2B mandate.
What the embedded XML looks like
The attachment is a CII document. Even a trimmed view shows the shape: a context block naming the profile, a header, and a settlement block with the totals that have to reconcile.
<rsm:CrossIndustryInvoice>
<rsm:ExchangedDocumentContext>
<ram:GuidelineSpecifiedDocumentContextParameter>
<ram:ID>urn:cen.eu:en16931:2017</ram:ID>
</ram:GuidelineSpecifiedDocumentContextParameter>
</rsm:ExchangedDocumentContext>
<rsm:ExchangedDocument>
<ram:ID>INV-2026-00481</ram:ID>
<ram:IssueDateTime>
<udt:DateTimeString format="102">20260901</udt:DateTimeString>
</ram:IssueDateTime>
</rsm:ExchangedDocument>
<rsm:SupplyChainTradeTransaction>
<ram:ApplicableHeaderTradeSettlement>
<ram:InvoiceCurrencyCode>EUR</ram:InvoiceCurrencyCode>
</ram:ApplicableHeaderTradeSettlement>
</rsm:SupplyChainTradeTransaction>
</rsm:CrossIndustryInvoice>The nesting under ram:IssueDateTime is not decoration. BT-2 is a
format-qualified date: the value sits in udt:DateTimeString with
format="102", meaning YYYYMMDD. Writing the date straight into
ram:IssueDateTime is invalid against the CII schema, and it is a common first
attempt when hand-building a document.
Both halves have to agree
The risk with a hybrid file is drift: a total shown on the PDF that does not match the XML. Nothing in the container prevents it. The two halves are produced by different code paths in most systems, and only one of them is what your customer’s software will book.
So a Factur-X document has three distinct things that can be wrong, and they are checked in different places:
- Structure. Is the XML well formed and valid against the CII schema, and against the per-profile XSD subset where one applies.
- Semantics. Does it satisfy EN 16931 and the rules layered on top: the
Schematron arm the document’s own
CustomizationIDselects, and France’s CTC rules when that identifier is EXTENDED-CTC-FR, when BT-23 carries a French cadre de facturation code, or when the request setsfranceCtc. - The container. Is the file really PDF/A-3, with the attachment present under a name a reader will look for.
Send a hybrid PDF to /v1/validate and Beliq covers the first two. It finds the
embedded XML, refuses the document outright if there is none, and then validates
that XML against the rules of point 2, reporting the exact rule versions it
used. The third, the container, /v1/validate does not check: its answer is
about the embedded XML only.
The PDFs Beliq builds itself are checked. POST /v1/generate with
output: "pdf" for Factur-X or ZUGFeRD, and POST /v1/convert when it re-wraps
a PDF around converted XML, run veraPDF on the finished file against its
PDF/A-3b profile and return no file in which it reports a violation. A test-mode
PDF is watermarked after that check, and the watermarked file does not pass the
profile. A PDF that /v1/convert hands back as you sent it is not checked as a
PDF.
That is the argument for generating the hybrid in one place rather than stapling a PDF to an XML file late in the process. Drift between the two halves is not a validation finding anywhere. It is a dispute six weeks later, when a person reads one number and a machine booked another.
