Skip to content

Repository files navigation

docket — local document AI, invoice & receipt OCR parser with LLMs

Turn scanned invoices, receipts, and contracts into structured, validated JSON using OCR and LLMs, then export them as EU e-invoices (XRechnung, ZUGFeRD / Factur-X, Peppol UBL, Facturae). Use it as a Python library, an HTTP service, or a CLI. Apache-2.0, so commercial use is fine.

demo

Ollama or any OpenAI-compatible API (Mistral, OpenAI, Azure, vLLM) · Pydantic schemas · Every value cited to its source line · Deterministic validation

Quick start

Requirements: Python 3.10+, Tesseract on PATH (brew install tesseract / apt install tesseract-ocr), and an LLM, meaning either Ollama with a text and a vision model pulled, or an OpenAI-compatible API key (see Configuration).

pip install docket-idp
docket invoice.pdf                       # JSON on stdout, exit code 2 if validation fails
docket invoice.pdf --export xrechnung    # e-invoice XML on stdout
{
  "doc_type": "invoice",
  "invoice_number": "FAC-2026-0042",
  "issue_date": "2026-03-15",
  "vendor_name": "Talleres Montjuïc S.A.",
  "subtotal": 1234.56,
  "tax_amount": 259.26,
  "total_amount": 1493.82,
  "currency": "EUR"
}

Use it in your application

Python library

from docket import Invoice, export_document, process

result = process("invoice.pdf", enqueue_review=False)  # your app owns the review flow
if result.is_valid and isinstance(result.document, Invoice):
    xml = export_document(result.document, "xrechnung")
else:
    print(result.review_reasons, result.validation_issues)

result.document is the typed schema (Invoice, Receipt, Contract, …), and result.field_sources gives the page and quote each value was read from.

HTTP service (any language)

docker run -p 8000:8000 -e DOCKET_API_KEY=secret ghcr.io/kazkozdev/docket
curl -H "Authorization: Bearer secret" -F file=@invoice.pdf localhost:8000/process

POST /process is synchronous. POST /jobs returns 202 and a job id to poll at GET /jobs/{id}, and an Idempotency-Key header makes retries safe. /review-queue serves flagged documents. Generate a typed client from docs/openapi.json; interactive docs are at /docs. Without Docker: pip install "docket-idp[api]" && docket-api.

examples/ has runnable scripts, a TypeScript client, a Mistral-backed docker-compose.yml and plugin packages.

Document types

Built in: invoice, receipt, contract, purchase order, bank statement, acceptance act, waybill, boarding pass. To add your own, write a Pydantic model and register it:

from datetime import date
from docket import CitedDocument, register_document_type

class DeliveryNote(CitedDocument):       # CitedDocument adds page/quote citations
    note_number: str
    supplier_name: str
    delivery_date: date

register_document_type(
    "delivery_note", DeliveryNote,
    description="Delivery note / Lieferschein listing goods handed over",  # read by the LLM classifier
    keywords=["delivery note", "lieferschein"],                             # free rules tier
)

Registered types are classified, extracted, citation-checked and exported like the built-in ones. add_validator("invoice", fn) adds your own rules to any type, and the docket.document_types entry point lets a separate package ship types. See examples/custom_document_type.py.

Export formats

Format Name
XRechnung (CII) xrechnung
ZUGFeRD 2.2 / Factur-X, EN 16931 zugferd
UBL 2.1 / Peppol BIS Billing 3.0 ubl
Facturae 3.2.2 (Spain) facturae
SAP IDoc / journal CSV sap-idoc, sap-csv
Xero, QuickBooks xero-csv, xero-json, quickbooks-iif, quickbooks-json

Add your own with register_exporter("my-erp", func, accepts=(Invoice,)) or the docket.exporters entry point. docket --list-formats shows everything available. Validate generated XML with the recipient's official validator (e.g. KoSIT for XRechnung) before going live.

How it works

document → text layer / OCR / VLM → classify → extract + cite → validate → JSON or review
  • Text comes from the cheapest source that works: the PDF text layer, then Tesseract, then a vision model, which is used only when OCR confidence is low or a cheap text model judges the scan unusable.
  • Classification tries keyword rules, then TF-IDF, then an LLM. Each tier runs only when the one before it was unsure. Rules and TF-IDF cover English, Spanish, German, French, Italian, Dutch and Portuguese; any other language falls through to the LLM.
  • Extraction fills a Pydantic schema under a JSON Schema contract and cites the verbatim line for every value. Output that fails the schema goes back to the model with the error attached.
  • Validation never calls a model. It checks arithmetic, dates, IBAN mod-97, VAT check digits (all 27 EU states, UK, CH, NO), national tax IDs, and that every cited line exists and contains the claimed value. Contracts also get counterparty, grounding and risk checks (unlimited liability, auto-renewal, notice periods).
  • Review: low confidence, failed extraction or a validation error sends the document to a review queue that keeps the original and an audit history. Nothing is silently reconciled. An invoice whose Amount Due: 500.00 disagrees with its own 270.60 subtotal and tax is flagged, not fixed.

Also included: cross-document matching (invoice ↔ PO, three-way PO/waybill/invoice, invoice ↔ contract, receipt ↔ bank transactions) and a heuristic stamp, signature and alteration check (docket file.pdf --forensics). Details are in ARCHITECTURE.md.

Configuration

Set in the environment or .env. .env.example and config.py have the full list.

Option Default What it does
DOCKET_LLM_PROVIDER ollama ollama, or openai for any OpenAI-compatible API
DOCKET_LLM_BASE_URL / DOCKET_LLM_API_KEY OpenAI / unset Endpoint and key for openai, e.g. https://api.mistral.ai/v1 (EU-hosted)
DOCKET_TEXT_MODEL / DOCKET_VISION_MODEL deepseek-v4.1-flash:cloud Models for extraction and for reading scans
OLLAMA_HOST http://localhost:11434 Where Ollama is listening
DOCKET_OCR_LANG eng Tesseract languages, e.g. eng+deu+fra+spa+ita
DOCKET_MIN_CONFIDENCE 0.55 Classification confidence below which a document goes to review
DOCKET_REVIEW_QUEUE_ENABLED true Write flagged documents to the file-based review queue
DOCKET_API_KEY unset Bearer token the HTTP API requires when set

Limitations

  • The TF-IDF tier is trained on a small embedded corpus (about 20 phrases per type), so it only answers when confident and leaves the rest to the LLM.
  • --forensics is a pixel heuristic, not a trained vision model. It finds colored stamps and seals and handwriting in colored or black ink, but never reports black stamps, which it can't tell apart from logos or table graphics. Its confidence scores come from geometry and aren't calibrated probabilities.
  • The vision model has been observed changing digits so that a page reconciles (a printed 450.00 read as 480.00 three times out of three). There is no fix for that in this repo.
  • Line items carry no source citations, so the citation check doesn't cover them.
  • The review queue is a single file: durable on one node, not across hosts.
  • Windows is untested. A document takes 3.6–9.5 s, longer when a page needs the vision model.
Install options, source setup, development
pip install docket-idp            # library + CLI
pip install "docket-idp[api]"     # + HTTP service (docket-api)
pip install "docket-idp[all]"     # + Langfuse tracing

From source:

git clone https://github.com/KazKozDev/docket.git
cd docket && python3 -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]" && cp .env.example .env
streamlit run examples/streamlit_demo.py   # demo UI: document preview + per-stage results

On macOS, double-clicking start.command sets everything up and opens the UI.

pytest                                # no test needs a running LLM
python eval/run_eval.py               # accuracy, P/R/F1, latency on the golden set
python eval/benchmark_methods.py      # rules vs TF-IDF vs LLM comparison

About

Local document AI & OCR pipeline for invoices, receipts, and contracts with Ollama, Pydantic schemas, math/checksum validation, and human-in-the-loop review.

Topics

Resources

Contributing

Stars

1 star

Watchers

0 watching

Forks

Releases

Contributors

Languages