# Factur-X Validator & Generator: ZUGFeRD, EN 16931, FR 2026 (`rigel/facturx-toolkit`) Actor

Validate Factur-X/ZUGFeRD invoices (XSD, official schematrons, French 2026 BR-FR rules, PDF/A-3), extract them to JSON, generate PDF/A-3 Factur-X with totals and French legal notes, or convert a plain PDF invoice (OCR included). Each check explained. Not an approved platform (PA).

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

## Pricing

Pay per event

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?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

**Validate, read and create Factur-X / ZUGFeRD e-invoices for the French 2026 reform (EN 16931 + French BR-FR rules, PDF/A-3), with every check explained, or turn a plain PDF invoice into Factur-X.**

### What is Factur-X Toolkit?

Factur-X Toolkit is a **Factur-X validator, extractor and generator** that runs as an API on Apify. It handles **Factur-X 1.0x / ZUGFeRD 2.x** hybrid invoices (a PDF/A-3 with embedded CII XML), plus CII and UBL XML files. Five modes:

| Mode | What it does | Typical user |
|---|---|---|
| `validate` | checks a PDF or XML, layer by layer, and explains every error | accounts payable, developers testing their output |
| `extract` | turns a Factur-X / CII / UBL invoice into clean JSON (summary + full EN 16931 model) | AP automation, ERP import |
| `validate-and-extract` | both in one pass | invoice inboxes |
| `generate` | builds a PDF/A-3 Factur-X invoice from simple JSON, totals and VAT computed, self-validated | developers, small invoicing tools |
| `from-pdf` | reads a plain PDF invoice (OCR included) and produces a Factur-X when every check passes | firms migrating from PDF invoices |

Validation and generation run inside the Actor with open-source libraries: no third-party invoice API. The only optional external call is AI field-filling in `from-pdf`, used only if you supply your own API key.

### Why use it

- **Layered validation, each layer reported separately:** XML well-formedness → official XSD → **official Factur-X 1.09.2 schematron for the detected profile** (MINIMUM, BASIC WL, BASIC, EN 16931, EXTENDED, including EN 16931 BR-xx rules) → **French 2026 rules (BR-FR)** (mandatory notes PMT / PMD / AAB, billing mode BT-23, SIREN, electronic addresses…) → **PDF/A-3 container** (XMP, AFRelationship, profile) → optional **full PDF/A-3 check with veraPDF**.
- **Two clear verdicts:** `valid` (EN 16931 / Factur-X conformance) and `frenchCtcReady` (also passes the French rules), a one-line French summary (`summaryFr`), and every rule with id, severity, message and XPath.
- **Generation that does the boring parts:** totals and VAT breakdown computed (rounding consistent with BR-CO rules), French legal notes added (€40 recovery fee, late penalties, discount), billing mode inferred, VAT franchise ("art. 293 B du CGI"), credit notes, foreign currency, intra-EU and export cases. Output: PDF/A-3b with embedded fonts + XML.
- **Round trip:** the `semantic` output of `extract` can be edited and fed back into `generate`.

### Use cases

- **Check supplier invoices received since September 2026** before booking them.
- **Test the Factur-X files your software produces** against the official schematrons and the BR-FR rules before going live with your plateforme agréée.
- **Extract invoice data to JSON** (number, dates, parties, totals, VAT, lines, IBAN) for your ERP or accounting tool.
- **Generate self-validated Factur-X invoices (EN 16931)** from your own app, Make, Zapier or n8n, without maintaining a PDF/A-3 stack.
- **Convert legacy PDF invoices** into Factur-X, with a review flag when a field is uncertain.

### Input examples

Validate a fully fictitious demo invoice (Factur-X EN 16931 generated without the French mandatory notes; runs in one click as the public example task [valider-facture-factur-x-2026](https://apify.com/rigel/facturx-toolkit/examples/valider-facture-factur-x-2026)):

```json
{
  "mode": "validate",
  "documents": [{ "name": "facturx-demo-fictive-sans-mentions.pdf", "base64": "<the PDF encoded in base64>" }],
  "frenchCtcRules": true
}
```

Generate an invoice:

```json
{
  "mode": "generate",
  "invoices": [{
    "number": "FA-2026-0042", "issueDate": "2026-10-16", "dueDate": "2026-11-15",
    "iban": "FR7630006000011234567890189",
    "seller": { "name": "Exemple Fictif SARL", "address": "1 rue de la Démonstration", "postcode": "75011", "city": "Paris", "siren": "687675066", "vat": "FR13687675066" },
    "buyer": { "name": "Démo Client SAS", "address": "2 avenue de l'Exemple", "postcode": "69002", "city": "Lyon", "siren": "345253645" },
    "lines": [{ "name": "Développement", "quantity": 3, "unitPrice": 450, "unit": "DAY", "vatRate": 20 }]
  }]
}
```

Turn a plain PDF into Factur-X:

```json
{ "mode": "from-pdf", "documents": [{ "url": "https://example.com/facture.pdf" }], "frenchCtcRules": true }
```

### Output examples

Real result of the `validate` input above (run of 17 October 2026; abridged). The demo invoice is a conformant EN 16931 Factur-X, but it lacks the notes required by the French 2026 rules, so `frenchCtcReady` is `false`:

```json
{
  "mode": "validate",
  "document": "facturx-demo-fictive-sans-mentions.pdf",
  "validation": {
    "inputType": "pdf",
    "flavor": "factur-x",
    "profile": "en16931",
    "steps": { "wellFormed": true, "xsd": true, "profileSchematron": true, "frenchCtcRules": false, "pdfContainer": true },
    "pdf": { "pdfaPart": "3", "pdfaConformance": "B", "embeddedFiles": ["factur-x.xml"] },
    "errorCount": 3,
    "warningCount": 0,
    "valid": true,
    "frenchCtcReady": false,
    "summaryFr": "Conforme Factur-X profil en16931 ; règles françaises 2026 (BR-FR) : 3 écart(s).",
    "issues": [
      {"ruleSet": "fr-ctc-2026", "ruleId": "BR-FR-05_BT-22_PMT", "severity": "error", "message": "BR-FR-05/BT-22 : La mention relative aux frais de recouvrement (code PMT) est absente. Elle est obligatoire dans les notes (BG-1)."},
      {"ruleSet": "fr-ctc-2026", "ruleId": "BR-FR-05_BT-22_PMD", "severity": "error", "message": "BR-FR-05/BT-22 : La mention relative aux pénalités de retard (code PMD) est absente. Elle est obligatoire dans les notes (BG-1)."},
      {"ruleSet": "fr-ctc-2026", "ruleId": "BR-FR-05_BT-22_AAB", "severity": "error", "message": "BR-FR-05/BT-22 : La mention relative à l’escompte ou à son absence (code AAB) est absente. Elle est obligatoire dans les notes (BG-1)."}
    ]
  },
  "charged": true
}
```

Real result of a `from-pdf` run on a fully fictitious demo invoice (17 October 2026; abridged):

```json
{
  "mode": "from-pdf",
  "document": "facture-demo-fictive.pdf",
  "textSource": "native",
  "fields": {
    "invoiceNumber": "DEMO-2026-001", "issueDate": "2026-10-01", "dueDate": "2026-10-31", "currency": "EUR",
    "sellerName": "Exemple Fictif SARL", "sellerSiren": "687675066", "sellerVat": "FR13687675066",
    "buyerName": "Démo Client SAS", "buyerVat": "FR88345253645",
    "totalNet": "500.00", "totalVat": "100.00", "totalGross": "600.00"
  },
  "checks": { "totalsConsistent": true, "sirenValid": true, "sellerVatKeyValid": true, "vatMatchesSiren": true },
  "needsReview": false,
  "overallConfidence": 0.85,
  "legalNotesFound": ["PMT", "PMD", "AAB"],
  "facturx": { "valid": true, "frenchCtcReady": false, "typeCode": "380", "summaryFr": "Conforme Factur-X profil en16931 ; règles françaises 2026 (BR-FR) : 1 écart(s)." },
  "pdfUrl": "https://api.apify.com/v2/key-value-stores/…/records/facturx-DEMO-2026-001-a1ef2a9e.pdf",
  "charged": true
}
```

Generated PDFs and XMLs are saved in the run's key-value store; the dataset returns their URLs. Anyone who has a URL can download the file.

### How much does it cost?

Pay per event, in USD. You pay only for a successful, deduplicated result:

| Event | When | Price |
|---|---|---|
| `document-validated` | XSD + profile schematron + BR-FR + PDF container check | $0.015 |
| `document-extracted` | Factur-X / CII / UBL to JSON | $0.008 |
| `document-validated-extracted` | both in one pass | $0.02 |
| `invoice-generated` | PDF/A-3 Factur-X + XML, charged only when the result is `valid` (EN 16931) | $0.04 |
| `pdfa-full-check` | veraPDF add-on (`fullPdfaCheck`); not charged on timeout or empty report | $0.005 |
| `invoice-from-pdf` | `from-pdf`: a valid Factur-X produced, OCR included | $0.04 |
| `pdf-read` | `from-pdf`: PDF read and returned for review, no valid Factur-X, OCR included | $0.01 |
| `apify-actor-start` (Apify platform event) | once per run, per GB of memory (default 2 GB = 2 events) | $0.00005 |

Examples: validating 100 received invoices costs $1.50; validating and extracting them costs $2.00; generating 100 invoices costs $4.00.

**Free:** unreadable files (not a PDF or XML, download error, private URL), invalid generated invoices, and duplicates within a run. `invoice-from-pdf` and `pdf-read` are never both charged for the same file. When your spending limit is reached the Actor stops and never charges beyond it (minimum limit: $0.05).

### FAQ

#### What changed for French e-invoicing on 1 September 2026?

Since **1 September 2026, every business established in France and subject to VAT must be able to receive electronic invoices**, whatever its size (micro-entrepreneurs included). Large companies and mid-sized companies (ETI) must also issue e-invoices and send e-reporting data from that date. SMEs and micro-businesses must issue them from **1 September 2027**. Invoices are exchanged through a **plateforme agréée** (approved platform), directly or via compatible software connected to one. Sources: [economie.gouv.fr – Tout savoir sur la facturation électronique](https://www.economie.gouv.fr/tout-savoir-sur-la-facturation-electronique-pour-les-entreprises), [Service Public Entreprendre – F23208](https://entreprendre.service-public.gouv.fr/vosdroits/F23208), [impots.gouv.fr – Fiche 6 : comment mon entreprise va recevoir les factures](https://www.impots.gouv.fr/sites/default/files/media/1_metier/2_professionnel/EV/2_gestion/290_facturation_electronique/fiche-6_comment-mon-entreprise-va-recevoir-les-factures.pdf).

#### Is Factur-X an accepted format in the French reform?

Yes. Factur-X is one of the three formats of the common base (*socle*) with UBL and UN/CEFACT CII, all based on EN 16931. Approved platforms must support them. Sources: [impots.gouv.fr – Spécifications externes](https://www.impots.gouv.fr/specifications-externes-b2b) and the AFNOR XP Z12-012 standard referenced there.

#### Does this Actor make me compliant with the reform?

No, not on its own. This is a **technical conformance tool, not a plateforme agréée** and not a transmission channel. To receive and send e-invoices you must choose an approved platform ([official list on impots.gouv.fr](https://www.impots.gouv.fr/je-consulte-la-liste-des-plateformes-agreees)). The toolkit helps you check, read and produce the files around that.

#### What is the difference between `valid` and `frenchCtcReady`?

`valid` means the invoice conforms to EN 16931 / Factur-X (XSD + profile schematron, real VAT identifiers, a real invoice profile; MINIMUM is never `valid`). `frenchCtcReady` means it also passes the French BR-FR rules. A business-to-consumer invoice can be `valid` and not `frenchCtcReady`.

#### Is ZUGFeRD supported? XRechnung?

ZUGFeRD 2.x is technically the same as Factur-X and is validated with the same schematrons. XRechnung-specific rules are not checked. UBL XML is checked against the XSD only.

#### Is my invoice data sent anywhere?

Validation, extraction, generation and OCR run inside the Actor. In `from-pdf`, if you set `aiApiKey`, the invoice text is sent to the OpenAI-compatible provider you choose, to fill missing or low-confidence fields; you pay that provider. Without a key, nothing is sent.

#### Can I use it from Python, Make, Zapier or n8n?

Yes, like any Apify Actor: Apify API and Python / JavaScript clients, or the native integrations.

### Limitations

- Not a plateforme agréée, not legal or tax advice.
- Schematron validation covers Factur-X / ZUGFeRD 2.x (CII). UBL is checked against the XSD only.
- BR-FR rules evolve with each version of the French specifications. This Actor uses the version bundled with the factur-x 7.7 library; check that it matches the version your platform uses.
- Full PDF/A-3 conformance of a Factur-X built on your own PDF depends on that PDF (fonts, colours); turn on `fullPdfaCheck`. veraPDF is limited to 30 seconds.
- `from-pdf` reads at most 5 pages with OCR. Uncertain results are flagged `needsReview` and are not turned into Factur-X unless you force it.
- The official FNFE sample `Avoir_FR_type381_BASIC.pdf` fails the Factur-X 1.09.2 BASIC schematron (rule `FX-SCH-A-000026`, old guideline URN). The schematron is not weakened to accept it.

### Data sources, standards and licences

| Component | Licence |
|---|---|
| [factur-x](https://github.com/akretion/factur-x) 7.7 (Akretion): XSD, CII parsing and PDF embedding | BSD-3-Clause |
| Factur-X 1.09.2 XSD and schematrons ([FNFE-MPE / FeRD, France_RFE](https://github.com/fnfempe/France_RFE)) | Apache-2.0 |
| French BR-FR schematron (FNFE-MPE), unmodified | EUPL 1.4 (per file header) |
| SaxonC-HE 13.1 (XSLT 3.0 engine) | MPL-2.0 |
| veraPDF 1.30 (optional, external process, unmodified) | MPL-2.0 |
| Tesseract OCR + tessdata (fra, deu, eng) | Apache-2.0 |
| pypdf, lxml, reportlab, pdfplumber, httpx | BSD / MIT |
| poppler-utils `pdftoppm` (separate binary, not linked) | GPL-2.0 |
| DejaVu Sans fonts | Bitstream Vera / public domain |
| Test samples: [ZUGFeRD/corpus](https://github.com/ZUGFeRD/corpus) incl. FNFE-MPE examples | Apache-2.0 |

### Related Actors

- [French SIREN/SIRET Bulk Enricher](https://apify.com/rigel/fr-siren-enricher): check your customers' SIRENs before invoicing (the buyer SIREN is a new mandatory mention).
- [French Supplier Check](https://apify.com/rigel/fr-supplier-check): status and insolvency notices for your suppliers.

### En français

**Valider, lire et générer des factures Factur-X / ZUGFeRD pour la réforme 2026 (EN 16931 + règles françaises BR-FR, PDF/A-3), ou transformer une facture PDF classique en Factur-X.**

- Depuis le 1er septembre 2026, toutes les entreprises assujetties à la TVA établies en France doivent pouvoir **recevoir** des factures électroniques ; les grandes entreprises et ETI doivent aussi les **émettre**, les PME et micro-entreprises à partir du 1er septembre 2027 (sources : economie.gouv.fr, service-public F23208, impots.gouv.fr fiche 6).
- Cas d'usage : contrôle des factures fournisseurs reçues, test des fichiers produits par votre logiciel, extraction en JSON pour l'ERP, génération de factures Factur-X, conversion de PDF existants.
- Prix : validation 0,015 $, extraction 0,008 $, les deux 0,02 $, génération 0,04 $, PDF → Factur-X 0,04 $ (0,01 $ si la facture reste à revoir), veraPDF +0,005 $. Fichiers illisibles, factures générées invalides et doublons gratuits.
- Limites : outil de conformité technique, **ce n'est pas une plateforme agréée** et ce n'est pas un conseil fiscal. Les règles BR-FR sont celles livrées avec factur-x 7.7.
- Licences : factur-x (BSD-3), schématrons Factur-X (Apache-2.0), schématron BR-FR (EUPL), SaxonC-HE et veraPDF (MPL-2.0), Tesseract (Apache-2.0).

# Actor input Schema

## `mode` (type: `string`):

What to do with the documents. from-pdf reads a plain PDF invoice (OCR included) and produces a Factur-X when every check passes.

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

List of {"url"}, {"base64", "name"} or {"kvKey"}. PDF (Factur-X/ZUGFeRD) or XML (CII/UBL). Max 20 MB each.

## `frenchCtcRules` (type: `boolean`):

Adds the BR-FR schematron of the French e-invoicing reform (mandatory notes, billing mode, SIREN, electronic addresses...). Reported separately from EN16931 conformance.

## `fullPdfaCheck` (type: `boolean`):

Runs veraPDF (extra charge). A lightweight Factur-X container check is always run.

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

Include warnings.

## `includeRawXml` (type: `boolean`):

Include raw XML in extraction.

## `invoices` (type: `array`):

Simple format (number, issueDate, seller, buyer, lines\[name, quantity, unitPrice, vatRate]) – totals and VAT breakdown are computed – or an EN16931 semantic dict (BT-xx keys, as returned by extract).

## `profile` (type: `string`):

Factur-X profile (generate).

## `basePdf` (type: `object`):

{"url"} or {"base64"}. If omitted, a clean PDF/A-3 invoice is rendered. Full PDF/A conformance then depends on your PDF (fonts embedded, etc.).

## `lang` (type: `string`):

PDF language.

## `returnBase64` (type: `boolean`):

Also return PDF as base64 in dataset.

## `ocr` (type: `string`):

auto OCRs only a PDF with no text layer. OCR is included in invoice-from-pdf and pdf-read, and reads at most 5 pages.

## `ocrLanguages` (type: `string`):

Tesseract language string.

## `generateFacturx` (type: `boolean`):

When false, a readable PDF is charged as pdf-read and no Factur-X is produced.

## `generateEvenIfReview` (type: `boolean`):

Not recommended. A result that is not valid EN 16931 is still charged as pdf-read.

## `keepOriginalLayout` (type: `boolean`):

Embed the XML in the source PDF only when veraPDF accepts the output as PDF/A-3. Otherwise a clean PDF/A-3 is rendered.

## `maxPages` (type: `integer`):

Pages read from the PDF. When OCR runs, at most 5 pages are passed to Tesseract.

## `timeoutSecsPerPdf` (type: `integer`):

A PDF that exceeds this is abandoned and not charged.

## `aiApiKey` (type: `string`):

OpenAI-compatible key. Fills missing or low-confidence fields only. You pay your provider. The invoice text is sent there.

## `aiBaseUrl` (type: `string`):

OpenAI-compatible base URL. Private, loopback and cloud-metadata hosts are refused.

## `aiModel` (type: `string`):

Model name sent to aiBaseUrl.

## `aiAlways` (type: `boolean`):

Call AI even when the rules did not flag a review.

## Actor input object example

```json
{
  "mode": "validate",
  "documents": [
    {
      "url": "https://raw.githubusercontent.com/ZUGFeRD/corpus/master/ZUGFeRDv2/correct/FNFE-factur-x-examples/Facture_FR_BASICWL.pdf"
    }
  ],
  "frenchCtcRules": true,
  "fullPdfaCheck": false,
  "includeWarnings": true,
  "includeRawXml": false,
  "profile": "en16931",
  "lang": "fr",
  "returnBase64": false,
  "ocr": "auto",
  "ocrLanguages": "fra+eng+deu",
  "generateFacturx": true,
  "generateEvenIfReview": false,
  "keepOriginalLayout": false,
  "maxPages": 5,
  "timeoutSecsPerPdf": 90,
  "aiBaseUrl": "https://api.openai.com/v1",
  "aiModel": "gpt-4o-mini",
  "aiAlways": false
}
```

# Actor output Schema

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

Default dataset, overview view selected.

## `files` (type: `string`):

Factur-X PDF and XML files stored in the default key-value store.

# 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 = {
    "documents": [
        {
            "url": "https://raw.githubusercontent.com/ZUGFeRD/corpus/master/ZUGFeRDv2/correct/FNFE-factur-x-examples/Facture_FR_BASICWL.pdf"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("rigel/facturx-toolkit").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 = { "documents": [{ "url": "https://raw.githubusercontent.com/ZUGFeRD/corpus/master/ZUGFeRDv2/correct/FNFE-factur-x-examples/Facture_FR_BASICWL.pdf" }] }

# Run the Actor and wait for it to finish
run = client.actor("rigel/facturx-toolkit").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 '{
  "documents": [
    {
      "url": "https://raw.githubusercontent.com/ZUGFeRD/corpus/master/ZUGFeRDv2/correct/FNFE-factur-x-examples/Facture_FR_BASICWL.pdf"
    }
  ]
}' |
apify call rigel/facturx-toolkit --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,rigel/facturx-toolkit"
        }
    }
}
```

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/oiLFg4SvaJ72M1Iru/builds/2Dncbs1pfayrDZqKx/openapi.json
