# E-Invoice Validator: XRechnung, ZUGFeRD, Peppol BIS 3, EN16931 (`fractionalhqforyou/eu-einvoice-validator`) Actor

Validates e-invoices against the official EU artefacts and returns every failed rule with its id, severity, message and location. XML or hybrid PDF. XRechnung 3.0, Peppol BIS 3, EN 16931, NLCIUS, Factur-X, plus Facturae, FatturaPA and KSeF FA(3). No key, no proxy, no browser.

- **URL**: https://apify.com/fractionalhqforyou/eu-einvoice-validator.md
- **Developed by:** [Jessy Mariau](https://apify.com/fractionalhqforyou) (community)
- **Categories:** Developer tools, Integrations, MCP servers
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 1,000 invoice validateds

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## E-Invoice Validator: XRechnung, ZUGFeRD/Factur-X, Peppol BIS 3, EN16931

German businesses have had to be able to receive electronic invoices since January 2025, and the deadline to start issuing them lands in 2027 for larger companies and 2028 for the rest. The rest of the EU is on the same road. So an XML file arrives, your system swallows it or spits it out, and nobody can tell you which of the several hundred numbered rules it broke or where.

The rules are public. They ship as Schematron from the people who wrote the standard: the EN 16931 core rules from the European Commission, XRechnung from KoSIT, Peppol from OpenPEPPOL. Running them is the fiddly part, because they need an XSLT 2.0 engine and a build step. I did that once so you do not have to. Point this Actor at a file and it hands back the same verdict a national validator would, rule by rule.

### Auch auf Deutsch / En français / En español / In italiano / In het Nederlands / Po polsku

**Deutsch:** Prüft Rechnungen im Format XRechnung 3.0 und ZUGFeRD (das eingebettete CII-XML im PDF) gegen die offiziellen Prüfregeln von KoSIT und EN 16931, und nennt jede verletzte Regel mit Nummer und Fundstelle.

**Français :** Contrôle les factures Factur-X et Peppol BIS 3 en extrayant le XML CII du PDF, puis les valide avec les règles officielles EN 16931, et renvoie chaque règle non respectée avec son identifiant.

**Español:** Comprueba facturas Facturae 3.2.2 con el esquema XSD oficial, y facturas EN 16931 y Peppol BIS 3 con las reglas oficiales, devolviendo cada regla incumplida con su identificador.

**Italiano:** Verifica le fatture FatturaPA 1.2.3 con lo schema XSD ufficiale, e le fatture EN 16931 e Peppol BIS 3 con le regole ufficiali, restituendo ogni regola non rispettata con il suo identificativo.

**Nederlands:** Controleert facturen volgens NLCIUS / SI-UBL 2.0 en Peppol BIS 3 met de officiële validatieregels, en geeft elke gefaalde regel terug met het regelnummer en de plek in het bestand.

**Polski:** Sprawdza faktury KSeF FA(3) na podstawie oficjalnego schematu XSD Ministerstwa Finansów, a faktury EN 16931 i Peppol BIS 3 na podstawie oficjalnych regul, zwracajac kazda zlamana regule wraz z jej numerem.

### What it does

You give it invoice files. It works out what each one is, runs the right official rule sets over it, and returns one row per invoice.

Detection is from the document itself, not from the file name. The root element and its namespace say which of the six supported XML formats you handed over, and the `format` column in the table below lists all six. A PDF is opened and the embedded invoice XML pulled out of it, which is how a hybrid file works: the human-readable PDF carries the machine-readable XML as an attachment called `factur-x.xml`, `zugferd-invoice.xml` or `xrechnung.xml`.

Then it reads the profile the document claims. UBL says so in `CustomizationID` and CII in the guideline parameter, and the value is a long URN naming XRechnung 3.0, Peppol BIS Billing 3.0, NLCIUS, Factur-X or plain EN 16931. By default the Actor runs the EN 16931 core rules plus the rules for whatever profile the document declares, so a German invoice gets both the European rules and the German ones. You can override that and force any list of profiles instead.

Every failure comes back with its rule id, the severity the rule itself carries, the message in the language the rule authors wrote it in, and an XPath to the element at fault. So you get `BR-DE-15`, not "invalid".

The exact artefacts, their versions and the URL each one was downloaded from are recorded in `artefacts/SOURCES.md` inside the Actor. The Schematron runs through SaxonJS and produces SVRL, the same output any other conformant validator produces. Nothing is reimplemented and no rule is approximated.

### What you need

Files. That is all. There is no API key, no account anywhere, no proxy and no browser.

Run it with nothing filled in and you get a demo: four bundled sample invoices from the official test suites, one valid XRechnung, one with three deliberate rule breaks, one Peppol invoice and one Factur-X PDF. Nothing is charged for the demo.

### How to run it

A list of files:

```json
{
  "invoices": "https://example.com/invoice-de.xml\nhttps://example.com/facture.pdf\nhttps://example.com/peppol.xml",
  "failOnWarning": false,
  "maxInvoices": 100
}
```

One pasted invoice, checked against a profile you name rather than the one it declares:

```json
{
  "inlineXml": "<?xml version=\"1.0\"?><Invoice xmlns=\"urn:oasis:names:specification:ubl:schema:xsd:Invoice-2\">...</Invoice>",
  "profiles": ["en16931", "peppol-bis-3"]
}
```

The `OUTPUT` record in the key-value store holds the run summary: invoices processed, how many were valid, how many events were charged and how many documents each profile checked.

There is also a Standby endpoint that speaks MCP, so an agent can call `validate_einvoices` as a tool with URLs or pasted XML and get the same rows back directly.

### Output fields

| Field | What it holds |
|---|---|
| `input` | the URL you gave, or `inlineXml` |
| `format` | `ubl-invoice`, `ubl-creditnote`, `cii`, `facturae`, `fatturapa`, `ksef` or `unknown` |
| `declared_profile` | the profile the document claims: `xrechnung`, `peppol-bis-3`, `nlcius`, `facturx-zugferd`, `en16931` |
| `declared_specification` | the raw specification URN, exactly as written in the file |
| `profiles_checked` | the rule sets that actually ran |
| `profiles_skipped` | rule sets that have no official artefact for this syntax, named so silence never reads as a pass |
| `valid` | true when at least one rule set ran and nothing fatal failed |
| `fatal_count`, `warning_count` | findings by severity |
| `failures` | every finding: `profile`, `rule_id`, `severity`, `message`, `location` |
| `invoice_number`, `issue_date`, `seller_name`, `buyer_name`, `total` | read out of the document so the row is readable without opening the file |
| `pdf_attachment` | for a hybrid PDF, the name of the attachment the XML came from |
| `error` | why a file could not be read at all, or null |
| `checked_at` | when the run happened |

### Pricing

Pay per event: one `invoice-validated` charge per invoice actually read and validated. A URL that will not fetch, a file that is neither XML nor PDF, a PDF with nothing embedded in it, or XML that will not parse costs you nothing. Neither does the demo. The price is per invoice however many rule sets run over it, so checking a German invoice against both EN 16931 and XRechnung is one event, not two.

### Limits

Covered and verified against the official test suites: **EN 16931 core** for UBL and CII (validation 1.3.16), **XRechnung 3.0** for UBL and CII (KoSIT Schematron 2.6.0), **Peppol BIS Billing 3.0** for UBL (3.0.21, the 2026 May release that became mandatory on 17 August 2026), **NLCIUS / SI-UBL 2.0** (2.0.3.13, in effect from the same date), and **Factur-X and ZUGFeRD** hybrid PDFs by extracting the embedded CII. **Facturae 3.2.2**, **FatturaPA 1.2.3** and **KSeF FA(3)** are checked against their official XSD schemas, which for those three formats is the rule set.

Not covered, and I would rather say so than fake it. The Factur-X and ZUGFeRD profile Schematron is published inside the FNFE-MPE and FeRD specification package rather than at a free stable URL, so a hybrid PDF is checked against the EN 16931 CII rules and not against the profile's own extra rules. Romania's RO\_CIUS has no freely downloadable Schematron, so e-Factura is not covered at all. Peppol CII is not covered, because Peppol BIS Billing 3.0 is a UBL profile. Spain's Verifactu is a different regime that certifies invoicing software rather than invoice files, and nothing here says anything about it.

One more thing worth being straight about. This checks the standard's rules, and the national rules where the standard has been extended. It does not tell you a tax authority will accept the invoice. A tax office also checks registration, VAT numbers against its own register, timing and duplicates, and it can reject a file that passes every rule here. Treat a green row as "the format and the arithmetic are right", which is where most rejections actually come from, and not as clearance.

Files are capped at 20 MB each and a run at 1000 invoices. Invoice contents are never logged.

### Pair it with a workflow

If you run n8n, my free templates on https://n8n.io/creators/fractional-hq take rows like these and turn them into a supplier chase, a rejection notice or a queue of files to fix, without another tool in the chain.

If you want to see how this was built and where I drew the line on what it covers, I wrote it up at https://fractionalhq.uk/use-cases/einvoice-validation

### Built by Fractional HQ

I run Fractional HQ, a one-man studio for design and automation. I do not write code by hand: I architect, review and hold the standard while AI agents do the typing. That method runs my own estate: seven brands, a Postgres operations system, more than 100 scheduled jobs, published n8n templates, Zapier Silver certified.

Want this wired into your CRM and outreach, or built and handed over working? Get in touch: https://fractionalhq.uk/brief

# Actor input Schema

## `invoices` (type: `string`):

One URL per line. Each can be an invoice XML or a hybrid PDF with the invoice embedded (Factur-X, ZUGFeRD, XRechnung). Any public URL works, including an Apify key-value store record URL. Leave empty and leave the pasted XML empty too to run the free demo over the bundled sample invoices.

## `inlineXml` (type: `string`):

A single invoice pasted straight in, instead of a URL. Checked alongside any URLs above.

## `profiles` (type: `array`):

Leave on auto to run the EN 16931 core rules plus whatever profile the document declares. Pick one or more to force them instead. A profile with no official artefact for the document's syntax is reported as skipped, never as a pass.

## `failOnWarning` (type: `boolean`):

Off by default, so an invoice is valid when it has no fatal rule failures. Switch on and any warning or information-level finding makes it invalid too.

## `maxInvoices` (type: `integer`):

Cap on how many files to read from the list. Each invoice actually read and checked is one charged event.

## Actor input object example

```json
{
  "profiles": [
    "auto"
  ],
  "failOnWarning": false,
  "maxInvoices": 100
}
```

# Actor output Schema

## `invoices` (type: `string`):

One row per invoice: detected syntax, declared profile, which rule sets ran, valid or not, fatal and warning counts, every failed rule with its id, severity, message and location, plus the invoice number, date, party names and total read from the document.

## `summary` (type: `string`):

The OUTPUT record: how many invoices were processed, how many were valid, how many events were charged and how many documents each profile checked.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "invoices": ""
};

// Run the Actor and wait for it to finish
const run = await client.actor("fractionalhqforyou/eu-einvoice-validator").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "invoices": "" }

# Run the Actor and wait for it to finish
run = client.actor("fractionalhqforyou/eu-einvoice-validator").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "invoices": ""
}' |
apify call fractionalhqforyou/eu-einvoice-validator --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,fractionalhqforyou/eu-einvoice-validator"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/cK1nnrz1xQr5EzVV3/builds/b9B1cNNSD35NztnXP/openapi.json
