Skip to content
Product

AI invoice data extraction

From a PDF or photo of a supplier invoice to a structured record you can reconcile — no templates, no per-supplier setup.

LedgerSync sends each uploaded invoice to Google’s Gemini model with a fixed response schema. The model has to answer in that shape, so the result is a typed record — supplier, invoice number, dates, amounts, line items — rather than free text that still needs parsing. The record then goes straight into reconciliation against your ERP records.

What you can upload

  • Invoices as PDF or image — PDF, JPEG, PNG, WebP, TIFF, HEIC/HEIF, BMP and GIF. Each file can be up to 50 MB (the default limit), and a single upload can carry up to 40 files.
  • Structured files — CSV, XLSX/XLS, XML, JSON, YAML and plain text are converted to text first (one CSV block per spreadsheet sheet) and extracted the same way.
  • Multi-invoice packs — one PDF containing many invoices is split into one document per invoice before extraction. See multi-invoice PDFs below.

The declared file type is never trusted on its own: for PDF, JPEG, PNG, WebP, TIFF, BMP and GIF the first bytes of the file are checked against the format’s signature, and a mismatch is rejected before anything is stored.

What gets extracted

Extraction runs with a strict JSON response schema (temperature 0.2), so every document comes back in the same shape. These are the fields.

Header

Header fields in the extraction schema
FieldNotes
supplier_name, supplier_idSupplier name and the supplier code if the invoice prints one
invoice_numberDocument-type prefixes such as “Invoice” or “Rechnung” are stripped from the value
invoice_dateNormalised to ISO YYYY-MM-DD
subtotal, tax_amount, total_amountNet, tax and gross amounts
tax_ratePercentage, when printed
currencyISO 4217 code
discount_amount, discount_percentEarly-payment or trade discounts (Skonto, Rabatt)
delivery_termsIncoterms: EXW, FCA, FAS, FOB, CFR, CIF, CPT, CIP, DAP, DPU, DDP
confidenceThe model’s own 0–1 confidence for the extraction

Line items

Line item fields in the extraction schema
FieldNotes
item_codeSKU, article or style code
descriptionLine text as printed
quantity, unit_price, total_priceA missing line total is computed as quantity × unit price
colorColour / Farbe column when present
extra_fieldsAny other per-line column, as key/value pairs

Everything else on the page

Additional fields in the extraction schema
FieldNotes
additional_fieldsEvery other labelled header field on the document, in any language, as key/value pairs
payment_terms, freight_amount, carrier, delivery_recipient, ilnLogistics and payment details common on DACH B2B invoices
tax_category, is_intra_community_exemptionSet when the document carries a reverse-charge or intra-Community supply statement

The raw JSON is kept with the document, so anything the model read but the cards don’t show is still there to cross-check against the original file.

German, EU VAT and Indian GST labels, without configuration

Label knowledge lives in small “lexicon packs” that are merged at runtime. They tell both the model and the matching engine which printed labels mean which field, and which printed phrases change the tax treatment.

Lexicon packs and the labels they cover
PackCovers
Core (English)Invoice No., Invoice Number, Invoice #, Bill No., Document No.; VAT No., Tax ID, EIN, ABN; Incoterms
DACH (German)RECHNUNG, Rechnungsnummer, Belegnummer, Beleg-Nr.; Belegdatum, Rechnungsdatum; Zwischensumme, Warenwert, Nettowert; MwSt, USt, Umsatzsteuer; Rechnungsbetrag, Gesamtbetrag; Skonto, Rabatt; USt.-IdNr., UID
EU VAT“Steuerfreie innergemeinschaftliche Lieferung”, “Intra-Community supply”, “Reverse charge”, “0% Reverse Charge” → tax category set, 0% rate, exemption flag
India GSTCGST, SGST, IGST, GST; HSN / SAC codes; GSTIN

Adding a country is a new pack file plus one registry line; nothing per customer or per supplier has to be configured.

Multi-invoice PDFs

Suppliers often send one PDF with twenty invoices and a cover sheet. Before extraction, every page’s text is scanned for an invoice-number label followed by a value — Invoice No. 9000120481, RECHNUNG 2/984712/3349120, Belegnummer SF-551249. Each such page starts a new invoice; pages without a label attach to the invoice before them; cover or summary pages before the first label are dropped. The parts are named pack-01.pdf, pack-02.pdf, … and processed as separate documents, so you get one row per invoice, not one row per file.

A PDF with a single invoice is ingested whole, and if a file’s text can’t be read the original is ingested unchanged rather than lost. How the split works, with a worked example.

Arithmetic checks after extraction

An extracted record is self-checked before it is scored: line totals (minus any discount) should add up to the subtotal, subtotal × tax rate should give the tax, and subtotal + tax should give the total, each within 0.5% or 0.02 in the invoice’s currency. A document that fails a check is never silently “fully matched” — it lands in the review queue with the exact figures that disagree.

Austrian 20% VAT invoice, net 290 → no discrepancies; the same invoice with the total mis-read as 358:
[{ "field": "arithmetic.totalAmount", "extracted": 358, "erp": 348 }]

A 0% intra-Community or reverse-charge invoice does not raise a tax alert.

What happens to a document

Document status lifecycle
StatusMeaning
PENDINGStored and queued for the background worker
PROCESSINGBeing read by the model and reconciled
PROCESSEDExtraction and match result are on the document
FAILEDUnreadable file or extraction error — the message is kept; “Try again” requeues it

Extraction and reconciliation run off the request path in a worker that lives in the same deployable as the app, so an upload returns immediately and the row updates when the work is done.

Where the document content goes

The file content is sent to the Gemini API solely to return the structured record above. See the security overview and the privacy policy for what is stored where.

Ready to see it on your own invoices?