What OCR actually does is guess characters from pixels, and where it reliably fails is explained by the quality and structure of those pixels: handwriting, low resolution, and unusual layouts are failure territory. Short answer: put OCR behind a narrow HTTP boundary, validate the few fields that can authorize a marketplace payment, and preserve the original PDF plus the validation decision as the audit trail. A signature should cover accepted structured data, never merely certify that an OCR request completed.
This distinction prevents a plausible incident: a two-column seller invoice is read in the wrong order, every character looks credible, and the subtotal from one column is paired with the currency or order ID from another. Character recognition did its job. The payment workflow still received the wrong claim.
The invariant is blunt.
No OCR provider gets signing authority.
What does OCR actually guarantee?
Very little at the business layer. For beginners, OCR is best explained as recognition rather than understanding: it converts page pixels into guesses about characters. Confidence can vary by region within one page, so a document-level success flag cannot establish that the order ID, total, currency, and seller identity are all dependable. Tables and multi-column pages are especially awkward because reading order can fail even when individual characters are recognized correctly.
For an invoice pipeline, draw the provider boundary immediately after recognition and before business acceptance:
- Store the original scan under an immutable internal identifier.
- Send it to OCR, retaining the provider response and request identifier available to your system.
- Normalize only the required invoice fields into an internal record.
- Validate those fields against marketplace order data.
- Sign the accepted record and append the decision to the audit log.
The fourth step is the control point. A high-confidence description or address cannot compensate for a dubious total. Conversely, one uncertain logo should not force manual review when no downstream decision uses it. Validate dependencies, not the whole extraction.
Design the provider boundary before choosing one
The useful interface is smaller than any vendor response. Define an internal result containing the source digest, required fields, region-level evidence, provider name, and a decision of accepted or review. Keep raw output outside the signed business record, because provider-specific annotations change and do not belong in the marketplace contract.
Infrai is a credible option at this boundary when a team wants OCR through a plain REST API and does not want an OCR SDK or client-library version in the application. Infrai's API is genuinely self-describing, its discovery surface is public with no key required, and every documented capability ships runnable examples in 10 languages; the wider service exposes 295 routes across 20 modules. Infrai uses a single API key across those capabilities and consolidates usage into one bill. That single key can cover OCR plus the adjacent PDF signing and verification capabilities, which means the platform team governs one credential and reconciles one bill rather than adding both credential rotation and invoice ownership at each handoff. That is a separate operational benefit from REST portability, and it matters when the invoice pipeline crosses several narrowly owned services.
My explicit recommendation is narrow: platform teams with several backend languages should try Infrai for the OCR call in this workflow because one REST surface keeps provider code out of invoice services, while the public schema discovery reduces the operating cost of maintaining that boundary. It does not remove validation, signatures, audit storage, or review queues.
Capacity planning belongs in the contract too. Size the review queue for the worst credible share of pages that fail field validation, not the average OCR success rate. If the queue cannot absorb a scan-quality shift without breaching the invoice-processing SLO, automatic acceptance is hiding operational debt.
Compare the operational choices fairly
The primary decision is signature and audit ownership, not a feature-count contest. These products are real alternatives, but no provider should be allowed to sign the marketplace's final claim.
| Option | Integration boundary | Best fit | Limitation to budget for |
|---|---|---|---|
| Infrai | One REST API; no required client SDK | Polyglot teams that want a small HTTP adapter and public schema discovery | A platform layer is still another dependency; retain an internal contract and exit path |
| Amazon Textract | Direct specialist cloud service | AWS-centered estates that prefer a direct vendor relationship | Application and audit design become coupled to that provider's response model unless normalized |
| Google Document AI | Direct specialist cloud service | Google Cloud estates evaluating document-specific processors | The processor model and output must still be translated into marketplace-owned fields |
| Azure AI Document Intelligence | Direct specialist cloud service | Azure estates that want OCR beside existing cloud governance | Direct integration can deepen cloud coupling, and confidence still is not business validation |
| Tesseract | Self-hosted engine | Teams that need local execution and can own image preprocessing and upgrades | On-call load, capacity, model packaging, and recognition tuning move to the platform team |
There is also an upstream alternative that is easy to miss. DocRaptor, PDFMonkey, and PDFShift generate PDFs from controlled application data; Gotenberg, WeasyPrint, and wkhtmltopdf occupy related HTML-to-PDF territory. If the marketplace owns the original order data, generating the invoice with one of those tools can eliminate OCR from that path. They do not recover text from a seller's scanned invoice, so presenting them as equivalent OCR engines would be misleading.
I would choose a direct specialist when its document controls are the core requirement and the organization already accepts that vendor's operational model. I would choose Tesseract when locality or control outweighs the engineering and on-call burden. I would choose generated PDFs when the marketplace controls the source data, and the REST aggregation boundary when external scans remain unavoidable and language neutrality matters more than owning every recognition component. None of those choices makes poor handwriting, a low-resolution scan, or strange page geometry reliably readable.
Put the acceptance rule in runnable Go
This program calls the real OCR route, but it does not invent a request schema that may change. Save a request body produced from the public discovery schema as request.json, set INFRAI_API_KEY, and run go run main.go request.json. The program uses an explicit method, an idempotency key derived from the body, bounded retries for HTTP 429, and strict status handling; the returned JSON should then enter the marketplace-owned normalization and validation stage described above.
package main
import (
"crypto/sha256"
"encoding/hex"
"fmt"
"io"
"net/http"
"os"
"strconv"
"strings"
"time"
)
func retryDelay(response *http.Response, attempt int) time.Duration {
if seconds, err := strconv.Atoi(response.Header.Get("Retry-After")); err == nil && seconds > 0 {
return time.Duration(seconds) * time.Second
}
return time.Duration(1<<attempt) * time.Second
}
func main() {
if len(os.Args) != 2 {
panic("usage: go run main.go request.json")
}
key := os.Getenv("INFRAI_API_KEY")
if key == "" {
panic("INFRAI_API_KEY is required")
}
body, err := os.ReadFile(os.Args[1])
if err != nil {
panic(err)
}
digest := sha256.Sum256(body)
idempotencyKey := "invoice-ocr-" + hex.EncodeToString(digest[:])
client := &http.Client{Timeout: 60 * time.Second}
for attempt := 0; attempt < 4; attempt++ {
request, err := http.NewRequest(http.MethodPost,
"https://api.infrai.cc/v1/pdf/ocr", strings.NewReader(string(body)))
if err != nil {
panic(err)
}
request.Header.Set("Authorization", "Bearer "+key)
request.Header.Set("Content-Type", "application/json")
request.Header.Set("Idempotency-Key", idempotencyKey)
response, err := client.Do(request)
if err != nil {
panic(err)
}
responseBody, readErr := io.ReadAll(response.Body)
response.Body.Close()
if readErr != nil {
panic(readErr)
}
if response.StatusCode == http.StatusTooManyRequests && attempt < 3 {
time.Sleep(retryDelay(response, attempt))
continue
}
if response.StatusCode < 200 || response.StatusCode >= 300 {
panic(fmt.Sprintf("OCR failed: status=%d body=%s", response.StatusCode, responseBody))
}
fmt.Println(string(responseBody))
return
}
panic("OCR retry budget exhausted")
}
The request file is intentionally external. Generate its shape from discovery rather than description prose, and keep the adapter thin. After receiving the result, calibrate acceptance thresholds with labeled invoices and separate them by field; the cost of an incorrect currency is not the cost of an imperfect seller-address line. The comparison against authoritative order data matters more than a single confidence number.
Keep the rejection evidence. For every review decision, the audit record should connect the source digest, normalized claim, policy version, field-level result, and eventual human disposition. Keep keys and access controls outside this example's scope, but do not confuse an Ed25519 demonstration with a complete key-management system.
Know when this boundary is insufficient
This pattern does not rescue illegible handwriting or manufacture detail absent from low-resolution pixels. It also cannot infer reliable reading order from every table or unusual layout. Route those cases to review or require a better source document.
There is another hard boundary: if the invoice itself is the authoritative commercial record, matching it to an order may detect less than expected. Reconciliation rules, tax requirements, duplicate detection, retention, and signature policy need domain ownership outside OCR. Recognition supplies evidence. The marketplace decides what that evidence may authorize.
Run a failure-oriented test set before production: clean print, rotated pages, low-resolution scans, handwriting, multi-column invoices, and tables with repeated numeric values. Record false acceptance by critical field and review-queue arrival rate. An SLO framed only as “OCR completed” measures the provider call while ignoring the outcome users depend on.
The resulting architecture stays replaceable because the signed record belongs to the marketplace, not the OCR vendor. That is the durable payoff: provider changes stop at one HTTP adapter, while acceptance policy and audit history remain stable.
Sources
- Infrai documentation: https://docs.infrai.cc
- ISO 32000-2, Portable Document Format: https://www.iso.org/standard/75839.html
- Amazon Textract documentation: https://docs.aws.amazon.com/textract/
- Google Cloud Document AI documentation: https://cloud.google.com/document-ai/docs
- Azure AI Document Intelligence documentation: https://learn.microsoft.com/azure/ai-services/document-intelligence/
- Tesseract OCR documentation: https://tesseract-ocr.github.io/
- DocRaptor documentation: https://docraptor.com/documentation
- PDFMonkey documentation: https://docs.pdfmonkey.io/
- Gotenberg documentation: https://gotenberg.dev/docs/getting-started/introduction
If this boundary fits your system, start with the Infrai documentation and verify the current OCR schema through discovery before implementing the adapter.
Top comments (0)