Faktura

API

One POST. No account, no key, no signup. Send an invoice, get back every rule it breaks with the reason and the fix.

Quick start

curl -X POST --data-binary @invoice.xml \
  -H 'Content-Type: application/xml' \
  https://invoicevalidator.eu/v1/validate?profile=peppol

Or as a file upload — this is also how you send a Factur-X / ZUGFeRD PDF:

curl -F file=@invoice.pdf https://invoicevalidator.eu/v1/validate

POST /v1/validate

Body: the invoice, either as a raw XML/PDF body or as multipart with a file part. Maximum 5 MB.

profileWhich rule set to check against. en16931 = EN 16931 (core). peppol = Peppol BIS Billing 3.0 (+ national rules). xrechnung = XRechnung 3.0.2 (Germany). Defaults to en16931.
The Peppol profile also carries the national rule sets for Germany, Denmark, Greece, Iceland, Italy, the Netherlands, Norway and Sweden. They switch on by themselves from the seller's country — a Norwegian seller is checked against NO-R-* as well, with no extra parameter.
syntaxubl or cii. Auto-detected from the root namespace if omitted; you should not normally need it.

Response

{
  "syntax": "ubl",
  "profile": "peppol",
  "source": null,               // PDF attachment the XML came from, if any
  "valid": false,               // true when there are no fatal findings
  "counts": { "fatal": 2, "warning": 0 },
  "findings": [
    {
      "rule": "BR-CO-17",
      "severity": "fatal",
      "message": "VAT category tax amount (BT-117) = ...",
      "cause": "In a VAT breakdown group the VAT category tax amount ...",
      "fix": "Set BT-117 = round(BT-116 x BT-119 / 100, 2) per breakdown group ...",
      "confidence": "curated",  // curated | template | none
      "line": 25,
      "excerpt": [ { "line": 24, "text": "..." } ],
      "location": "/Invoice/TaxTotal/TaxSubtotal",
      "business_terms": ["BT-116", "BT-117", "BT-119"]
    }
  ]
}

confidence tells you where the wording came from: curated is written by hand, template is generated from the rule's own shape, none means only the official rule text is available. It is exposed so you never have to guess.

POST /v1/validate/batch

A ZIP of invoices in, one result per file out — the shape of the job if you build accounting software and want to know which of your generated invoices are wrong.

curl -F file=@invoices.zip https://invoicevalidator.eu/v1/validate/batch?profile=peppol

Limits: 20 MB archive, 200 files, 60 MB uncompressed, 5 MB per invoice. A file that cannot be read comes back with an error instead of findings, so one bad document never fails the batch.

GET /v1/rules

The whole catalogue. Filter with ?syntax=ubl, ?category=model, ?severity=fatal.

GET /v1/rules/{id}

One rule with its cause, fix and the XPath actually evaluated, per syntax. Case-insensitive: https://invoicevalidator.eu/v1/rules/br-co-17.

Limits and errors

429Rate limit — 20 validations and 240 reads per minute per IP.
422A PDF with no embedded invoice XML. A printed PDF is not a Factur-X invoice; the message says so.
400Empty body, or XML that cannot be parsed.
413Larger than 5 MB.

Machine-readable schema: /openapi.json · interactive: /docs.

Privacy

Invoices are validated in memory and never written to disk or logged. There is no account, so there is nothing to delete. The only thing kept is an anonymous daily count of how many validations ran and how many pages were served — no IP addresses, no user agents, no document content.