Everything here calls the xmlvalid API with a key from Dashboard → API keys. Each document is one API validation: 100 a month on Free, 10,000 on Pro. The documents are validated on our servers and deleted as soon as the check finishes.
Command-line client
@xmlvalid/cli needs Node.js 20 or newer and has no dependencies.
npm install -g @xmlvalid/cli
export XMLVALID_API_KEY=xv_…
xmlvalid validate invoices/*.xml --schema ubl-2.1-invoice
xmlvalid validate "feeds/**/*.atom" --schema atom-1.0
xmlvalid validate order.xml --schema-file schemas/order.xsd
xmlvalid validate --url https://example.com/sitemap.xml --schema sitemap-0.9
It prints each document with its errors, as file:line:column:
✓ invoices/0412.xml valid
✗ invoices/0413.xml 2 errors
invoices/0413.xml:14:0 Element 'cbc:IssueDate': '2026-13-01' is not a valid value of the atomic type 'xs:date'.
invoices/0413.xml:31:0 Element 'cac:TaxTotal': This element is not expected.
2 documents: 1 passed, 1 failed.
-s, --schema <name>- A catalog slug or a saved schema, see choosing the schema.
autogives each e-invoice the catalog profile it names (Peppol BIS, XRechnung, EN 16931, French, KSeF and other national formats), for a folder of mixed invoices. --schema-file <path>- One XSD, DTD or RelaxNG file sent with each document.
--rules <path>- Your own Schematron (
.sch) file, run after the schema check (and after a catalog profile's own rules); each finding shows its rule id, such as[BR-CO-15]. --doctype- Validate against each document's own DOCTYPE.
--url <address>- A document at a public URL; repeat for several.
-f, --formattext,jsonfor scripts, orgithubfor annotations and a job summary.--fail-on-warnings- Treat warnings as failures.
--report <file.html>- Also write an HTML report of every document, with links to each rule's page, to keep as a build artifact or send on. Open it and print it to save a PDF.
--share- Also make a public link to that report, open for 30 days, and print it at the end (Pro). With
--format githubthe link goes into the job summary too. If the link can't be made, the run carries on without it. -j, --concurrency <n>- Documents validated at once, 4 by default. Keep it within your plan's requests per minute.
Globs support *, **, ? and {a,b}; quote them so the shell passes them through. xmlvalid schemas lists the schemas you can use and xmlvalid usage shows this month's count.
GitHub Actions
Store the key as a repository secret named XMLVALID_API_KEY, then:
name: XML
on: [push, pull_request]
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: xmlvalid/validate-action@v1
with:
files: |
feeds/**/*.xml
sitemap.xml
schema: atom-1.0
api-key: ${{ secrets.XMLVALID_API_KEY }}
Failing lines are annotated in the pull request's Files changed view, and the run gets a summary table. The inputs are files, urls, schema, schema-file, rules, doctype, fail-on-warnings, report (a path for the HTML report, to keep with actions/upload-artifact), share (true for a report link in the summary and the report-url output, Pro) and api-key. To check different folders against different schemas, add one step per schema.
GitLab CI
Add XMLVALID_API_KEY as a masked CI/CD variable:
validate-xml:
image: node:22-alpine
script:
- npx --yes @xmlvalid/cli@1 validate "invoices/**/*.xml" --schema ubl-2.1-invoice
pre-commit
With pre-commit, staged XML files are checked before each commit (the key comes from your environment):
repos:
- repo: https://github.com/xmlvalid/cli
rev: v1.0.0
hooks:
- id: xmlvalid
args: [--schema, atom-1.0]
files: ^feeds/
Other CI and scripts
Anywhere with Node.js, npx --yes @xmlvalid/cli@1 validate … works without installing anything: Jenkins, CircleCI, Bitbucket Pipelines, Azure Pipelines, a Makefile. Without Node.js, call the API with curl and check result.valid:
curl -sf https://xmlvalid.com/api/v1/validations \
-H "Authorization: Bearer $XMLVALID_API_KEY" \
-F [email protected] -F schema=sitemap-0.9 -F wait=60 \
| jq -e '.result.valid'
For your own client, the API is described as OpenAPI 3.1, which most languages can generate a client from.
Choosing the schema
- A catalog schema
- Pass its slug, shown on each page of the schema catalog:
ubl-2.1-invoice,atom-1.0,sitemap-0.9… - Your own schema
- A single file can go with each request (
--schema-file). A schema that spans several files (imports, includes, a folder of common types) is easiest to save once in the dashboard and pass asmy-12; it is then versioned there, not sent with every document. - None
- Without a schema the documents are checked for well-formedness only.
Limits and exit codes
Files are limited by your plan (10 MB on Free, 1 GB on Pro), as are requests per minute; the client waits and retries when it goes over the per-minute limit. The exit code tells the result:
0- Every document is valid (warnings allowed unless
--fail-on-warnings). 1- At least one document is invalid or could not be read.
2- A usage problem: a missing key, an unknown or broken schema, the monthly quota used up.
3- The API could not be reached or failed.