jkunz c0e88c1088
Default (tags) / security (push) Failing after 0s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
v5.2.0
2026-04-16 20:30:56 +00:00
2024-04-22 16:30:55 +02:00
2026-04-16 20:30:56 +00:00
2025-05-27 18:02:19 +00:00
2025-05-26 04:04:51 +00:00

@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, and fatturapa documents.
  • 🧾 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, or cii.
  • 📎 Extract XML from invoice PDFs and attach XML back into existing PDFs.
  • 🧱 Use the high-level EInvoice class 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 peppol export format. PEPPOL-specific checks exist, but the XML export targets are still ubl / xrechnung.
  • FatturaPA should be documented as detection-only right now.
  • creditnote / debitnote handling exists in parts of the lower-level code, but the high-level invoiceType setter on EInvoice only accepts 'invoice'.

Public API overview

Top-level exports from @fin.cx/einvoice include:

  • EInvoice
  • createEInvoice()
  • validateXml(xml, level?)
  • FormatDetector
  • PDFExtractor, PDFEmbedder
  • DecoderFactory, EncoderFactory, ValidatorFactory
  • BaseDecoder, BaseEncoder, BaseValidator
  • UBLBase* and CIIBase* extension classes
  • FacturX* and ZUGFeRD* format-specific classes
  • ValidationLevel, 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 postinstall lifecycle.
  • 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 when invoice.pdf already 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 implementation
  • ts_install/: install-time resource bootstrap and download helpers
  • assets_downloaded/: downloaded validation resources
  • test/: corpus-heavy validation, format, PDF, performance, and security coverage

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.

S
Description
No description provided
Readme 40 MiB
Languages
TypeScript 56.3%
XSLT 43%
JavaScript 0.4%
HTML 0.2%