What it is
XPath is the W3C language for navigating XML. A path expression like /Invoice/cac:AccountingSupplierParty/cac:Party/cbc:EndpointID selects a node (or a set of nodes) inside a document — in this case the seller's electronic address inside a UBL invoice. XPath is to XML what a file path is to a filesystem, but with the added ability to filter by attribute, predicate, and namespace.
Three versions matter in practice:
substring() and contains().for/if/let expressions.Why it matters in e-invoicing
Every layer of an EN 16931 validation pipeline speaks XPath:
BR-CO-15 — "Invoice total amount with VAT (BT-112) = Invoice total amount without VAT (BT-109) + Invoice total VAT amount (BT-110)" — is expressed as an XPath expression evaluated against the invoice tree.An ERP vendor implementing EN 16931 will read XPath expressions every working day during the integration phase, and again every time the validation artefacts are updated (typically twice a year).
XPath in a Schematron rule
A typical EN 16931 Schematron rule looks like:
test="(round(cbc:LineExtensionAmount * 100) =
round(sum(../cac:InvoiceLine/cbc:LineExtensionAmount) * 100))">
[BR-CO-13] Invoice total amount without VAT (BT-106)
must equal the sum of Invoice line net amounts (BT-131).
The context is an XPath that selects which node the rule runs against. The test is an XPath that must evaluate to true() — if it does not, the rule fires and the engine produces an error.
Note the multiplication by 100 and the round(). Decimal arithmetic in XPath 1.0 is famously fragile; the EN 16931 rules normalise to integer cents to avoid floating-point drift between implementations. This single workaround is the source of more developer confusion than almost any other detail in the standard.
Namespaces — the trap that catches everyone
UBL invoices use two prefixes by convention: cbc: for urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2 (single-element values like amounts and identifiers) and cac: for urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2 (groups). CII uses ram: and rsm: for its own namespaces. XPath is namespace-aware, which means:
Invoice/AccountingSupplierParty/Party/EndpointID — wrong; matches nothing.//*[local-name()='EndpointID'] — works but is sloppy and slow./cbc:Invoice/cac:AccountingSupplierParty/cac:Party/cbc:EndpointID — correct, provided the prefixes are bound to the right namespace URIs in the evaluation context.Almost every "I wrote the XPath and it returns nothing" bug is a namespace binding issue, not a path issue.
XPath toolchain choices
ERP vendors typically reach for one of three execution engines:
lxml), and Go. Most are XPath 1.0; check before committing.A common architecture is to pre-compile the EN 16931 Schematron into XSLT once (Saxon does this with iso_svrl_for_xslt2.xsl) and then run the resulting XSLT against each invoice. This makes validation cheap per invoice — typically <10ms — and means production runtimes only need an XSLT 2.0 processor, not a full Schematron implementation.
Relation to EN 16931
EN 16931 itself is syntax-agnostic. The semantic standard (the BT- and BG- business terms) does not care whether you express an invoice in UBL or CII. But every conformance check that exists — the official Schematron, the country CIUS Schematrons, the Peppol BIS Schematrons — is written in XPath. The semantic standard travels through an XPath-shaped door.
For an ERP vendor, this means XPath fluency is not optional. The team that owns the e-invoicing module needs at least one engineer who can read an XPath expression, locate the failing element in the document, and explain to product why the rule fired. Without that, every validation error becomes a ticket to an external consultancy.