# Peppol BIS 3.0 / UBL Invoice Validator (EN 16931) (`fetchlab/peppol-invoice-validator`) Actor

Validate UBL invoices and credit notes against the official Peppol BIS Billing 3.0 and EN 16931 rules: XSD, CEN Schematron and Peppol Schematron. Returns every failed rule with its ID, message and location, plus an invoice summary.

- **URL**: https://apify.com/fetchlab/peppol-invoice-validator.md
- **Developed by:** [fetchlab](https://apify.com/fetchlab) (community)
- **Categories:** Business, Developer tools, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$20.00 / 1,000 document validateds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## Peppol BIS 3.0 / UBL Invoice Validator (EN 16931)

Check UBL invoices and credit notes against **the enabled official document rules** before sending them. Passing these checks does not guarantee network acceptance, registration of either party or compliance with every national extension. The Actor runs three available layers using the **official rule sets** published by OpenPeppol and CEN:

1. **UBL 2.1 XSD** — is the XML structurally valid (right elements, right order, right types)?
2. **EN 16931 Schematron (CEN)** — the European core invoice business rules: mandatory fields, totals that add up, VAT breakdown consistency, code lists (BR-*, BR-CO-*, BR-CL-*, BR-S-*, BR-E-\*…).
3. **Peppol BIS Billing 3.0 Schematron** — the Peppol-specific rules on top: business process and customization IDs, endpoint identifier schemes, country-specific requirements (PEPPOL-EN16931-R\*, PEPPOL-EN16931-CL\*, PEPPOL-COMMON-R\*).

For every document you get a clear verdict (`valid: true/false`), the full list of failed official rules with **rule ID, message and location in the document**, and a summary of the invoice header (number, dates, parties, endpoints, totals).

### Why you need this

Belgian B2B e-invoicing requirements and the French reform create demand for structured invoice workflows, but national delivery requirements differ. This Actor verifies only the scope described below; it is not a legal or tax eligibility check. See the [Belgian official guidance](https://efacture.belgium.be/fr/article/quest-ce-quune-facture-electronique) and [French official guidance](https://www.impots.gouv.fr/facturation-electronique-et-plateformes-agreees).

Typical uses:

- **ERP / accounting integrations** — validate each generated UBL file in your pipeline; block or fix the invalid ones before they leave.
- **Software vendors** — regression-test your UBL generator against the current rule release every time Peppol updates it.
- **Accountants and e-invoicing consultants** — check a client's files in bulk and get a readable list of what is wrong.
- **AI agents** — a single tool call that answers "is this invoice Peppol-compliant, and if not, why?".

### Input

| Field | Description |
|---|---|
| `documents` | Complete UBL Invoice or CreditNote XML documents, one per item (paste the whole file). |
| `urls` | Optional. URLs of XML files to download and validate (public URLs or Apify key-value store URLs). |
| `ruleSet` | `peppol-bis-billing-3` (default: EN 16931 + Peppol rules) or `en16931-only` (CEN rules only). |
| `xsdValidation` | Run the UBL 2.1 XSD check first (default `true`). |
| `includeWarnings` | Report non-blocking rule failures too (default `true`). Warnings never make a document invalid. |
| `includeTestExpressions` | Add each failed rule's XPath test to the output (for developers). |
| `maxDocuments` | Safety limit per run (default 500). |

Supported documents: UBL 2.1 `Invoice` and `CreditNote` (the two document types of Peppol BIS Billing 3.0). Other UBL documents and CII (Cross Industry Invoice) are reported as unsupported.

### Output

One record per document:

```json
{
    "source": "document #1",
    "documentType": "Invoice",
    "valid": false,
    "xsdValid": true,
    "errorCount": 2,
    "warningCount": 0,
    "errors": [
        {
            "ruleSet": "EN 16931 (CEN)",
            "ruleId": "BR-CO-16",
            "severity": "error",
            "message": "[BR-CO-16]-Amount due for payment (BT-115) = Invoice total amount with VAT (BT-112) - Paid amount (BT-113) + Rounding amount (BT-114).",
            "location": "Invoice/LegalMonetaryTotal"
        },
        {
            "ruleSet": "Peppol BIS Billing 3.0",
            "ruleId": "PEPPOL-EN16931-R001",
            "severity": "error",
            "message": "[PEPPOL-EN16931-R001]-Business process MUST be provided.",
            "location": "Invoice"
        }
    ],
    "warnings": [],
    "summary": {
        "documentId": "2026-0042",
        "issueDate": "2026-09-01",
        "dueDate": "2026-10-01",
        "typeCode": "380",
        "currency": "EUR",
        "customizationId": "urn:cen.eu:en16931:2017#compliant#urn:fdc:peppol.eu:2017:poacc:billing:3.0",
        "profileId": null,
        "supplierName": "Supplier SRL",
        "supplierEndpoint": "0208:0202239951",
        "supplierVat": "BE0202239951",
        "customerName": "Customer NV",
        "customerEndpoint": "0208:0214596464",
        "customerVat": "BE0214596464",
        "lineCount": 2,
        "taxExclusiveAmount": 1300.0,
        "taxInclusiveAmount": 1573.0,
        "payableAmount": 1573.0,
        "taxAmount": 273.0
    },
    "ruleVersions": {
        "xsd": "OASIS UBL 2.1 XSD",
        "cen": "CEN/EN 16931-1 UBL Schematron 1.3.16 (2026-04-10)",
        "peppol": "Peppol BIS Billing 3.0 rules — release 3.0.21 (May 2026)"
    },
    "durationMs": 540,
    "validatedAt": "2026-09-13T20:00:00.000Z",
    "fatalError": null
}
```

Malformed XML or unsupported document types produce a record with `valid: false` and a single `XML` error explaining why.

### Meaning of a positive result

`valid` reports the enabled official rule checks only; `xsdValid: null` means XSD was skipped. `validationScope` is `document-rules-only` and `networkAcceptance` is `not-checked`. Inspect `supplementaryChecks` separately: it checks that GS1 GLN endpoint identifiers use 13 digits, a shape condition not fully covered by the embedded checksum rule. These extra checks do not change the official `valid` verdict. Passing either set does not prove registration or delivery.

Omitting both `documents` and `urls` runs a synthetic demo invoice, which is not an invoice to send. Supplying either source disables the demo; explicitly empty sources are rejected.

### Rule sets and versions

The Actor embeds the current official artefacts: the CEN/EN 16931-1 UBL Schematron and the Peppol BIS Billing 3.0 Schematron from [docs.peppol.eu](https://docs.peppol.eu/poacc/billing/3.0/), plus the OASIS UBL 2.1 XSD. The exact versions are returned in every record (`ruleVersions`). Peppol publishes rule updates twice a year (spring and autumn); this Actor is updated within days of each release.

### Pricing

Pay per event: a fixed price per document validated, whether it passes or fails: **USD 0.02 per document**. No subscription. The engine can process a small document in under a second; run startup and downloads add latency.

### Using it from your code

```js
// Apify JS client
const run = await client.actor('fetchlab/peppol-invoice-validator').call({
    documents: [xmlString],
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
if (!items[0].valid) console.log(items[0].errors);
```

```python
## Apify Python client
run = client.actor("fetchlab/peppol-invoice-validator").call(run_input={"documents": [xml_string]})
result = client.dataset(run["defaultDatasetId"]).list_items().items[0]
```

Works with the Apify API, the Apify MCP server (so AI agents can call it as a tool), Make, n8n, Zapier and scheduled runs.

### Notes and limits

- Validation covers the document itself. It does not check whether the receiver is registered on Peppol — [Peppol Directory Lookup](https://apify.com/fetchlab/peppol-lookup) provides voluntary Directory records, not a definitive routing check.
- Country-specific extensions that sit on top of BIS Billing 3.0 (e.g. national CIUS rules beyond Peppol's own country rules) are not included.
- Documents up to 20 MB. Very large invoices (thousands of lines) take a few seconds each.

### Related

[Peppol Directory Lookup](https://apify.com/fetchlab/peppol-lookup) — find a company in the Peppol Directory. More e-invoicing and company-data tools by [fetchlab](https://apify.com/fetchlab).

# Actor input Schema

## `documents` (type: `array`):

One complete UBL Invoice or CreditNote XML per item. Paste the whole file content.

## `urls` (type: `array`):

Optional. Public URLs (or Apify key-value store URLs) of UBL XML files to download and validate.

## `ruleSet` (type: `string`):

Peppol BIS Billing 3.0 = EN 16931 rules + Peppol rules (what a Peppol Access Point enforces). EN 16931 only = the European core invoice rules without the Peppol-specific ones.

## `xsdValidation` (type: `boolean`):

Check the XML structure against the OASIS UBL 2.1 schema before running the business rules.

## `includeWarnings` (type: `boolean`):

Also report non-blocking rule failures (severity 'warning'). They do not make a document invalid.

## `includeTestExpressions` (type: `boolean`):

Add the Schematron XPath test of each failed rule to the output (useful for developers, verbose).

## `maxDocuments` (type: `integer`):

Safety limit on the number of documents validated in one run.

## Actor input object example

```json
{
  "ruleSet": "peppol-bis-billing-3",
  "xsdValidation": true,
  "includeWarnings": true,
  "includeTestExpressions": false,
  "maxDocuments": 500
}
```

# Actor output Schema

## `results` (type: `string`):

All validation results.

# 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("fetchlab/peppol-invoice-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 = {}

# Run the Actor and wait for it to finish
run = client.actor("fetchlab/peppol-invoice-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 '{}' |
apify call fetchlab/peppol-invoice-validator --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,fetchlab/peppol-invoice-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/WCsa5YkxTzjD5noFy/builds/LK7hEXmv0d0MGmnqw/openapi.json
