Jackson Kojo Osei
statusoperationalaccra--:--:--regiongh · uk
building

DocuRead

Turn receipts, invoices and certificates into clean JSON, with a confidence score and a human review queue.

What it does

Send a photo or PDF, get structured data back. Anything the model is unsure about goes to a review queue instead of straight into your database. It grew out of the licence check I built for a US healthcare platform.

The problem

AI models read documents well most of the time and confidently wrong some of the time. Without a way to measure that and catch the wrong answers, you can't trust them with real decisions.

What you get

How DocuRead works: a document goes to Claude, rule checks test the answer, then fields are accepted or sent to a review queue
Every answer is checked before it's trusted.
A Ghanaian supermarket receipt next to the JSON DocuRead returns, every field accepted
A receipt in, clean JSON out. Money comes back in pesewas so totals compare exactly.
A receipt whose subtotal plus tax does not equal the total, sent to review with a totals_mismatch issue
Every number was read correctly, but 80.00 + 12.00 isn't 99.00, so a person checks it.
An expired certificate read correctly and flagged as expired
Certificates past their expiry are flagged without needing review.

How to use it

DocuRead is a small HTTP service. Run it next to your app, send it each uploaded document, and only store the fields it accepted.

1. Install and run

git clone https://github.com/Jacksonoseikojo1122/docuread.git
cd docuread && npm install
cp .env.example .env   # set ANTHROPIC_API_KEY and DOCUREAD_API_KEYS
npm run dev            # http://localhost:8787

2. Send a document

curl -X POST http://localhost:8787/v1/extract \
  -H "Authorization: Bearer $DOCUREAD_KEY" \
  -F type=receipt -F file=@receipt.png

3. Use it from your app (TypeScript)

const form = new FormData();
form.set("type", "invoice");
form.set("file", file);

const res = await fetch(`${DOCUREAD_URL}/v1/extract`, {
  method: "POST",
  headers: { Authorization: `Bearer ${DOCUREAD_KEY}` },
  body: form,
});
const result = await res.json();

// The one rule: only trust fields DocuRead accepted.
const trusted = Object.fromEntries(
  Object.entries(result.fields ?? {})
    .filter(([, f]) => f.status === "accepted")
    .map(([k, f]) => [k, f.normalized]),
);

4. Or from Python

res = requests.post(
    f"{DOCUREAD_URL}/v1/extract",
    headers={"Authorization": f"Bearer {DOCUREAD_KEY}"},
    data={"type": "certificate"},
    files={"file": open("cert.pdf", "rb")},
)
result = res.json()
if result["decision"] == "needs_review":
    notify_reviewer(result["id"])

Documentation

Features

  • Document to JSON against a schema you define
  • Confidence per field, with low-confidence fields sent for review
  • Evaluation suite: a labelled test set and an accuracy score on every change
  • Rules that override the model where you know better
  • API keys with a per-key rate limit, since every read is a paid model call
  • Review endpoints to list, confirm and correct doubtful documents

What building it covers

  • LLM evaluation
  • Test sets and regression tracking
  • Structured output
  • Human-in-the-loop design

Stack

  • TypeScript
  • Node.js
  • Hono
  • Claude API
  • Zod
  • Vitest

Questions

What documents can DocuRead read?

Receipts, invoices and certificates, as PNG, JPEG, WebP, GIF or PDF. Each type has a fixed set of fields, for example merchant, date, currency, subtotal, tax and total for receipts.

How does it stop the AI from being confidently wrong?

The model's answer is treated as untrusted. Code checks that dates are real, money parses, subtotal plus tax equals the total, and dates are in a sensible order. Any field that fails a check or falls below the confidence threshold goes to a human review queue instead of being accepted.

What format is the money in?

Integer minor units, such as pesewas or cents. 259.13 comes back as 25913, so totals compare exactly with no rounding errors. Currencies are ISO 4217 codes like GHS or USD.

How do I know it's accurate?

Run npm run eval. It scores 12 sample documents and reports silent errors: fields that were wrong and still accepted. A run fails if that number goes up.

Does it store my documents?

No. DocuRead doesn't save uploaded files. For documents that need review it keeps the extracted fields so a person can check them. The document is sent to the Anthropic API to be read.

Is it free?

The code is open source under the MIT licence. You pay Anthropic for the model calls your instance makes.

All projects