Skip to main content
Glama

pdf-to-einvoice

Turn the invoice PDF you already have into an e-invoice (ZUGFeRD / Factur-X, EN 16931) without changing how it looks. In the browser, on the command line, as a TypeScript library, or as an MCP server for AI assistants.

CI npm license

The web app: your PDF on the left, its data on the right

An e-invoice (German: E-Rechnung) is an invoice that software can read. The hybrid kind, ZUGFeRD in Germany and Factur-X in France, is a PDF that carries the invoice a second time as XML. Most tools make you write your invoices in their format. This one starts from the PDF you already produce with Word, your invoicing tool or a template:

  • Your layout stays. The pages are not redrawn. The data is attached as factur-x.xml and the file becomes a PDF/A-3.

  • It tells you what is missing. Required fields, sums that do not add up, and numbers, names, addresses, dates and amounts that do not appear on your PDF are listed before anything is created.

  • Nothing is uploaded. The web app works in your browser; the library and the command line tool run on your machine.

  • The common kinds of invoice. Domestic with VAT, several rates, small businesses, reverse charge, intra-community supplies, exports, services to non-EU customers, credit notes and corrections.

  • XRechnung for public bodies, as plain XML or inside the PDF, see XRechnung.

  • Checked against the reference. CI creates each of those kinds and has an independent validator check the result: veraPDF for PDF/A-3, and the official EN 16931, Factur-X and XRechnung rules. What your own PDF brings along is a different matter, see What changes in your PDF.

In the browser

Open the web app, or start it locally with npm run site. Drop a PDF, check the data that was read from it, download the e-invoice. The page is in English and German. The site at that address can count page views and created files, never anything of an invoice, with a cookieless Umami we run ourselves; its privacy policy says whether it does. A copy you build yourself counts nothing.

Reading works by pattern matching, without any AI. On request a small language model fills in what the patterns did not find. It runs in your browser (WebGPU) and is downloaded once from Hugging Face (huggingface.co, hf.co) and raw.githubusercontent.com; if you point the page at a model service of your own, the invoice text goes to that service.

Compared with online converters

Many services convert PDF invoices too. The main difference is where your invoice is processed.

typical online converter

pdf-to-einvoice

Where is your invoice processed?

On the provider's servers: the file is uploaded.

On your device, in the browser. Nothing is uploaded.

Who reads the data?

Depending on the provider, an AI service in the cloud, also outside the EU, or you type everything in.

Rules on your device; on request also a language model that runs in your browser too, or a service whose address you enter yourself.

Where does each value come from?

You get a form to correct.

What was read from the PDF is marked on it. Numbers, names, addresses, dates, prices and totals are compared with its text: whether they are there, not where.

Account, ads, tracking

Depending on the provider, sign-up, ads or a subscription.

No sign-up, no ads, no cookies.

Source code

Mostly closed.

Open (MIT licence): anyone can check what the page does.

For software and AI assistants

Mostly an API that invoices are sent to.

Library, command line tool and MCP server, all on your own machine.

Checking against the official rule sets

Some offer their own validator.

Its own plausibility check; not a complete check against the official rule sets.

Related MCP server: Kontor MCP

As a library

npm install pdf-to-einvoice
import { readFile, writeFile } from 'node:fs/promises';
import { createEInvoice } from 'pdf-to-einvoice';

const { pdf, xml, findings } = await createEInvoice({
  pdf: await readFile('invoice.pdf'),
  invoice: {
    number: 'RE-2026-0042',
    issueDate: '2026-11-03',
    deliveryDate: '2026-10-31',
    dueDate: '2026-11-17',
    iban: 'DE02120300000000202051',
    seller: { name: 'Muster Software GmbH', street: 'Beispielweg 1', postalCode: '12345', city: 'Musterstadt', country: 'DE', vatId: 'DE123456789' },
    buyer: { name: 'Beispiel AG', street: 'Hauptstraße 5', postalCode: '54321', city: 'Beispielstadt', country: 'DE' },
    lines: [{ name: 'Consulting', quantity: 10, unit: 'HUR', unitPrice: 100, vatRate: 19 }],
  },
});

await writeFile('invoice.einvoice.pdf', pdf);

It works the same in a browser: pdf is a Uint8Array in and out. A bundler needs two things set up, as site/vite.config.ts and site/main.ts do: an alias that points @e-invoice-eu/core to its browser bundle, and the worker of pdf.js.

function

what it does

createEInvoice({ pdf, invoice, profile? })

checks the data, then returns { pdf, xml, findings, totals }; throws InvoiceError if data is wrong

createXRechnung({ invoice, syntax? })

the data as an XRechnung: returns { xml, findings, totals }, no PDF; throws InvoiceError as well

check(invoice, { pdfText?, rules? })

returns { findings, totals, invoice? } without creating anything; never throws on incomplete data

extractText(pdf)

the text of a PDF, row by row

extractLayout(pdf)

the text plus its geometry: blocks (text that stands together), pieces with their position, pages

guessFromText(text, { blocks })

reads an invoice without a model: totals that add up, items that add up to them, seller and buyer, labelled values

keepWhatThePdfShows(draft, text)

drops every value the PDF does not show: a guard for data read by a small language model

languageOf(text)

'de' or 'en': the language an invoice is written in

validateEInvoice(pdf, { pdfText? })

looks at an existing e-invoice: profile and embedded data; with pdfText also whether the XML matches the page

readEmbeddedXml(pdf)

the XML of an existing e-invoice, or null

calculate(invoice)

the totals, following the EN 16931 rounding rules

invoiceJsonSchema

JSON Schema of the invoice data, for forms, validation and language models

A finding is { path, severity, code, message }. An error blocks the e-invoice; a warning asks for a second look.

On the command line

npx pdf-to-einvoice read invoice.pdf --json      # text, guessed values and facts about the PDF
npx pdf-to-einvoice schema                       # the JSON Schema of the invoice data
npx pdf-to-einvoice check invoice.json --pdf invoice.pdf
npx pdf-to-einvoice create invoice.pdf invoice.json   # writes invoice.einvoice.pdf
npx pdf-to-einvoice validate invoice.einvoice.pdf
npx pdf-to-einvoice xml invoice.einvoice.pdf     # prints the embedded XML
npx pdf-to-einvoice xrechnung invoice.json -o invoice.xml   # the data as an XRechnung, see below

create and xrechnung never overwrite a file unless you pass --force, and never write over their input.

Many invoices at once

An invoicing program already has what this tool needs: the PDF it renders and the data it renders it from. Pass that data on instead of reading it back from the PDF. Reading is a first draft for a person to check, not something to run unattended over a batch.

import { readFile, writeFile } from 'node:fs/promises';
import { createEInvoice, InvoiceError } from 'pdf-to-einvoice';

for (const { pdfPath, invoice } of invoicesToConvert) {
  try {
    const { pdf } = await createEInvoice({ pdf: await readFile(pdfPath), invoice });
    await writeFile(pdfPath.replace(/\.pdf$/i, '.einvoice.pdf'), pdf);
  } catch (error) {
    if (!(error instanceof InvoiceError)) throw error;
    console.error(pdfPath, error.findings); // the data has errors: nothing was written
  }
}

On the command line, with a data file next to each PDF (RE-1.pdf and RE-1.json):

for pdf in invoices/*.pdf; do
  npx pdf-to-einvoice create "$pdf" "${pdf%.pdf}.json" || echo "not created: $pdf"
done

For AI assistants

The MCP server lets an AI assistant do the whole job: read the PDF, fill in the data, show you what is missing, and create the file after you confirm.

{
  "mcpServers": {
    "pdf-to-einvoice": { "command": "npx", "args": ["-y", "pdf-to-einvoice", "mcp"] }
  }
}

Tools: read_invoice_pdf, check_invoice, create_einvoice, create_xrechnung, validate_einvoice, read_einvoice_xml. A summary for language models is in llms.txt.

For agents that work with a shell, such as Claude Code, the folder skills/pdf-to-einvoice is a skill. The agent reads the PDF itself, a scanned one too, compares its reading with what the tool found by pattern, and lets the tool check the data and create the file. Copy the folder into the agent's skills folder; for Claude Code that is ~/.claude/skills/ for all projects or .claude/skills/ in one. The npm package ships it in node_modules/pdf-to-einvoice/skills/.

The server and the skill run on your machine, need no API key and call no model themselves: the assistant you already use reads the invoice. Its content therefore goes wherever that assistant runs.

The invoice data

Small and flat on purpose. Required: number, issueDate, seller and buyer with name and country, a tax identifier of the seller (vatId or taxNumber), at least one line, and a dueDate or paymentTerms when money is owed. Defaults: typeCode 380 (invoice), currency EUR, unit C62 (piece), vatCategory S.

VAT category

meaning

typical case

S

standard rate

domestic sale with VAT (any rate above 0)

Z

zero rated

goods taxed at 0 %

E

exempt

small business (§ 19 UStG), exempt services

AE

reverse charge

service for a business in another EU country

K

intra-community supply

goods delivered to a business in another EU country

G

export

goods delivered outside the EU

O

not subject to VAT

service for a business outside the EU, e.g. in the USA

Every category except S and Z needs vatExemptionReason: the sentence on your invoice that says why no VAT is charged. totals are the sums printed on the PDF; if they contradict the calculation, that is an error, because the XML must not contradict the page. A line's netAmount may differ from quantity x unit price by rounding only (0.02); with a discount on a line, give the unit price after the discount. The full list of fields is in src/model.ts and in npx pdf-to-einvoice schema.

XRechnung

XRechnung is the German version of EN 16931 that public bodies ask for. The same invoice data can be written as one:

  • As plain XML in UBL (default) or CII: createXRechnung({ invoice, syntax? }), the command xrechnung <data.json> [--syntax ubl|cii] [-o out.xml], the MCP tool create_xrechnung.

  • Inside the PDF, in place of the EN 16931 profile: profile: 'xrechnung' for createEInvoice, --profile xrechnung for create, profile for create_einvoice.

  • In the web app: choose XRechnung above the form, and you get both files.

XRechnung asks for more than EN 16931 does. check with rules: 'xrechnung' (--rules xrechnung) reports what is missing, under codes that start with xrechnung-, and both ways of creating run it first:

  • buyerReference: for a German public body its Leitweg-ID, which the buyer names

  • a contact at the seller: seller.contactName, seller.phone, seller.email

  • buyer.email, which is written as the buyer's electronic address

  • city and postalCode of seller and buyer

  • a line of paymentTerms that starts with # in the form of a cash discount: #SKONTO#TAGE=14#PROZENT=2.00#

The XML declares XRechnung 3.0. What it cannot do yet:

  • An intra-community supply (K) and a deliveryCountry are refused: XRechnung wants a place of delivery with city and postal code, and the data has no delivery address.

  • The electronic address of seller and buyer is always an e-mail address, not a Leitweg-ID or a Peppol ID.

  • The extension of XRechnung (sub-lines, payments by a third party) is not written.

  • The file is not sent anywhere. How a public body takes invoices in (portal, e-mail, Peppol) is for it to say.

  • validateEInvoice reads the XML inside a PDF, not a plain XML file.

What changes in your PDF

Only what the standard asks for: the XML attachment, XMP metadata that declares PDF/A-3 and Factur-X, an sRGB output intent and a document ID. Two things PDF/A forbids are taken out: the flag of images that ask viewers to smooth them (/Interpolate true), and scripts and actions the file would run (JavaScript, starting a program and the like), which you are told about. The page content is not touched.

One defect is detected and reported, but not repaired: fonts that are not embedded. The XML is not affected, but the file is not a strict PDF/A-3; export the PDF with embedded fonts (or as PDF/A) and run it again. Other things PDF/A forbids in your original are neither found nor repaired. If the software of a recipient rejects the file, check it with a PDF/A validator.

Limits

  • Only the issuer can turn an invoice into an e-invoice. Converting a PDF you received does not make it one.

  • Check every value. Reading is a first draft: it knows some layouts well and others less, leaves empty what it cannot place, and can be wrong. A scanned PDF has no text to read at all. The comparison with the PDF tells whether a value stands in its text, not where: an address entered for the wrong party passes. The XML is the part that counts legally, and the tool cannot know what you meant.

  • Not modelled yet: discounts and charges on the whole invoice (use a line with a negative quantity), VAT in a second currency, several payment accounts, attachments, the profiles BASIC and EXTENDED.

  • Nothing is assumed about the delivery. Without a delivery date and a deliveryCountry the delivery part of the XML is empty, as in official samples of the standard; a validator may remark on the empty element. Only an intra-community supply names the buyer's country there, and the check asks about it.

  • validateEInvoice is a plausibility check, not a replacement for a full validator.

  • No encryption or password protection: PDF/A forbids it.

  • Country rules: in Germany a hybrid PDF is one accepted form of e-invoice. Italy, Poland, Belgium and France route invoices through national platforms or networks; a file alone is not enough there.

  • This is a tool, not tax or legal advice.

Develop

npm install
npm test
npm run site             # the web app on http://localhost:4350

The rest is in CONTRIBUTING.md: the checks, the reference validation and how the reader is measured.

Licence

MIT, see LICENSE. The libraries this project builds on, and their licences, are listed in THIRD_PARTY_NOTICES.md. The software uses a subset of the semantic data model of the European standard EN 16931-1:2017; the standard itself is the property of CEN and is not part of this repository.

ZUGFeRD, Factur-X and XRechnung are names of their respective owners. This project is independent: it is not affiliated with the organisations behind these standards, and it is neither endorsed nor certified by them.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    Related MCP Connectors

    Related MCP Servers

    • F
      license
      Not graded
      quality
      C
      maintenance
      Enables AI agents to create and manage e-invoices through natural language, supporting EU compliance formats like ZUGFeRD and XRechnung, as well as US plain PDF invoices.
      7
      -
    • A
      license
      Not graded
      quality
      B
      maintenance
      Enables AI assistants to locally parse, validate, audit, explain, generate, and convert XRechnung and ZUGFeRD/Factur-X e-invoices using official rule sets, fully offline with no API keys required.
      Apache 2.0
    • A
      license
      A
      quality
      B
      maintenance
      Enables AI assistants and agents to create, validate, read and convert German and EU e-invoices in ZUGFeRD, XRechnung and Factur-X formats, including extracting structured data from scanned or PDF invoices and embedding XML into PDF/A-3 files. It can run as a hosted remote server or locally, with each request authenticated by the caller's own API token.
      10
      20 npm
      MIT
    • A
      license
      B
      quality
      C
      maintenance
      Enables AI assistants to locate invoice files, extract embedded ZUGFeRD/Factur-X XML from PDF/A-3 documents, and validate compliance with § 14 UStG and EN 16931, including direct PDF invoice checks.
      3
      1
      MIT