Factur-X / ZUGFeRD electronic invoices in TypeScript.
- CII XML from a plain invoice object. EN 16931 semantic model, UN/CEFACT CII D16B
syntax, element order of the schema, VAT breakdown rules (
BR-S,BR-Z,BR-E,BR-AE,BR-IC,BR-G,BR-O). Profiles MINIMUM, BASIC WL, BASIC (default), EN 16931, EXTENDED. - PDF/A-3 embedding. Attaches the XML as
factur-x.xmlwithAFRelationship /Alternativeand writes the XMP packet with the Factur-X extension schema. - Extraction. Reads
factur-x.xmlorzugferd-invoice.xmlback from any PDF. - Totals you can trust. One VAT group per category and rate, VAT computed on the rounded basis, prepayments, credit notes referencing the original invoice.
- Zero dependencies for the XML part;
pdf-libonly for the PDF part (separate entry point).
Install
npm install @ekwo-ai/factur-x
Usage
import { generateCiiXml, computeTotals } from '@ekwo-ai/factur-x';
import { embedFacturX, extractFacturX } from '@ekwo-ai/factur-x/pdf';
const invoice = {
number: 'INV-2026-0042',
issueDate: '2026-03-31',
dueDate: '2026-04-30',
seller: {
name: 'Exemple Conseil SA',
vatId: 'BE0123456749',
legalId: { scheme: '0208', value: '0123456749' }, // Belgian enterprise number
address: { line1: "Rue de l'Exemple 1", postalCode: '1000', city: 'Bruxelles', country: 'BE' },
},
buyer: {
name: 'Société Fictive SAS',
vatId: 'FR12345678901',
legalId: { scheme: '0002', value: '345678901' }, // SIREN
address: { line1: '10 avenue des Tests', postalCode: '75001', city: 'Paris', country: 'FR' },
},
buyerReference: 'SERVICE-EXEC-42',
lines: [
{ name: 'Consulting days', quantity: 3, unitCode: 'day', unitPrice: 950, vatRate: 0 },
{ name: 'Software licence', quantity: 1, unitPrice: 1200, vatRate: 0 },
],
payment: { iban: 'BE71 0961 2345 6769' },
};
const xml = generateCiiXml(invoice); // BASIC profile
const totals = computeTotals(invoice); // { lineTotal, taxTotal, grandTotal, duePayable, breakdown }
const pdf = await embedFacturX(visualPdfBytes, xml, { title: 'Invoice INV-2026-0042' });
const back = await extractFacturX(pdf); // { filename: 'factur-x.xml', xml }
npm run example runs examples/basic.ts end to end.
VAT categories
Each line carries a vatRate. The category (BT-151) is derived when you do not state it:
positive rate → S; zero rate at home → Z; zero rate to an EU business with a VAT id → K
(intra-community); to an EU buyer without one → E; outside the EU → G. Set vatCategory
on the line to override (AE reverse charge, O not subject to VAT…). Exempt groups get a
reason text (BT-120, override with invoice.exemptionReasons) and the VATEX code where one
exists.
Identifiers
| Field | Example | XML |
|---|---|---|
vatId |
BE0123456749, FR12345678901 |
SpecifiedTaxRegistration/ID[@schemeID="VA"] |
legalId |
{ scheme: '0208', value: '0123456749' } (BE), { scheme: '0002', value: '345678901' } (FR SIREN) |
SpecifiedLegalOrganization/ID[@schemeID] |
buyerReference |
French « service exécutant » code | BuyerReference (BT-10) |
orderReference |
purchase order | BuyerOrderReferencedDocument (BT-13) |
Units
unitCode accepts a UN/ECE Recommendation 20 code (HUR, DAY, KWH…) or an everyday
word in French, Dutch, English or German (heures, dagen, m², Stück). Unknown values fall
back to C62 (unit). See UNIT_CODES and toUnitCode.
API
| Export | Description |
|---|---|
generateCiiXml(invoice, { profile?, businessProcess? }) |
CII XML string. |
computeTotals(invoice) |
VAT breakdown and document totals (BT-106 … BT-117). |
defaultVatCategory, EXEMPTION_REASON_CODES, DEFAULT_EXEMPTION_REASONS |
VAT helpers. |
toUnitCode, UNIT_CODES |
Unit helpers. |
buildXmpMetadata, CONFORMANCE_LEVELS, GUIDELINES, DOCUMENT_TYPE_CODES |
Constants of the specification. |
embedFacturX(pdf, xml, { profile?, title?, creator?, producer?, date? }) (/pdf) |
Returns a new PDF with the XML attached and the PDF/A-3 XMP packet. |
extractFacturX(pdf) (/pdf) |
{ filename, xml } or null. |
Scope and limitations
- Allowances and charges (BG-20, BG-21, BG-27, BG-28), gross prices with discounts and multiple deliveries are not modelled yet.
- The PDF part embeds the data and declares PDF/A-3; it does not convert the visual PDF to PDF/A (fonts, colour profiles, output intent). Validate the result with the tool of your platform before going live.
- No XSD or Schematron validation is performed. Test against a validator such as the FNFE Factur-X validator or the Mustang project.
Development
npm install
npm run typecheck
npm test
npm run build
License
MIT © Ekwo AI