Documentation · CI

CI and the command line

Check XML in every pull request, in a Git hook or in any script, with the same engine as the validator.

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. auto gives 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, --format
text, json for scripts, or github for 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 github the 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 as my-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.