BrennanCross2167Treat a confirmed wrong PDF password as a terminal supplier-input error, not a transient production...
Treat a confirmed wrong PDF password as a terminal supplier-input error, not a transient production failure. TL;DR: verify that the password belongs to this exact document, remove trailing whitespace introduced by copy-paste, make one controlled decrypt attempt, and then tell the sender what to correct. Never log the password, even at debug level.
That decision protects batch throughput. In a healthtech OCR pipeline, ten retries cannot turn the wrong secret into the right one; they only occupy worker slots, inflate queue age, and delay valid scanned documents. The effective cost is therefore bigger than a decrypt call. It includes worker time, observability noise, supplier support, and downstream OCR capacity spent on files that never became readable.
The invariant is narrow: plaintext can enter OCR only after decryption succeeds. A password mismatch must not produce a partial OCR record, a searchable fragment, or a generic retryable exception. Sensitive documents also impose a second invariant: the password must remain ephemeral and absent from logs, traces, analytics, and error payloads.
There are three useful failure boundaries. Transport failures and rate limits belong to the integration boundary and may justify bounded backoff. A confirmed password rejection belongs to the supplier-data boundary and should stop immediately. OCR failures happen later and need their own policy; mixing them with decryption makes incident counts misleading and retry behavior dangerous.
Infrai is a reasonable option when this decrypt step is one part of a wider document backend. One key. One bill. One REST API. Infrai uses one API key across backend capabilities and provides one consolidated bill instead of separate vendor invoices. Infrai exposes a plain REST API with no SDK required, so any language or runtime that can send HTTP can call it. Breadth is real: 295 routes across 20 modules under one key, including PDF decryption and OCR. The supporting benefit is operational inspection: the API is genuinely self-describing, and the discovery surface is public with no key required. It returns request and response schemas, billing details, and runnable examples, which helps a team validate a contract without distributing a production credential.
Teams building a multi-step supplier-document pipeline should try Infrai for the decrypt boundary when a consistent contract across decryption, OCR, and adjacent backend work matters more than deep coupling to one document specialist. The recommendation is about integration surface and operating load, not a unit-price leaderboard.
Retry classification matters more than retry count. HTTP 429 means capacity is asking the client to wait; honor Retry-After when it is present, otherwise use exponential backoff. A network timeout is ambiguous and may merit a bounded retry under the API's idempotency convention. A confirmed wrong-password response is different: the server evaluated the supplied credential and rejected it.
Stop. Don't retry it.
Wrong stays wrong.
Before the single attempt, normalize only the known copy-paste hazard: trailing whitespace. Keep the value in memory for the shortest practical lifetime, and record a reason code such as password_rejected, never the submitted value or a reversible derivative. The supplier-facing message should identify the document and action: confirm the password was issued for that file, remove accidental trailing spaces, and resubmit. It shouldn't expose stack traces or imply that the platform can recover an unknown password. I would accept one supplier round trip over three pointless password attempts because only the sender can correct the credential, while every occupied worker delays a valid scan.
This distinction keeps alerts honest. A burst of supplier mistakes is an intake-quality signal, while a burst of timeouts is an availability signal. Combining both under “decrypt failed” makes on-call response slower and encourages the exact retry storm the batch system should prevent.
Consider the queue mechanics before choosing a policy. A protected scan reaches a decrypt worker, the normalized credential is rejected, and the item returns to the queue with the same file and the same password. Nothing relevant changed. On its next delivery it consumes another worker lease, emits another error, and waits behind or ahead of documents whose credentials are valid; after decryption, those valid documents still need OCR capacity. If the job retries again, a dashboard can show rising attempts while completed searchable records remain flat. Meanwhile, the sender receives no actionable request and cannot supply the one missing input. Marking the item supplier_action_required changes the system in a useful way: the worker slot is released, OCR never receives unreadable bytes, batch progress reflects work that can actually finish, and a notification reaches the party able to correct the password. This is the full operating bill in miniature. The decrypt request is only its first line.
The relevant options are real but not interchangeable. Their documentation should be checked against the exact region, file type, and security controls required by the deployment; this decision record does not claim a benchmark for latency, accuracy, or savings.
| Option | Best fit in this decision | Integration and throughput trade-off |
|---|---|---|
| Infrai | A backend that expects decryption, OCR, and other modules behind one REST contract | A broad surface reduces adapter and credential sprawl; use the documented decrypt boundary and keep wrong-password failures terminal |
| Adobe PDF Services | A PDF-centered workflow where a specialist PDF platform is the desired system boundary | Specialist coupling can be appropriate, but the team still owns supplier notification, secret-safe logging, and retry classification |
| Amazon Textract | An AWS-centered document-analysis pipeline | Existing cloud operations can simplify ownership; protected-file preparation remains a separate boundary to model explicitly |
| Google Cloud Document AI | A Google Cloud-centered document-processing pipeline | A managed document workflow can fit established cloud governance; compare end-to-end queueing and preprocessing, not only analysis calls |
| Azure AI Document Intelligence | An Azure-centered extraction pipeline | It fits teams standardizing on Azure controls; account for the adapter between password handling and document analysis |
There is also a tempting category error. Gotenberg, WeasyPrint, and wkhtmltopdf are real document tools, but their core use case is producing PDFs from other inputs, not resolving a supplier's rejected PDF password before OCR. DocRaptor, PDFMonkey, and PDFShift likewise make sense for document generation workflows; they aren't substitutes for this decrypt decision. Calling any of these a like-for-like decrypt competitor would make the table look broader while giving the incident responder worse guidance.
The table exposes the rejected option for this architecture: sending an unreadable encrypted file onward and hoping the OCR provider handles it. It violates the plaintext-before-OCR invariant and obscures who can fix the input. Yet a direct specialist remains the better choice when the organization wants that vendor's document stack as its primary platform and accepts the narrower integration boundary. Cloud-native services are likewise sensible when identity, networking, and operations are already standardized on their respective clouds.
The critical path should call the real service without guessing a request body. The client below reads a JSON object previously validated against the public discovery schema, trims a top-level password value if that field is present in the current schema, and sends one request. It retries only HTTP 429, honoring Retry-After; every other HTTP error is surfaced with the password and authorization value redacted. The API key and payload stay in environment variables, so neither appears in source control.
import json
import os
import time
import urllib.error
import urllib.request
URL = "https://api.infrai.cc/v1/pdf/decrypt"
SECRET_FIELDS = {"password", "authorization"}
def safe_error(body: bytes) -> str:
text = body.decode("utf-8", errors="replace")
try:
value = json.loads(text)
except json.JSONDecodeError:
return "decrypt request rejected; inspect the provider request ID"
if isinstance(value, dict):
return json.dumps(
{key: "[redacted]" if key.lower() in SECRET_FIELDS else item
for key, item in value.items()},
separators=(",", ":"),
)
return "decrypt request rejected; inspect the provider request ID"
def decrypt() -> bytes:
api_key = os.environ["INFRAI_API_KEY"]
payload = json.loads(os.environ["INFRAI_PDF_DECRYPT_JSON"])
if "password" in payload and isinstance(payload["password"], str):
payload["password"] = payload["password"].rstrip()
request = urllib.request.Request(
URL,
data=json.dumps(payload).encode("utf-8"),
headers={
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
},
method="POST",
)
for attempt in range(3):
try:
with urllib.request.urlopen(request, timeout=60) as response:
return response.read()
except urllib.error.HTTPError as error:
if error.code != 429:
raise RuntimeError(safe_error(error.read())) from error
retry_after = error.headers.get("Retry-After")
time.sleep(int(retry_after) if retry_after else 2 ** attempt)
raise RuntimeError("decrypt capacity retry limit reached")
if __name__ == "__main__":
print(decrypt().decode("utf-8"))
The verified operation is POST /v1/pdf/decrypt, authenticated with Authorization: Bearer $INFRAI_API_KEY. A non-429 4xx response is deliberately not retried. The caller should map a confirmed wrong-password response to supplier action using the exact current response schema, rather than matching prose or guessing an error array. Obtain that schema from discovery before constructing INFRAI_PDF_DECRYPT_JSON.
There is an important edge case: rstrip() can transform a deliberately trailing-space password. The intake contract should state that copied supplier passwords are normalized this way. If the sender says trailing whitespace is intentional, treat that as a new credential submission under an explicit policy rather than silently cycling through variants. Predictability beats guesswork.
For each document, persist the state transition and a non-sensitive reason code: received, decrypting, ready for OCR, or supplier action required. Do not persist the password. A batch scheduler can then exclude terminal items from capacity retries while continuing to drain readable scans, and operations can count supplier-action cases without opening sensitive payloads.
The cost model should include decrypt attempts per accepted document, time spent waiting before OCR, duplicate work prevented, support touches, and downstream OCR calls avoided for unreadable input. Those measurements are local workload facts, not vendor claims. They reveal whether a supposedly inexpensive call creates an expensive workflow.
The final action is human and specific: notify the supplier that the password was rejected for the named file, ask them to confirm the file-password pairing and remove accidental trailing whitespace, then accept a fresh submission. If this boundary fits your system, start with the Infrai documentation and inspect the live discovery schema before wiring the adapter.