API · E-invoicing
xmoo E-Invoice API
Validate, read, convert and render European e-invoices – XRechnung, ZUGFeRD, Factur-X and any EN 16931 invoice in UBL or CII – with one REST call.
What it does
- Validate with the official artefacts: XML Schema, the CEN EN 16931 validation artefacts, the KoSIT XRechnung Schematron and the ZUGFeRD / Factur-X rules for each profile. You get every message with its rule ID, severity and location, plus a recalculation of the totals.
- Parse an XML or a hybrid PDF into one JSON model that follows the business terms (BT/BG) of EN 16931 – the same model for UBL and CII.
- Convert between XRechnung (UBL or CII) and ZUGFeRD / Factur-X (XML or PDF/A-3). The API never invents data: if the target format needs something the invoice does not contain, it tells you exactly what to send. A result is only returned if it passes the official validation.
- Render a readable A4 PDF of any e-invoice, e.g. for archiving or for people who cannot open XML.
Invoices are processed in memory and never stored. The API runs on a server in Helsinki, Finland (EU). It uses the same engine as the free browser tools on this site, the e-invoice viewer and the e-invoice converter (both in German).
Access
The API is offered through RapidAPI. There you subscribe to a plan, get your key and find the host name for your requests; billing also runs through RapidAPI: xmoo E-Invoice API on RapidAPI. Questions: julian@haag-automation.com.
Every request carries two headers from your RapidAPI account:
x-rapidapi-key: <your key> x-rapidapi-host: <host name from the listing>
The examples below expect them in the shell variables RAPIDAPI_KEY and
RAPIDAPI_HOST.
Endpoints
| Endpoint | Purpose | Answer |
|---|---|---|
| POST /v1/detect | Format, syntax, specification and profile; which rule sets apply | JSON |
| POST /v1/validate | Validation report with all messages and a totals check (BR-CO-10 to 16) | JSON |
| POST /v1/parse | All EN 16931 business terms as JSON | JSON |
| POST /v1/convert?to=… | Convert to xrechnung-ubl, xrechnung-cii, zugferd (XML) or zugferd-pdf (PDF/A-3) | JSON with the file as Base64 |
| POST /v1/render | Readable A4 PDF of the invoice | |
| GET /v1/formats | Supported formats, rule sets and targets | JSON |
The machine-readable description is at api.xmoo.app/openapi.json (OpenAPI 3.0).
Sending an invoice
Send a JSON body with the file (XML or PDF) as Base64 in document. This is the
recommended way and works for every endpoint:
base64 -w0 invoice.xml | jq -Rs '{document: .}' |
curl -s -X POST "https://$RAPIDAPI_HOST/v1/validate" \
-H "x-rapidapi-key: $RAPIDAPI_KEY" \
-H "x-rapidapi-host: $RAPIDAPI_HOST" \
-H "Content-Type: application/json" \
--data-binary @-
The same in Python:
import base64, os, requests
with open("invoice.pdf", "rb") as f:
body = {"document": base64.b64encode(f.read()).decode()}
r = requests.post(f"https://{os.environ['RAPIDAPI_HOST']}/v1/validate", json=body, headers={
"x-rapidapi-key": os.environ["RAPIDAPI_KEY"],
"x-rapidapi-host": os.environ["RAPIDAPI_HOST"],
})
report = r.json()
print(report["valid"], report["summary"])
The API also accepts the raw file as the body (Content-Type: application/xml or
application/pdf); options then go into the query string. Further fields of the JSON
body: to (target format for /v1/convert), additions (see
below) and lang.
Language
Field names are always English. Descriptive texts – changes, losses, notes and error messages –
are English by default; lang=de returns them in German. Rule texts come verbatim from
the official artefacts: those of EN 16931 and ZUGFeRD / Factur-X are English, those of the
KoSIT XRechnung rules (BR-DE-…) are German.
Examples
The answers below are real answers of the API (shortened where marked). Test files: the KoSIT XRechnung test suite and the Mustang project's ZUGFeRD samples. A test on our side sends exactly these requests and checks that the answers still match.
Detect a hybrid PDF
POST /v1/detect with a ZUGFeRD PDF, profile EN 16931:
{
"source": "pdf",
"syntax": "CII",
"documentType": "invoice",
"specification": "urn:cen.eu:en16931:2017",
"profile": "EN 16931 (for ZUGFeRD: profile EN 16931)",
"rulesets": [
"schema-facturx-en16931",
"facturx-en16931"
]
}
Validate an XRechnung
POST /v1/validate with an XRechnung 3.0 in UBL. The invoice is valid; the KoSIT
rules add one note (severity info). Shortened to the first two totals checks:
{
"source": "xml",
"syntax": "UBL",
"documentType": "invoice",
"specification": "urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_3.0",
"profile": "XRechnung 3.0",
"rulesets": [
"schema-ubl",
"en16931-ubl",
"xrechnung-ubl"
],
"valid": true,
"summary": {
"errors": 0,
"warnings": 0,
"infos": 1
},
"reports": [
{
"ruleset": "schema-ubl",
"title": "XML Schema UBL 2.1",
"messages": []
},
{
"ruleset": "en16931-ubl",
"title": "EN 16931 (validation artefacts 1.3.17, UBL)",
"messages": []
},
{
"ruleset": "xrechnung-ubl",
"title": "XRechnung 3.0 (KoSIT-Schematron 2.6.0, UBL)",
"messages": [
{
"rule": "BR-DE-TMP-32",
"severity": "info",
"text": "Eine Rechnung sollte zur Angabe des Liefer-/Leistungsdatums entweder BT-72 \"Actual delivery date\", BG-14 \"Invoicing period\" oder in jeder Rechnungsposition BG-26 \"Invoice line period\" enthalten.",
"location": "/Invoice[1]"
}
]
}
],
"note": null,
"totalsCheck": [
{
"rule": "BR-CO-10",
"text": "Sum of invoice line net amount (BT-106) = sum of all line net amounts",
"expected": 314.86,
"actual": 314.86,
"ok": true
},
{
"rule": "BR-CO-13",
"text": "Invoice total amount without VAT (BT-109) = sum of lines − allowances + charges",
"expected": 314.86,
"actual": 314.86,
"ok": true
}
]
}
valid is true when no message has the severity error. The
answer is 200 for invalid invoices too – a failed validation is a result, not an
error.
Read it as JSON
POST /v1/parse, same invoice, shortened to a few fields. Amounts and dates stay
strings exactly as written in the file, so nothing is lost by rounding:
{
"source": "xml",
"syntax": "UBL",
"profile": "XRechnung 3.0",
"invoice": {
"number": "123456XX",
"issueDate": "2016-04-04",
"typeCode": "380",
"currencyCode": "EUR",
"buyerReference": "04011000-12345-03",
"seller": {
"name": "[Seller name]",
"vatIdentifier": "DE 123456789",
"address": {
"city": "[Seller city]",
"postCode": "12345",
"countryCode": "DE"
}
},
"totals": {
"totalWithoutVat": "314.86",
"totalVatAmount": "22.04",
"totalWithVat": "336.9",
"amountDueForPayment": "336.9"
},
"lines": [
{
"quantity": "1",
"unitCode": "XPP",
"netAmount": "288.79",
"vatRate": "7",
"itemName": "Zeitschrift [...]",
"description": "Zeitschrift Inland"
}
]
},
"unmapped": []
}
Convert XRechnung to ZUGFeRD
POST /v1/convert?to=zugferd with the same XRechnung. The answer lists every change and
every loss and carries the validation of the result; the file itself is in
document.base64 (left out here):
{
"target": "zugferd",
"changes": [
"Specification identifier (BT-24): urn:cen.eu:en16931:2017 instead of urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_3.0"
],
"losses": [],
"ignoredAdditions": [],
"validation": {
"valid": true,
"summary": {
"errors": 0,
"warnings": 1,
"infos": 0
}
},
"document": {
"contentType": "application/xml",
"filename": "invoice_zugferd.xml"
}
}
When data is missing
An XRechnung needs data that a ZUGFeRD invoice may not contain, such as the buyer reference
(Leitweg-ID), electronic addresses and a seller contact. POST /v1/convert?to=xrechnung-ubl
with the ZUGFeRD PDF from above answers 422 and lists what is needed (shortened to the
first two of nine fields):
{
"error": {
"code": "additions_required",
"message": "The target format requires data the document does not contain. Nothing is invented: send it as \"additions\".",
"missing": [
{
"path": "buyerReference",
"name": "Buyer reference / Leitweg-ID",
"rule": "BR-DE-15",
"help": "For invoices to German public authorities the Leitweg-ID, otherwise the reference your customer gave you.",
"optional": false,
"provided": false
},
{
"path": "seller.electronicAddress",
"name": "Seller email address for invoices",
"rule": "PEPPOL-EN16931-R020",
"help": null,
"optional": false,
"provided": false
}
]
}
}
Send the values as additions, keyed by path, and repeat the request.
Existing data is never overwritten; additions the invoice does not need are listed in
ignoredAdditions.
{
"document": "<Base64>",
"to": "xrechnung-ubl",
"additions": {
"buyerReference": "04011000-12345-03",
"seller.electronicAddress": "invoices@example.com",
"payment.iban": "DE02120300000000202051"
}
}
Errors
Errors come as JSON with a stable code and a readable message:
{"error": {"code": "not_an_e_invoice", "message": "…"}}
| Status | Code | Meaning |
|---|---|---|
| 400 | empty, bad_json, missing_document | No file, invalid JSON or no document field |
| 400 | invalid_target, invalid_lang, invalid_additions | Unknown target format or language, additions not an object of strings |
| 401 | unauthorized | Request did not come through RapidAPI |
| 404, 405 | not_found, method_not_allowed | Unknown endpoint or wrong HTTP method |
| 413 | too_large | Request larger than 50 MB |
| 422 | not_an_e_invoice, not_readable | Neither UBL nor CII, or a PDF without embedded invoice |
| 422 | doctype_not_allowed | XML with DOCTYPE or ENTITY declarations is rejected for security |
| 422 | additions_required | The target format needs data – see missing |
| 422 | not_convertible, already_target_format | Conversion not possible or not needed |
| 422 | result_not_valid | The converted invoice fails the official validation, so it is not returned (report in validation) |
| 503, 504 | busy, timeout | Too many requests at once, or processing took longer than 30 s – retry later |
| 500 | internal_error | Our fault; under RapidAPI's billing rules, answers with status 500 or higher are not counted |
Rule sets and versions
| Rule set | Version |
|---|---|
| XML Schema | UBL 2.1, UN/CEFACT CII D16B |
| EN 16931 validation artefacts (CEN) | 1.3.17 (UBL and CII) |
| XRechnung (KoSIT Schematron) | XRechnung 3.0, Schematron 2.6.0 |
| ZUGFeRD / Factur-X | ZUGFeRD 2.5.2 / Factur-X 1.09.2, profiles MINIMUM, BASIC WL, BASIC, EN 16931, EXTENDED |
Peppol BIS Billing 3.0 invoices are read and validated against EN 16931. When a new version of a rule set is published, the API is updated and this table with it.
Limits
- Request size up to 50 MB; processing is stopped after 30 seconds.
- Requests per month and per second depend on your plan on RapidAPI.
- A valid report means the invoice meets the published rules. It is no tax or legal advice and does not guarantee that a recipient or an authority accepts the invoice.
- Transmission (Peppol, e-mail) and electronic signatures are not part of the API.
Use with AI agents (MCP)
RapidAPI offers its APIs as tools for AI agents through the Model Context Protocol (MCP). With your RapidAPI key, an agent can call each endpoint as a tool; the plan you subscribed to applies.
Privacy and terms
Invoices contain personal data. We process them only to answer your request, in memory, and keep nothing. Details: API privacy notice and data processing agreement (Art. 28 GDPR). The API terms of use apply in addition to RapidAPI's terms.
Licences
The validation uses the official rule artefacts and open-source components under their licences: the CEN EN 16931 artefacts and KoSIT XRechnung rules (EUPL 1.2, Apache 2.0), the Factur-X rules (licence), SaxonJS (copyright notice and disclaimer), xmllint-wasm, pdf-lib, fontkit, big.js and the DejaVu fonts.
Provider
Haag Automation, Julian Haag, Mühlstraße 35, 74360 Ilsfeld, Germany · julian@haag-automation.com · xmoo is a brand of Haag Automation. All APIs