Validation API: first check in four steps

Send UBL or CII XML to POST /v1/validate and get a JSON report: schema check, EN 16931 rules, rule codes and locations. You only need curl or Node.js 18+.

Status: available at https://api.erechio.de (server in Nuremberg, Germany; status and measured availability). Good to know: the JSON field names and the detailed explanations in the response are in German; rule codes and the original rule messages are in English. The table in step 3 explains every field. The free test key is available to everyone; paid plans are currently available only to businesses based in Germany.

1. Get a free test key

Get a test key now

Free, 50 documents per month, valid one month, no sign-up and no payment details. The page shows your key (starts with pk_test_) exactly once; we store only a hash of it. If today's instant keys are used up, request one by e-mail and you get a one-time pick-up link. Set both values as environment variables:

export ERECHIO_URL="https://api.erechio.de"
export ERECHIO_KEY="pk_test_…"

2. Send the sample invoice

Download the sample invoice with errors (UBL, sample data only) and send it:

curl -sS -X POST "$ERECHIO_URL/v1/validate" \
  -H "Authorization: Bearer $ERECHIO_KEY" \
  -H "Content-Type: application/xml" \
  --data-binary @rechnung-mit-fehlern.xml

Use --data-binary, not -d: -d strips line breaks, so line numbers of schema errors no longer match.

Node.js 18+, no packages – file validate.mjs:

import { readFile } from 'node:fs/promises';

const file = process.argv[2] || 'rechnung-mit-fehlern.xml';
const res = await fetch(process.env.ERECHIO_URL + '/v1/validate', {
  method: 'POST',
  headers: { Authorization: 'Bearer ' + process.env.ERECHIO_KEY, 'Content-Type': 'application/xml' },
  body: await readFile(file),
});
const r = await res.json();
if (res.status !== 200) { console.error('No validation:', res.status, r.fehler.code); process.exit(2); }
console.log(r.vollstaendig_validiert ? 'Fully validated' : 'Not valid: ' + r.fehler + ' error(s)');
for (const m of r.meldungen.filter(m => m.level === 'fehler')) console.log('-', m.code, m.location);
process.exit(r.vollstaendig_validiert ? 0 : 1);

3. Read the response

FieldMeaning
HTTP 200The validation ran. It does not mean the invoice is free of errors.
vollstaendig_validiertThe field to rely on (“fully validated”). true = XML schema and all rules without errors, as the KoSIT validator accepts the file. Only send the invoice if true.
regelpruefung_bestanden“Rule check passed”: the business rules report no error and the identifier matches a KoSIT profile. Can be true while the schema is violated.
schemapruefungSchema check: bestanden (passed) or fehlgeschlagen (failed).
fehler, warnungenNumber of errors and warnings. Errors lead to rejection, warnings do not.
meldungen[]Messages: code (e.g. BR-CO-16; XSD = schema error), level (fehler error, warnung warning, hinweis info), text (original rule message), location (XPath, for schema errors the line), erklaerung (German explanation for frequent rules; English versions are under error codes).
kontingentQuota after this call: genutzt used, limit, rest remaining.
regelstandIdentifier of the rule version used.

Other status codes: 401 key missing or wrong · 400 body is not XML (forgot @ in curl) · 422 XML but not a UBL or CII invoice (for ZUGFeRD send the embedded XML, not the PDF) · 413 larger than 2 MB · 429 quota or 60 per minute reached · 503 time limit or overloaded. None of these count against the quota.

4. Validate your own invoice

  1. Same call with your file: --data-binary @my-invoice.xml or node validate.mjs my-invoice.xml.
  2. Cross-check with the sample invoice without errors (KoSIT test file): it must return vollstaendig_validiert: true.
  3. Fix schema errors (XSD) first, then the rule errors.
  4. Without the API: the same check in the browser validator, the file stays on your computer.

Full reference: OpenAPI file · API reference.

Technical notes on the data format, not tax or legal advice. Whether VAT rate, supply and amounts are correct in substance is a question for your tax adviser.