Validate e-invoices against your own XSD or Schematron

Check e-invoices against the rules you maintain yourself: a CIUS, an extension or your organisation's internal rules. Upload your XSD, your ISO Schematron or both, and optionally run the built-in Peppol and EN 16931 checks in the same pass.

XML files

Up to 20 files · 5 MB each

The files to check. Each one is checked against all of your schema files.

Drop .xml files here, or click to browse

Your XSD

Up to 50 files · 5 MB together

XSD 1.0. Add the main schema and every file it includes or imports; drop the folder to keep their paths.

Drop .xsd files or their folder here, or click to pick files

Your Schematron

64 KB · 50 checks · 20 includes

One ISO Schematron file with queryBinding="xslt2" or "xslt3", at most 50 assert and report checks.

Drop a .sch file here, or click to browse

Add an XML file, and your XSD or Schematron.

No files at hand? : a Peppol invoice with a Schematron of in-house rules.

What it is for

The main validator picks the rule sets itself from the file. This page runs the rules you bring:

  • A CIUS or national profile with its own Schematron that is not among the built-in schemas.
  • In-house rules on top of Peppol BIS: a purchase order number, a payee IBAN, a fixed currency. Tick the built-in checks to get both in one result.
  • Writing a Schematron: see which asserts fire on your test files, and XSD errors with their line and column.

The result page is the same as for the built-in checks, with a shareable link. It names your files (“Your XSD: invoice.xsd”, “Your Schematron: cius-rules.sch”) and claims Peppol or EN 16931 compliance only when the built-in checks ran.

What is supported

XSD
XSD 1.0, checked with libxml2. The main schema and every file it reaches by xs:include, xs:import, xs:redefine or xs:override, by a relative schemaLocation.
Schematron
ISO Schematron with queryBinding="xslt2" or "xslt3" (XPath 3.1), compiled with the ISO skeleton and run by SaxonJS. sch:include, sch:extends, abstract patterns and sch:let work. The severity comes from flag or role: fatal or error, warning, info.
Not supported
Schematron 1.5, the XSLT 1.0 binding; in a Schematron xsl:import, xsl:include, xsl:evaluate, fn:transform, text value templates (expand-text), static parameters and shadow attributes; XSD 1.1, DTD and RELAX NG.
Network
None. A schemaLocation or href that is a URL or an absolute path is rejected: nothing is downloaded, so upload what your schema imports.
Order of checks
Not well-formed XML stops at the parse errors. Then your XSD (and the built-in one, if ticked), then your Schematron and the built-in rule sets.

Limits

XML files
Up to 20 per validation, 5 MB each
XSD
Up to 50 files, 5 MB together
Schematron
One file plus up to 20 include files, 64 KB together
Schematron checks
Up to 50 sch:assert and sch:report, counted after includes and abstract patterns are expanded
Schematron size
Up to 50 sch:let and 16 namespaces, and at most 48 KB of generated XSLT: about 50 checks with short messages
File names
Letters A-Z, digits, dot, underscore, hyphen and / for folders
Kept
7 days after the upload

Your files are checked before anything is stored. Compiling a Schematron takes a few seconds, and each XML file takes a few seconds more.

Storage

Your XSD and Schematron are kept for 7 days, stored by the SHA-256 hash of their contents: the XSD files as uploaded, the Schematron as the XSLT it compiles to. Uploading the same files again within 6 days skips the compile step. After the 7 days, an unfinished result asks you to upload them again. XML files are stored like any other validation: see the privacy policy.

API and MCP

POST /api/v1/validate takes the same files as multipart fields next to file: xsd (repeat it for every schema file), xsdMain (needed with more than one XSD), schematron, schematronInclude (repeat) and builtin=true. A file name may contain folders: curl sends only the base name unless you set filename=.

curl -X POST https://peppolvalidator.com/api/v1/validate \
  -F "file=@invoice.xml" \
  -F "xsd=@invoice.xsd" \
  -F "xsd=@common/types.xsd;filename=common/types.xsd" \
  -F "xsdMain=invoice.xsd" \
  -F "schematron=@cius-rules.sch" \
  -F "builtin=true"

The result lists your files under custom, and the issues from them have custom: true: XSD issues with type: "xsd", line and column, Schematron issues with type: "schematron". Without builtin, ruleSets is empty.

"custom": {
  "xsd": { "main": "invoice.xsd", "files": ["common/types.xsd", "invoice.xsd"], "hash": "…" },
  "schematron": { "filename": "cius-rules.sch", "includes": [], "queryBinding": "xslt2", "hash": "…" },
  "builtin": true
}

A problem with your files is a 400 with error, code and details (file, line, the compiler's message):

CodeMeaning
XSD_INCLUDE_MISSING, SCHEMATRON_INCLUDE_MISSINGA schemaLocation or href names a file you did not upload
XSD_REMOTE_LOCATION, SCHEMATRON_REMOTE_LOCATIONA URL or an absolute path: nothing is downloaded
XSD_MAIN_MISSINGSeveral XSD files and no xsdMain
XSD_INVALID, SCHEMATRON_INVALIDNot well-formed, not xs:schema, or not ISO Schematron
SCHEMATRON_QUERY_BINDINGNo queryBinding (XSLT 1.0) or one other than xslt2 / xslt3
SCHEMATRON_UNSUPPORTEDxsl:import, xsl:include, xsl:evaluate, fn:transform, function-lookup, text value templates, static parameters, shadow attributes or SaxonJS extensions
XSD_COMPILE_FAILED, SCHEMATRON_COMPILE_FAILEDThe compiler's messages, with the file and line
…_TOO_LARGE, …_TOO_MANY_FILES, SCHEMATRON_TOO_COMPLEXOver a limit above

The MCP server tool validate_invoice takes the files as text: xsd (file name to XSD text), xsdMain, schematron, schematronIncludes (file name to text) and builtin. See the API docs.

FAQ

Can I check an invoice against a CIUS this validator has no rule set for?

Yes. Upload the CIUS Schematron (and its XSD, if it has one) together with the invoice. Tick the built-in checks to run EN 16931 and Peppol BIS or XRechnung in the same run; the result then lists the built-in issues and those of your files side by side.

Does the result say my invoice is Peppol compliant?

Only when the built-in checks ran and passed. With your own files alone the result says the file passed your XSD and your Schematron, and names them; it makes no claim about Peppol or EN 16931.

Why is my Schematron rejected with SCHEMATRON_QUERY_BINDING?

Your schema has no queryBinding (which means XSLT 1.0) or one other than xslt2 or xslt3. Set queryBinding="xslt2" on the sch:schema element. Most XSLT 1.0 tests run unchanged as XPath 2.0.

My XSD includes common/types.xsd. How do I upload it?

Drop the folder with your schemas: the files keep their paths. You can also pick the files one by one; a file picked on its own is sent under the path another file refers to it by, when no other file has that name. Over the API, give the path as the file name, e.g. curl -F "xsd=@common/types.xsd;filename=common/types.xsd".

Can my schema import the UBL or xmldsig schemas from the web?

No. The validator never downloads anything: a schemaLocation or href that is a URL or an absolute path is rejected. Upload the schemas you import and refer to them by a relative path.

How long are my XSD and Schematron kept?

7 days after the upload that stored them, under the SHA-256 hash of their contents. Uploading the same files again within 6 days reuses them and skips the compile step; after that they are compiled again and kept for another 7 days.

Only need the built-in checks? Go to the Peppol validator