InvoiceLens
Read the invoice. Check the maths.
InvoiceLens is a tool for a bookkeeping firm that receives supplier invoices as PDFs and photos. Each file goes through a queue, an LLM extracts vendor, dates, line items, tax and totals as schema-validated JSON, the totals are cross-checked in code, and anything doubtful lands in a split-pane review screen before it is exported. I built it to show how I integrate an LLM into a workflow where wrong numbers are expensive.
- React.js
- Laravel
- PHP
- MySQL
- Queues
- LLM API
- Structured outputs
- REST API
Overview
- Business problem
- A bookkeeping firm receives supplier invoices in every shape: clean PDFs, phone photos, re-sent emails. Staff retype vendor names, dates and totals into the accounting system, and the errors that matter are the quiet ones: a transposed digit in a total, the same invoice paid twice because it arrived by email and again by post.
- Product objective
- Remove the retyping without removing the accountability: extraction is automatic, but every number is checked by code, every doubtful field is shown to a person, and nothing reaches the accounting export unreviewed.
- Target users
- Bookkeepers who enter supplier bills, reviewers who sign them off, and the firm admin who owns validation rules and exports.
- Solution
- A queued extraction pipeline that asks the LLM for JSON matching a strict schema, validates it, recomputes the arithmetic itself, scores confidence per field, and routes anything below threshold or failing a rule to a review screen that shows the source document next to the data.
Project classification
- Project type
- AI document extraction tool
- Industry
- Accounting / bookkeeping
- Frontend
- React.js review application served by the Laravel backend
- Backend
- Laravel REST API with queued extraction jobs and a validation service
- Database
- MySQL
- Architecture
- Upload API, job queue, extraction worker, validation layer and review UI
User roles
- Bookkeeper
- Reviewer
- Admin
My role
- System architecture: upload API, queue, extraction worker, validation layer and review application.
- Backend design in Laravel: queued jobs, retry and idempotency rules, the validation service and export builder.
- LLM integration design: extraction prompt, JSON schema for structured output, response validation and fallback behaviour.
- Database design: invoices, extracted fields with confidence, validation results, review decisions and export batches in MySQL.
- Frontend: the review split-pane, queue, results table, rules and export screens in React.
- This is a self-initiated concept; there is no client, live deployment or user base.
Key features
Review split-pane. Interface designed and built by Vikrant Review with the document in view
The reviewer sees the invoice on the left with each extracted field highlighted where it was read, and the structured fields on the right with a confidence value and any validation flag. When the printed total does not equal subtotal plus tax, the field is flagged with the expected value, and the reviewer corrects it or rejects the invoice in one step.
Upload queue. Interface designed and built by Vikrant An upload queue you can trust
Files are queued as they arrive, not processed in the request. The queue shows each file's stage, attempt count and status, so a slow scan, a retried job or a held duplicate is visible instead of silently missing. Retrying a file reuses the same job key and never creates a second invoice.
Extraction results. Interface designed and built by Vikrant Extraction results at a glance
Every processed invoice appears in one table with vendor, number, date, subtotal, tax, total, lowest field confidence and status. Sorting by confidence or filtering to flagged rows lets a reviewer clear the doubtful ones first and approve the clean ones quickly.
Validation rules. Interface designed and built by Vikrant Validation rules the firm controls
Schema validity, line totals, subtotal plus tax, duplicate detection, date sanity, currency and the confidence threshold are explicit rules with a severity and an action. An admin tunes them without touching the extraction prompt, and the arithmetic never depends on the model being right.
Export history. Interface designed and built by Vikrant Export batches with history
Approved invoices are exported as CSV batches in a bills layout the accounting system can import. Each batch records who created it, which invoices it holds and where it went, and an invoice already exported is skipped rather than exported twice.
Also in the product
- PDF and image upload with a processing queue per file
- Structured extraction of vendor, dates, line items, tax and totals
- Schema validation of every model response before it is stored
- Arithmetic cross-checks: line totals, subtotal, tax and grand total
- Per-field confidence with a configurable review threshold
- Split-pane review: the document beside the extracted fields
- Duplicate invoice detection before anything is exported
- Idempotent retries and CSV export batches with history
User flow
- Bookkeeper uploads PDFs or photos, or forwards them to an intake address
- Each file is stored, hashed and queued as a job
- A worker sends the document to the LLM with a strict JSON schema
- The response is schema-validated; invalid output is retried, then escalated
- The validation layer recomputes line totals, subtotal, tax and total
- Duplicate detection compares vendor, invoice number and total
- Fields below the confidence threshold or failing a rule go to review
- Reviewer checks the document against the fields, corrects and approves
- Admin exports approved invoices as a CSV batch
- Export history records the batch so nothing is exported twice
Technical architecture
- React review application: queue, split-pane review, results, rules, exports
- Laravel REST API with role-based access for bookkeeper, reviewer and admin
- File storage and an upload endpoint that hashes each file and enqueues a job
- Queue workers running extraction jobs with retries and backoff
- LLM API called with a JSON schema for structured output
- Validation service: schema check, arithmetic cross-checks, duplicate detection, confidence thresholds
- MySQL for invoices, fields, validation results, review decisions and export batches
- Export builder producing CSV batches with a history table
Database and backend
Extraction pipeline
- Upload: the file is stored and hashed; the API creates an invoice row in a queued state and returns immediately.
- Extraction job: a worker sends the document and a JSON schema to the LLM and requests structured output only.
- Schema validation: the response is parsed and validated against the same schema server-side; a malformed response is retried with a limited number of attempts, then marked failed for a person to handle.
- Confidence: each field carries a model-reported confidence, and a configurable threshold decides what goes to review.
- Review: fields that are low confidence or failing a rule are shown in the split-pane; edits are stored next to the original extracted value.
Validation and arithmetic
- Line totals: quantity times unit price is recomputed for each line and compared with the extracted amount.
- Subtotal: the sum of line totals is compared with the extracted subtotal, to the cent.
- Total: subtotal plus tax is compared with the printed total; a mismatch is flagged with the expected value, never silently corrected.
- Money is handled as integer cents on the server so rounding is deterministic.
- Dates and currency are sanity-checked: no future invoice dates, due date not before invoice date, currency matches the client.
Duplicates, retries and idempotency
- Duplicate detection looks for the same vendor, invoice number and total, plus a file hash for byte-identical re-uploads.
- A probable duplicate is held and linked to the original instead of entering the review queue as new work.
- Jobs use a key made of file hash and schema version, so a retry or a double submit reuses the same invoice record.
- Timeouts and provider errors retry with backoff; after the last attempt the invoice moves to a failed state with the reason visible.
Data model and export
- Client, Invoice, InvoiceFile, ExtractedField (value, confidence, source box), LineItem, ValidationResult, ReviewDecision.
- ExportBatch and ExportItem tie each approved invoice to at most one batch.
- Role-based access: bookkeepers upload, reviewers approve, admins edit rules and export.
- Every approval and rule change is written to an audit log with the acting user.
Challenges and solutions
Getting reliable JSON out of a language model
The model is asked for structured output against a strict schema, and the server validates the response against the same schema before storing anything. Malformed output is retried a limited number of times and then surfaced to a person, never patched by guessing.
Catching numbers that look right but are not
The arithmetic is recomputed in code from the extracted lines: line totals, subtotal, tax and total. A mismatch is flagged with the expected value, because the model reading a wrong printed total correctly is still a wrong invoice.
Duplicate invoices that arrive twice
A vendor, invoice number and total match, plus a file hash for identical uploads, holds the second copy and links it to the first. Reviewers see why it was held and can release it if it is genuinely a different invoice.
Deciding what a person must look at
Per-field confidence and rule results drive routing: below the threshold or failing a rule means review, otherwise the invoice is ready for approval. The threshold is a setting, so the firm can trade reviewer time against caution.
Retries that must not create duplicates or double exports
Jobs are keyed by file hash and schema version, so retries update the same record. Exports mark each invoice with its batch, and an already exported invoice is skipped on the next run.
Scans and photos with unreadable fields
Low-quality input produces low confidence rather than a confident guess. Those fields are highlighted for review, and the reviewer can reject the file and ask for a better copy.
Results
- A case study of an LLM integration where the model's output is treated as untrusted input: schema-checked, recomputed and reviewed.
- A pipeline design that keeps a person in the loop only where it matters, instead of reviewing everything or nothing.
- A queue and idempotency design that makes retries safe for both extraction and export.
- Shows how to build AI features for accounting-style workflows where auditability matters more than speed.
Qualitative outcomes only; no usage or revenue figures.
Visual identity
- Primary #1FA3A3
- Secondary #14213D
- Accent #E9B44C
- Background #F5F7FA
Manrope throughout, with a monospace face for extracted values. Rounded square with a lens ring over a document corner. Navy sidebar, light workspace, split-pane review with highlighted fields and flag pills.
Related projects
- Portfolio ProjectHealthcare
CareBridge
Clinic portal with appointments, records and role-based access for five user types.
- React.js
- Laravel
- PHP
- MySQL
- REST API
- Sanctum
- RBAC
- Portfolio ProjectProfessional services (multi-vertical)
Slotwise
Scheduling SaaS for any business that sells time: calendars, availability rules and automated reminders.
- React.js
- Laravel
- PHP
- MySQL
- REST API