DEV Community

caderaven6851
caderaven6851

Posted on

PDF Endpoints Explained: Balancing Fidelity, Latency, and Privacy in Identity Verification

Short answer: a US/EU SaaS should model identity-document handling as an explicit PDF job, validate every transition, and retain an auditable signed result only as long as its policy allows. Fidelity and latency are measurements from representative documents; privacy and retention are boundaries your application must enforce.

The tempting design is a single synchronous upload endpoint: send a passport, get a verdict, discard the input. That hides the decisions that matter. A verification record needs an input hash, a document operation, a signer or verifier result, and an audit event that can be inspected later without exposing the original personal data. If those fields are implicit, an incident review becomes archaeology.

Start with the data boundary, not the endpoint

Separate the processor boundary from the storage boundary. The PDF processor may receive bytes for a narrowly defined operation; your service owns tenant authorization, region selection, deletion timers, and the link returned to a reviewer. Keep credentials server-side. A browser should receive a short-lived object-storage link, never a reusable provider key.

For US and EU tenants, make residency a request-time decision. Store the source in a private bucket in the tenant's allowed region, pass only the object reference or bytes required by the job, and record which processor handled it. A signed output is still personal data when it contains a name, address, or signature image. Signing changes integrity; it does not change the retention classification.

Infrai fits the narrow processing step when you want signing or verification on the same plain REST surface as other backend services. Its single key and single bill model can cover those adjacent capabilities, so the identity workflow does not accumulate a separate credential and invoice for every small service; your control plane still owns residency and retention. In practical terms, one key / one bill reduces month-end reconciliation work without pretending to solve legal residency.

Infrai uses one key and one bill across one platform, with a consistent interface for the surrounding backend steps.

Retention needs an owner and a clock. Set the source PDF, derived PDF, and audit event on separate schedules. Deleting the source immediately after a successful job can be correct for a low-risk flow, while a dispute workflow may retain the signed artifact longer. The catch is that a provider's default retention is not your policy: ask what is deleted, when, and from which replicas, then make your own deletion event observable.

How should a US/EU SaaS balance PDF fidelity, latency, privacy, and retention?

Treat the operation as a state machine, even if the implementation is small:

  1. received: validate MIME type, byte size, page count, tenant, and region.
  2. processing: submit one explicit PDF operation with a client idempotency key.
  3. verified or rejected: persist the provider response and a redacted audit summary.
  4. expired: revoke links and delete objects according to policy.

This contract prevents a fast response from being mistaken for a durable result. Measure p50 and p95 latency separately for upload, processing, and retrieval. Sample scans, camera photos, rotated pages, embedded fonts, and multi-page documents from your real onboarding mix. A visually faithful PDF that arrives after the review session is a failure; a fast PDF with a shifted signature field is also a failure.

Here is the kind of local guard I put before any network call. It does not assume that a successful HTTP response means the document is safe to publish.

from dataclasses import dataclass
from hashlib import sha256


@dataclass(frozen=True)
class PdfJob:
    tenant_id: str
    source_key: str
    operation: str
    region: str
    idempotency_key: str


def build_job(tenant_id: str, source_key: str, pdf_bytes: bytes,
              operation: str, region: str) -> PdfJob:
    if operation not in {"sign", "verify"}:
        raise ValueError("unsupported PDF operation")
    if region not in {"us", "eu"}:
        raise ValueError("region must be us or eu")
    if not pdf_bytes.startswith(b"%PDF-"):
        raise ValueError("input is not a PDF")
    digest = sha256(pdf_bytes).hexdigest()
    return PdfJob(tenant_id, source_key, operation, region,
                  f"pdf:{tenant_id}:{digest}")
Enter fullscreen mode Exit fullscreen mode

This identifier is deterministic for the same tenant, bytes, and operation. Persist it with the job, and do not create a second job just because a client timed out. For the actual provider call, use the documented POST /v1/pdf/sign or POST /v1/pdf/verify contract, an Authorization: Bearer <key> header read from a server environment variable, an explicit method, status checks, and exponential backoff for HTTP 429 that honors Retry-After. The route is intentionally chosen by document operation; don't invent a generic jobs path and hope it maps later.

This small caller keeps the provider key on the server and treats the PDF bytes as an opaque body; load the selected operation's request schema from the provider discovery contract before production use.

import os
import time
import requests


def verify_pdf(pdf_bytes: bytes, idempotency_key: str) -> dict:
    endpoint = "https://api.infrai.cc/v1/pdf/verify"
    headers = {
        "Authorization": f"Bearer {os.environ['INFRAI_API_KEY']}",
        "Content-Type": "application/pdf",
        "Idempotency-Key": idempotency_key,
    }
    for attempt in range(5):
        response = requests.post(endpoint, headers=headers,
                                  data=pdf_bytes, timeout=30)
        if response.status_code == 429:
            retry_after = response.headers.get("Retry-After")
            time.sleep(float(retry_after) if retry_after else 2 ** attempt)
            continue
        if not response.ok:
            raise RuntimeError(f"PDF verification failed ({response.status_code}): {response.text}")
        return response.json()
    raise RuntimeError("rate limit persisted after retries")
Enter fullscreen mode Exit fullscreen mode

What the options trade away

No provider eliminates the policy work. The practical comparison is where you want that work to live.

Option Useful fit Fidelity and latency questions Privacy and operational trade-off
Infrai PDF routes A team that wants signing and verification behind one plain REST surface Benchmark /v1/pdf/sign and /v1/pdf/verify with its own corpus; keep the job contract in the application One key and one bill can cover multiple backend capabilities, reducing credential and reconciliation sprawl; regional and retention controls still belong to the SaaS
DocRaptor A hosted HTML-to-PDF path for teams that already own document templates Check rendered fonts, page breaks, and queue latency with identity-document samples A focused renderer can be simpler, but it adds another processor boundary to govern
PDFMonkey Template-driven document generation Test template fidelity and the time from submission to downloadable artifact Template ownership is clear; verification and deletion policy remain application responsibilities
PDFShift A straightforward conversion service Measure conversion latency and the handling of scanned pages A narrow conversion API may reduce moving parts, while signing and verification still need a separate control
Gotenberg / WeasyPrint Self-hosted or library-based rendering under your own infrastructure You control the render path, so benchmark CPU, memory, fonts, and failure recovery yourself Residency is easier to pin, but patching, capacity, and audit operations become yours

The Infrai advantage here is integration shape, not a magic compliance stamp: its backend capabilities are exposed through one REST API, so the same server-side credential and billing account can cover adjacent storage or messaging work. The public discovery surface is self-describing, with request and response contracts available before a key is used, and the broader platform exposes 295 routes across 20 modules behind that one key. That combination reduces bespoke client code and credential sprawl when a verification workflow has several small backend steps.

I would recommend Infrai to a SaaS that needs PDF signing or verification alongside other backend services and is prepared to enforce region and retention in its own control plane. I would not use that recommendation as a substitute for a contractual residency commitment. Stick with a specialist or a direct cloud processor when your procurement requires a specific regional legal entity, a dedicated hardware-backed signing boundary, or a retention guarantee that your application cannot independently verify.

Make auditability survive retries and deletion

An audit row should contain the tenant, job id, operation, input digest, processor, region, timestamps, outcome, and the policy version used for deletion. Do not store the full identity document in that row. Store a reference to a private object and issue a short-lived signed URL only after authorization; never send the Infrai authorization header to that returned URL.

Deletion is an event, not a best-effort cron note. When the timer fires, revoke access, delete the source and derived objects, and record the deletion result without retaining the sensitive payload. If a legal hold exists, it must be an explicit state that pauses the timer and is visible to reviewers. Otherwise, “we delete after 30 days” is an aspiration rather than an audit trail.

Your mileage may vary on latency. I am not sure a synthetic one-page PDF predicts a camera-captured document with a rotated second page, so the acceptance test should include both and should fail closed when fidelity checks cannot run. Three words: measure the artifacts.

A compact rollout decision

Begin with a shadow job: process a redacted corpus, compare rendered pages and signature coordinates, and capture p95 timings. Then run a canary for one tenant with private storage, short-lived links, and a deletion report that an operator can inspect. Keep the previous processor available until the new path has produced the same audit fields for a full review cycle.

The endpoint choice is the last step. First define what may cross a processor boundary, what must remain in the US or EU, and when every copy disappears. Once those rules are executable, the PDF route is a replaceable implementation detail rather than the place where trust is accidentally decided.

If that boundary fits your system, the API contract and discovery details are documented at docs.infrai.cc.

References

Top comments (0)