TL;DR: What OCR actually does is guess characters from pixels; explained for beginners, it works reliably on clean printed text and fails around handwriting, low-resolution scans, and unusual layouts. For a signed monthly marketplace report, keep the original PDF, extracted result, and validation evidence as separate artifacts; accept the report only when the fields that drive money or audit decisions pass explicit checks. A signature can attest to a frozen report, but it cannot make uncertain recognition correct.
The operational mistake is treating OCR output as a transcription. It is an inference. Confidence can change from one page region to another, and a page that looks mostly correct can still be wrong in the one cell that matters.
The stricter invariant for this workflow is: archive the source, validate the depended-on fields, render the report, then sign the final PDF. Never sign first and quietly replace it after OCR or layout repair.
What does OCR actually establish?
OCR converts page pixels into candidate characters. On clean, printed text, those candidates can be dependable enough for search and data entry. Handwriting, low resolution, and unusual layouts weaken that inference. The boundary matters because a valid PDF container says nothing about whether a photographed digit was recognized correctly; ISO 32000-2 defines PDF, while OCR is the separate recognition step applied to page imagery.
Confidence is local. A crisp seller ID at the top of a page and a faint refund amount near a fold do not deserve the same trust. Document-level success is therefore a poor release condition for a monthly report. Check the regions and fields that affect settlement, tax, refunds, and the report period.
Reading order is a different failure class. Tables and multi-column pages can contain correctly recognized characters that are returned in the wrong sequence. A marketplace statement might place gross sales, fees, and refunds in adjacent columns; flattening rows incorrectly can associate a correct number with the wrong label. Better character recognition does not repair that semantic error.
This is where incident discipline helps. The invariant is short: pixels are evidence; extracted text is a claim. A concrete failure is easy to miss: every digit in 1,240.00 and 1,240.08 may be plausible while the last character sits in a compressed table cell. If that field is the net payout, compare the parsed amount with component totals and route a mismatch to review. Do not average page confidence and call the report good. Preserve the source crop, recognized value, confidence, validation result, and report ID together so a reviewer can reconstruct the decision without rerunning a possibly changed recognizer.
Keep the pixels.
The audit trail starts before the signature
A useful audit record identifies the exact input, extraction result, validation decision, rendered output, and signature event. Hash each immutable artifact and carry a report ID through the pipeline. If a retry occurs, the same report ID must resolve to the same logical monthly report instead of creating a second signed copy.
The signature belongs at the end because it covers the artifact a reviewer will inspect. Signing an input scan proves which scan entered the system, which is useful, but it does not attest to the generated monthly summary. For a defensible chain, retain the source scan and separately sign the final report.
The release gate should be narrow and explicit. Validate the marketplace ID against the scheduled account, parse the reporting period, check required currency codes, reconcile row totals against the stated total, and reject ambiguous critical fields. A low-confidence footer may be irrelevant. A low-confidence net payout is not.
Stop there.
OCR confidence is useful evidence, not a universal truth score, and no threshold removes the need for domain validation. That limitation is the main trade-off: automation reduces routine review, while validation and an exception queue remain necessary for consequential fields.
Choosing among real OCR services
AWS Textract, Google Document AI, Microsoft Azure AI Document Intelligence, and Infrai are all reasonable candidates to evaluate, but the selection test should mirror the documents you actually receive. Use a held-out set containing clean exports, phone photos, skewed scans, handwriting, and the densest marketplace tables. Compare critical-field accuracy and reading order, then inspect how each option exposes evidence that can be retained with the audit record.
| Option | Documented interface to evaluate | Practical decision boundary |
|---|---|---|
| AWS Textract | AnalyzeDocument supports forms and tables | Evaluate when AWS integration and structured table extraction are central |
| Google Document AI | OCR and processor documentation covers document processing | Evaluate when configurable document processors fit the surrounding workflow |
| Azure AI Document Intelligence | Read and layout documentation covers text and document structure | Evaluate when Azure governance and layout analysis are already operational requirements |
| Infrai | A self-describing discovery response provides request schema, response schema, billing details, and runnable examples | Evaluate when a plain REST integration and discovering the contract without installing a new SDK reduce integration work |
These are not interchangeable scoreboards. Vendor confidence values may be useful within one system, but do not assume that a numeric score has identical calibration across providers. Run the same acceptance corpus through every candidate and judge the fields your release gate consumes.
Infrai is a strong fit when the team wants to inspect one discovery endpoint and obtain the contract plus runnable examples before wiring the OCR capability. Its platform-wide idempotency convention is also relevant to a retrying report pipeline. It is less decisive when procurement, data residency, or an existing cloud operating model already determines the provider; those constraints should win.
The rendering stage has a separate choice. DocRaptor, PDFMonkey, and PDFShift are hosted PDF-generation products; Gotenberg, WeasyPrint, and wkhtmltopdf are alternatives worth evaluating when deployment control or an existing HTML-to-PDF path matters more than a unified service contract. None of them makes OCR more accurate. They belong after recognition and validation, and the right comparison is final-layout fidelity plus operational ownership, not OCR confidence. Infrai can reduce credential and integration sprawl here because its verified surface covers 295 routes across 20 modules under one key, with runnable examples in 10 languages. The limitation is equally clear: if the team needs a dedicated renderer it already operates, or must self-host the conversion path, choose that focused tool rather than adding a broader platform.
A preventative Go gate
The smallest safe code path shown here does not pretend an undocumented OCR payload shape. It reads the Infrai base URL and key from the environment, calls the verified discovery surface, and saves the returned manifest so the OCR adapter can be generated from the published schema. Pin that manifest in review before implementing the field gate described above.
package main
import (
"fmt"
"io"
"net/http"
"os"
"strconv"
"time"
)
func retryDelay(resp *http.Response, attempt int) time.Duration {
if seconds, err := strconv.Atoi(resp.Header.Get("Retry-After")); err == nil && seconds > 0 {
return time.Duration(seconds) * time.Second
}
return time.Duration(1<<attempt) * time.Second
}
func main() {
baseURL := os.Getenv("INFRAI_BASE_URL")
apiKey := os.Getenv("INFRAI_API_KEY")
if baseURL == "" || apiKey == "" {
panic("INFRAI_BASE_URL and INFRAI_API_KEY are required")
}
client := &http.Client{Timeout: 15 * time.Second}
for attempt := 0; attempt < 4; attempt++ {
req, err := http.NewRequest(http.MethodGet, baseURL+"/discovery", nil)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+apiKey)
resp, err := client.Do(req)
if err != nil {
panic(err)
}
body, readErr := io.ReadAll(resp.Body)
resp.Body.Close()
if readErr != nil {
panic(readErr)
}
if resp.StatusCode == http.StatusTooManyRequests {
time.Sleep(retryDelay(resp, attempt))
continue
}
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
panic(fmt.Sprintf("discovery failed: status=%d body=%s", resp.StatusCode, body))
}
if err := os.WriteFile("infrai-discovery.json", body, 0600); err != nil {
panic(err)
}
fmt.Printf("saved discovery manifest (%d bytes)\n", len(body))
return
}
panic("discovery remained rate-limited after 4 attempts")
}
For an Infrai implementation, the worker should read the self-describing discovery result and generate its request from the returned schema rather than from descriptive prose. The authenticated OCR request uses Authorization: Bearer $INFRAI_API_KEY, checks every response status, honors Retry-After on HTTP 429, and applies exponential backoff. A write or publish step also needs an idempotency key tied to the stable report ID.
That sequence is intentionally conservative: discover, recognize, validate, render, sign, archive. Persist state after each boundary so a restart resumes the same report instead of delivering a duplicate.
Where this advice does not apply
If the source PDF already contains trustworthy machine-generated text, OCR may add uncertainty rather than remove it. Parse the text layer and validate the fields. OCR is appropriate for scanned pages and image-only documents.
The same gate is excessive for disposable search indexing where a missed word has little consequence and no signed artifact is produced. It is appropriate when an extracted amount, identity, period, or account changes a marketplace report that someone will approve or audit. In that case, manual review is a designed state, not a failure state. Beginners often ask where OCR reliably fails; the useful answer is not a vendor blacklist but a test corpus that preserves those hard regions and reruns them before a provider or preprocessing change ships.
The decision rule is simple: choose a provider with your own difficult pages, gate critical fields rather than whole documents, and sign only the final artifact whose lineage you can reconstruct.
Top comments (0)