Skip to main content
AIDiveForge AIDiveForge
🧾

Receipt OCR Normalizer

Data Extraction & Parsing · by AIDiveForge · Apr 20, 2026 · Beginner

Normalize supplied receipt text into reconciled expense data with uncertainty flags.

Get structured vendor, line items, tax, total and reconciliation status.

Free · 2 KB · SKILL.md, instructions and example files. Provide readable receipt text or a vision/OCR-capable assistant. No OCR engine is included.

A worked example

A small example to try before using your own data.

Input

Vendor: Paper Shop
Date: 2026-10-15
Notebook 8.00
Pens 2.00
Tax 0.80
TOTAL USD 10.80

Expected result

{
  "vendor": "Paper Shop",
  "date": "2026-10-15",
  "currency": "USD",
  "items": [
    {
      "description": "Notebook",
      "amount": "8.00"
    },
    {
      "description": "Pens",
      "amount": "2.00"
    }
  ],
  "tax": "0.80",
  "total": "10.80",
  "reconciled": true,
  "review_flags": []
}

What to check: 8.00 + 2.00 + 0.80 equals 10.80 exactly. An unreadable date or currency must remain unresolved.

Limits: This is extraction and reconciliation, not approval of an expense or tax treatment. OCR and vision accuracy depend on the supplied tool and image.

Worked example included. Model-specific performance and vendor compatibility have not been benchmarked.

Installation options and other formats

Install this skill

An agent skill starts with SKILL.md. The ZIP also includes its examples and any helper scripts. Drop the file into your tool of choice — or pick a different format if you use Cursor, Windsurf, Copilot, or something else.

Download SKILL.md
python3 - <<'ADF_SKILL_INSTALL'
import hashlib, io, os, stat, tempfile, urllib.request, zipfile
from pathlib import Path, PurePosixPath
folder = "receipt-ocr-normalizer"
root = Path(os.environ.get('ADF_SKILL_ROOT', str(Path.home() / '.claude' / 'skills'))).expanduser()
destination = root / folder
if destination.exists() or destination.is_symlink():
    raise SystemExit('An installation already exists. Back it up and merge the ZIP deliberately.')
root.mkdir(parents=True, exist_ok=True)
request = urllib.request.Request("https://aidiveforge.com/skill/receipt-ocr-normalizer.zip", headers={'User-Agent': 'AIDiveForge-Resource-Installer/1.0 (+https://aidiveforge.com/skills/)'})
with urllib.request.urlopen(request, timeout=30) as response:
    data = response.read(8 * 1024 * 1024 + 1)
if len(data) > 8 * 1024 * 1024 or hashlib.sha256(data).hexdigest() != "ab5ca4a6934771abb2672c4f0f0fc4f364d8193a035c62d6b7fa50519827b499":
    raise SystemExit('The download did not match this reviewed bundle. No files were installed.')
with zipfile.ZipFile(io.BytesIO(data)) as archive, tempfile.TemporaryDirectory(prefix='.adf-skill-', dir=root) as temporary:
    members = archive.infolist()
    if len(members) > 100 or sum(m.file_size for m in members) > 16 * 1024 * 1024:
        raise SystemExit('Unexpected archive size')
    for member in members:
        name = PurePosixPath(member.filename)
        if name.is_absolute() or '..' in name.parts or not name.parts or name.parts[0] != folder or '\\' in member.filename or stat.S_ISLNK(member.external_attr >> 16):
            raise SystemExit('Unsafe archive entry; nothing was installed')
        target = Path(temporary).joinpath(*name.parts)
        if member.is_dir():
            target.mkdir(parents=True, exist_ok=True)
        else:
            target.parent.mkdir(parents=True, exist_ok=True)
            with target.open('xb') as stream:
                stream.write(archive.read(member))
    staged = Path(temporary) / folder
    if not (staged / 'SKILL.md').is_file() or destination.exists() or destination.is_symlink():
        raise SystemExit('The skill could not be installed without replacing existing data')
    os.rename(staged, destination)
print('Installed the complete skill folder:', destination)
ADF_SKILL_INSTALL

Claude Code: ~/.claude/skills/receipt-ocr-normalizer/SKILL.md. Codex: ~/.codex/skills/receipt-ocr-normalizer/SKILL.md. Keep the complete ZIP folder when helper scripts are included.

🧠 Why it works

8.00 + 2.00 + 0.80 equals 10.80 exactly. An unreadable date or currency must remain unresolved.

⚙️ How it works

  1. Read vendor, date, currency, line items, discounts, tax and total from the receipt; retain a source locator for uncertain characters.
  1. Keep decimal money as strings or integer minor units, and state currency rather than guessing from an ambiguous symbol.
  1. Reconcile line items minus discounts plus tax against the displayed total using exact decimal arithmetic.
  1. Use null and a review flag for unreadable fields; never make the arithmetic work by inventing a line item.
  1. Return normalized JSON and a list of discrepancies for human review before an expense is posted.

Description

Get structured vendor, line items, tax, total and reconciliation status.

Provide readable receipt text or a vision/OCR-capable assistant. No OCR engine is included.

Limits: This is extraction and reconciliation, not approval of an expense or tax treatment. OCR and vision accuracy depend on the supplied tool and image.

Recommended Use

Tools and workflow packs this skill pairs well with. Forge picks are auto-generated from category + capability signals; Community picks are added by people who've used the pairing.