Skip to content

Python API reference

from invoice_agent.data import load_purchase_orders, load_receipts
from invoice_agent.pipeline import Pipeline

pipeline = Pipeline(load_purchase_orders("pos.csv"), load_receipts("receipts.csv"))
result = pipeline.process_pdf("invoice.pdf")
print(result.decision, [r.code for r in result.reasons])

Pipeline

invoice_agent.pipeline

End-to-end pipeline: ingest, extract, validate, match, decide.

Pipeline

Process invoices one after another against the same PO data.

The pipeline keeps a :class:Ledger, so the order of calls matters: a second invoice for the same goods is caught as a duplicate or as overbilling.

process_invoice(invoice, *, source=None)

Validate and match an already extracted invoice, then record it in the ledger.

decide(reasons)

The strictest severity wins: any reject rejects, any review needs review.

Schema

invoice_agent.schema

Strict data models shared by every stage of the pipeline.

Money is always :class:decimal.Decimal. Floats never touch an amount.

LineItem

Bases: _Strict

One billed line on an invoice.

Invoice

Bases: _Strict

Structured invoice produced by an extractor.

missing_required()

Return the names of fields that matching cannot run without.

Reason

Bases: _Strict

A single explainable finding. code is stable; message is for humans.

Result

Bases: _Strict

Pipeline output for one invoice.

Policy

invoice_agent.policy.MatchPolicy

Bases: BaseModel

Thresholds used by validation and matching. All fields have safe defaults.

Data loading

invoice_agent.data

Load purchase orders and goods receipts from JSON, CSV or SQLite.

JSON files hold a list of objects shaped like :class:PurchaseOrder or :class:GoodsReceipt. CSV files and SQLite tables hold one row per line:

  • purchase orders: po_number, vendor, currency, receipt_required, line_no, sku, description, quantity, unit_price
  • receipts: receipt_id, po_number, date, line_no, quantity

A SQLite database uses the tables po_lines and receipt_lines with the same columns.

load_purchase_orders(path)

Load purchase orders from a .json, .csv or SQLite file.

load_receipts(path)

Load goods receipts from a .json, .csv or SQLite file.

po_from_rows(rows)

Group flat PO line rows into purchase orders, keeping first-seen order.

receipts_from_rows(rows)

Group flat receipt line rows into goods receipts.

po_to_rows(pos)

Flatten purchase orders into CSV/SQLite rows.

receipts_to_rows(receipts)

Flatten goods receipts into CSV/SQLite rows.

write_sqlite(path, pos, receipts)

Write purchase orders and receipts to a SQLite database.

Matching

invoice_agent.match

Two-way and three-way matching of invoices against POs and goods receipts.

The matcher is stateful on purpose. A :class:Ledger remembers every invoice it has seen and how much of each PO line is already billed, so it can catch duplicate submissions and cumulative overbilling across several invoices.

Ledger dataclass

Invoices processed so far, and billed quantity per (PO, line).

record(invoice, decision, po_number, matches, source=None)

Remember an invoice. Quantities count as billed unless it was rejected.

Matcher

Matches invoices against a fixed set of POs and receipts.

infer_po(inv)

Pick the vendor's open PO whose lines best fit the invoice.

vendor_similarity(a, b)

Fuzzy similarity (0-100) of two vendor names after dropping legal suffixes.

description_similarity(a, b)

Similarity (0-1) of two line descriptions, insensitive to case, order and punctuation.

align_lines(lines, po_lines, threshold)

Pair invoice lines with PO lines, one-to-one.

A SKU match scores 1.0. Otherwise the description similarity is used, and pairs below threshold stay unmatched. Lines where both sides carry a different SKU never pair. Pairs are assigned greedily, best score first.

Validation

invoice_agent.validate

Arithmetic checks: line amounts, subtotal, and total.

validate_arithmetic(inv, policy=None)

Check that the numbers on the invoice add up.

  • each line: quantity * unit_price == amount (within line_rounding)
  • sum(line amounts) == subtotal
  • subtotal - discount + tax == total

Differences up to policy.line_rounding per line are reported as info so the reviewer can see them, without blocking approval.

Normalization

invoice_agent.normalize

Parsing helpers for amounts, dates, currencies and identifiers.

parse_amount(text)

Parse 1,234.56, 1.234,56 €, (12.00), USD 99 and similar.

The decimal separator is whichever of . or , appears last and is followed by one to four digits at the end of the string.

detect_currency(text)

Return the most frequent ISO code or symbol found in text.

parse_date(text, *, day_first=False)

Parse common invoice date formats.

Supports 2026-03-14, 14.03.2026, 03/14/2026 (or 14/03/2026 when day_first), 14 Mar 2026 and March 14, 2026.

normalize_doc_number(value)

Canonical form of an invoice or PO number: uppercase alphanumerics only.

inv-001 042 and INV001042 both become INV001042.

normalize_po(value)

Canonical PO number. Drops a leading PO so PO-2026-0101 equals 2026-0101.