# Validate E-Invoice
Checks an e-invoice file against the official rule sets and lists every error and warning with its rule id: XML well-formedness, the XML schema, the EN 16931 business rules and the national XRechnung rules. For a ZUGFeRD / Factur-X PDF it also checks PDF/A-3 conformance and the embedded invoice file. Format, syntax, profile and version are detected from the file.
Technical validation, not tax advice
The result says whether the file conforms to the rule sets named in ruleset_version. It is not tax or legal advice, and it does not check whether the invoice's content is right for the underlying transaction.
Request Method: POST
Request URL: https://api.vatcheckapi.com/v2/einvoice/validate
# Sending the Invoice
Send one invoice file, XML or PDF, at most 10 MB, in one of three ways:
| Way | Content-Type | Body |
|---|---|---|
| Raw body | application/xml (or text/xml) for an XML file, application/pdf for a PDF | The file itself |
| File upload | multipart/form-data | The file in the field file |
| JSON | application/json | {"content_base64": "...", "filename": "invoice.pdf"}: the whole file, base64-encoded. filename is optional. A data URL (data:application/pdf;base64,...) is accepted too |
The MCP server (opens new window) uses the JSON way. Whether the file is XML or a PDF is detected from its content, not from Content-Type or the file name.
# Request Parameters
| Parameter | Type | Mandatory | Description |
|---|---|---|---|
apikey | string | ️ | Your API Key |
format | string | The format you expect: xrechnung, zugferd or en16931. The invoice is always validated against the rules its own specification identifier (BT-24) names; if it declares another format, the response gets the error FORMAT_MISMATCH. XRechnung and ZUGFeRD / Factur-X invoices also count as en16931, and an EN 16931 invoice in CII syntax also counts as zugferd. Default auto: no check | |
language | string | en (default) or de: the language of the API's own messages and of the disclaimer. Rule set messages are not translated, see Message language |
Send format and language in the query string. With a file upload they may also be form fields, with JSON also properties of the JSON object.
# Sample Requests
# cURL: XML file as the request body
curl "https://api.vatcheckapi.com/v2/einvoice/validate" \
-H "apikey: YOUR-APIKEY" \
-H "Content-Type: application/xml" \
--data-binary @invoice.xml
# cURL: PDF as the request body, German messages
curl "https://api.vatcheckapi.com/v2/einvoice/validate?language=de" \
-H "apikey: YOUR-APIKEY" \
-H "Content-Type: application/pdf" \
--data-binary @invoice.pdf
# cURL: file upload
curl "https://api.vatcheckapi.com/v2/einvoice/validate" \
-H "apikey: YOUR-APIKEY" \
-F "file=@invoice.pdf" \
-F "format=zugferd"
# JavaScript: base64 in JSON
import { readFile } from 'node:fs/promises';
const file = await readFile('invoice.pdf');
const response = await fetch('https://api.vatcheckapi.com/v2/einvoice/validate?language=de', {
method: 'POST',
headers: { 'apikey': 'YOUR-APIKEY', 'Content-Type': 'application/json' },
body: JSON.stringify({ content_base64: file.toString('base64'), filename: 'invoice.pdf' }),
});
const result = await response.json();
console.log(result.valid, result.errors);
# Python: base64 in JSON
import base64
import requests
with open("invoice.xml", "rb") as f:
content = base64.b64encode(f.read()).decode("ascii")
resp = requests.post(
"https://api.vatcheckapi.com/v2/einvoice/validate",
params={"language": "de"},
headers={"apikey": "YOUR-APIKEY"},
json={"content_base64": content, "filename": "invoice.xml"},
)
result = resp.json()
print(result["valid"])
for error in result["errors"]:
print(error["rule_id"], error["message"])
# PHP: XML file as the request body
$curl = curl_init("https://api.vatcheckapi.com/v2/einvoice/validate");
curl_setopt_array($curl, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => ["apikey: YOUR-APIKEY", "Content-Type: application/xml"],
CURLOPT_POSTFIELDS => file_get_contents("invoice.xml"),
CURLOPT_RETURNTRANSFER => true,
]);
$result = json_decode(curl_exec($curl), true);
var_dump($result["valid"], $result["errors"]);
# Sample Response
An XRechnung 3.0 invoice in UBL syntax without the buyer reference (BT-10). An invalid invoice is a normal result: the status is 200 and valid is false.
{
"valid": false,
"format": "xrechnung",
"syntax": "UBL",
"profile": "XRECHNUNG",
"version": "3.0",
"container": "xml",
"specification_identifier": "urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_3.0",
"scenario": "EN16931 XRechnung (UBL Invoice)",
"ruleset_version": "xrechnung-3.0.2+cfg-2026-08-31;en16931-1.3.16;xrechnung-schematron-2.6.0;kosit-1.6.3;verapdf-1.30.2",
"pdfa": null,
"errors": [
{
"rule_id": "BR-DE-15",
"severity": "error",
"message": "Das Element \"Buyer reference\" (BT-10) muss übermittelt werden.",
"message_language": "de",
"source": "business_rules",
"location": "/ubl:Invoice[1]",
"line": null,
"column": null
}
],
"warnings": [
{
"rule_id": "BR-DE-TMP-32",
"severity": "information",
"message": "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.",
"message_language": "de",
"source": "business_rules",
"location": "/ubl:Invoice[1]",
"line": null,
"column": null
}
],
"summary": {
"invoice_number": "123456XX",
"issue_date": "2016-04-04",
"type_code": "380",
"currency": "EUR",
"seller": { "name": "[Seller name]", "vat_id": "DE 123456789" },
"buyer": { "name": "[Buyer name]" },
"totals": { "net_amount": "314.86", "tax_amount": "22.04", "gross_amount": "336.9", "payable_amount": "336.9" }
},
"language": "en",
"disclaimer": "Technical validation against the rule sets listed in ruleset_version. It is not tax or legal advice."
}
A PDF without an embedded invoice XML gets "container": "pdf", "pdfa": {"compliant": null, "profile": null} and the error NO_EINVOICE_XML ("No e-invoice XML found"). A ZUGFeRD / Factur-X PDF that passes gets "valid": true, "format": "zugferd" and "pdfa": {"compliant": true, "profile": "PDF/A-3B"}.
# Response Fields
Every field is always present; fields that do not apply are null.
| Field | Description |
|---|---|
valid | true when the rule sets accepted the invoice and no check reported an error; for a PDF, PDF/A conformance is required too. Warnings and information messages do not make an invoice invalid |
format | xrechnung, zugferd (ZUGFeRD and Factur-X, including a PDF whose invoice has the EN 16931 or XRECHNUNG profile), en16931 (plain EN 16931 or another EN 16931 specification), peppol-bis, or null when the file is not an e-invoice the API recognises |
syntax | UBL or CII |
profile | XRECHNUNG, XRECHNUNG-EXTENSION, XRECHNUNG-CVD, EN 16931, MINIMUM, BASIC WL, BASIC, EXTENDED, EXTENDED-CTC-FR, BILLING (Peppol BIS), or BASIC / COMFORT / EXTENDED for ZUGFeRD 1.0 |
version | The version the invoice declares, e.g. 3.0 for XRechnung 3.0 or 2017 for EN 16931 |
container | xml or pdf: what you sent |
specification_identifier | The specification identifier (BT-24) exactly as the invoice declares it |
scenario | The validation scenario (rule set) that was applied; null when no rule set ran |
ruleset_version | The pinned versions of the rule sets and engines that ran; null when no rule set ran, sandbox for sandbox keys |
pdfa | null for an XML file. For a PDF: compliant (null when PDF/A was not checked) and the PDF/A profile checked against |
errors | Findings of severity error |
warnings | Findings of severity warning and information |
summary | Key facts read from the invoice XML, exactly as written there: invoice_number (BT-1), issue_date (BT-2; CII dates are given as YYYY-MM-DD), type_code (BT-3), currency (BT-5), seller.name, seller.vat_id (BT-31), buyer.name, and totals as strings in the invoice currency: net_amount (BT-109), tax_amount (BT-110), gross_amount (BT-112), payable_amount (BT-115). null when no UBL or CII invoice could be read |
language | The language of the request |
disclaimer | The note that this is technical validation, not tax or legal advice |
Each finding in errors and warnings has these fields:
| Field | Description |
|---|---|
rule_id | The rule id: from a rule set (e.g. BR-DE-15, BR-CO-10, the XML schema's cvc-complex-type.2.4.a, the PDF/A rule PDFA-6.6.2.1-1) or one of the API's own codes |
severity | error, warning or information |
message | What is wrong |
message_language | en or de: the language of message |
source | Which check reported it: input (the request), xml (well-formedness), schema (XML schema), business_rules (EN 16931 and XRechnung rules), container (the PDF's embedded file and XMP metadata) or pdfa (PDF/A conformance) |
location | XPath of the element in the invoice XML (e.g. /ubl:Invoice[1]/cac:LegalMonetaryTotal[1]), or the PDF object path for PDF/A findings |
line, column | Position in the invoice XML, for well-formedness and schema errors |
When the XML schema check fails, the business rules are not run; the warning BUSINESS_RULES_SKIPPED says so. Fix the schema errors and validate again.
# Message Language
Rule set messages keep the language their rule set is published in, and message_language says which:
- the national XRechnung rules (
BR-DE-*,BR-DEX-*,BR-TMP-*) are German; - the EN 16931 business rules (e.g.
BR-CO-10), the XML schema messages and the PDF/A rules are English.
language=de or language=en switches only the API's own messages (for example NO_EINVOICE_XML) and the disclaimer. Rule set messages are not translated.
# Supported Formats
Validated against the rule sets:
| Format | Syntax | Checks |
|---|---|---|
| XRechnung 3.0, also the Extension and CVD specifications | UBL (Invoice, CreditNote), CII | XML, schema, EN 16931 and XRechnung rules |
EN 16931 (urn:cen.eu:en16931:2017) | UBL (Invoice, CreditNote), CII | XML, schema, EN 16931 rules |
| ZUGFeRD / Factur-X PDF with the profile EN 16931 (COMFORT) or XRECHNUNG | CII, embedded in a PDF | The above for the embedded XML, plus PDF/A-3 and the embedded file (name, relationship, MIME type, XMP metadata) |
Recognised, but answered with the error UNSUPPORTED_PROFILE and not validated, free of charge:
- ZUGFeRD / Factur-X MINIMUM and BASIC WL (with the warning
PROFILE_NOT_EINVOICE_DE: in Germany these profiles do not count as e-invoices), BASIC and EXTENDED, and Factur-X EXTENDED-CTC-FR. Their profile-specific rules are not included yet. - ZUGFeRD 1.0.
- XRechnung versions other than 3.0, and an XRechnung 3.0 Extension credit note in UBL syntax.
- Peppol BIS Billing 3.0, other EN 16931 specifications and unknown specification identifiers.
An XML file that is not a UBL Invoice, UBL CreditNote or CII invoice gets UNSUPPORTED_FORMAT; an invoice without a specification identifier gets BR-01.
# The API's Own Rule IDs
Besides the rule ids of the rule sets, the API reports these codes:
rule_id | Severity | Meaning |
|---|---|---|
NOT_WELL_FORMED | error | The XML cannot be parsed (line and column are given) |
DOCTYPE_NOT_ALLOWED | error | The XML has a document type declaration (DOCTYPE); it is not processed |
UNSUPPORTED_FORMAT | error | The XML is not a UBL Invoice, UBL CreditNote or CII invoice |
BR-01 | error | The invoice has no specification identifier (BT-24), so it does not say which rules apply |
UNSUPPORTED_PROFILE | error | A recognised format, profile or version that is not validated, see Supported Formats |
PROFILE_NOT_EINVOICE_DE | warning | MINIMUM and BASIC WL do not count as e-invoices in Germany |
FORMAT_MISMATCH | error | The invoice declares another format than the format you asked for |
BUSINESS_RULES_SKIPPED | warning | The XML schema check failed, so the business rules were not run |
INVOICE_TOO_COMPLEX | error | More than 50 VAT breakdowns (BG-23) or more than 1,000 document level allowances and charges (BG-20, BG-21); not validated |
NO_EINVOICE_XML | error | The PDF has no embedded ZUGFeRD, Factur-X or XRechnung XML |
EMBEDDED_XML_TOO_LARGE | error | The invoice XML in the PDF is larger than 10 MB; not validated |
PDF_UNREADABLE | error | The PDF is damaged or uses a structure the reader does not support |
PDF_ENCRYPTED | error | The PDF is encrypted, which PDF/A-3 does not allow |
PDF_TOO_COMPLEX | error | Reading the PDF needs more memory or time than the API allows for one file |
HYBRID_ATTACHMENT_NAME | error | The embedded invoice file has a name its profile does not allow (usually it must be factur-x.xml; the message names the expected name) |
HYBRID_AF_RELATIONSHIP | error | The embedded file's AFRelationship is missing or not Data, Source or Alternative |
HYBRID_NOT_ASSOCIATED | error | The embedded file is missing from the document catalog's AF array |
HYBRID_MIME_TYPE | warning | The embedded file's MIME type is not text/xml |
XMP_MISSING, XMP_UNREADABLE, XMP_FACTURX_MISSING | error | The PDF's XMP metadata is missing, broken, or lacks the ZUGFeRD / Factur-X properties |
XMP_TOO_LARGE | warning | The XMP metadata is larger than 2 MB, so its properties were not checked |
XMP_DOCUMENT_FILE_NAME, XMP_CONFORMANCE_LEVEL | error | The XMP metadata names another file or another profile than the embedded XML |
PDFA_NOT_DECLARED, PDFA_NOT_PDFA3 | error | The PDF declares no PDF/A conformance, or not PDF/A-3 |
PDFA_UNREADABLE | error | The PDF/A checker could not parse the file |
Failed PDF/A rules are reported as PDFA-<clause>-<test>, e.g. PDFA-6.6.2.1-1.
# Costs
| Result | Requests counted |
|---|---|
An XML file that was validated, or that is not well-formed (NOT_WELL_FORMED, DOCTYPE_NOT_ALLOWED), not an e-invoice (UNSUPPORTED_FORMAT) or has no specification identifier (BR-01) | 1 |
| A ZUGFeRD / Factur-X PDF whose invoice was validated (XML rules and PDF/A) | 2 |
A PDF without readable invoice XML (NO_EINVOICE_XML, PDF_UNREADABLE, PDF_ENCRYPTED, PDF_TOO_COMPLEX), or whose embedded XML is not well-formed or not an invoice | 1 |
UNSUPPORTED_PROFILE, EMBEDDED_XML_TOO_LARGE, INVOICE_TOO_COMPLEX | 0 |
| Any request with a sandbox key | 0 |
The X-Cost response header states the cost of each call. Requests rejected with 422, 403 or 429, and 503 answers, are not counted.
Before any work, the API checks that your remaining quota can pay for the request: 1 request for an XML file, 2 for a PDF, also when the result later costs less. If it cannot, the API answers 429 with quota.required set to that amount, and validates nothing. The error code names the quota that runs short: quota_exceeded, grace_quota_exceeded or overage_quota_exceeded. As for every endpoint, the request that uses the last unit of your quota is still served.
On the free plan the cost also counts against the minute rate limit (opens new window) of 10 requests: a PDF uses 2 of them, a free result none.
# Limits
- At most 10 MB per file (after base64 decoding for JSON). The limit also applies to the invoice XML inside a PDF (
EMBEDDED_XML_TOO_LARGE). - Invoices with more than 50 VAT breakdowns or more than 1,000 document level allowances and charges are not validated (
INVOICE_TOO_COMPLEX). - If the validation service is unavailable, busy or does not finish in time, the API answers
503(not counted). Retry the request later.
# Sandbox
With a sandbox API key (opens new window) the API reads the file, detects the format, extracts the summary and checks the PDF container as with a live key, but it does not run the rule sets:
- the rule set result is fixed: the invoice is accepted, unless its invoice number (BT-1) is
SANDBOX-INVALID, which gets the errorBR-DE-15; - a PDF whose invoice XML reaches the rule sets (well-formed, an invoice with a supported profile, within the limits) gets
"pdfa": {"compliant": true, "profile": "PDF/A-3B"}; any other PDF gets"compliant": null, as with a live key; scenarioisSandboxandruleset_versionissandbox;- the call costs nothing.
# Privacy
Invoices are processed transiently and never persisted: the API keeps no copy of the file, writes none of its content to logs, and does not store the result. Large uploads can be buffered to temporary files while the request runs; these are deleted when it ends.
# Rule Sets and Licences
The validation runs on these open-source projects, unmodified. ruleset_version in each response names the exact versions used.
- KoSIT validator (opens new window) and the XRechnung validator configuration (opens new window) with the XRechnung schematron rules, by KoSIT (Koordinierungsstelle für IT-Standards), Apache License 2.0.
- EN 16931 validation artefacts (opens new window) by CEN and the European Commission, European Union Public Licence (EUPL) 1.2.
- veraPDF (opens new window) for PDF/A conformance, used under the Mozilla Public License 2.0.