Quickstart¶
Access updated
Try fixed samples without a key on the MCP setup page. Processing your own documents requires a key. New free subscriptions and anonymous trial keys are closed; existing keys retain their allowance.
Five minutes from zero to a compliant Factur-X invoice.
1. Get an API key¶
Try fixed samples without a key. To generate, embed, validate or extract your own documents, choose a paid plan, copy the key after checkout and send Authorization: Bearer <key>.
2. Describe the invoice¶
Amounts are decimal strings ("1200.00"), dates are ISO 8601. Totals and the VAT breakdown are computed server-side; do not send them unless you need to override.
{
"number": "F-2026-0042",
"issue_date": "2026-09-01",
"due_date": "2026-10-01",
"seller": {
"name": "Atelier Numérique SAS",
"siren": "732829320",
"vat_id": "FR40732829320",
"legal_info": "SAS au capital de 10 000 € - RCS Paris 732 829 320",
"address": {"line1": "12 rue de la Paix", "postal_code": "75002", "city": "Paris", "country": "FR"}
},
"buyer": {
"name": "Boulangerie Dupont SARL",
"siren": "552081317",
"vat_id": "FR09552081317",
"address": {"line1": "8 avenue des Champs", "postal_code": "69002", "city": "Lyon", "country": "FR"}
},
"payment": {"means_code": "30", "iban": "FR7630006000011234567890189", "terms": "Paiement à 30 jours."},
"lines": [
{"item_name": "Développement site web", "quantity": "1", "unit_price": "2500.00", "vat_rate": "20"},
{"item_name": "Hébergement annuel", "quantity": "12", "unit_code": "MON", "unit_price": "15.00", "vat_rate": "20"}
]
}
French sellers
Provide siren (the API derives the 0002 scheme), vat_id and a full postal address. The three mandatory French mentions (late-payment penalties, EUR 40 recovery fee, early-payment discount) are added automatically as notes; override them with french_mentions.
3. Generate¶
Set FACTURX_API_KEY in your environment. The curl example uses Bash; Python requires httpx (python -m pip install httpx); Node requires Node.js 22+ and a .mjs file. Each successful generation consumes your quota.
curl --fail --silent --show-error --max-time 60 -X POST https://facturx.orvel.dev/v1/invoices/generate \
-H "Authorization: Bearer $FACTURX_API_KEY" \
-H "Content-Type: application/json" \
-d '{"invoice": '"$(cat invoice.json)"', "options": {"profile": "en16931", "language": "fr"}}' \
-o F-2026-0042-facturx.pdf -D -
import json
import os
from pathlib import Path
import httpx
API_KEY = os.environ["FACTURX_API_KEY"]
invoice = json.loads(Path("invoice.json").read_text(encoding="utf-8"))
r = httpx.post(
"https://facturx.orvel.dev/v1/invoices/generate",
headers={"Authorization": f"Bearer {API_KEY}"},
json={"invoice": invoice, "options": {"profile": "en16931", "output": "facturx-pdf"}},
timeout=60,
)
r.raise_for_status()
if r.headers.get("content-type", "").split(";")[0] != "application/pdf" or not r.content.startswith(b"%PDF-"):
raise RuntimeError("Expected a PDF response")
with open("invoice-facturx.pdf", "xb") as output:
output.write(r.content)
print(r.headers["X-Facturx-Amount-Due"]) # "3216.00"
import fs from "node:fs";
const apiKey = process.env.FACTURX_API_KEY;
if (!apiKey) throw new Error("Set FACTURX_API_KEY");
const invoice = JSON.parse(fs.readFileSync("invoice.json", "utf8"));
const res = await fetch("https://facturx.orvel.dev/v1/invoices/generate", {
method: "POST",
redirect: "error",
signal: AbortSignal.timeout(60_000),
headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" },
body: JSON.stringify({ invoice, options: { profile: "en16931" } }),
});
if (!res.ok) throw new Error(`HTTP ${res.status}; check invoice fields, key and quota`);
const pdf = Buffer.from(await res.arrayBuffer());
if (res.headers.get("content-type")?.split(";")[0] !== "application/pdf" || pdf.subarray(0, 5).toString() !== "%PDF-") {
throw new Error("Expected a PDF response");
}
fs.writeFileSync("invoice-facturx.pdf", pdf, { flag: "wx" });
The response is the PDF/A-3 file. Useful headers: X-Facturx-Profile, X-Facturx-Amount-Due, X-Facturx-Warnings, X-Usage-Used, X-Usage-Quota.
Want XML only? Set "output": "cii-xml" or "ubl-xml". Want a JSON envelope with the file in base64 plus the computed totals and validation report? Send Accept: application/json.
Runnable example: PDF and report in one call¶
Download generate.mjs and invoice.json into the same folder. The JSON is a demonstration fixture, not an invoice to send or evidence of the companies' identities. With Node.js 22+ and FACTURX_API_KEY set, run:
The script makes one request with Accept: application/json. It requires successful XSD, EN 16931 and fr-ctc checks with none skipped, then writes invoice.pdf and validation.json into a new subdirectory of facturx-output. Both files contain invoice data: keep them private. The expected amount for this example is EUR 3,216.00 (EUR 2,680.00 net and EUR 536.00 VAT).
The report describes the service's checks on the generated XML; it is not an independent PDF/A verification or proof of delivery to a PA. The script never retries automatically: after a network interruption, check quota before rerunning. No second validation request is needed to obtain this report.
4. Validate anything¶
curl --fail --silent --show-error --max-time 60 -X POST "https://facturx.orvel.dev/v1/invoices/validate?check=fr-ctc" \
-H "Authorization: Bearer $FACTURX_API_KEY" \
-F file=@supplier-invoice.pdf
{
"valid": false,
"flavor": "factur-x",
"profile": "en16931",
"checks_run": ["pdf", "xsd", "schematron:base", "schematron:fr-ctc"],
"error_count": 1,
"findings": [
{"source": "schematron:fr-ctc", "severity": "fatal", "rule": "BR-FR-08",
"message": "[BR-FR-08] Le SIREN du vendeur (BT-30) doit être cohérent avec le SIRET ...",
"location": "/rsm:CrossIndustryInvoice/..."}
],
"pdf": {"pages": 1, "attachments": ["factur-x.xml"], "pdfa": "3B", "facturx_xmp": "EN 16931"}
}
5. Read an inbound e-invoice¶
curl --fail --silent --show-error --max-time 60 -X POST "https://facturx.orvel.dev/v1/invoices/extract?include_xml=false" \
-H "Authorization: Bearer $FACTURX_API_KEY" \
-F file=@supplier-invoice.pdf
Returns fields (number, dates, seller, buyer, totals, VAT breakdown, lines) and, unless disabled, the raw embedded XML.
Next¶
- Full endpoint reference: REST API
- Use it from an AI agent: MCP server
- Every field explained: Invoice fields