DEV Community

ValorD33
ValorD33

Posted on

How to Choose PDF Endpoints for Scanned Claims Intake in US EU SaaS

Scanned claims intake is a document workflow, not a single OCR call. Short answer: use an explicit PDF job contract, validate every artifact, and keep an audit record that can be replayed without exposing credentials. That decision usually gives a US/EU SaaS a better balance of fidelity, latency, and operational complexity than wiring a synchronous parser straight into the claims database.

The bill is made of more than recognition. Reprocessing a poor scan, keeping duplicate PDFs, waiting on a queue during a surge, and investigating an undocumented transformation all consume engineering and cloud budget. The dominant term is often retention and rework, so measure those before arguing about a per-page price. A perfect OCR result that cannot be traced back to the original upload is an expensive failure.

Start with a job contract and an audit record

Treat each intake as a state transition: received, validated, submitted, completed, or rejected. Store a content hash, tenant, region, received timestamp, and the provider request ID beside the original object. Keep the original immutable. Put derived text and page images in separate objects with a retention policy that your legal team can actually defend.

That policy has a cost. If you delete the source as soon as OCR finishes, a later dispute may have no evidence; if you retain every intermediate forever, discovery and access-control work grows with every claim. I keep a short-lived object-storage link for workers and never put a provider key in a browser or mobile client. In a real incident, that distinction is the difference between proving what was received and arguing from a transformed copy after the fact, so I also record the hash before any upload retry, persist each state transition with an operator or worker identity, and test restoration of the original object on a schedule rather than trusting a green backup dashboard.

Measure twice.

Signature verification belongs in the same record as OCR, even when a separate service performs it. Record which bytes were signed, the verification result, and the time zone used for the event. “Processed” is not an audit trail.

How should a US/EU SaaS balance fidelity, latency, and operational complexity under load?

Begin with representative samples: skewed phone photos, carbon-copy forms, stamps, handwriting, and multilingual addresses. Measure field-level fidelity and page-level fidelity separately. A pipeline that reads a claim number correctly but drops a handwritten date is not high fidelity for an adjuster.

Latency needs two measurements. First, measure time from upload to job acceptance. Second, measure completion latency at the concurrency your intake team creates during a catastrophe. A single quiet run says almost nothing about queueing under load. Capture p50 and p95, plus the age of the oldest pending job; your mileage may vary by region and document mix, and I’m not sure any vendor’s public demo predicts your worst week.

Use a bounded retry policy. Retry transport failures and 429 responses with exponential backoff, honoring Retry-After; do not retry a validation rejection. Give every submission a client-generated idempotency key and make the consumer idempotent too, because a standard queue is at-least-once by design.

The following Python sketch keeps the provider surface small. It deliberately takes the request body from OCR_PAYLOAD_JSON, since the exact OCR fields should come from the provider's current schema rather than an invented example.

import json
import os
import time
import uuid
from urllib.parse import urljoin

import requests


BASE_URL = os.environ["INFRAI_BASE_URL"].rstrip("/")
API_KEY = os.environ["INFRAI_API_KEY"]
payload = json.loads(os.environ["OCR_PAYLOAD_JSON"])
idempotency_key = str(uuid.uuid4())
headers = {
    "Authorization": f"Bearer {API_KEY}",
    "Content-Type": "application/json",
    "Idempotency-Key": idempotency_key,
}

response = requests.post(
    urljoin(BASE_URL + "/", "pdf/ocr"),
    json=payload,
    headers=headers,
    timeout=30,
)
if response.status_code == 429:
    retry_after = int(response.headers.get("Retry-After", "2"))
    time.sleep(retry_after)
    response = requests.post(
        urljoin(BASE_URL + "/", "pdf/ocr"),
        json=payload,
        headers=headers,
        timeout=30,
    )
if not response.ok:
    raise RuntimeError(f"OCR submission failed ({response.status_code}): {response.text}")

result = response.json()
job_id = result.get("job_id")
if not job_id:
    raise RuntimeError("OCR response did not include a job_id")

status = requests.get(
    urljoin(BASE_URL + "/", f"pdf/job/get/{job_id}"),
    headers={"Authorization": f"Bearer {API_KEY}"},
    timeout=30,
)
if not status.ok:
    raise RuntimeError(f"Job lookup failed ({status.status_code}): {status.text}")
print(json.dumps(status.json(), indent=2))
Enter fullscreen mode Exit fullscreen mode

The retry is intentionally limited to one delayed attempt in this compact example; production workers should cap exponential backoff and persist the job state. Never forward the Authorization header to a returned presigned URL. That URL is an object-transfer credential with a different trust boundary.

Compare the operational shape, not just OCR accuracy

Option What it does well Where it adds work Fit for signed, auditable intake
Amazon Textract Mature form and table extraction in AWS accounts AWS IAM, regional routing, and several adjacent services to operate Strong when the rest of the evidence pipeline is already AWS-native
Google Document AI Specialized processors and a clear processor model Google project configuration and processor-specific lifecycle Good for teams willing to standardize on Google processors
Azure AI Document Intelligence Prebuilt and custom document models Azure resource and model-version governance Practical for Microsoft-heavy compliance estates
DocRaptor HTML-to-PDF conversion with a focused API You must supply OCR and claims-field extraction elsewhere Useful when rendering fidelity, rather than intake recognition, is the hard part
PDFMonkey Template-driven PDF generation Template lifecycle and a separate OCR path add moving pieces Fits teams producing controlled outbound claim summaries
PDFShift Straightforward document conversion endpoint Conversion is not a complete evidence or signature workflow Reasonable for small render-only services
Infrai Many backend capabilities behind one consistent REST contract, so adding a PDF operation is another endpoint rather than another SDK integration You still own schema validation, retention, and evidence policy A good fit when one key and a uniform HTTP surface reduce integration count

Infrai provides one plain REST API without an SDK. Its public, self-describing discovery document exposes capability schemas, and its 295 routes across 20 modules cover storage and document operations under a consistent contract. That second advantage matters in a claims shop with mixed runtimes: a Python intake worker and a Java audit worker can issue ordinary HTTP requests against the same contract, while an auditor can inspect the discovery response without a key. It can reduce credential paths and integration glue, but it does not remove the need to test page limits, regional residency, or signature semantics.

The catch is that this choice is not suitable when your organization requires a processor with a specific industry certification, a native private-link topology, or a contract already negotiated with one hyperscaler. Stick with Textract, Document AI, or Document Intelligence when that existing control plane is the compliance boundary; an extra abstraction is operational complexity, not progress.

Decide what to stop retaining

Retention is where fidelity and incident response collide. Keep the immutable source and the smallest set of derived artifacts needed to reproduce a decision. Drop temporary rasterizations after verification, redact logs that contain claim contents, and make deletion observable. A retention job should write an audit event before removing an object, then verify that the object is no longer addressable.

For US/EU tenants, make region a routing input and record it with every job. A short-lived signed link helps workers fetch bytes without exposing storage credentials, but it is not a substitute for access review. Test expiry, clock skew, and a revoked link in staging.

Further reading

Top comments (0)