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/validateOption 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.xmlOption 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.pdfJavaScript / 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
errorsandwarnings, withtype: "pdf", a rule from PDF-01 to PDF-21 and alocationsuch asXMP ConformanceLevel. - The result has a
pdfobject:attachment,attachments,standard,xmp(documentType, documentFileName, version, conformanceLevel),pdfa(e.g. 3B) andxmlLevel. - A PDF error makes the result
invalideven when the XML passes. When no invoice XML can be checked (none embedded, encrypted, ZUGFeRD 1.0),ruleSetsis empty anderrorMessagesays so. - A body or multipart file sent as
application/pdf(or a file named .pdf) without a%PDF-header is a 400NOT_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.
| Field | Description |
|---|---|
xsd | Repeat 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 |
xsdMain | File name of the main XSD; needed with more than one xsd |
schematron | One ISO Schematron file, queryBinding "xslt2" or "xslt3" |
schematronInclude | Repeat 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".
| Field | Type | Description |
|---|---|---|
status | string | "valid", "invalid", or "error" |
format | string? | "ubl" or "cii" (detected automatically) |
specification | string? | e.g. "Peppol BIS Billing 3.0", "XRechnung 3.0 (CII)", "Factur-X / ZUGFeRD EN 16931" or "EN16931 (CII / Factur-X)" |
schema | string? | 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" |
ruleSets | array | Built-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 |
custom | object? | Your own files the file was checked against: xsd {main, files}, schematron {filename, includes, queryBinding}, builtin |
errors | array | Validation errors (rule, message, XPath location). XSD and XML syntax errors have rule "XSD" or "XML", type "xsd", and line and column |
warnings | array | Validation warnings |
infos | array | Informational messages |
advisories | array? | 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 |
invoiceNumber | string? | Extracted invoice number |
sellerCountry | string? | Seller country code (ISO 3166-1 alpha-2) |
validatedAt | string | ISO 8601 timestamp |
errorMessage | string? | Present when status is "error" |
resultsUrl | string | Link 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
- No signup or API keys needed. Start calling the API immediately.
- Send a UBL or CII XML invoice (or a ZUGFeRD / Factur-X PDF) to
POST /api/v1/validateas the request body. - Check the
statusfield in the JSON response:"valid","invalid", or"error". - If
"invalid", iterate over theerrorsarray for rule IDs, XPath locations, and fix guidance. - Share the
resultsUrlwith your team to view results in the browser.
HTTP status codes
| Code | Meaning |
|---|---|
200 | Validation completed (check status field for result) |
400 | Bad request (empty body, missing file field, exceeds 5MB) |
500 | Server error during validation |
503 | Validation rules temporarily unavailable |
Error codes
All error responses include a machine-readable code field alongside the human-readable error message.
| Code | HTTP | Description |
|---|---|---|
EMPTY_BODY | 400 | No XML or PDF content in the request body |
MISSING_FILE | 400 | No "file" field in multipart form data |
FILE_TOO_LARGE | 400 | Uploaded file exceeds the 5MB limit |
BODY_TOO_LARGE | 400 | Raw request body exceeds the 5MB limit |
NOT_A_PDF | 400 | Body or file sent as application/pdf (or named .pdf) has no %PDF- header |
RULES_UNAVAILABLE | 503 | Validation rules are loading; retry after 5-10 seconds |
VALIDATION_FAILED | 500 | Internal error during validation; retry with exponential backoff |
PDF_GENERATION_FAILED | 400 | XML 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-pdfcURL example
curl -X POST https://peppolvalidator.com/api/v1/xml-to-pdf \
-H "Content-Type: application/xml" \
-d @invoice.xml \
-o invoice.pdfResponse
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/mcpAvailable tools
| Tool | Description |
|---|---|
validate_invoice | Validate 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_url | Fetch an XML invoice or a ZUGFeRD / Factur-X PDF from an HTTPS URL (≤5MB) and validate it |
detect_format | Cheap pre-check that returns whether an XML invoice is UBL or CII |
explain_rule | Plain-language explanation of a BR-/PEPPOL-EN16931-R rule code with a link to the official spec |
xml_to_pdf | Convert a UBL or CII XML invoice to a human-readable PDF |
get_verification_challenge | Get a HATCHA challenge to prove the caller is an AI agent (required before submitting feedback) |
submit_feedback | Report 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.
Install
Download SKILL.mdDownload and place in your project. For Claude Code, use .claude/skills/. For other agents, use .agents/skills/.
.claude/skills/validating-peppol-invoices/SKILL.mdOr 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.