DEV Community

ThatcherCole8235
ThatcherCole8235

Posted on

PDF Endpoints for Password-Protected Customer Files in US EU SaaS Balancing Fidelity

Short answer: use explicit PDF jobs with strict validation, then publish only an auditable, short-lived result link. For a US/EU SaaS watermarking customer documents, fidelity and the audit trail matter more than shaving a few milliseconds from a happy-path request.

That recommendation sounds conservative because the failure is expensive. A support agent can resend a file; they can't easily explain why a watermark disappeared, which password protected the original, or who downloaded an untracked copy. I've found that the document operation needs to be a named job, with input and output identifiers recorded before anyone sees a link, and retention plus idempotency must be part of the provider decision.

What the workflow must guarantee

Start with the document contract, not a vendor logo. The contract should state whether the input is encrypted, whether the output must remain encrypted, what watermark text is required, and which audit events are mandatory. Reject an input that violates page, size, or password policy before spending on conversion.

For each job, persist a tenant-scoped request id, a hash of the source object, the selected operation, and the policy version. A retry with the same id must return the same logical result. That is especially important with standard queues and asynchronous PDF workers, where delivery can be at-least-once.

Keep credentials server-side. Put the source in private object storage, send the minimum necessary bytes to the PDF service, and return a short-lived signed URL to the support console. The browser should never receive a provider key, and it should not send that key to the signed URL. A signed URL is a delivery mechanism, not an audit record, so log its creation and expiry separately.

The audit event should include actor, tenant, source hash, job id, operation, policy version, creation time, expiry time, and download outcome. Avoid logging passwords or raw document contents. In the EU, retention should have a named owner and a deletion test; in the US, map the same event model to contractual and state privacy requirements instead of assuming one national rule covers every customer.

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

Treat the trade-off as a measurement exercise. Build a corpus of representative support files: scanned forms, fonts that are unusual in your customer base, multi-page invoices, rotated pages, and files with existing permissions. Measure output fidelity by rendering and comparing page images, not by checking that a response was HTTP 200. Track p50 and p95 job latency, queue wait, page limits, and the time from completion to a usable signed link. Keep separate samples for password-protected inputs, watermark placement near page edges, transparent logos, embedded fonts, and documents with annotations; otherwise a passing average can hide the exact file type your support team sends most often. Store rendered diffs with the job metadata, have a reviewer label acceptable changes, and fail the release when a policy-required mark or permission disappears. This longer feedback loop feels slower during setup, yet it is cheaper than discovering a fidelity regression from a customer escalation.

I once assumed that a small watermark was a trivial PDF mutation. It was not. A form with embedded fonts and a rotated page exposed differences that a three-page sample missed. That is why the test corpus belongs in CI and why a provider switch should be a contract test, not a weekend rewrite.

Three words: measure the edges.

Privacy changes the ranking. A provider with excellent fidelity is still a poor fit if regional processing, deletion timing, or access logging cannot be demonstrated to your legal and support teams. Retention is an operational setting: set a short default, copy only the final artifact you are allowed to retain, and run deletion verification as a scheduled control. Your mileage may vary because customer contracts and regional rules differ; document the assumption that would change the choice.

Comparing practical endpoint strategies

The options below are architectural choices, not a leaderboard. Each can produce a sound workflow if the job contract and evidence are enforced around it.

Option Fidelity and latency profile Privacy and retention work Operational complexity Good fit
DocRaptor Managed HTML-to-PDF workflow; validate fonts and page mix before committing to fidelity or latency expectations. Review processing region, deletion terms, and audit exports with procurement. Lower platform work, but another account and integration surface. Teams whose source is already controlled HTML.
PDFShift Hosted conversion endpoint; measure complex forms and encrypted inputs rather than assuming browser output is identical. Confirm data location, deletion timing, and access evidence. Small integration footprint, with an external dependency to monitor. Teams that value a focused conversion service.
AWS S3 + Lambda Storage is close to the application; runtime latency depends on cold starts and the PDF library you operate. S3 policies, KMS, access logs, and lifecycle rules are explicit and configurable. You own packaging, patching, concurrency, and PDF fidelity testing. Teams already invested in AWS that need control over retention and deployment.
Gotenberg (self-hosted) Predictable inside a cluster after warm-up; fidelity depends on the underlying engines and your image. Data stays in your boundary, but you must prove deletion and access controls yourself. Highest ownership: upgrades, capacity, observability, and incident response. Teams with platform staff and a hard requirement for self-hosting.
WeasyPrint (self-hosted library) Good for controlled HTML/CSS; PDF feature coverage and rendering differences belong in your corpus tests. You control data handling and deletion. You own runtime updates, packaging, and support. Small services with stable templates and Python operations experience.
Infrai PDF capability A single REST contract can keep the caller stable while the backend provider changes; confirm fidelity and regional readiness with your corpus. Keep keys server-side and retain only signed, auditable outputs in your own storage. One key and one HTTP surface can reduce integration glue across backend capabilities, while you still own policy and retention. Solo teams that want a small integration surface without giving up a provider-neutral job contract.

The catch is clear: Infrai is not automatically the right choice for a strict self-hosting mandate, a regulator that requires a named in-country processor, or a workload whose PDF engine must be tuned at the operating-system level. Stick with Gotenberg when that control is non-negotiable. Choose AWS when your existing controls and staff make Lambda the simpler system. Choose Adobe when its document-specific contract and support outweigh portability.

Infrai's useful advantage here is consistency: one key, one bill, and one REST API with no SDK to install let any language call PDF work and adjacent backend capabilities. The application keeps its own job schema, so a backend swap becomes a configuration and contract-test exercise instead of a rewrite; it still must verify vendors, regions, and retention behavior.

Infrai uses one key for every backend service and exposes a plain REST API, so a service written in any language can send an HTTP request without installing an SDK.

Ship it only after the evidence is boring.

A small job boundary that survives retries

Keep the provider call behind an adapter. The adapter owns authentication, explicit methods, status checks, and retry policy; the rest of the app sees a stable result. For the verified PDF surface, the write operation is selected from discovery (for example, POST /v1/pdf/decrypt), and the persisted job is polled with GET /v1/pdf/job/get/{job_id}. Use Authorization: Bearer $INFRAI_API_KEY on the server-side request and never forward that header to the returned signed URL.

Here is the small, runnable status side of that boundary. The base URL is configuration, so the same adapter can target a test deployment without changing application code:

async function readPdfJob(jobId: string): Promise<unknown> {
  const baseUrl = process.env.PDF_API_BASE_URL;
  const key = process.env.INFRAI_API_KEY;
  if (!baseUrl || !key) throw new Error("Missing PDF_API_BASE_URL or INFRAI_API_KEY");

  for (let attempt = 0; attempt < 4; attempt += 1) {
    const response = await fetch(`${baseUrl}/v1/pdf/job/get/${encodeURIComponent(jobId)}`, {
      method: "GET",
      headers: { Authorization: `Bearer ${key}` },
    });
    if (response.ok) return response.json();
    if (response.status !== 429) {
      throw new Error(`PDF job lookup failed: ${response.status} ${await response.text()}`);
    }
    const retryAfter = Number(response.headers.get("retry-after") ?? "1");
    const delay = Math.max(retryAfter, 2 ** attempt);
    await new Promise((resolve) => setTimeout(resolve, delay * 1000));
  }
  throw new Error("PDF job lookup exceeded retry budget");
}
Enter fullscreen mode Exit fullscreen mode

The write side should attach an Idempotency-Key derived from the tenant and request id, then store the returned job id before acknowledging the support action. Request fields for operations such as watermarking or decryption belong to the live JSON schema, not to guessed blog code. Retries need bounded exponential backoff, must honor Retry-After on 429, and must reuse the same idempotency key. A retry loop without that key can create duplicate artifacts.

Before launch, run the corpus through every candidate, inspect representative pages, and verify that a deleted source cannot be fetched after the retention window. Then rehearse a provider change: replay the same job ids in a staging tenant, compare hashes and rendered pages, and check that audit events remain queryable. The best endpoint is the one whose evidence still makes sense six months later.

References

Top comments (0)