REST API¶
Base URL: https://facturx-api.fly.dev. Interactive OpenAPI UI at /api, machine-readable spec at /openapi.json.
Authentication¶
| Endpoint | Anonymous | With API key |
|---|---|---|
POST /v1/invoices/validate |
20 req/min per IP | counted against your quota, no rate limit |
POST /v1/invoices/extract |
20 req/min per IP | counted against your quota, no rate limit |
POST /v1/invoices/generate |
401 | required |
POST /v1/invoices/embed |
401 | required |
POST /v1/invoices/preview-xml |
401 | required |
GET /v1/usage |
401 | required |
Send the key as Authorization: Bearer <key> (or X-API-Key: <key>). Keys are the license keys issued at purchase, see Pricing.
Every metered response carries X-Plan, X-Usage-Used, X-Usage-Quota and X-Usage-Remaining.
Endpoints¶
POST /v1/invoices/generate¶
Body: {"invoice": Invoice, "options": GenerateOptions}. See Invoice fields for the Invoice object.
options:
| Field | Default | Values |
|---|---|---|
profile |
en16931 |
basicwl, en16931, extended, extended-ctc-fr (MINIMUM/BASIC are read and validated, not produced: the French reform requires at least EN 16931) |
output |
facturx-pdf |
facturx-pdf (PDF/A-3 + CII), cii-xml, ubl-xml |
language |
fr |
fr, en (visual PDF language) |
check |
fr-ctc |
base (EN 16931 only), fr-ctc (EN 16931 + French BR-FR rules) |
logo_url |
none | HTTPS URL of a PNG/SVG logo shown in the PDF header |
accent_color |
#1f3a5f |
Hex color of the PDF headings |
footer_text |
none | Free text in the PDF footer |
Response: the document as binary (application/pdf or application/xml) with a Content-Disposition filename. With Accept: application/json you get:
{
"filename": "F-2026-0042-facturx.pdf",
"media_type": "application/pdf",
"profile": "en16931",
"output": "facturx-pdf",
"content_base64": "JVBERi0xLjcK...",
"totals": {"line_total": "2739.70", "tax_exclusive": "2739.70", "tax_total": "539.28",
"tax_inclusive": "3278.98", "amount_due": "3278.98",
"vat_breakdown": [{"category": "S", "rate": "20", "taxable_amount": "2680.00", "tax_amount": "536.00"}]},
"validation": {"valid": true, "checks_run": ["xsd", "schematron:base", "schematron:fr-ctc"], "findings": []}
}
Extra headers: X-Facturx-Profile, X-Facturx-Output, X-Facturx-Amount-Due, X-Facturx-Warnings.
The generated XML is always validated (XSD + schematron for the requested check) before it is returned. If a rule fails you get a 400 invalid_input with the findings, never a non-compliant file.
POST /v1/invoices/embed¶
multipart/form-data:
| Part | Required | Description |
|---|---|---|
pdf |
yes | Your visual invoice PDF |
xml |
one of | Factur-X / CII XML file to embed |
invoice_json |
one of | Invoice JSON; the XML is generated from it |
profile |
no | Profile when generating from invoice_json (default en16931) |
check |
no | base or fr-ctc (default) |
language |
no | fr or en metadata language |
Response: application/pdf, a PDF/A-3 file with the XML attached as factur-x.xml and the Factur-X XMP metadata. The XML is validated first; UBL cannot be embedded (Factur-X is CII only).
POST /v1/invoices/validate?check=fr-ctc¶
Send the file as multipart/form-data (file) or as the raw request body (Content-Type: application/pdf or application/xml). Accepts Factur-X / ZUGFeRD PDFs, CII XML and UBL 2.1 Invoice or CreditNote XML.
{
"valid": true,
"flavor": "factur-x",
"profile": "en16931",
"checks_run": ["pdf", "xsd", "schematron:base", "schematron:fr-ctc"],
"checks_skipped": [],
"error_count": 0,
"warning_count": 1,
"findings": [
{"source": "schematron:base", "severity": "warning", "rule": "BR-CO-25", "message": "...", "location": "/rsm:..."}
],
"pdf": {"pages": 1, "attachments": ["factur-x.xml"], "pdfa": "3B", "facturx_xmp": "EN 16931"}
}
flavor:factur-x(CII),ubl-2.1-invoice,ubl-2.1-creditnote.profile: detected from the XML (minimum,basicwl,basic,en16931,extended,extended-ctc-fr).severity:error(fatal) orwarning.validisfalsewhen there is at least one error.pdf: only for PDF input.pdfais read from the XMP (3Bexpected),facturx_xmpis the ConformanceLevel.
POST /v1/invoices/extract?include_xml=true¶
Same input as validate. Returns:
{
"flavor": "factur-x",
"profile": "en16931",
"xml_filename": "factur-x.xml",
"xml": "<?xml ...>",
"fields": {
"number": "F20260023", "type_code": "380", "issue_date": "2026-01-15", "due_date": "2026-02-14",
"currency": "EUR",
"seller": {"name": "...", "siren": "...", "vat_id": "FR...", "address": {...}},
"buyer": {"name": "...", "siren": "...", "address": {...}},
"totals": {"tax_exclusive": "...", "tax_total": "...", "tax_inclusive": "...", "amount_due": "..."},
"vat_breakdown": [{"category": "S", "rate": "20", "taxable_amount": "...", "tax_amount": "..."}],
"lines": [{"id": "1", "item_name": "...", "quantity": "1", "unit_price": "...", "net_amount": "...", "vat_rate": "20"}]
}
}
POST /v1/invoices/preview-xml¶
Same body as generate. Returns the CII (or UBL) XML without the schematron pass. Cheap way to iterate on a field mapping; still validated against the XSD.
GET /v1/schema/invoice, GET /v1/schema/options¶
JSON Schema (draft 2020-12) of the Invoice and GenerateOptions objects. Use them for client generation or form validation.
GET /v1/usage¶
GET /v1/plans¶
Public, no key. Plan catalog with the self-service checkout links and the customer portal URL (what the pricing page buttons use).
{"currency": "EUR", "plans": [{"id": "free", "name": "Free", "price_eur_month": 0, "quota": 50, "checkout_url": "https://..."}, ...], "portal_url": "https://...", "mcp_url": "https://facturx-api.fly.dev/mcp"}
GET /health¶
{"status": "ok", "version": "0.1.0", "schematron": true}. schematron: false means the rule engine is restarting; validation calls return 503 until it is back (a few seconds).
Errors¶
All errors share one shape:
{"code": "invalid_input", "message": "XML does not pass EN 16931 business rules", "details": [ ... ]}
| HTTP | code |
Meaning |
|---|---|---|
| 400 | invalid_input |
Bad document, or the generated/embedded XML fails a business rule (details = findings) |
| 400 | unsupported_profile |
Requested profile/output combination is not possible (e.g. UBL into a Factur-X PDF) |
| 400 | not_an_einvoice |
The PDF has no Factur-X attachment / the XML is neither CII nor UBL |
| 401 | api_key_required, invalid_api_key |
Missing or unknown key |
| 402 | quota_exceeded |
Monthly quota of the plan reached |
| 413 | too_large |
Body above 15 MB |
| 422 | validation_error |
Request JSON does not match the schema (details[].loc points at the field) |
| 429 | rate_limited |
Anonymous rate limit hit; add a key |
| 500 | render_error |
PDF rendering failed (report it, include the request id from logs) |
| 503 | schematron_unavailable |
Rule engine warming up; retry after a few seconds |
Limits¶
- Body size 15 MB. Typical Factur-X PDFs are under 500 KB.
- Generation takes 0.5 to 2 s (PDF rendering + schematron). Validation 0.2 to 1 s.
- No document is stored. Logs contain the invoice number and rule ids, never amounts or party data.