Turn XRechnung XML into ERP JSON
If your ERP receives XRechnung files, the hard part is not parsing XML. The hard part is validating the invoice, understanding what was actually checked, and returning a JSON payload your integration can trust.
Updated
Recommended Pipeline
XRechnung XML
|
v
/v1/validate -> EN 16931 compliance + validation completeness
|
v
/v1/serialize -> ERP-ready JSON + fallbacks_applied (Pro)
|
v
/v1/extract -> heuristic JSON fallback (Community)
Validate First
Start with /v1/validate so your application knows whether the invoice passed EN 16931 checks,
whether PDF/A validation ran, and whether the validation report is complete or partial.
curl -X POST "http://localhost:8000/v1/validate" \
-F "file=@invoice-xrechnung.xml"
{
"valid": true,
"format": "ubl",
"flavor": "xrechnung_3.0",
"pdfa_valid": null,
"validation_completeness": "full"
}
Normalize into ERP JSON
For production-grade ingestion, use /v1/serialize. It returns typed invoice data plus
fallbacks_applied and xml_recovery_applied, so your integration can decide what is acceptable.
curl -X POST "http://localhost:8000/v1/serialize" \
-F "file=@invoice-xrechnung.xml"
{
"success": true,
"invoice": {
"invoice_number": "R-2026-0042",
"format": "ubl",
"profile": "xrechnung_3.0"
},
"xml_recovery_applied": false,
"fallbacks_applied": []
}
Community Fallback
If you only need best-effort extraction, /v1/extract returns invoice JSON with
invoice_json._meta.limitations so your team can distinguish heuristic mapping from strict normalization.
{
"invoice_json": {
"_meta": {
"limitations": ["heuristic_mapping", "fallback_values_used"]
}
}
}
Extract vs Serialize — Which Should You Use?
Both endpoints return invoice JSON from an XRechnung file. The difference is in schema guarantees, field coverage, and what happens when the XML is ambiguous.
| /v1/extract | /v1/serialize Pro | |
|---|---|---|
| Availability | Community + Pro | Pro only |
| Mapping strategy | Heuristic best-effort | Strict, typed, versioned |
| Fallback transparency | _meta.limitations[] |
fallbacks_applied[] + xml_recovery_applied |
| Schema version | No versioning | Versioned JSON schema |
| Best for | Prototyping, low-risk ingestion | Production AP pipelines, DB insertion |
Python Integration Example
A complete validation-then-extract pipeline in Python — the pattern used in most ERP webhook handlers:
import requests
ENGINE = "http://localhost:8000"
def ingest_xrechnung(xml_path: str) -> dict:
with open(xml_path, "rb") as f:
data = f.read()
# Step 1: Validate against EN 16931 + XRechnung 3.0 rules
r = requests.post(f"{ENGINE}/v1/validate",
files={"file": ("invoice.xml", data, "application/xml")})
report = r.json()
if not report["valid"]:
errors = report.get("errors", [])
raise ValueError(f"EN 16931 violation: {errors[0]}" if errors else "Invalid")
# Step 2: Extract structured data
r = requests.post(f"{ENGINE}/v1/extract",
files={"file": ("invoice.xml", data, "application/xml")})
result = r.json()
invoice = result["invoice_json"]
limitations = invoice.get("_meta", {}).get("limitations", [])
if limitations:
print(f"Warning: heuristic fields used: {limitations}")
return invoice
Node.js Integration Example
Using native fetch (Node 18+) — no dependencies:
import { readFile } from 'node:fs/promises'
const ENGINE = 'http://localhost:8000'
async function ingestXRechnung(xmlPath) {
const data = await readFile(xmlPath)
const blob = new Blob([data], { type: 'application/xml' })
// Validate first
const form = new FormData()
form.append('file', blob, 'invoice.xml')
const vRes = await fetch(`${ENGINE}/v1/validate`, { method: 'POST', body: form })
const report = await vRes.json()
if (!report.valid) throw new Error(`Invalid: ${report.errors?.[0]}`)
// Extract JSON
const form2 = new FormData()
form2.append('file', blob, 'invoice.xml')
const eRes = await fetch(`${ENGINE}/v1/extract`, { method: 'POST', body: form2 })
const { invoice_json } = await eRes.json()
return invoice_json
}
Frequently Asked Questions
How do I convert XRechnung XML to JSON?
Send the XML file to POST /v1/extract.
The API returns structured JSON with all invoice fields. For production pipelines requiring strict
typing and fallback transparency, use POST /v1/serialize (Pro).
Both accept CII (Factur-X, ZUGFeRD) and UBL (XRechnung) formats — no pre-conversion needed.
Does Factur-X Engine validate XRechnung 3.0 Schematron rules?
Yes. /v1/validate runs the full
EN 16931 Schematron ruleset including XRechnung 3.0 extension rules via Saxon-HE. The response
includes flavor: "xrechnung_3.0", a list of BR-code violations,
and validation_completeness so you know whether all checks ran.
Can I run this without a Pro license?
Yes. /v1/validate and
/v1/extract are fully available in
community mode (no license required). /v1/serialize
requires a Pro license and returns 403 LICENSE_REQUIRED otherwise.
Start with extract and upgrade when you need guaranteed schema stability.
See Also
Start Normalizing XRechnung
docker run -d -p 8000:8000 facturxengine/facturx-engine:latest