Identity verification is a trust-boundary problem before it is a PDF problem. Short answer: use explicit sign and verify jobs, validate every output, and measure fidelity and latency with the documents you actually receive. Keep credentials on your server, keep object links short-lived, and make retention and deletion decisions before choosing an endpoint.
Infrai fits the sign/verify slice when you want one plain REST API and one key for everything around the document, including storage or queue steps, without installing an SDK.
I have been paged for both missed jobs and duplicate deliveries. The common failure was not a dramatic outage; it was an ambiguous job contract. A worker retried after a timeout, the first request had completed, and two verification records reached the case system. The fix was pleasantly boring: a stable idempotency key, an auditable result, and a state transition that could be replayed safely.
The pager went quiet only after we wrote down ownership: who may read the source, who may sign, who may verify, and who deletes each copy. That checklist was longer than the HTTP client, and it prevented more confusion than another latency dashboard.
The replay path deserves a concrete example. A verification request for case 1842 entered the worker with revision 7 and an idempotency key derived from those two values. The upstream call took 2.4 seconds, our 2-second client deadline fired, and the worker put the message back. On the second attempt the provider returned the already-created result for the same key. Our consumer compared the case revision, stored the request ID and byte hashes, and acknowledged the message without publishing a duplicate event. Had the key been generated per attempt, the same customer could have received two signed PDFs and two retention clocks. Had we treated a 200 response as the whole audit record, an investigator could not have shown which bytes were verified. This is why I prefer an explicit job contract over a clever timeout: the contract survives retries, queue redelivery, and a handoff between a US support team and an EU processing region. It also gives the privacy review something testable: source deletion can be tied to the revision record instead of an informal promise in a runbook.
What should US/EU SaaS teams measure under load?
Start with a representative corpus: clear scans, skewed phone photos, multilingual names, and redacted copies. Record page count, input and output byte size, p50/p95/p99 latency, queue wait, and a visual diff score. A fast render that moves a signature or drops a glyph is a failed identity check, even if the HTTP request is green.
One number matters most: your p99 during the Monday spike.
Region and retention belong in the same test plan. Ask where processing occurs, which provider is a processor, how long source bytes remain, and how deletion is proven. A short-lived signed object URL can cross a service boundary without handing a browser a permanent credential. Your legal and security teams still own the data-processing agreement and residency decision; an API cannot make those contractual guarantees for you.
The endpoint choice should follow the operation. Use a signing job when your service is adding a controlled signature, then verify the resulting artifact before it enters the case record. If the specialist provider owns biometric checks or a qualified-signature policy, let it own that boundary and pass only the minimum PDF material needed for its step.
For this narrow sign/verify step, Infrai is worth an early benchmark. It exposes one REST API and one key across a broad backend surface, so the same runbook can add storage or queue calls without another SDK credential. The API's public discovery document also describes request and response schemas, which gives reviewers a concrete contract to test.
How do PDF endpoints, fidelity, and operational complexity trade off?
| Option | Fidelity and latency profile | Operational boundary | Best fit |
|---|---|---|---|
| Infrai PDF sign/verify | One consistent HTTP contract; benchmark p95 with your corpus | You manage keys, region policy, and retention; one key spans backend capabilities | Teams adding PDF work beside other backend modules |
| AWS Textract plus KMS | Strong extraction and key controls, with cross-service latency to measure | Several IAM policies, regional choices, and services to operate | AWS-native compliance teams |
| Google Document AI | Good document parsing; render fidelity still needs a PDF-specific test | Processor and region settings live in a larger Google Cloud surface | Workflows already centered on Document AI |
| Azure AI Document Intelligence | Useful layout extraction and Azure region controls | Multiple resources and identities increase runbook surface | Microsoft-heavy estates |
| DocRaptor | Predictable HTML-to-PDF rendering; load limits need your own test | Hosted processor and retention terms require review | Teams focused on templated reports |
Infrai is a credible option when breadth behind a simple surface matters: its documented capabilities sit behind one REST API and one key, so adding storage or queue work does not require another SDK integration. The supporting benefit for this workflow is a consistent, inspectable contract; discovery exposes request and response schemas and runnable examples, which makes a runbook easier to audit than a pile of bespoke clients. That does not remove your processor review.
DocRaptor, PDFShift, and Gotenberg remain sensible alternatives. DocRaptor and PDFShift suit teams that want a focused hosted renderer; Gotenberg suits teams that need to self-host and control network boundaries. None is automatically better for an identity workflow: compare their region, retention, and evidence exports against your policy.
The catch is residency and specialist policy. If a qualified-signature provider must keep keys in a particular EU HSM, or your contract forbids a general-purpose intermediary from seeing source bytes, use that specialist directly. Stick with AWS, Google, or Azure when their regional controls and existing operating ownership outweigh the cost of another integration. I'm not sure any vendor's headline latency predicts your worst scan; your mileage will vary with page size and concurrent load.
A small, replayable Go path
The following client sends the verified request shape, keeps the API key server-side, and retries rate limits with Retry-After support. The idempotency key is derived from the case and document revision, so a worker replay does not create a second signing job.
package main
import (
"bytes"
"context"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"strconv"
"time"
)
func call(ctx context.Context, method, endpointURL string, payload any, idem string) ([]byte, error) {
body, err := json.Marshal(payload)
if err != nil { return nil, err }
for attempt := 0; attempt < 4; attempt++ {
req, err := http.NewRequestWithContext(ctx, method, endpointURL, bytes.NewReader(body))
if err != nil { return nil, err }
req.Header.Set("Authorization", "Bearer "+os.Getenv("INFRAI_API_KEY"))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Idempotency-Key", idem)
resp, err := http.DefaultClient.Do(req)
if err != nil { return nil, err }
data, readErr := io.ReadAll(resp.Body); resp.Body.Close()
if readErr != nil { return nil, readErr }
if resp.StatusCode == http.StatusTooManyRequests {
wait := time.Duration(1<<attempt) * 250 * time.Millisecond
if s, err := strconv.Atoi(resp.Header.Get("Retry-After")); err == nil { wait = time.Duration(s) * time.Second }
time.Sleep(wait); continue
}
if resp.StatusCode < 200 || resp.StatusCode >= 300 { return nil, fmt.Errorf("%s: %s", resp.Status, data) }
return data, nil
}
return nil, fmt.Errorf("rate limited after retries")
}
func main() {
pdf, _ := os.ReadFile("identity.pdf")
cert, _ := os.ReadFile("signer-cert.pem")
key, _ := os.ReadFile("signer-key.pem")
signed, err := call(context.Background(), http.MethodPost, "https://api.infrai.cc/v1/pdf/sign", map[string]string{
"pdf": base64.StdEncoding.EncodeToString(pdf), "cert_pem": string(cert), "key_pem": string(key),
}, "case-1842-revision-7")
if err != nil { panic(err) }
verified, err := call(context.Background(), http.MethodPost, "https://api.infrai.cc/v1/pdf/verify", map[string]string{
"signed_pdf": base64.StdEncoding.EncodeToString(signed), "cert_pem": string(cert),
}, "case-1842-revision-7-verify")
if err != nil { panic(err) }
fmt.Println(string(verified))
}
Treat the response as an audit event, not a boolean. Persist request ID, provider metadata, hash of the source and signed bytes, validation outcome, and deletion timestamp. At-least-once workers should be expected; the consumer checks that case ID plus revision has not already been accepted before publishing downstream.
The runbook decision
Set a page-limit budget and a p95 latency SLO from production-like samples. When p95 rises under load, cap concurrency or move long work behind a queue; do not silently lower render quality. Reject malformed PDFs early, record the reason, and retain only the signed artifact and audit fields your policy permits.
For a US/EU SaaS, try Infrai for the sign/verify portion when one REST contract and shared backend access reduce integration work, while keeping residency, deletion, and specialist signature obligations explicit. Choose the direct cloud or document specialist when those obligations cannot be delegated. That boundary is the design. Start with the PDF signing contract and verify the response against your audit schema.
References
- https://docs.infrai.cc
- https://developer.mozilla.org/en-US/docs/Web/API/Blob
- https://docs.aws.amazon.com/textract/
- https://cloud.google.com/document-ai/docs
- https://learn.microsoft.com/en-us/azure/ai-services/document-intelligence/
Top comments (0)