◉CLAUDE LABJP
●2.1.301 — Adds the vim % motion, LaTeX math rendering in the VS Code panel, and a retry-watchdog limit for usage-limit waits (10/15)●2.1.299 — Conversations over 200K tokens left idle about 54 minutes get compacted; MCP allow rules now match only the server they name●SONNET 4.5 — Retires on the Claude API on 11/30, 45 days left. Migrate to Sonnet 5.5●WINDOWS — Reports say hooks break silently only on Japanese-locale Windows; check the character encoding first●FAST MODE — Opus 5.5 fast mode is out of research preview and no longer needs the beta header (10/15)●NEW — I Put a Ten-Line Promise File at the To…●2.1.301 — Adds the vim % motion, LaTeX math rendering in the VS Code panel, and a retry-watchdog limit for usage-limit waits (10/15)●2.1.299 — Conversations over 200K tokens left idle about 54 minutes get compacted; MCP allow rules now match only the server they name●SONNET 4.5 — Retires on the Claude API on 11/30, 45 days left. Migrate to Sonnet 5.5●WINDOWS — Reports say hooks break silently only on Japanese-locale Windows; check the character encoding first●FAST MODE — Opus 5.5 fast mode is out of research preview and no longer needs the beta header (10/15)●NEW — I Put a Ten-Line Promise File at the To…
Articles/API & SDK
⬡ API & SDK/2026-06-17Advanced

When Claude API Extracts the Wrong Value With Full Confidence — Designing the Verification Layer

When you extract invoices or contracts with Claude API, the scariest failure isn't an exception — it's plausible-but-wrong JSON. Here is how I build a verification layer that catches silent extraction errors with schema checks, arithmetic reconciliation, and dual-extraction agreement, in TypeScript.

Claude API128document processingstructured extractionverification5TypeScript24production111

✦ Premium Article

A few weeks into running an automated invoice intake, someone asked me why the accounting numbers didn't add up even though the error logs were spotless. The cause was easy to find. On one low-resolution scan, Claude had misread a digit in the total amount, reported it as confidence: 0.96, and sailed through without throwing anything.

As an indie developer running several services in parallel, I assumed at first that exception handling alone could keep this class of error out. The most dangerous thing in structured extraction isn't the API going down or the JSON breaking. It is a plausible but wrong value flowing downstream without raising a single exception. JSON.parse succeeds, Zod passes, the logs stay green, and only the ledger quietly drifts. This article is a field note on how to build the verification layer that catches those silent errors before they reach production. It isn't about assembling the extraction pipeline itself — it's about the judgment that comes after extraction: whether the result can be trusted at all.

Don't treat confidence as a signal

The first assumption to drop is that the model's confidence is a usable quality metric. An LLM's self-reported confidence correlates almost not at all with whether the output is correct. The more badly an invoice is misread, the more decisively it can return a high number. Confidence is the model's self-assessment of its own output, not the result of checking against external truth.

So I never use confidence as a gate — only for prioritization. If a value comes back low, it goes near the front of the human review queue; that's all. The accept/reject decision is made by verification that lives outside the model. There are three sources of verification: schema (is it structurally valid?), arithmetic reconciliation (do the numbers agree internally?), and dual-extraction agreement (does an independent pass produce the same result?). Let's go through them.

Stage one: schema guards shape, nothing more

Zod validation is the first wall, but you have to size what it can defend accurately. A schema can only reject structural anomalies — wrong type, missing required field, a value outside an enum. It can guarantee that total is a number, but it can say nothing about whether that number is right.

Even so, weaving extraction-specific constraints into the schema drops a fair share of errors at the first stage. Force dates into ISO format, make amounts non-negative, restrict currency to the ISO 4217 enum. By dropping every "impossible shape" here, the arithmetic stage downstream can focus purely on substantive errors.

// src/schema.ts
import { z } from "zod";
 
const isoDate = z
  .string()
  .regex(/^\d{4}-\d{2}-\d{2}$/, "Extract as YYYY-MM-DD")
  .refine((s) => !Number.isNaN(Date.parse(s)), "Must be a real date");
 
const money = z.number().finite().nonnegative();
 
export const InvoiceSchema = z.object({
  invoiceNumber: z.string().min(1).optional(),
  issueDate: isoDate.optional(),
  dueDate: isoDate.optional(),
  vendor: z.object({ name: z.string().min(1), taxId: z.string().optional() }),
  lineItems: z
    .array(
      z.object({
        description: z.string().min(1),
        quantity: z.number().positive().optional(),
        unitPrice: money.optional(),
        amount: money,
      })
    )
    .min(1, "An invoice with zero line items is treated as a failed extraction"),
  subtotal: money.optional(),
  tax: money.optional(),
  total: money,
  currency: z.string().length(3), // ISO 4217
});
 
export type Invoice = z.infer<typeof InvoiceSchema>;

The quiet win is .min(1) on lineItems. An invoice with no line items essentially never exists, so if zero comes back you can declare the extraction failed. When you design the schema as a "detector of business-impossible shapes" rather than a "data-correctness check," the first net tightens considerably.

✦

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
✦A verification layer that ignores the model's self-reported confidence and instead catches bad extractions mechanically through schema, arithmetic reconciliation, and dual-extraction agreement
✦Checksum validation that uses arithmetic constraints like subtotal + tax = total to reject plausible-looking errors, plus how to draw the line for routing to human review
✦A staged design that extracts with Sonnet first and only re-arbitrates disagreements with Opus, balanced against prompt caching and batch processing for realistic cost allocation
Secure payment via Stripe · Cancel anytime
✦

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Claude Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • ✦Copy-paste ready implementation code
  • ✦New advanced guides published daily
  • ✦$5/mo or $10 for lifetime access
View Membership →

Related Articles

⬡ API & SDK2026-05-24
Archiving Claude API Responses to Cloudflare R2: An Implementation Memo for Audit, Replay, and Incident Analysis
An implementation memo on persisting Claude API requests and responses into Cloudflare R2 so you can audit, replay, and triage production incidents — covering Workers waitUntil patterns, PII masking, and a D1 metadata index for fast lookups.
⬡ API & SDK2026-05-04
Claude API on Bun in Production: Migration Decisions and Implementation Patterns That Actually Survive Real Traffic
A practical guide to running Claude API services on Bun in production. Covers migration triggers from Node.js, built-in SQLite/WebSocket usage, streaming optimization, and the pitfalls that only surface after deployment — with working code and measured numbers.
⬡ API & SDK2026-04-25
Claude API × Convex: Reactive AI Apps — Data Flow, Streaming, and Agent Patterns
How to combine Convex's reactive database with the Claude API to build chat and agent applications that hold up in production. Covers schema design, the Action/Mutation/Query boundary, streaming, tool-call state, and the cold-start pitfalls nobody warns you about.