Peppol Validator API & Agent Skill

Peppol API documentation for developers. Validate Peppol UBL and CII / Factur-X XML invoices with a free Peppol check API. No signup, no API key. Just send XML and get results.

REST API endpoint

Send a UBL or CII XML invoice and receive validation results as JSON. The format is detected automatically. Every file is first checked against its XML Schema (UBL 2.1, UN/CEFACT CII D16B or the XSD of its Factur-X profile). UBL invoices are then validated against CEN EN16931 and Peppol BIS Billing 3.0 rules. Factur-X / ZUGFeRD invoices are validated against the official rules of their profile (MINIMUM to EXTENDED, Factur-X 1.09.2 / ZUGFeRD 2.5.2), XRechnung against EN16931 and the KoSIT rules, and other CII invoices against CEN EN16931 (CII binding) rules.

POST https://peppolvalidator.com/api/v1/validate

Option 1: Raw XML body

Send the XML directly as the request body with a Content-Type: application/xml header.

curl -X POST https://peppolvalidator.com/api/v1/validate \
  -H "Content-Type: application/xml" \
  -d @invoice.xml

Option 2: Multipart form data

Upload the file as a file field in a multipart request.

curl -X POST https://peppolvalidator.com/api/v1/validate \
  -F "file=@invoice.xml"

ZUGFeRD / Factur-X PDF

Send the PDF as the raw body with Content-Type: application/pdf or as the multipart file field (max 5MB). An optional Content-Disposition: attachment; filename="rechnung.pdf" header names a raw body in the result. See PDF results below.

curl -X POST https://peppolvalidator.com/api/v1/validate \
  -H "Content-Type: application/pdf" \
  --data-binary @invoice.pdf

JavaScript / Node.js

const xml = fs.readFileSync("invoice.xml", "utf-8");

const res = await fetch("https://peppolvalidator.com/api/v1/validate", {
  method: "POST",
  headers: { "Content-Type": "application/xml" },
  body: xml,
});

const result = await res.json();
console.log(result.status); // "valid" | "invalid" | "error"

Python

import requests

with open("invoice.xml") as f:
    xml = f.read()

res = requests.post(
    "https://peppolvalidator.com/api/v1/validate",
    headers={"Content-Type": "application/xml"},
    data=xml,
)

result = res.json()
print(result["status"])  # "valid" | "invalid" | "error"

ZUGFeRD / Factur-X PDF results

A PDF is checked first: the embedded invoice XML (factur-x.xml, xrechnung.xml, zugferd-invoice.xml), its file specification (AFRelationship, /AF, MIME type), the XMP metadata (ConformanceLevel against the XML's profile) and PDF/A-3 (basic check, not a full PDF/A validation). Then the embedded XML gets the XML checks above. One result holds both.

  • PDF issues come first in errors and warnings, with type: "pdf", a rule from PDF-01 to PDF-21 and a location such as XMP ConformanceLevel.
  • The result has a pdf object: attachment, attachments, standard, xmp (documentType, documentFileName, version, conformanceLevel), pdfa (e.g. 3B) and xmlLevel.
  • A PDF error makes the result invalid even when the XML passes. When no invoice XML can be checked (none embedded, encrypted, ZUGFeRD 1.0), ruleSets is empty and errorMessage says so.
  • A body or multipart file sent as application/pdf (or a file named .pdf) without a %PDF- header is a 400 NOT_A_PDF.

Your own XSD or Schematron

Check the invoice against your XSD and/or your ISO Schematron instead of the built-in checks, for example a CIUS or in-house rules. Send them as multipart fields next to file. builtin=true runs the built-in checks as well. In the browser: XSD / Schematron Validator, with the limits and error codes.

FieldDescription
xsdRepeat for the main XSD and every file it includes or imports. The file name is the relative path the schemaLocation uses, e.g. common/types.xsd
xsdMainFile name of the main XSD; needed with more than one xsd
schematronOne ISO Schematron file, queryBinding "xslt2" or "xslt3"
schematronIncludeRepeat for the files the Schematron includes (sch:include, sch:extends)
builtin"true": also run the built-in checks (XSD and rule sets)
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"

curl sends only the base name of a file unless you set filename=. Issues from your files have custom: true; a problem with the files themselves is a 400 with error, code and details.

Response format

The API returns a JSON object with the validation result. The status field is either "valid", "invalid", or "error".

FieldTypeDescription
statusstring"valid", "invalid", or "error"
formatstring?"ubl" or "cii" (detected automatically)
specificationstring?e.g. "Peppol BIS Billing 3.0", "XRechnung 3.0 (CII)", "Factur-X / ZUGFeRD EN 16931" or "EN16931 (CII / Factur-X)"
schemastring?XSD the file was checked against: "UBL 2.1 Invoice", "UBL 2.1 Credit Note", "UN/CEFACT CII D16B" or a Factur-X profile XSD such as "Factur-X 1.09.2 EN 16931"
ruleSetsarrayBuilt-in rule sets that ran, e.g. "EN 16931 (UBL)" and "Peppol BIS Billing 3.0", or one of "Factur-X / ZUGFeRD MINIMUM" to "Factur-X / ZUGFeRD EXTENDED"; empty when only your XSD or Schematron ran
customobject?Your own files the file was checked against: xsd {main, files}, schematron {filename, includes, queryBinding}, builtin
errorsarrayValidation errors (rule, message, XPath location). XSD and XML syntax errors have rule "XSD" or "XML", type "xsd", and line and column
warningsarrayValidation warnings
infosarrayInformational messages
advisoriesarray?Data quality hints, not part of the official rules: type "advisory", severity "info", rules ADV-EMPTY, ADV-VAT, ADV-IBAN and ADV-ENDPOINT. They never change status, errors or warnings. Present when the built-in rules ran
invoiceNumberstring?Extracted invoice number
sellerCountrystring?Seller country code (ISO 3166-1 alpha-2)
validatedAtstringISO 8601 timestamp
errorMessagestring?Present when status is "error"
resultsUrlstringLink to shareable results page

Each hint in advisories has a rule page: data quality hints ADV-*.

Example response

{
  "fileId": "a1b2c3d4-...",
  "filename": "invoice.xml",
  "status": "invalid",
  "format": "ubl",
  "specification": "Peppol BIS Billing 3.0",
  "schema": "UBL 2.1 Invoice",
  "sellerCountry": "BE",
  "invoiceNumber": "INV-2026-001",
  "errors": [
    {
      "id": "XSD",
      "severity": "error",
      "rule": "XSD",
      "location": "/Invoice/cbc:IssueDate",
      "message": "Element 'cbc:IssueDate': '2026-13-01' is not a valid value of the atomic type 'xs:date'.",
      "type": "xsd",
      "line": 8,
      "column": 5
    },
    {
      "id": "1",
      "severity": "error",
      "rule": "BR-16",
      "location": "/Invoice",
      "message": "An Invoice shall have at least one Invoice line (BG-25)."
    }
  ],
  "warnings": [
    {
      "id": "2",
      "severity": "warning",
      "rule": "PEPPOL-EN16931-R080",
      "location": "/Invoice/cac:TaxTotal",
      "message": "Tax category tax amount must equal..."
    }
  ],
  "infos": [],
  "advisories": [],
  "validatedAt": "2026-02-17T12:00:00.000Z",
  "resultsUrl": "https://peppolvalidator.com/r/a1b2c3d4-..."
}

Quickstart

  1. No signup or API keys needed. Start calling the API immediately.
  2. Send a UBL or CII XML invoice (or a ZUGFeRD / Factur-X PDF) to POST /api/v1/validate as the request body.
  3. Check the status field in the JSON response: "valid", "invalid", or "error".
  4. If "invalid", iterate over the errors array for rule IDs, XPath locations, and fix guidance.
  5. Share the resultsUrl with your team to view results in the browser.

HTTP status codes

CodeMeaning
200Validation completed (check status field for result)
400Bad request (empty body, missing file field, exceeds 5MB)
500Server error during validation
503Validation rules temporarily unavailable

Error codes

All error responses include a machine-readable code field alongside the human-readable error message.

CodeHTTPDescription
EMPTY_BODY400No XML or PDF content in the request body
MISSING_FILE400No "file" field in multipart form data
FILE_TOO_LARGE400Uploaded file exceeds the 5MB limit
BODY_TOO_LARGE400Raw request body exceeds the 5MB limit
NOT_A_PDF400Body or file sent as application/pdf (or named .pdf) has no %PDF- header
RULES_UNAVAILABLE503Validation rules are loading; retry after 5-10 seconds
VALIDATION_FAILED500Internal error during validation; retry with exponential backoff
PDF_GENERATION_FAILED400XML could not be parsed or converted to PDF

Retry guidance

  • •400 errors: Do not retry. Fix the request based on the error code.
  • •500 errors: Retry with exponential backoff (1s, 2s, 4s). Maximum 3 retries.
  • •503 errors: Validation rules (XSD and schematron) are loading into memory. Retry after 5-10 seconds.

Monitor service health at /status or via the GET /api/v1/health endpoint.

Authentication

No authentication required. All API endpoints are publicly accessible without API keys, tokens, or signup. Send requests directly with no authorization headers.

Rate limiting may be introduced in the future. For high-volume usage (thousands of invoices per hour), reach out at hello@peppolvalidator.com.

CORS

The API sets Access-Control-Allow-Origin: * so you can call it from any domain, including browser-based applications.

XML to PDF conversion

Convert a UBL XML invoice or credit note to a professional PDF document. Send the XML and receive a PDF file in return.

POST https://peppolvalidator.com/api/v1/xml-to-pdf

cURL example

curl -X POST https://peppolvalidator.com/api/v1/xml-to-pdf \
  -H "Content-Type: application/xml" \
  -d @invoice.xml \
  -o invoice.pdf

Response

Returns application/pdf with a Content-Disposition: attachment header. On error, returns JSON with an error field and a 400 status code. Supports both raw XML body and multipart form upload (same as the validation endpoint).

Peppol MCP server

Peppol Validator exposes a Model Context Protocol (MCP) server so AI agents like Claude, ChatGPT, and others can call validation tools natively. The server uses Streamable HTTP transport.

https://peppolvalidator.com/mcp

Available tools

ToolDescription
validate_invoiceValidate a Peppol UBL or CII XML invoice (or a ZUGFeRD / Factur-X PDF as pdfBase64, with an optional filename) against its XSD, then the schematron rules for its format (EN16931 and BIS Billing 3.0, XRechnung, or the Factur-X / ZUGFeRD profile), or against your XSD and Schematron. Valid does not mean the data is correct: see what a valid result means
validate_invoice_from_urlFetch an XML invoice or a ZUGFeRD / Factur-X PDF from an HTTPS URL (≤5MB) and validate it
detect_formatCheap pre-check that returns whether an XML invoice is UBL or CII
explain_rulePlain-language explanation of a BR-/PEPPOL-EN16931-R rule code with a link to the official spec
xml_to_pdfConvert a UBL or CII XML invoice to a human-readable PDF
get_verification_challengeGet a HATCHA challenge to prove the caller is an AI agent (required before submitting feedback)
submit_feedbackReport validation accuracy issues, API problems, or general feedback (requires HATCHA verification)

Configuration

Add this to your MCP client configuration (e.g. claude_desktop_config.json or .cursor/mcp.json):

{
  "mcpServers": {
    "peppol-validator": {
      "url": "https://peppolvalidator.com/mcp"
    }
  }
}

Discovery

MCP-compatible agents can discover the server automatically via:

  • •/.well-known/mcp: server URL and metadata
  • •/.well-known/mcp/server-card.json: full server card with tool schemas

Test it

curl -X POST https://peppolvalidator.com/mcp \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'

Agent Skill

An Agent Skill is a structured instruction file that teaches AI coding agents how to use a tool. Add the Peppol Validator skill to your project and your agent can validate invoices automatically.

Download and place in your project. For Claude Code, use .claude/skills/. For other agents, use .agents/skills/.

.claude/skills/validating-peppol-invoices/SKILL.md

Or create the file manually with these contents:

---
name: validating-peppol-invoices
description: Validates Peppol UBL XML invoices and credit notes
  against the UBL 2.1 XSD, EN16931 (CEN) and Peppol BIS Billing 3.0
  schematron rules via the Peppol Validator API. Use when validating
  e-invoices, checking Peppol compliance, debugging UBL XML
  validation errors, or verifying invoices before sending via
  the Peppol network.
---

# Validating Peppol Invoices

POST XML to the Peppol Validator API and parse the JSON result.

## Endpoint

\`POST https://peppolvalidator.com/api/v1/validate\`

Send raw XML with \`Content-Type: application/xml\`:

```bash
curl -X POST https://peppolvalidator.com/api/v1/validate \
  -H "Content-Type: application/xml" \
  -d @invoice.xml
```

Returns JSON with status ("valid" / "invalid" / "error"),
errors, warnings, and invoice metadata.

A ZUGFeRD / Factur-X PDF goes to the same endpoint with
\`Content-Type: application/pdf\` (curl \`--data-binary @invoice.pdf\`).
Its PDF issues have type "pdf" and rules PDF-01 to PDF-21.
Data quality hints (rules ADV-*, not part of the official
rules) are in a separate advisories array.

What your agent can do with it

  • •Validate XML invoices during development and flag issues inline
  • •Run validation as part of CI/CD before deploying invoice generation code
  • •Debug Peppol validation errors by examining rule IDs and locations
  • •Verify fixes by re-validating after changes

Try it now

Validate an invoice via the web UI or send one to the API. No signup needed.

Common questions

Is the API free, and do I need an API key?

Yes. The API is free with no signup or API key required. We may introduce rate limits in the future if needed, but casual and CI/CD usage is welcome.

What validation rules are applied?

Every file is first checked against its XML Schema, then against the schematron rules for its format. UBL invoices: UBL 2.1 Invoice or Credit Note XSD, CEN EN16931 business rules (BR-*) and Peppol BIS Billing 3.0 rules (PEPPOL-EN16931-*). Factur-X / ZUGFeRD invoices (MINIMUM, BASIC WL, BASIC, EN 16931, EXTENDED, detected from BT-24; the plain EN 16931 identifier counts as the EN 16931 profile inside a Factur-X / ZUGFeRD PDF only): the official Factur-X 1.09.2 / ZUGFeRD 2.5.2 XSD and schematron of the profile, which contain the EN16931 rules of the profile (BR-*), structure and code list rules (FX-SCH-A-*) and, for EXTENDED, BR-FXEXT-*. XRechnung (UBL or CII): EN16931 plus the KoSIT rules (BR-DE-*). Other CII invoices, including standalone XML with the plain EN 16931 identifier: UN/CEFACT CII D16B XSD and CEN EN16931 rules, syntax rules (CII-SR-*) and code lists. The format is detected automatically.

What file formats are supported?

UBL 2.1 XML invoices and credit notes (Peppol BIS Billing 3.0), CII XML invoices (EN16931 / Factur-X / ZUGFeRD) and ZUGFeRD / Factur-X PDFs. The format is detected automatically from the XML namespace; a PDF by its %PDF- header. For a PDF the API checks the embedded XML, the XMP metadata and PDF/A-3 (basic check), then validates the embedded XML.

Is there a rate limit?

There is no hard rate limit today. Please be reasonable. If you need to validate thousands of invoices per hour, reach out first.

What is an Agent Skill?

An Agent Skill is a structured instruction file that tells AI coding agents (like Claude Code, Cursor, Windsurf, etc.) how to use a tool. By adding the Peppol Validator skill to your project, your AI agent can validate invoices during development without manual steps.