InvoicePilot
AI invoice microservice + Web3 cryptographic seal engine
[ 01 ] — Context
Problem
- Bookkeeping firms and small businesses drown in manual data entry — hours spent transcribing invoices, matching purchase orders, and reconciling duplicates.
- Duplicate payments, PO mismatches, and fraud go undetected because there is no structured audit trail, and files are scattered across inboxes and drives.
- Existing tools either cannot process varied invoice formats at enterprise scale or offer no tamper-evident record of what was processed, when, and by whom.
[ 02 ] — Response
Solution
InvoicePilot is a three-sub-system platform. The AI pipeline turns any invoice file — PDF, webp, jpeg, png — into structured, validated line data. An async engine absorbs spikes without blocking request threads. A Web3 seal kernel anchors every processed record on-chain.
The AI/ML service runs an 11-stage pipeline: file validation → Mindee OCR extraction (30+ fields, per-field confidence) → transformation → normalization (10 functions for currencies, vendor names, dates, emails, phones, URLs) → exact-match duplicate detection → a 12-rule business-logic engine → a 9-rule fraud engine → classification → validation → persistence.
Processing is decoupled through Redis Streams consumer groups. A Fastify API gateway fronts the system with JWT auth (Supabase JWKS), RBAC, sliding-window rate limiting, Redis query caching, and AES-256-GCM encryption for vendor bank details at rest.
[ 03 ] — Structure
Architecture
┌───────────────────────────────────────────────┐
│ Fastify API Gateway │
│ JWT (Supabase JWKS) · RBAC · rate-limit │
└──────────────┬────────────────────────────────┘
│ HTTPS / multipart
▼
┌───────────────────────────────────────────────┐
│ invoice-ai-service (FastAPI) │
│ 11-stage pipeline · 30+ fields · validation │
└──────────────┬────────────────────────────────┘
│ XADD (Redis Streams)
▼
┌───────────────────────────────────────────────┐
│ Async workers · asyncio.Semaphore(5) │
│ retry (3×, backoff) · DLQ (7-day TTL) │
└──────────────┬────────────────────────────────┘
│ RLS + service role
▼
┌───────────────────────────────────────────────┐
│ PostgreSQL (Supabase, Row-Level Security) │
└──────────────┬────────────────────────────────┘
│ hash + type + ts + signer
▼
┌───────────────────────────────────────────────┐
│ BullMQ seal worker → SealRegistry.sol │
│ Viem + Turnkey KMS · append-only · no PII │
└───────────────────────────────────────────────┘[ 04 ] — Trade-offs
Key engineering decisions
- 01
Hash-only on-chain (privacy by design)
The contract stores only {recordHash, recordType, sealedAt, sealedBy} — never invoice amounts or PII. Records are anchored with sha256(domainTag + canonicalJSON(payload)) using NFC normalization, minor-unit money, and RFC3339 UTC. Clients can verify a document without any sensitive data leaving their control.
- 02
Provider-agnostic OCR boundary
Exactly one file imports the Mindee SDK. Every other module consumes a neutral `DocumentFields` interface, so OCR vendors can be swapped or A/B-tested without touching the pipeline.
- 03
Signer abstraction
Seal signing runs in two modes — local keypair for dev, Turnkey enclave KMS for production — behind a single `SealSigner` interface. The audit logs record which signer produced each seal.
- 04
RLS FORCE + service-role split
Tenant isolation is enforced at the database, not the ORM. The API connects with a service role and row-level security FORCE policies keep multi-tenant reads scoped no matter how the query is written.
- 05
Soft-ref seals, crash-safe reclaim
Seals are soft references (no hard FK) so the ledger stays append-only, and the worker uses XCLAIM idle-reclaim so a crashed consumer's stream entries resume instead of silently dropping.
[ 05 ] — Under the hood
Engineering highlights
from hashlib import sha256
from utils import canonical_json # stable key order + NFC
def seal_hash(payload: dict) -> bytes:
domain = b"invoice-pilot.v1" # domain tag, versioned
data = canonical_json(payload) # deterministic serialization
return sha256(domain + data.encode("utf-8")).digest()# Redis Streams consumer group
stream, group = "invoices:processing", "workers"
for message in stream.pending_and_new(group):
async with asyncio.Semaphore(5): # max 5 concurrent
await asyncio.to_thread(run_pipeline, message) # release GIL
await xack(stream, group, message) # commit offset[ 06 ] — Outcomes
Results
[ 07 ] — Visuals
Screenshots
[ 08 ] — Where it's going
Status & next steps
- QuickBooks / Xero OAuth two-way sync — currently the named biggest integration risk.
- SOC 2 readiness suite (5 policy docs, trust center, RLS migrations) — continue toward audit.
- Public demo environment with seeded sample invoices so the pipeline is explorable without credentials.