# France E-Invoice Validator API - Factur-X, UBL & CII (`kamerozkan/france-einvoice-validator`) Actor

Validate French UBL, CII, and Factur-X invoices against pinned France\_RFE 1.4.0.02, EN 16931, and EXTENDED-CTC-FR technical rules. Offline facture electronique preflight for French invoices before Chorus Pro or Plateforme Agreee submission, ahead of France's September 2026 e-invoicing mandate.

- **URL**: https://apify.com/kamerozkan/france-einvoice-validator.md
- **Developed by:** [Kamer Ozkan](https://apify.com/kamerozkan) (community)
- **Categories:** Developer tools
- **Stats:** 1 total users, 0 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.00 / 1,000 invoice 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/platform/actors/running/actors-in-store#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

## France E-Invoice Validator API - Factur-X, UBL & CII

[![France\_RFE](https://img.shields.io/badge/France__RFE-1.4.0.02-0055a4)](https://github.com/fnfempe/France_RFE/releases/tag/v1.4.0.02)
[![EN 16931](https://img.shields.io/badge/EN_16931-1.3.16-0a7f5a)](https://github.com/fnfempe/France_RFE)
[![Factur-X](https://img.shields.io/badge/Factur--X-1.09-6f42c1)](https://fnfe-mpe.org/factur-x/factur-x_en/)
[![Rules](https://img.shields.io/badge/rules-SHA--256_pinned-1f6feb)](#pinned-stack)
[![Billing](https://img.shields.io/badge/Billing-%240.004%20per%20evaluated%20invoice-6f42c1)](https://apify.com/kamerozkan/france-einvoice-validator)

> Technical validation only. `ACCEPTED` means the submitted invoice passed the pinned offline checks described below. It does not establish legal or tax validity, issuer authenticity, approved-platform acceptance, successful transmission, or recipient acceptance.

Validate French electronic invoices in UBL 2.1, UN/CEFACT CII D22B, and Factur-X 1.09 through one batch API. Every evaluated document receives a deterministic result, structured findings, the input SHA-256, and the exact rule versions used.

[Run the live Actor](https://apify.com/kamerozkan/france-einvoice-validator)

### Supported routes

| Input | Profiles | Required checks |
|---|---|---|
| UBL 2.1 Invoice | EN 16931, EXTENDED-CTC-FR | UBL XSD, EN 16931 or EXTENDED-CTC-FR profile rules, French BR-FR rules |
| UBL 2.1 CreditNote | EN 16931, EXTENDED-CTC-FR | UBL XSD, EN 16931 or EXTENDED-CTC-FR profile rules, French BR-FR rules |
| CII D22B XML | EN 16931, EXTENDED-CTC-FR | UN/CEFACT CII XSD, profile rules, French BR-FR rules |
| Factur-X XML | EN16931, EXTENDED | CII or Factur-X profile XSD, profile rules, French BR-FR rules |
| Factur-X PDF/A-3 | EN16931, EXTENDED | veraPDF PDF/A-3, safe embedded XML extraction, attachment and XMP consistency, Factur-X checks, then France\_RFE checks |

The active business-rule package is the FNFE-MPE `France_RFE v1.4.0.02` release for the French reform effective 1 September 2026. The production image does not download rules at runtime.

For Factur-X PDF only, the attachment relationship accepts `Data`, `Alternative`, and the `Source` value used by the official France\_RFE examples. `Source` is enabled only for the Factur-X XMP namespace, the canonical `factur-x.xml` filename, and supported EN16931 or EXTENDED profiles. Other relationship values remain rejected.

### Quick start

The default input uses a public fixture pinned to an immutable commit:

```json
{
  "documents": [
    {
      "documentId": "france-en16931-ubl-sample",
      "fileName": "ubl-21-en16931.xml",
      "url": "https://raw.githubusercontent.com/akretion/factur-x/5edb9af9186e5f06f446372688e6fdbbd0b790d3/tests/fixtures/xml/ubl-21-en16931.xml"
    }
  ],
  "resultDetail": "FINDINGS",
  "maxFindingsPerDocument": 100,
  "storeXmlReport": false,
  "storeHtmlReport": false,
  "storePdfaReport": false,
  "storeExtractedInvoiceXml": false
}
```

Send each document through exactly one source field:

- `url`: public or signed HTTPS URL
- `xml`: inline XML
- `base64`: base64 XML or PDF
- console file upload
- a record in a customer-selected Apify key-value store

The input and output schemas are agent-readable and work with the Apify API, MCP clients, Make, n8n, webhooks, and standard SDKs.

### Result contract

One dataset item is returned for every submitted entry. A technically rejected invoice is a completed evaluation, not a processing failure.

| Processing | Conformance | Meaning | `invoice-validated` event |
|---|---|---|---|
| `SUCCEEDED` | `ACCEPTED` | Every required pinned technical layer passed | Charged |
| `SUCCEEDED` | `REJECTED` | The invoice was evaluated and one or more required rules failed | Charged |
| `FAILED` | `NOT_EVALUATED` | A source, format, engine, timeout, or safety failure prevented a decision | Not charged |

The common family schema includes:

- `processingStatus`
- `conformanceStatus`
- `validationScope`
- `externalStateStatus`
- `rulesetEffectiveAt`
- `validationFamily`
- `syntax`
- `profile`
- `scenario`
- `versions`
- `counts`
- `findings`
- `sha256`
- `error`

Each finding includes a severity, validation stage, rule ID, message, and location when the upstream report provides one. `previewConformanceStatus` remains `NOT_EVALUATED` until a future France\_RFE package is deliberately added as a separate preview ruleset.

`externalStateStatus` is always `NOT_EVALUATED_EXTERNAL_STATE` because approved-platform, transmission, registry, delivery, and recipient state are outside offline preflight.

### Real run examples

<details>
<summary><strong>Accepted official UBL sample</strong></summary>

```json
{
  "documentId": "accepted-official-sample",
  "processingStatus": "SUCCEEDED",
  "conformanceStatus": "ACCEPTED",
  "previewConformanceStatus": "NOT_EVALUATED",
  "validationScope": "OFFLINE_PREFLIGHT",
  "validationFamily": "FRANCE_RFE",
  "syntax": "UBL_INVOICE",
  "profile": "FRANCE_EN16931_UBL",
  "counts": {
    "fatal": 0,
    "error": 0,
    "warning": 0,
    "information": 0
  },
  "findings": [],
  "sha256": "90c598998073f00952bbcdb75ab9dbce02c2caeed26e88d3094eabeda982435a",
  "error": null
}
```

</details>

<details>
<summary><strong>Rejected incomplete UBL</strong></summary>

```json
{
  "documentId": "rejected-incomplete-ubl",
  "processingStatus": "SUCCEEDED",
  "conformanceStatus": "REJECTED",
  "previewConformanceStatus": "NOT_EVALUATED",
  "validationScope": "OFFLINE_PREFLIGHT",
  "counts": {
    "fatal": 16,
    "error": 2,
    "warning": 6,
    "information": 0
  },
  "findings": [
    {
      "severity": "ERROR",
      "stage": "XSD",
      "ruleId": "SCHEMAV_ELEMENT_CONTENT",
      "location": "/*"
    },
    {
      "severity": "FATAL",
      "stage": "EN16931",
      "ruleId": "BR-01",
      "message": "[BR-01]-An Invoice shall have a Specification identifier (BT-24)."
    }
  ],
  "findingsTruncated": true,
  "sha256": "c93ffbc01ae38cdb76d5e2176ea2ddf20e0ea0d9a7cf4c04ab884e330a24d8b9",
  "error": null
}
```

</details>

<details>
<summary><strong>Blocked non-HTTPS source, not evaluated</strong></summary>

```json
{
  "documentId": "not-evaluated-http-source",
  "processingStatus": "FAILED",
  "conformanceStatus": "NOT_EVALUATED",
  "previewConformanceStatus": "NOT_EVALUATED",
  "validationScope": "OFFLINE_PREFLIGHT",
  "counts": {
    "fatal": 0,
    "error": 0,
    "warning": 0,
    "information": 0
  },
  "findings": [],
  "sha256": null,
  "error": {
    "code": "SOURCE_FETCH_FAILED",
    "message": "Only HTTPS source URLs are allowed"
  }
}
```

</details>

These compact examples are taken from a real three-document Actor run.

### Pricing

The `invoice-validated` event costs **$0.004**.

- `ACCEPTED`: billed once
- `REJECTED`: billed once
- `NOT_EVALUATED`: free
- Dataset delivery does not add a second charge
- The Actor stops before processing unless the event price is exactly $0.004

One malformed or unavailable source does not kill the remaining batch.

### Optional reports

All optional records are off by default:

- `storeXmlReport`: normalized active-rule report
- `storeHtmlReport`: script-free human-readable report
- `storePdfaReport`: sanitized veraPDF report for a Factur-X PDF
- `storeExtractedInvoiceXml`: complete XML extracted from a Factur-X PDF

Reports can contain invoice values and follow the retention settings of the customer's Apify account.

### Security and limits

- Validation runs locally inside the Actor container.
- No invoice is sent to a third-party validation service.
- HTTPS downloads block private, local, reserved, and cloud metadata targets.
- DTDs, entities, and external XML resources are blocked.
- XML Schema parsing disables network resolution.
- Java validation stages use bounded heap, timeouts, and bounded reports.
- PDF parsing runs in an isolated process with CPU, memory, file, and descriptor limits.
- Raw invoice content is not copied to the output dataset or application logs.
- Each XML is limited to 5 MiB.
- Each PDF is limited to 20 MiB and 100 pages.
- A run can evaluate up to 100 documents, including at most 20 PDFs and 100 MiB total input.
- Finding fields and the total returned findings payload are bounded.

Validation findings can quote an offending invoice value. Treat results and optional reports as sensitive accounting data.

### Pinned stack

| Component | Version or identity |
|---|---|
| France\_RFE | `v1.4.0.02` |
| France\_RFE commit | `e9520ce398cc99bed4bab493773a494af8ca5aff` |
| France\_RFE tag archive SHA-256 | `5d30054c99d970457ca50c89ce895770cae695f5fe8d73750433079f9f1ec3a1` |
| XP Z12-012 profile generation | `1.4` |
| EN 16931 Schematron | `1.3.16` |
| UBL | `2.1` |
| UN/CEFACT CII | `D22B` |
| Factur-X | `1.09` |
| Factur-X EXTENDED schema patch | `1.09.2` |
| Mustangproject | `2.24.0` |
| SaxonJ-HE in Mustangproject | `12.8` |
| veraPDF | `1.30.2` |

Docker builds verify every downloaded archive or executable against a fixed SHA-256. Every executed XSD and XSLT also has its own SHA-256 in the manifest and is verified again before use. A mismatch stops validation.

### Release gates

The release fixture runner downloads the immutable France\_RFE tag archive, verifies its SHA-256, and extracts official FNFE examples only inside a temporary test directory. It checks:

- official EN 16931 UBL invoice: `ACCEPTED`
- official EN 16931 UBL credit note: `ACCEPTED`
- official EN 16931 CII invoice: `ACCEPTED`
- official EXTENDED-CTC-FR UBL invoice: `ACCEPTED`
- official EXTENDED-CTC-FR CII invoice: `ACCEPTED`
- official Factur-X EXTENDED XML: `ACCEPTED`
- official Factur-X EXTENDED PDF/A-3 using `AFRelationship Source`: `ACCEPTED`
- deliberately corrupted official UBL invoice: `REJECTED`
- unsupported Factur-X attachment relationships: `REJECTED`

The unit suite also verifies root syntax routing, exact specification identifiers, unsupported-profile rejection, source safety, independent batch errors, bounded findings, non-root runtime, and pay-per-event guards.

Official example files are downloaded only for release testing and are not included in the production image.

### Current scope

This Actor does not:

- transmit an invoice through an approved platform
- call the French public invoicing portal or prove platform acceptance
- verify tax treatment, legal validity, fraud, duplicates, or payment approval
- verify digital signatures or issuer identity
- prove that visible PDF text equals embedded XML values
- validate invoice lifecycle CDAR messages
- accept Factur-X MINIMUM, BASIC WL, or BASIC as France reform invoice profiles

### Official references

- [FNFE-MPE France\_RFE rules](https://github.com/fnfempe/France_RFE)
- [France\_RFE v1.4.0.02 release](https://github.com/fnfempe/France_RFE/releases/tag/v1.4.0.02)
- [French external B2B specifications](https://www.impots.gouv.fr/specifications-externes-b2b)
- [Factur-X 1.09](https://fnfe-mpe.org/factur-x/factur-x_en/)
- [veraPDF validation documentation](https://docs.verapdf.org/cli/validation/)
- [Third-party licenses and notices](THIRD_PARTY_NOTICES.md)

This Actor is also exposed to AI agents through Apify's MCP server
(mcp.apify.com): an agent can discover it by search and run it with the same
pay-per-event billing, with no separate integration.

### E-invoice validator family

Same engine, same output contract, one validator per market:
[XRechnung & ZUGFeRD (Germany)](https://apify.com/kamerozkan/xrechnung-xml-batch-validator-api) | [Peppol BIS Billing](https://apify.com/kamerozkan/peppol-bis-preflight-validator) | [Poland KSeF FA(3)](https://apify.com/kamerozkan/poland-ksef-preflight-validator) | [Italy FatturaPA](https://apify.com/kamerozkan/italy-fatturapa-validator)

# Actor input Schema

## `invoiceFiles` (type: `array`):

Upload France EN 16931 or EXTENDED-CTC-FR UBL/CII XML, or Factur-X 1.09 PDF/A-3 invoice files. Source documents are not copied to the output dataset.

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

Each object needs one source: url, xml, or base64. URL and base64 sources can contain XML or PDF. HTTPS URLs only. documentId and fileName are optional.

## `sourceKeyValueStoreId` (type: `string`):

Optional. Select a key-value store that contains XML or PDF records, then list the record keys below.

## `keyValueStoreKeys` (type: `array`):

Record keys to read from the selected source key-value store.

## `resultDetail` (type: `string`):

FINDINGS returns rule IDs, messages, and locations. SUMMARY returns only status and counts.

## `maxFindingsPerDocument` (type: `integer`):

Caps the findings array without changing the total severity counts.

## `storeXmlReport` (type: `boolean`):

Optional. Stores a normalized XML report for France\_RFE, EN 16931, Factur-X, and PDF/A findings. Reports can contain invoice values.

## `storeHtmlReport` (type: `boolean`):

Optional. HTML reports contain the invoice content and are stored in your run key-value store.

## `storePdfaReport` (type: `boolean`):

Optional. For hybrid PDFs, stores the sanitized veraPDF XML report in your run key-value store.

## `storeExtractedInvoiceXml` (type: `boolean`):

Optional. For hybrid PDFs, stores the extracted invoice XML in your run key-value store. This record contains the complete invoice.

## Actor input object example

```json
{
  "documents": [
    {
      "documentId": "france-en16931-ubl-sample",
      "fileName": "ubl-21-en16931.xml",
      "url": "https://raw.githubusercontent.com/akretion/factur-x/5edb9af9186e5f06f446372688e6fdbbd0b790d3/tests/fixtures/xml/ubl-21-en16931.xml"
    }
  ],
  "resultDetail": "FINDINGS",
  "maxFindingsPerDocument": 100,
  "storeXmlReport": false,
  "storeHtmlReport": false,
  "storePdfaReport": false,
  "storeExtractedInvoiceXml": false
}
```

# Actor output Schema

## `validationResults` (type: `string`):

No description

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

No description

# 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": [
        {
            "documentId": "france-en16931-ubl-sample",
            "fileName": "ubl-21-en16931.xml",
            "url": "https://raw.githubusercontent.com/akretion/factur-x/5edb9af9186e5f06f446372688e6fdbbd0b790d3/tests/fixtures/xml/ubl-21-en16931.xml"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("kamerozkan/france-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 = { "documents": [{
            "documentId": "france-en16931-ubl-sample",
            "fileName": "ubl-21-en16931.xml",
            "url": "https://raw.githubusercontent.com/akretion/factur-x/5edb9af9186e5f06f446372688e6fdbbd0b790d3/tests/fixtures/xml/ubl-21-en16931.xml",
        }] }

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

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).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": [
    {
      "documentId": "france-en16931-ubl-sample",
      "fileName": "ubl-21-en16931.xml",
      "url": "https://raw.githubusercontent.com/akretion/factur-x/5edb9af9186e5f06f446372688e6fdbbd0b790d3/tests/fixtures/xml/ubl-21-en16931.xml"
    }
  ]
}' |
apify call kamerozkan/france-einvoice-validator --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=kamerozkan/france-einvoice-validator",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/acts/Y1W5FA3wV8IF3fUla/builds/y3dkk15kqhIfVev81/openapi.json
