Request a callbackBook a call
← All posts

Build an AI Document Processing Pipeline: OCR vs VLM Routing, Review Queues and Cost Per Page (2026)

TL;DR
  • Human review is 80% of the cost of a document pipeline, not the models. Extraction on a three-page invoice costs about 1.3 cents; sending 22% of documents to a reviewer at 45 seconds each costs 5.5. Every engineering hour should go at the review rate, not the token count.
  • Route before you extract. A born-digital PDF has a text layer and costs nothing to read; a scanned page needs OCR at about $1.50 per 1,000 pages or a vision model at about $3.60. Sending every page down the most capable path is the single most common way teams pay 40x more than they need to.
  • Validation is arithmetic, not a model. Line items must sum to the subtotal, tax must reconcile, dates must be ordered, and the vendor must exist in your master data. These checks are free, deterministic, and catch more real extraction errors than any confidence score a model reports about itself.
The routing decision that sets your entire bill
How should this page be turned into structured data?
Born-digital PDF with a reliable text layer (roughly 55‑70% of AP volume)
Extract text directly

pdftotext or an equivalent library. $0.00 per page, milliseconds, no accuracy loss. Then run one LLM extraction over the whole document text rather than per page.

Clean scan, simple layout, no tables that matter
Traditional OCR

About $1.50 per 1,000 pages for basic OCR across AWS, Google and Azure, which have converged on the same headline rate. Cheapest path for high volume, and the least capable on layout.

Complex tables, multi-column, handwriting, stamps, poor scan quality
Vision model extraction

About $3.60 per 1,000 pages on a Flash-class model or $9.60 on a Sonnet-class one, modelled below. Reads layout and content in one pass, which is what a line-item table actually needs.

Structured forms where a vendor has a prebuilt model
Prebuilt document model

Azure prebuilt models are documented around $10 per 1,000 pages, Google Document AI in the $10-$30 band, and AWS Textract Forms plus Tables around $65. Worth it only when the prebuilt output beats your schema.

Anything the classifier could not confidently type
Human triage, then re-route

Do not guess a document type. A contract processed as an invoice produces confidently wrong structured data that passes every schema check you have.

Four of these five paths differ in cost by more than an order of magnitude for the same page. The first one is free and applies to the majority of accounts-payable volume, which is why a five-line check for an existing text layer is the highest-return code in the whole pipeline.

What is an AI document processing pipeline, architecturally?

An intake stage, a classifier, a router that picks between free text extraction, OCR and a vision model, an extraction step bound to a typed schema, a deterministic validation layer, a confidence gate that feeds a human review queue, and an export. Seven stages, of which only two involve a model and only one is expensive.

Internalise where the money goes before designing any of it. On a three-page invoice, the modelled model cost is about 1.3 cents. The modelled human review cost, at a 22% review rate and 45 seconds per review, is 5.5 cents. Human time is 80% of the bill. That should reorganise your priorities: an afternoon on deterministic validation rules that safely reduce the review rate beats a month optimising extraction tokens.

This is a reference design, how I would build it and what the arithmetic says it costs, grounded in architecting AccioMatrix, an event-driven platform orchestrating multiple LLMs, agents and MCP tooling for 20+ enterprise clients. I have not shipped a document processing pipeline in production. Every figure below is modelled with its inputs printed, and you should substitute your own document mix before quoting any of it.

The workload shape differs from a support or research agent in a way that changes the architecture. Document processing is bursty batch work: an accounts-payable inbox delivers nothing for six hours, then 400 invoices at 09:00 on the first of the month. That is a queue workload with backpressure, per-document idempotency and a dead-letter path, not a request/response API. The general argument for that backbone is in the AI product architecture reference design. The specific consequence: a document must be processable exactly once no matter how many times the same PDF arrives, which means a content hash is part of your primary key.

The last framing point: this is barely an agent. There is no loop, no tool selection and no autonomy in the core path. It is a pipeline with two model calls in it. Teams that reach for an agent framework here inherit the debugging surface of an agent runtime for a workload that is a directed acyclic graph, and they pay for it in every incident. Use a queue and typed stages.

StageWhat it doesImplementationCost per pagePrimary failure modeSkip in v1?
Intake and dedupeEmail, SFTP, API and scanner drops into one document record keyed by content hashQueue plus object store; originals immutable and never overwritten~$0.0002The same invoice arrives twice on two channels and gets paid twiceNo — the content hash is the idempotency story
Classification and splittingDocument type, and where one multi-document PDF should be splitFirst page only, Flash-class vision model, ~2k in / 60 out$0.0017 per documentA 40-page scan containing eight invoices is processed as one documentSplitting: no. Fine-grained typing: yes
RoutingText layer, OCR or VLM, decided per page from measurable propertiesDeterministic: character count, glyph coverage, table detection$0.0000Everything routed to the most capable path; a 40x cost multiple for no accuracy gainNo — this is the cost lever
Text acquisitionGet characters off the page by the cheapest sufficient meanspdftotext free; OCR ~$1.50/1k pages; VLM ~$3.60/1k on Flash$0.0000-$0.0036OCR mangles a table and the line items silently shift a columnNo
ExtractionStructured fields bound to a typed schema, with a page and bbox reference per fieldOne LLM call over the whole document, not per page$0.0033 (3-page doc)Confidently returns a plausible total that appears nowhere on the pageNo
ValidationArithmetic, referential and temporal checks. No model involvedPure functions: sums, tax reconciliation, date ordering, vendor master lookup$0.0000Not built, so every error has to be caught by a human insteadAbsolutely not
Confidence gate and review queueDecides which documents a human sees, and shows them the right thingField-level flags plus a review UI that shows the source crop next to the value$0.0183 (22% at 45s)Review UI without a source crop; reviewers become slow and inaccurateNo — it is 80% of the cost
ExportWrite into the ERP or AP system, idempotently, with a receiptMCP tool or direct API, keyed by document hash~$0.0000A retried export creates a duplicate payableNo
Journal and auditEvery page, route, cost, model version and human edit, queryablePostgres, one row per page and one per field correction~$0.0003Cannot answer why field X was wrong on document Y three months agoNo

What does the full architecture look like?

Twelve components across four columns: intake and normalisation, classification and routing, three parallel text-acquisition paths, and validation, review and export. The router sits in the middle because it is the only component that touches every page and the only one that can change your bill by a factor of forty.

Intake is more interesting than it sounds. Documents arrive by email attachment, SFTP drop, API upload and scanner, and the same document routinely arrives twice: forwarded by a colleague, re-sent by a supplier, scanned again because the first scan was crooked. The defence is a content hash as part of the document identity, computed on the raw bytes before any processing. Two identical PDFs collapse into one record with two intake events. Two visually identical but byte-different scans do not, which is why a second, weaker duplicate check on extracted invoice number plus vendor plus amount runs after extraction as well.

The classifier runs on the first page only and does two jobs: assign a document type, and detect boundaries in a multi-document scan. The second job matters more than teams expect. A supplier who scans thirty invoices into one PDF and emails it is not an edge case, it is Tuesday, and a pipeline that treats that file as one document produces one confidently wrong record instead of thirty correct ones. Page-boundary detection is a cheap vision call and it belongs before everything else.

The three acquisition paths are the architecture's whole point. Native text extraction is free and instantaneous and covers the majority of accounts-payable volume, because most invoices are software-generated and emailed as born-digital PDFs. OCR handles clean scans at around $1.50 per 1,000 pages. Vision models handle the hard remainder (complex tables, multi-column layouts, handwriting, stamps, poor scans) at about $3.60 per 1,000 pages on a Flash-class model. Build all three behind one interface that returns text plus positional metadata, and the router becomes a small deterministic function rather than an architectural commitment.

Everything after extraction is deliberately unglamorous. Validation is arithmetic. The confidence gate is a set of field-level rules. The review queue is a UI with a source crop next to each flagged value. The export is an idempotent write keyed on document hash. The journal is a Postgres table with one row per page and one per human correction. That last table is the most valuable asset the system produces, a labelled dataset of exactly the fields your extraction gets wrong.

The system
Document processing pipeline: full reference architectureoriginalstype + page rangeshas text layerclean scantables / handwritingroute + costall checks passany flaghuman confirms
Intakeemail · SFTP · API · scanner
Normalise + hashrender pages · content hash · dedupe
Object storeoriginals, immutable, never overwritten
Classifier + splitterdoc type · multi-doc page boundaries
Routerdeterministic: text layer? tables? scan quality?
Page + field journalroute, cost, model version, human edits
Native textpdftotext · $0.00 per page
OCR engine~$1.50 per 1,000 pages
VLM extractor~$3.60 per 1,000 pages (Flash-class)
Schema + arithmetic validationsums · tax · dates · vendor master
Review queuefield flags + source crop · 22% of docs
Export to ERP / APidempotent, keyed on document hash
Two edges leave validation and they are the entire economics of the system. Every document that takes the lower edge costs about 25 cents of reviewer time; every one on the upper edge costs nothing. The engineering goal is not better extraction in the abstract. It is moving documents from the lower edge to the upper edge without letting a wrong number through.

How do you decide between OCR and a vision model?

Per page, deterministically, from three measurable properties: whether the page already has a usable text layer, whether it contains a table whose structure carries meaning, and how clean the scan is. Never per project, and never by defaulting everything to the most capable path. That decision is where teams pay forty times more than they need to.

Start with the free path because it is the largest. A born-digital PDF carries an embedded text layer, and reading it costs nothing, takes milliseconds and is character-perfect by construction. The check is mechanical: extract the text layer, count characters per page, and compare against the page area. A page with a few hundred characters spread over an A4 area is a scan with an accidental text fragment; a page with three thousand characters is a real text layer. In accounts payable this path typically covers the majority of volume, because most invoices are software-generated and emailed.

The second decision is whether structure carries meaning. Traditional OCR returns characters and positions; it does not reliably return the fact that this number belongs to that row and that column. For a simple remittance advice that is fine. For a twelve-line invoice table with merged cells, a continuation page and a discount column, a character stream produces line items that are individually plausible and collectively wrong: the worst kind of extraction error, because it passes a schema check and fails an arithmetic one. That is exactly the case for a vision model, which reads layout and content in the same pass.

The third input is scan quality, worth measuring rather than assuming. Skew, resolution, contrast and the ratio of low-confidence OCR tokens are all cheap to compute, and a page that scores badly should escalate to the vision path rather than produce a text stream the extraction model then confidently misreads. Same shape as any routing problem: a policy table and a deterministic function, argued in full in LLM routing and caching, cost per request.

One note on prebuilt document models. Azure prebuilt models at around $10 per 1,000 pages, Google Document AI in the $10-$30 band and AWS Textract Forms plus Tables at around $65 are all more expensive than a Flash-class vision model at roughly $3.60. They earn their price only when their output schema is closer to what you need than your own prompt and schema, and when you value the vendor's accuracy commitment over the flexibility. For a bespoke schema, which most real AP pipelines have because your ERP has opinions, the vision path is usually cheaper and more controllable.

pipeline/route-page.ts
export type Route = "native_text" | "ocr" | "vlm" | "human_triage";

export type PageSignals = {
  /** characters recovered from the embedded PDF text layer */
  textLayerChars: number;
  /** page area in square inches, from the media box */
  areaSqIn: number;
  /** ruling lines / cell candidates detected by a cheap CV pass */
  tableCellCandidates: number;
  /** 0-1, from a fast skew + contrast + resolution heuristic */
  scanQuality: number;
  /** classifier confidence that this page belongs to the assigned doc type */
  typeConfidence: number;
  /** true when the doc type declares that table structure is load-bearing */
  structureIsLoadBearing: boolean;
};

/** Below this, an apparent text layer is stray metadata, not real text. */
const MIN_CHARS_PER_SQ_IN = 4.5;

export function routePage(s: PageSignals): { route: Route; reason: string } {
  if (s.typeConfidence < 0.7) {
    return { route: "human_triage", reason: "unclassified_document" };
  }

  const density = s.textLayerChars / Math.max(s.areaSqIn, 1);
  const hasRealTextLayer = density >= MIN_CHARS_PER_SQ_IN;

  // Structure beats characters. A perfectly OCR'd table is still the wrong
  // shape, and a text layer does not tell you which cell a number lives in.
  if (s.structureIsLoadBearing && s.tableCellCandidates >= 12) {
    return { route: "vlm", reason: "load_bearing_table_structure" };
  }

  if (hasRealTextLayer) {
    return { route: "native_text", reason: "embedded_text_layer" };
  }

  if (s.scanQuality < 0.55) {
    return { route: "vlm", reason: "poor_scan_quality" };
  }

  return { route: "ocr", reason: "clean_scan_simple_layout" };
}

/** Modelled unit economics, Aug 2026 list prices. Keep this next to the router
 *  so nobody changes a threshold without seeing what it costs. */
export const ROUTE_COST_PER_PAGE_USD: Record<Route, number> = {
  native_text: 0.0,
  ocr: 0.0015,     // ~$1.50 per 1,000 pages, basic OCR
  vlm: 0.0036,     // ~1,600 image + 1,200 prompt tokens in, 400 out, Flash-class
  human_triage: 0.25,
};
The router in full. Every input is measurable and no model is involved, which means the decision is reproducible, auditable and free. The escalation rules at the bottom are the ones that matter in practice: a low text-density page is a scan, and a page with meaningful table structure goes to the vision path regardless of how clean the OCR looks, because character-accurate OCR of a table is still structurally wrong.
Text acquisition options, with published rates
 Rate per 1,000 pagesReturns structure?Best forWhere it fails
Native text layer (pdftotext)$0.00Reading order onlyBorn-digital PDFs — the majority of AP volumeSilently returns a few stray characters on a scan; needs a density check
Basic cloud OCR (AWS / Google / Azure)~$1.50, ~$0.60 above 1M/month on AWS and AzureCharacters and positionsClean scans with simple layouts at high volumeTables. Character-perfect output can still be structurally wrong
Mistral OCR~$1-2, flat regardless of complexityCharacters, layout-awareMixed document sets where the forms surcharge would biteVendor concentration; verify current rates before committing
Azure prebuilt document models~$10Typed fieldsStandard forms where the prebuilt schema matches yoursA bespoke ERP schema you then have to map onto anyway
Google Document AI processors~$10-$30 depending on processorTyped fieldsSpecialised document classes with a good processorCosts more than a VLM for a schema you did not choose
AWS Textract Forms + Tables~$65Forms and table cellsDeep AWS shops needing table cell fidelity with an SLAMost expensive path on this list by a wide margin
VLM extraction (Flash-class)~$3.60 modelledAnything your schema asks forComplex tables, handwriting, stamps, poor scansHallucinates a plausible total; needs arithmetic validation behind it
VLM extraction (Sonnet-class)~$9.60 modelledAnything your schema asks forThe hard tail: contracts, dense legal tables, ambiguous layoutsCosts 2.7x the Flash path; reserve it for documents that failed once
The cheapest row is free and covers most of the volume; the most expensive is 18 times the price of the Flash-class vision path for output you then have to remap onto your own schema. VLM figures are modelled from a 1,600-token page image plus a 1,200-token prompt and 400 tokens out at August 2026 list rates. The OCR and prebuilt figures are published vendor headline rates and should be re-checked before you commit, because they moved twice in the year before this was written.

What happens to one document, end to end?

Hash and dedupe, classify and split, route each page, acquire text on the cheapest sufficient path, one extraction call bound to a typed schema, deterministic validation, a confidence gate, and either an idempotent export or a place in a review queue. Roughly six seconds for a three-page born-digital invoice, two model calls, about 1.3 cents.

The single most important structural decision: extraction runs once over the whole document, not once per page. A three-page invoice has a header on page one, line items spanning pages one and two, and totals on page three. Extract page by page and you get three partial records that then need reassembling by logic that gets the continuation cases wrong. One call, whole document, one typed record. If the document is genuinely too long for a single context, split on the semantic boundary the classifier found rather than on page count.

Validation runs before the confidence gate and is entirely deterministic. Line items sum to subtotal. Subtotal plus tax equals total. Tax rate is one your jurisdiction permits. Invoice date precedes due date. Currency is one this vendor bills in. The vendor exists in your master data. Every one is a pure function, costs nothing, and catches a class of error no self-reported confidence score will. In my experience building extraction systems, the arithmetic checks find more real errors than anything the model says about itself.

Export is idempotent on the document hash. A retried export must not create a second payable, and the mechanism is the same as any irreversible tool call: write the export record with its key before dispatching, and on a unique-constraint violation read back the prior receipt instead of calling again. The full pattern with the crash-window reconciliation branch is in the agent loop in production.

Note what does not appear in the sequence: an agent. No loop, no tool selection, no planning. Two model calls, some deterministic functions, a queue and a human. If your document pipeline has an agent runtime in it, ask what the loop is for. Occasionally there is a good answer, such as an agent that fetches a purchase order to reconcile against, but usually a framework was chosen before the problem was described.

The happy path
One three-page invoice, end to end, with cost on every callMailboxIntakeClassifierRouterExtractorValidatorReviewERP
attachment received (3-page PDF)
sha256 of raw bytes, dedupe check
$0.0002 · exactly-once key
render page 1 to image
classify + split: 2.0k in / 60 out
$0.0017 · Flash-class
type=invoice · 1 document · pages 1-3
per-page signals: density 41 c/sqin, 14 cells
$0.0000 · deterministic
route=native_text (all 3 pages)
$0.0000 acquisition
extract: 10.2k in / 600 out
$0.0099 · Flash-class, whole document
typed record: 6 header fields, 12 line items
sums, tax, dates, currency, vendor master
$0.0000 · pure functions
2 fields flagged: PO number, line 7 qty
reviewer confirms, 41s
$0.228 · $20/hr loaded
POST payable, Idempotency-Key = doc hash
receipt id, stored in journal
Total modelled cost: $0.0118 of models plus $0.228 of reviewer time on this particular document, because it happened to be one of the 22% that gets reviewed. Across a large population the blended figure is about 6.8 cents. The arithmetic in the validator costs nothing and generated the two field flags. No model reported low confidence on either.
extraction/invoice-schema.ts
import { z } from "zod";

const Located = <T extends z.ZodTypeAny>(inner: T) =>
  z.object({
    value: inner,
    page: z.number().int().min(1),
    bbox: z.tuple([z.number(), z.number(), z.number(), z.number()]),
  });

export const LineItemSchema = z.object({
  description: Located(z.string().min(1)),
  quantity: Located(z.number().positive()),
  unit_price_minor: Located(z.number().int().nonnegative()),
  line_total_minor: Located(z.number().int()),
});

export const InvoiceSchema = z.object({
  vendor_name: Located(z.string().min(1)),
  vendor_tax_id: Located(z.string()).nullable(),
  invoice_number: Located(z.string().min(1)),
  invoice_date: Located(z.string().date()),
  due_date: Located(z.string().date()).nullable(),
  currency: Located(z.string().length(3)),
  po_number: Located(z.string()).nullable(),
  line_items: z.array(LineItemSchema).min(1),
  subtotal_minor: Located(z.number().int()),
  tax_minor: Located(z.number().int().nonnegative()),
  total_minor: Located(z.number().int()),
});

export type Invoice = z.infer<typeof InvoiceSchema>;

export type Flag = { field: string; rule: string; detail: string };

/** Pure arithmetic. No model, no cost, and it finds more real extraction
 *  errors than any self-reported confidence score. */
export function validate(inv: Invoice, vendorIds: Set<string>): Flag[] {
  const flags: Flag[] = [];
  const v = <T>(x: { value: T }) => x.value;

  for (const [i, li] of inv.line_items.entries()) {
    const expected = Math.round(v(li.quantity) * v(li.unit_price_minor));
    if (Math.abs(expected - v(li.line_total_minor)) > 1) {
      flags.push({
        field: "line_items[" + i + "]",
        rule: "line_total_mismatch",
        detail: "qty x unit = " + expected + ", extracted " + v(li.line_total_minor),
      });
    }
  }

  const summed = inv.line_items.reduce((a, li) => a + v(li.line_total_minor), 0);
  if (Math.abs(summed - v(inv.subtotal_minor)) > 1) {
    flags.push({ field: "subtotal_minor", rule: "lines_do_not_sum", detail: "sum " + summed });
  }

  if (v(inv.subtotal_minor) + v(inv.tax_minor) !== v(inv.total_minor)) {
    flags.push({ field: "total_minor", rule: "total_mismatch", detail: "subtotal + tax != total" });
  }

  if (inv.due_date && v(inv.due_date) < v(inv.invoice_date)) {
    flags.push({ field: "due_date", rule: "due_before_issue", detail: v(inv.due_date) });
  }

  if (!vendorIds.has(v(inv.vendor_name).toLowerCase().trim())) {
    flags.push({ field: "vendor_name", rule: "unknown_vendor", detail: v(inv.vendor_name) });
  }

  return flags;
}
The extraction contract and the validators behind it. Two design choices carry the weight. Every field carries a page number and a bounding box, which lets the review UI show a reviewer the crop instead of asking them to hunt through a PDF, the single biggest determinant of review speed. And validate() is pure arithmetic: it costs nothing, runs in microseconds, and catches the structural errors a model will never flag on itself.

How should the human review queue work?

Field-level rather than document-level, with a source crop shown next to every flagged value, keyboard-driven, and ordered by financial exposure rather than arrival time. A reviewer should be confirming or correcting four fields in forty-five seconds, not reading a PDF and retyping an invoice.

The economics justify unusual care here. At a 22% review rate and 45 seconds per document, review is 80% of the pipeline's cost. Two design decisions move that number more than any model change. First, the source crop: show the extracted value beside a tight image crop of exactly where it came from and a reviewer confirms in two seconds; show a value and a full-page PDF viewer and the same confirmation takes twenty. The bounding box in the extraction schema exists entirely to make this possible, which is why it is a required field, not a nice-to-have.

Second, review fields, not documents. A document with one flagged quantity does not need a human to re-read the vendor name, the dates and eleven other line items. Present the flags, let everything else through, and record which fields were touched. That cuts per-document review time and, more importantly, produces a corrections log precise about which field failed rather than which document was wrong.

Third, order the queue by exposure rather than arrival, the easiest decision to skip. A flagged $180,000 invoice and a flagged $42 one are not equally urgent, and a queue that drains in arrival order occasionally leaves the large one until the end of the day. Sort by amount, by approaching payment terms, and by vendor risk, and put a hard SLA on the top band.

One thing to resist: routing to review on model-reported confidence alone. Self-reported confidence tracks how typical the document looks, not whether the number is right. A beautifully laid out invoice with a transposed digit scores high. Deterministic validation failures, master-data mismatches, out-of-distribution vendor templates and value thresholds are all better triggers, and all of them are free.

Confidence threshold sweep: cost per document vs errors that escape
7657381900.800.900.950.980.990.995Cents per document · escaped errors per 1,000 docsConfidence / flag threshold (stricter to the right)
Most AP teams land near 0.95
Cost per document (cents)Escaped errors per 1,000 documentsReview rate (%)
Cost is dominated by the review rate, which is why the two solid lines move in opposite directions and why the whole design problem is finding threshold policies that lower the review rate without raising escaped errors. Deterministic validation does exactly that: it moves the curve rather than moving you along it, because an arithmetic check catches a wrong number without sending a correct one to a human. Escaped-error figures are modelled from plausible per-field error rates, not measured on your document mix.
Checklist
Seven properties of a review queue that is actually fast
  • Field-level review, not document-levelOne flagged quantity does not require re-reading eleven correct line items.
  • A tight source crop beside every flagged valueThe single largest determinant of review speed. This is why bbox is required in the extraction schema.
  • Keyboard-only flow: confirm, correct, nextA mouse-driven review UI costs several seconds per field, which is several thousand dollars a year at volume.
  • Queue ordered by exposure and payment terms, not arrivalA flagged six-figure invoice and a flagged $42 one are not equally urgent.
  • Every correction written to a corrections logDocument, field, extracted value, corrected value, route, model version. Your only real error dataset.
  • Reviewer disagreement sampling on a small share of clean documentsSend 1-2% of auto-approved documents to review anyway. It is the only way to measure escaped errors.
  • Per-vendor template memoryAfter N corrections on the same vendor layout, store the fix. Month three, not month one.
The sixth item separates a team that knows its error rate from a team that believes it. Auto-approved documents are, by definition, never checked, so unless you deliberately sample some, your escaped-error rate is a number you made up.

What does one page actually cost?

About 0.45 cents per page in models on a three-page born-digital invoice, and about 2.3 cents per page all-in once human review is included at a 22% review rate. Extraction is 1.3 cents per document; review is 5.5. The models are not the bill.

Break the document down. Intake and hashing are $0.0002. Classification on page one is $0.0017. Routing is free. Text acquisition on the native path is free, or $0.0045 for three OCR pages, or $0.0108 for three vision-model pages. One extraction call over roughly 10,200 input tokens and 600 output tokens on a Flash-class model is $0.0099. Validation is free. A second-pass re-extraction on the 12% of documents that fail a check is $0.0012 amortised. Journal storage is $0.0003. Model subtotal: $0.0134 on the native path.

Then review. Twenty-two per cent of documents go to a human for 45 seconds at $20/hour fully loaded: $0.25 per reviewed document, $0.055 amortised across all documents. That is 80% of a $0.0684 total. As an engineering priority: cutting the review rate from 22% to 12% saves $0.025 per document, nearly twice the entire model spend. No amount of prompt optimisation competes with a validation rule that safely auto-approves a document class.

Against manual keying the comparison is not close. Four minutes of data entry per invoice at $20/hour is $1.33. At 33,000 documents a month, roughly 100,000 pages, that is about $44,000 of keying against about $2,260 of pipeline cost, a modelled saving of roughly $41,700 a month. Even at a pessimistic 40% review rate the pipeline lands near $0.11 a document and the comparison still holds by an order of magnitude.

Two caveats on that saving. First, the manual baseline assumes people currently key every invoice, often only partly true: many AP teams already have partial automation, so the real delta is smaller. Second, the review rate on your document mix is unknowable in advance. A portfolio of forty regular suppliers with stable templates behaves nothing like a long tail of one-off vendors. Run 500 real documents through a two-week pilot and measure the review rate before anyone builds a business case, because that one number moves the answer by a factor of three.

Line itemModel / rateUnitsPer document (3 pages)Per pageNote
Intake, hash, dedupe, renderCompute, amortised3 pages$0.00020$0.00007Content hash of raw bytes is the exactly-once key
Classification and splittingGemini 3.7 Flash · $0.75 / $3.75 per 1M2,000 in / 60 out$0.00173$0.00058First page only. Splitting multi-doc scans matters more than fine-grained typing
RoutingDeterministic function3 pages$0.00000$0.00000Free, reproducible, auditable. The biggest cost lever in the pipeline
Text acquisition — native pathpdftotext3 pages$0.00000$0.00000Majority of AP volume. Character-perfect by construction
Text acquisition — OCR pathCloud OCR at ~$1.50 per 1,000 pages3 pages$0.00450$0.00150AWS and Azure drop to ~$0.60 per 1,000 above 1M pages a month
Text acquisition — VLM pathFlash-class, 1,600 img + 1,200 prompt in / 400 out3 pages$0.01080$0.00360Sonnet-class is ~$9.60 per 1,000 pages; reserve for the hard tail
ExtractionGemini 3.7 Flash10,200 in / 600 out$0.00990$0.00330One call over the whole document, never page by page
ValidationPure functions~20 checks$0.00000$0.00000Sums, tax, dates, currency, vendor master. Finds what confidence scores miss
Second-pass re-extractionFlash-class, on 12% of documentsamortised$0.00119$0.00040Re-run with the failed check quoted back, on a stronger model
Journal and corrections logPostgres, amortised~8 rows$0.00030$0.00010One row per page, one per human correction
Model subtotal (native path)$0.01332$0.00444Modelled from Aug 2026 list prices, not measured
Human review$20/hr loaded, 45s, 22% of documentsamortised$0.05500$0.0183380% of the total. The number every engineering hour should target
Total per document$0.06832$0.02277vs $1.33 for 4 minutes of manual keying at $20/hr
Where 6.8 cents goes
$ per document, 3-page born-digital invoice, 22% review ratelower is better
Human review (22% at 45s, $20/hr)80% of the bill$0.0550
Extraction (Flash-class, whole document)14%$0.0099
Classification and splitting3%$0.0017
Second-pass re-extraction (12% of docs)2%$0.0012
Journal + corrections log<1%$0.0003
Intake, hash, render<1%$0.0002
Routing + validationfree, and the highest-leverage code in the system$0.0000
Total$0.0683
The two rows at the bottom cost nothing and determine the size of the row at the top. That is the whole thesis of this post: in a document pipeline, the free deterministic components control the expensive human one, and optimising the middle rows, the models, is rearranging 14% of the bill.
The four numbers to take to a finance sponsor
$0.068
modelled all-in cost per 3-page invoice at a 22% review rate
$1.33
manual keying baseline at 4 minutes and $20/hour fully loaded
19x
80%
share of pipeline cost that is human review time, not model spend
$0.025
saved per document by moving the review rate from 22% to 12%
~2x total model spend
The fourth figure is the one that should set your roadmap. Two weeks spent writing validation rules and per-vendor template memory to safely halve the review rate is worth more than any model upgrade available to you, and it is work that gets cheaper over time because the corrections log tells you exactly where to aim.

What breaks in production, and how would you know?

Ten things, and the ones that cost real money all produce a well-formed record. A transposed digit in a total, a line-item table that shifted a column, a duplicate invoice paid twice, and a wrong vendor match all pass schema validation and arrive in your ERP looking exactly like correct data.

Duplicate payment deserves its own paragraph because it is the failure with a direct cash cost and a reconciliation tail. Two defences, both needed. The content hash catches byte-identical resubmissions, covering forwarded emails and repeated SFTP drops. It does not catch the same invoice rescanned, or sent as a PDF once and an image the next time, so a second check on extracted vendor plus invoice number plus amount, run after extraction and before export, is mandatory. Neither check is a model.

The quiet, systemic failure is a vendor template change. A supplier redesigns their invoice, extraction starts putting the discount column where the quantity used to be, and every invoice from that vendor is subtly wrong until someone notices. The detection signal is not an error rate. It is a per-vendor correction rate that jumped. Group your corrections log by vendor and alert on the delta, and this becomes a Tuesday morning ticket instead of a quarterly write-off.

Retention and privacy are real here in a way they are not in most AI products, because invoices and contracts contain personal data, bank details and commercially sensitive terms. Two rules to settle before the first line of ingestion code: originals live in one place with a retention policy enforced by a job rather than a document, and page images must not leak into model-provider training or long-lived trace storage. Check the data-handling terms of the specific API tier you use, not the marketing page, and record which tier each document was processed under in the journal.

Four signals catch most of the rest: review rate by document type, per-vendor correction rate, escaped-error rate measured by deliberately sampling auto-approved documents, and cost per document with a price snapshot. The third requires sending one to two per cent of clean documents to a human anyway, which feels wasteful and is the only honest way to know your accuracy. Everything else is a number you assumed.

Failure modeWhat the user seesWhere to fix itDetection signalCost of getting it wrong
Duplicate invoice paid twiceTwo payables for the same supplier documentIntake: content hash; plus a post-extraction check on vendor + invoice number + amountDuplicate-key rejection rate; supplier credit notesDirect cash loss plus a reconciliation task nobody owns
Line items shift a columnPlausible quantities against the wrong pricesRouting: send load-bearing tables to the VLM path regardless of OCR qualityLine-total arithmetic failures clustered by vendorWrong amounts approved. Schema validation alone will never catch it
Vendor template changedA steady drip of corrections from one supplierExtraction: per-vendor template memory and few-shot examples from correctionsPer-vendor correction rate delta week over weekSystemic — every invoice from that vendor until someone notices
Multi-document scan processed as oneOne record where there should have been eightClassifier: page-boundary detection before anything else runsPage-count distribution per document type; documents with implausible totalsSeven invoices silently lost, which is worse than seven wrong ones
Model invents a plausible totalA number that appears nowhere on the pageValidation: subtotal plus tax equals total, and the value must be locatable on the pageTotal-mismatch flag rate; bbox pointing at empty spaceApproved payment on a fabricated figure
Text layer looked real but was stray metadataGarbled extraction on an obvious scanRouter: characters-per-square-inch density check, not a boolean has-text testExtraction failure rate on native-routed pagesOne wasted extraction and a document in review that should have gone to OCR
Everything routed to the most capable pathNothing. The invoice arrives correctlyRouter: measure and enforce the route mix; alert when native-path share dropsRoute distribution; cost per page trending up with flat volumeUp to 40x the necessary acquisition cost, invisible until the invoice
Review queue backs up on the first of the monthInvoices approved late; discount terms missedQueue: exposure-ordered, with autoscaling reviewer capacity and an SLA per bandQueue depth and age p95; documents past payment termsLost early-payment discounts, which often exceed the pipeline's entire cost
Retried export creates a second payableDuplicate entry in the ERPExport: idempotency key on document hash written before dispatchDuplicate idempotency-key attempts; ERP-side duplicate detectionSame as duplicate payment, but caused by your own retry logic
Escaped errors never measuredNothing. A confident accuracy number nobody verifiedEvals: sample 1-2% of auto-approved documents into review deliberatelyDivergence between sampled error rate and assumed accuracyYou do not know your accuracy, and you will find out from finance
Auto-approved documents are by definition never checked. Unless you deliberately send one or two per cent of them to a human anyway, your escaped-error rate is not a measurement. It is a number you assumed and then put on a slide.The reason clean-document sampling belongs in v1

What does it take to build, and what should you skip?

About seven engineer-weeks for a two-person team to a production v1, plus two to three engineer-days a month of maintenance. Skip fine-tuning, skip a document-specific vector database, skip fine-grained document typing, and skip anything that promises to remove the human entirely. What you cannot skip is the router, the deterministic validators, the corrections log and clean-document sampling.

Week one is intake, hashing, rendering and the journal: the plumbing that makes documents processable exactly once and debuggable afterwards. Week two is the classifier and the splitter, and the splitter is the part with real value: multi-document scans are common and getting them wrong loses records rather than corrupting them. Week three is the router and all three acquisition adapters behind one interface, less work than it sounds because two of the three are thin wrappers around existing services.

Week four is extraction with the typed schema and the located-field structure. Week five is validation, twenty or so arithmetic, referential and temporal rules, the cheapest week in the build measured by cost avoided. Weeks six and seven are the review UI, the corrections log, the export path with idempotency, and the sampling job. The review UI is the piece most likely to be handed to whoever is free, and it should not be: it directly controls 80% of the running cost.

On skipping: do not fine-tune a model in v1. Few-shot examples drawn from your corrections log give most of the benefit at none of the operational cost, and the corrections log does not exist on day one anyway. Do not build a vector database over documents unless you have a retrieval use case beyond extraction. The pipeline described here never searches, it processes. If you add document search later, RAG over private documents covers the permission-aware version, the only version worth building over contracts.

And be sceptical of straight-through-processing targets above about 85% for a heterogeneous document mix. The last band is long-tail by construction: unusual vendors, damaged scans, handwritten annotations, one-off formats. Chasing them with engineering has poor returns compared with making review fast, which is why the review UI deserves a full week. For the general scoping discipline for compressing a build like this, building an MVP in days with AI covers it, and the ongoing architecture work is what AI product development exists for.

Seven engineer-weeks, sequenced
  1. Week 1
    Intake, hashing, journal

    Four channels into one document record keyed by content hash. Page rendering, object store for immutable originals, per-page journal and cost attribution with a price snapshot.

  2. Week 2
    Classifier and splitter

    Document type on page one, and page-boundary detection for multi-document scans. The splitter is the higher-value half: a missed boundary loses records rather than corrupting them.

  3. Week 3
    Router and three acquisition adapters

    Density check, table detection, scan-quality heuristic, and native-text, OCR and VLM adapters behind one interface returning text plus positions. This is the week that sets your bill.

  4. Week 4
    Typed extraction

    One call over the whole document, located fields with page and bbox, structured-output enforcement, and the second-pass path for documents that fail validation.

  5. Week 5
    Deterministic validation

    Roughly twenty arithmetic, referential and temporal rules plus vendor master lookup. Cheapest week in the build measured by cost avoided, and the one that controls the review rate.

  6. Weeks 6‑7
    Review UI, export, sampling

    Field-level review with source crops and keyboard flow, the corrections log, idempotent export keyed on document hash, and the job that samples 1-2% of auto-approved documents into review.

Weeks five to seven are the ones that get compressed when a date moves, and they are the three that control 80% of the running cost. If you have to cut something, cut fine-grained document typing in week two: a coarse type plus a good splitter is enough for a first production system.
Accounts payable economics, modelled at 33,000 documents (100,000 pages) a month
Manual keying baseline
Documents keyed by a human
33,000
Average time per document
4.0 min
Fully loaded cost per hour
$20
Model and infrastructure spend
$0
Monthly cost
$44,000
Cost per document
$1.33
Pipeline at a 22% review rate
Auto-approved
25,740 at $0.0134 = $345
Reviewed by a human
7,260 at 45s = $1,815
Model cost on reviewed documents
included above
Infrastructure, storage, observability
$350
Amortised build + maintenance
$2,600
Monthly cost
$5,110 · $0.155 per document
Modelled saving ~$38,900/month, and it survives a pessimistic 40% review rate
Even including roughly seven engineer-weeks amortised over 24 months plus maintenance, the pipeline lands near 15.5 cents a document against $1.33 for manual keying. The assumption most worth challenging is the manual baseline: many AP teams already have partial automation, so the true delta is the gap against what you do today, not against a fully manual process nobody has run since 2015.

Building an AI document processing pipeline: common questions

How much does AI document processing cost per page?

About 0.44 cents per page in model spend for a three-page born-digital invoice, and about 2.3 cents per page all-in once human review is included at a 22% review rate. Text acquisition ranges from free on a native text layer, through roughly $1.50 per 1,000 pages for basic cloud OCR, to about $3.60 per 1,000 for a Flash-class vision model and about $65 per 1,000 for AWS Textract Forms plus Tables. These are modelled figures from August 2026 published rates with the token counts shown, not measurements of a system I have run.

Should I use OCR or a vision model for document extraction?

Decide per page from three measurable properties. If the PDF has a real text layer (check characters per square inch, not just whether text exists) read it directly for nothing. If it is a clean scan with a simple layout, use basic OCR at around $1.50 per 1,000 pages. If table structure carries meaning, or the scan is poor, or there is handwriting, use a vision model at around $3.60 per 1,000 pages, because character-perfect OCR of a table can still be structurally wrong.

What straight-through-processing rate is realistic?

For a heterogeneous document mix, plan for 75-85% and treat higher as upside. The remaining band is long-tail by construction: unusual vendors, damaged scans, handwritten annotations and one-off formats. Chasing it with engineering has poor returns compared with making human review fast, which is why the review UI deserves as much attention as the extraction model. Measure your own rate with a 500-document pilot before anyone builds a business case, because the number moves the answer by a factor of three.

How do you stop the model hallucinating an invoice total?

With arithmetic rather than confidence. Line items must multiply out and sum to the subtotal, subtotal plus tax must equal total, the tax rate must be one your jurisdiction permits, dates must be ordered, and the vendor must exist in your master data. Every extracted field also carries a page number and a bounding box, so a value that points at empty space is detectable. These checks are pure functions, cost nothing, and catch the class of error that self-reported model confidence systematically misses.

How do you prevent paying the same invoice twice?

Two independent checks. A content hash of the raw bytes at intake catches byte-identical resubmissions (forwarded emails, repeated SFTP drops) and becomes the document's exactly-once key. That misses rescans and format changes, so a second check runs after extraction on vendor plus invoice number plus amount. The export into the ERP is then made idempotent on the document hash, with the export record written before dispatch, so a retry reads back the prior receipt instead of creating a second payable.

How long does it take to build a document processing pipeline?

About seven engineer-weeks for a two-person team to a production v1, plus two to three engineer-days a month of maintenance. One week on intake, hashing and the journal, one on classification and multi-document splitting, one on the router and three acquisition adapters, one on typed extraction, one on deterministic validation, and two on the review UI, export and sampling. The last three weeks control roughly 80% of the running cost and are the ones most likely to be compressed when a date moves.

Do I need to fine-tune a model for my documents?

Not in version one, and probably not in version two. Few-shot examples drawn from your corrections log give most of the benefit at none of the operational cost, and the corrections log does not exist until you have been running for a month. Per-vendor template memory (storing the fix after several corrections on the same layout) is a cheaper and more targeted intervention than fine-tuning, and it degrades gracefully when a vendor redesigns their invoice, which fine-tuning does not.

Ready to talk numbers?

Twenty minutes, straight to the engineer. No sales rep, no deck.