Validate and parse e-invoices — XRechnung, ZUGFeRD/Factur-X and EN 16931 UBL/CII — through two endpoints. Files are processed in memory and never stored. Also available as a self-hosted container, so invoice data never leaves your infrastructure.
You received it by e-mail during the private beta.
export BILLHORSE_API_KEY="bh_live_…"
Use our example file (valid XRechnung 3.0) or your own XML/PDF:
# Download the sample, then validate it
curl -sO https://billhorse.com/docs/examples/xrechnung-valid.xml
curl -s -X POST https://api.billhorse.com/v1/validate \
-H "Authorization: Bearer $BILLHORSE_API_KEY" \
--data-binary @xrechnung-valid.xml
Response (abridged):
{
"valid": true,
"profile": { "label": "XRechnung 3.0", "en16931": true, "xrechnung": true },
"meta": { "number": "RE-2026-0815", "grandTotal": "178.50", … },
"findings": [],
"counts": { "error": 0, "warning": 0, "info": 0 }
}
The broken sample triggers eight findings. Every finding carries a rule id — each one has a human explanation page:
{
"valid": false,
"findings": [
{ "id": "BR-DE-15", "severity": "error",
"message": "XRechnung: buyer reference (BT-10, …) is mandatory." },
{ "id": "BR-CO-16", "severity": "error",
"expected": "178.50", "actual": "170.00", … }
]
}
→ billhorse.com/en/rules/br-de-15 explains the rule and the fix, in EN, DE and FR. Messages come in English by default — add ?lang=de or ?lang=fr to show them to your end users in their language.
/v1/parse extracts the EN 16931 semantic model — the same JSON shape whether the input was UBL, CII or a hybrid PDF:
curl -s -X POST https://api.billhorse.com/v1/parse \
-H "Authorization: Bearer $BILLHORSE_API_KEY" \
--data-binary @rechnung.pdf
{
"invoice": {
"number": "RE-2026-0815", "docType": "invoice", "currency": "EUR",
"seller": { "name": "…", "vatId": "DE123456789", "country": "DE" },
"totals": { "netTotal": "150", "taxTotal": "28.5", "due": "178.5" },
"lines": [ … ], "vat": [ … ]
},
"report": { "valid": true, … }
}
| Topic | Details |
|---|---|
| Authentication | Bearer API key. Errors come as RFC 9457 application/problem+json. |
| Body | Raw bytes (XML or PDF), max 10 MB. No multipart, no base64. |
| Rate limit | 120 requests/minute per key. HTTP 429 with Retry-After. |
| Languages | Finding messages: English by default, ?lang=de / ?lang=fr on /validate and /parse. Rule ids and expected/actual/found are language-neutral. |
| Data privacy | Requests are processed in memory only — invoices are never stored or logged. |
| Self-hosted | The identical container runs in your infrastructure (BILLHORSE_NO_AUTH=1 behind your gateway). Ask us. |
| Formats | XRechnung 1.2–3.0 (UBL & CII), ZUGFeRD 2.x/Factur-X (all profiles, PDF/A-3 & XML), EN 16931 UBL/CII. ZUGFeRD 1.0 detected with migration notice. |
| Country rules | Germany (mandatory fields incl. Leitweg-ID) and France (SIREN/SIRET & VAT, 2026 reform). |
Every endpoint, schema and error — rendered from the OpenAPI spec. Open reference →
Machine-readable contract, ready for codegen and testing. openapi.yaml →
All 42 rule ids explained, with fixes. Rule index →
The browser validator uses the same engine. Open validator →