@fin.cx/einvoice ⚡
TypeScript e-invoicing for the real world: load invoice XML or hybrid PDFs, detect the format, map it into a typed invoice model, validate it, convert it, and embed XML back into PDFs.
@fin.cx/einvoice is built for programmers who need a practical toolkit around European e-invoice formats without writing a parser zoo from scratch.
Issue Reporting and Security
For reporting bugs, issues, or security vulnerabilities, please visit community.foss.global/. This is the central community hub for all issue reporting. Developers who sign and comply with our contribution agreement and go through identification can also get a code.foss.global/ account to submit Pull Requests directly.
Why this library?
- 🚀 Load invoices from XML strings, files, or PDFs with embedded XML.
- 🧭 Detect
ubl,xrechnung,cii,facturx,zugferd, andfatturapadocuments. - 🧾 Work on a typed in-memory invoice model based on
@tsclass/tsclass. - ✅ Validate invoices on syntax, semantic, and business-rule levels.
- 🔄 Export invoices as
facturx,zugferd,xrechnung,ubl, orcii. - 📎 Extract XML from invoice PDFs and attach XML back into existing PDFs.
- 🧱 Use the high-level
EInvoiceclass or lower-level decoders, encoders, validators, and PDF extractors directly.
Install
pnpm add @fin.cx/einvoice
postinstall will try to download Schematron validation resources on a best-effort basis when the package is installed in a normal online environment. Install will not fail if the resources cannot be downloaded.
Useful commands:
pnpm download-schematron
pnpm download-test-samples
Useful environment flag:
EINVOICE_SKIP_RESOURCES=1
Quick Start
import { EInvoice, ValidationLevel } from '@fin.cx/einvoice';
const invoice = await EInvoice.fromFile('./invoice.xml');
console.log(invoice.getFormat());
console.log(invoice.id);
console.log(invoice.from.name, '->', invoice.to.name);
const validation = await invoice.validate(ValidationLevel.BUSINESS);
if (!validation.valid) {
console.log(validation.errors);
}
const xrechnungXml = await invoice.exportXml('xrechnung');
What it can do
Load and inspect invoices
import { EInvoice } from '@fin.cx/einvoice';
const fromXml = await EInvoice.fromXml(xmlString);
const fromFile = await EInvoice.fromFile('./invoice.xml');
const fromPdf = await EInvoice.fromPdf(pdfBuffer);
console.log(fromXml.subject);
console.log(fromXml.items.length);
console.log(fromXml.currency);
Create invoices in code
import { EInvoice } from '@fin.cx/einvoice';
const invoice = new EInvoice();
invoice.accountingDocId = 'INV-2026-001';
invoice.issueDate = new Date('2026-04-16');
invoice.currency = 'EUR';
invoice.dueInDays = 14;
invoice.from = {
type: 'company',
name: 'Sender GmbH',
description: '',
status: 'active',
foundedDate: { year: 2020, month: 1, day: 1 },
address: {
streetName: 'Example Street',
houseNumber: '1',
city: 'Berlin',
postalCode: '10115',
country: 'DE',
},
registrationDetails: {
vatId: 'DE123456789',
registrationId: 'HRB 123456',
registrationName: 'Sender GmbH',
},
};
invoice.to = {
type: 'company',
name: 'Receiver SAS',
description: '',
status: 'active',
foundedDate: { year: 2020, month: 1, day: 1 },
address: {
streetName: 'Rue Example',
houseNumber: '10',
city: 'Paris',
postalCode: '75001',
country: 'FR',
},
registrationDetails: {
vatId: 'FR12345678901',
registrationId: 'RCS 123456789',
registrationName: 'Receiver SAS',
},
};
invoice.items = [
{
position: 1,
name: 'Implementation work',
articleNumber: 'IMPL-001',
unitType: 'HUR',
unitQuantity: 8,
unitNetPrice: 120,
vatPercentage: 19,
},
];
const xml = await invoice.exportXml('facturx');
Validate at different levels
import { EInvoice, ValidationLevel } from '@fin.cx/einvoice';
const invoice = await EInvoice.fromXml(xmlString);
const syntax = await invoice.validate(ValidationLevel.SYNTAX);
const semantic = await invoice.validate(ValidationLevel.SEMANTIC);
const business = await invoice.validate(ValidationLevel.BUSINESS, {
featureFlags: ['EN16931_BUSINESS_RULES', 'CODE_LIST_VALIDATION'],
reportOnly: true,
});
console.log(business.valid);
console.log(business.errors);
console.log(business.warnings ?? []);
Convert between supported XML targets
import { EInvoice } from '@fin.cx/einvoice';
const invoice = await EInvoice.fromFile('./source.xml');
const facturxXml = await invoice.exportXml('facturx');
const zugferdXml = await invoice.exportXml('zugferd');
const xrechnungXml = await invoice.exportXml('xrechnung');
const ublXml = await invoice.exportXml('ubl');
const ciiXml = await invoice.exportXml('cii');
Work with PDFs
import { EInvoice, PDFExtractor } from '@fin.cx/einvoice';
const extracted = await new PDFExtractor().extractXml(pdfBuffer);
if (extracted.success && extracted.xml) {
const invoice = await EInvoice.fromXml(extracted.xml);
console.log(extracted.format, invoice.id);
}
const invoice = await EInvoice.fromXml(xmlString);
const pdfWithXml = await invoice.embedInPdf(existingPdfBuffer, 'facturx');
Supported formats
| Format | Detect | Import | Export | Validation | Notes |
|---|---|---|---|---|---|
ubl |
Yes | Yes | Yes | Yes | Generic UBL flow |
xrechnung |
Yes | Yes | Yes | Yes | UBL-based profile |
cii |
Yes | Yes | Yes | Partial | Generic CII export currently uses the Factur-X encoder path |
facturx |
Yes | Yes | Yes | Yes | Main CII-based generation path |
zugferd |
Yes | Yes | Yes | Partial | Input supports v1 and v2+, export focuses on the current encoder |
fatturapa |
Yes | No | No | Basic placeholder | Detection exists, decoder/encoder do not |
Important format notes
- There is no dedicated root-level
peppolexport format. PEPPOL-specific checks exist, but the XML export targets are stillubl/xrechnung. FatturaPAshould be documented as detection-only right now.creditnote/debitnotehandling exists in parts of the lower-level code, but the high-levelinvoiceTypesetter onEInvoiceonly accepts'invoice'.
Public API overview
Top-level exports from @fin.cx/einvoice include:
EInvoicecreateEInvoice()validateXml(xml, level?)FormatDetectorPDFExtractor,PDFEmbedderDecoderFactory,EncoderFactory,ValidatorFactoryBaseDecoder,BaseEncoder,BaseValidatorUBLBase*andCIIBase*extension classesFacturX*andZUGFeRD*format-specific classesValidationLevel,InvoiceFormat- exported types like
TInvoice,ValidationResult,ValidationError,ExportFormat,IPdf,EInvoiceOptions
Validation resources and install-time behavior
The package includes an install bootstrap in ts_install/ that tries to download validation resources into assets_downloaded/schematron.
Behavior to know:
- It only runs in a real
postinstalllifecycle. - It skips cleanly when offline.
- It skips when
dist_ts/is not present yet. - It can be disabled in CI with
EINVOICE_SKIP_RESOURCES=1. - Missing resources do not break install, but they can reduce advanced validation coverage.
If you need the resources manually:
pnpm download-schematron
pnpm download-test-samples
Architecture in one screen
XML / PDF
-> format detection
-> decoder
-> EInvoice model
-> validation / editing
-> encoder
-> XML / PDF with embedded XML
This repo also exposes the lower-level pieces separately so you can plug into the pipeline at the stage you actually need.
Good things to know before using it
embedInPdf()attaches XML to an existing PDF buffer. It does not render invoice PDFs from scratch.saveToFile('something.pdf', ...)only works wheninvoice.pdfalready exists, for example after loading from a PDF first.- Validation coverage is useful and broad, but this README deliberately does not claim blanket certification or “100% compliance”.
- Schematron integration and richer business-rule layers exist, but they depend on downloaded resources and are not the same as guaranteed certification against every profile.
Example workflow
import { EInvoice, ValidationLevel } from '@fin.cx/einvoice';
const invoice = await EInvoice.fromFile('./incoming.pdf');
const validation = await invoice.validate(ValidationLevel.BUSINESS, {
featureFlags: ['EN16931_BUSINESS_RULES', 'CODE_LIST_VALIDATION'],
reportOnly: false,
});
if (!validation.valid) {
throw new Error(validation.errors.map((error) => error.message).join('\n'));
}
const xmlForGermanB2G = await invoice.exportXml('xrechnung');
Module layout
ts/: main published API and implementationts_install/: install-time resource bootstrap and download helpersassets_downloaded/: downloaded validation resourcestest/: corpus-heavy validation, format, PDF, performance, and security coverage
License and Legal Information
This repository contains open-source code licensed under the MIT License. A copy of the license can be found in the LICENSE file.
Please note: The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.
Trademarks
This project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH or third parties, and are not included within the scope of the MIT license granted herein.
Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines or the guidelines of the respective third-party owners, and any usage must be approved in writing. Third-party trademarks used herein are the property of their respective owners and used only in a descriptive manner, e.g. for an implementation of an API or similar.
Company Information
Task Venture Capital GmbH
Registered at District Court Bremen HRB 35230 HB, Germany
For any legal inquiries or further information, please contact us via email at hello@task.vc.
By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.