v1.5 · judged by the official validators

Conformant Factur-X invoices,
in TypeScript, nothing left to guesswork.

A pure SDK — no native dependencies, edge and serverless friendly — to generate, validate, embed and read Factur-X invoices at the EN 16931 profile, with the French rules built in. Every promise here is judged by an official validator, not by us.

npm add facturx-sdk
Read the documentation

Factur-X is ZUGFeRD — same standard, two names. A file produced here is a valid ZUGFeRD 2.x EN 16931 file, and the reader is exercised against the German KoSIT corpus on every commit.

Judged by the reference tools, on every commit

  • XSDFactur-X EN 16931
  • CENEN 16931 schematron · CII & UBL
  • Factur-Xprofile schematron 1.09
  • BR-FRFlux 2 schematron V1.3.0
  • veraPDFPDF/A-3b
  • ZUGFeRDKoSIT corpus, 30 files
0
tests, every one of them run before publishing
0/26
French BR-FR rules — the other four need the directory
0
invoices from other tools, read and re-emitted losslessly
0
native dependencies — edge, serverless, browser

Online validator · free

Drop in an invoice, get the verdict of the official rules.

The three schematrons — EN 16931, Factur-X and BR-FR — run in your browser. The file is never uploaded anywhere, and each finding comes with its rule code and the exact place in the document.

Open the validator

Factur-X PDF or CII XML · no account, your file stays put

Three calls, and conformance stops being your problem.

Integer amounts, totals computed then verified, typed errors carrying the exact field path. The SDK never quietly fixes anything.

import { cents, computeTotals, percent, quantity, unitPrice, assertValidInvoice } from 'facturx-sdk';

const draft = {
  id: 'F-2026-0001', issueDate: '2026-09-11', typeCode: '380', currency: 'EUR',
  operationCategory: 'services',                       // goods / services / mixed (French reform)
  seller: { name: 'Atelier Exemple SAS', siren: '443061841', vatId: 'FR64443061841',
            address: { postCode: '75011', city: 'Paris', countryCode: 'FR' } },
  buyer:  { name: 'Client Démo SARL', siren: '732829320',
            electronicAddress: { value: '732829320', scheme: '0225' },   // platform routing
            address: { postCode: '69002', city: 'Lyon', countryCode: 'FR' } },
  delivery: { date: '2026-09-10' },
  lines: [{ id: '1', name: 'Consulting', quantity: quantity(20000), unitCode: 'DAY',
            unitPrice: unitPrice(8000000), netAmount: cents(160000),
            tax: { category: 'S', rate: percent('20') } }],
  paymentTerms: { dueDate: '2026-10-11', latePenaltyRate: percent('10'),
                  recoveryIndemnity: cents(4000), earlyPaymentDiscount: 'none' },
};

const invoice = assertValidInvoice({ ...draft, ...computeTotals(draft) });
// otherwise: FacturXValidationError { issues: [{ code: 'BR-CO-15', path: 'totals.taxInclusiveAmount', … }] }

Built to leave nothing to chance.

No silent rounding, ever

Cents, quantities and prices as integers, products in bigint, a single commercial rounding. The totals you supply are checked — a VAT-inclusive total that does not add up is a named error, not a value quietly corrected behind your back.

Typed errors, exact path

BR-CO-15 @ totals.taxInclusiveAmount. Official EN 16931 and AFNOR codes, ready to wire into your form.

The French rules, officially

22 BR-FR rules from the XP Z12-012 standard: SIREN, invoicing framework, legal statements, 0225 addresses, VAT on debits.

All three socle syntaxes

Factur-X, plain CII and UBL: any profile, any prefix, attachments, prepayments, credit notes. The same invoice read from CII and from UBL yields the same object. Exercised on 30 invoices from other tools, re-emitted conformant.

Lifecycle statuses

The four statuses a platform reports to the French public portal — the official annex knows no others for an invoice — the 40 normalised refusal reasons, and the CDV message written and read back, validated against the UN/CEFACT CDAR schema.

The invoice, drawn

Since 1.5 the SDK also produces the readable page — header, lines, totals, VAT, legal statements, pagination. Those statements come from the same source as the XML: the page and the structured data cannot drift apart.

A command, not a program

npx facturx-sdk validate invoice.pdf — the syntax is detected on its own, every finding carries its field path, and the exit code lets a pipeline block a non-conformant invoice.

Safe by default

No DTD, no external entities, bounded depth and size, no content ever executed. SECURITY.md

Edge, serverless, browser

No Node API in the main entry point; pdf-lib only inside facturx-sdk/pdf. A Request → Response handler is enough for Next.js, Hono or Cloudflare Workers — the example is in the repository.

Where the SDK sits in the French reform

Receiving structured invoices is mandatory for every business, then issuing them according to company size, through an accredited platform. The SDK is the format and compliance brick; the platform is the transport one.

  1. Receiving for every business · issuing for large companies
  2. Issuing for SMEs and micro-businesses
CII EN 16931 XML, PDF/A-3 Factur-X
EN 16931 + French validation, typed errors
Reform statements, prepayments, credit notes, attachments
Reading all three socle syntaxes: Factur-X, CII, UBL
Lifecycle statuses: official rules, CDV message written and read back
Rendering the readable page from the model, then embedding
facturx command line: validate, summarise, extract
Submitting to an accredited platform, e-reporting, directory
Turning an arbitrary PDF into PDF/A
Writing UBL, writing the other Factur-X profiles

Spelled out in the English documentation; the in-depth reform guide stays in French, because it comments on French regulation.

Start with one invoice.

Four runnable examples, all executed in continuous integration: issuing from a JSON model, receiving a folder of PDFs, an HTTP handler, and prepayments leading to a final invoice.

npm add facturx-sdk
Browse the examples