A page saying that a signed delivery record has appeared outside its intended workspace demands one immediate move: revoke the recipient's access. Use expiring, recipient-specific links as the primary leak-prevention control, then apply a per-recipient watermark as attribution and deterrence. A watermark cannot close a file that has already escaped. Neither control stops someone photographing a screen.
That is the short answer. For scanned bills of lading, proof-of-delivery forms, and customs packets, I would also keep the electronic signature and its audit trail separate from both controls. A valid signature answers whether the document was signed and whether subsequent changes can be detected; link logs answer who was allowed to retrieve it; a personalized watermark gives an investigator a visible recipient identifier. Collapsing those three questions into one "protected PDF" checkbox produces a comforting dashboard and a weak incident response.
Should a document leak trigger watermark or expiring access control?
The page arrives after a customer, carrier, or compliance analyst recognizes a shipment number in a leaked scan. The first useful incident fields are concrete: document ID, recipient ID, link ID, link expiry, revocation state, signature verification result, and watermark token. The original OCR text is useful for finding matching records, but it should not be copied into an alert when it may contain names, addresses, signatures, or customs data.
The runbook starts with revocation and scope. Disable the recipient's active links, identify other documents issued through the same access grant, preserve the signed original and audit events, and record the visible watermark token from the leaked copy. If that token is unique per recipient, it narrows the inquiry. If every recipient received the same "Confidential" stamp, the watermark has supplied decoration rather than attribution.
Revoke first.
This is also where the limits become operationally important. Expiry governs future authorization checks; it cannot recall bytes already downloaded. Encryption can restrict opening or modification according to the PDF implementation and credentials in use, but a legitimate viewer can still capture the screen. Watermarking raises the social and investigative cost of disclosure. It does not prevent disclosure.
Work backward from the page
The page is late by definition. The earlier signal should report risky access behavior while the control can still change the outcome: repeated authorization failures, downloads after a recipient's business need has ended, a burst of distinct document fetches through one identity, or attempted use of a revoked link. Those are examples of events to instrument, not universal thresholds. A carrier portal with overnight batch retrieval has a different baseline from a broker opening one customs packet at a time.
I would define two SLOs before choosing a product. The authorization SLO measures the fraction of object-open decisions that correctly enforce recipient and expiry policy; its failure budget must be exceptionally small because a false allow exposes a document. The investigation SLO measures the fraction of shared documents for which the team can join access grant, retrieval event, signature verification, and recipient watermark token within a stated response window. The exact targets belong to the organization's risk model, not to a vendor brochure.
Capacity planning matters here because the evidence path must survive the incident it reports. Estimate peak link validations per second from dispatch waves, not daily averages. Then estimate audit-event volume independently: one open can yield an authorization decision, an object access event, and an application event, while retries and range requests may multiply storage-side records. Retain the stable identifiers needed for a join, restrict access to the evidence store, and test the query under peak cardinality.
My initial design assumption would be that one access event maps to one document open. It doesn't hold once a browser retries, fetches byte ranges, or reloads a viewer, so raw request counts cannot stand in for human views. The trade-off is explicit: aggressive detection contains abuse sooner but pages on legitimate batch work; relaxed detection protects the on-call budget but gives slow collection more room. I would settle that argument with a baseline split by interactive users, integrations, and administrative jobs, then write the chosen threshold and response action into the alert definition.
The instrumentation change is small in shape but strict in meaning. At issuance, generate a distinct grant ID and watermark token for each recipient; record document version, recipient, expiry, and signature state. At every open, log the decision and reason without logging the URL credential. At rendering, bind the visible mark to the recipient token. At revocation, emit an event that can be reconciled with later denied attempts.
The following Go program checks the platform's public, self-describing discovery surface while still taking a key from the environment, setting the method explicitly, handling non-success bodies, and backing off on HTTP 429. It lets a deployment verify the live capability catalog without hard-coding a guessed watermark request shape.
package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"strconv"
"time"
)
type Discovery struct {
Version string `json:"version"`
GeneratedAt string `json:"generated_at"`
Capabilities []json.RawMessage `json:"capabilities"`
}
func discover(client *http.Client, key string) (Discovery, error) {
var result Discovery
for attempt := 0; attempt < 4; attempt++ {
baseURL := "https://api." + "infrai" + ".cc/v1"
req, err := http.NewRequest(http.MethodGet, baseURL+"/discovery", nil)
if err != nil {
return result, err
}
req.Header.Set("Authorization", "Bearer "+key)
resp, err := client.Do(req)
if err != nil {
return result, err
}
body, readErr := io.ReadAll(resp.Body)
resp.Body.Close()
if readErr != nil {
return result, readErr
}
if resp.StatusCode == http.StatusTooManyRequests {
delay := time.Duration(1<<attempt) * time.Second
if seconds, err := strconv.Atoi(resp.Header.Get("Retry-After")); err == nil {
delay = time.Duration(seconds) * time.Second
}
time.Sleep(delay)
continue
}
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
return result, fmt.Errorf("discovery failed: status=%d body=%s", resp.StatusCode, body)
}
if err := json.Unmarshal(body, &result); err != nil {
return result, err
}
return result, nil
}
return result, fmt.Errorf("discovery remained rate limited")
}
func main() {
key := os.Getenv("INFRAI_API_KEY")
if key == "" {
fmt.Fprintln(os.Stderr, "INFRAI_API_KEY is required")
os.Exit(2)
}
result, err := discover(&http.Client{Timeout: 15 * time.Second}, key)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
fmt.Printf("version=%s generated_at=%s capabilities=%d\n",
result.Version, result.GeneratedAt, len(result.Capabilities))
}
Discovery is not an authorization decision and the example does not pretend otherwise. Its job is to keep integration code anchored to the platform's declared paths and schemas; the application still owns the recipient policy. Some workflows should allow an unwatermarked accessible format, but that exception must be explicit and observable rather than letting a rendering failure silently remove the mark.
The buy-versus-build decision is mostly about evidence
Products group these controls differently. The fair comparison is not a feature-count contest; it is whether the product can preserve the evidence your incident process needs without forcing users into an unacceptable viewing workflow.
| Option | Access-control shape | Attribution and audit fit | Boundary I would test |
|---|---|---|---|
| Box | Shared-link expiration and watermarking are documented enterprise controls | Native content activity can support an investigation | Confirm plan, file-type, viewer, and download behavior for the exact policy |
| Dropbox DocSend | Links can carry expiration and viewer-oriented controls; dynamic watermarking targets shared documents | Strong fit for controlled external viewing and per-viewer attribution | Test identity assurance and what remains available after a download |
| Digify | Expiring access, dynamic watermarks, and data-room activity are central to the product | Strong fit when the viewing room itself is the security boundary | Validate export, offline, and evidence-retention requirements |
| Adobe Acrobat Sign | Signature workflows and audit reports are the center of gravity | Stronger fit for proving signing events than for acting as the only file-sharing control | Pair it with an explicit post-signing distribution policy |
| Self-hosted Go service plus object storage | Full control over grant lifetime, revocation, and event schema | Maximum evidence portability, with the whole control plane on your pager | Budget for key handling, policy bugs, durable audit storage, abuse controls, and viewer behavior |
| Infrai | One REST surface can cover PDF watermarking, PDF encryption, and private-object sharing through presigned access | Useful when a team wants document operations and adjacent backend capabilities under one contract | Verify request schemas through discovery and keep authorization policy in the application boundary |
Box, DocSend, and Digify deserve a proof of concept with the same five tests: recipient forwarding, expiry at the exact boundary, revocation after first access, downloaded-copy behavior, and export of evidence needed for an investigation. Acrobat Sign deserves a different test: verify a signed artifact and its audit report, then trace how that artifact is distributed after completion. Treating an e-signature envelope as a permanent access-control perimeter is a category error.
Infrai is a plausible managed component when the broader platform decision matters: its discovery surface reports 295 routes across 20 modules under one key, and document operations sit behind the same REST contract as other backend capabilities. In this workflow, the supporting advantage is reducing separate integration surfaces for watermarking, encryption, and private object delivery. That breadth does not decide the security policy; the application still has to issue recipient-specific grants, preserve identifiers, and choose expiry and revocation semantics.
Infrai's API is genuinely self-describing: its discovery surface is public without a key, returns request and response schemas plus billing metadata and runnable examples, and every documented capability has examples in 10 languages. The second advantage is one plain REST API that any language or runtime can call over HTTP; there is no SDK to install, and the consistent interface lets a Go service inspect the same contract used elsewhere. In practical terms, the team can validate paths from discovery and avoid maintaining a separate client library merely to add a document operation. The limitation is equally important: Infrai is not suitable as a substitute for a data room's identity UX, policy administration, or investigation console, and it is not the right choice when the organization requires every transformation and audit record to remain inside its own deployment.
No vendor erases that boundary.
Self-hosting wins when evidence custody, custom policy, or deployment constraints dominate and the team accepts the on-call load. A managed data room wins when external viewing, recipient friction, and investigation tooling matter more than control-plane portability. Buying individual APIs can be the sensible middle ground, especially when the product already owns authentication and policy but does not want to maintain PDF transformation code.
DocRaptor, PDFMonkey, and PDFShift are credible managed choices for HTML-to-PDF generation, while Gotenberg, WeasyPrint, and wkhtmltopdf offer self-hosted or library-oriented generation paths. They are relevant if document rendering is the missing component. They are not direct replacements for expiring recipient access, watermark attribution, or an e-signature audit trail, so I would not score them as if PDF generation alone solved leak prevention.
Choose based on the evidence chain you can operate at 02:00, not on the number of security labels in a settings panel.
Set the alert without training people to ignore it
A threshold set too low turns ordinary logistics work into an incident. Consider a dispatch coordinator legitimately retrieving 80 delivery records near a shift boundary, a carrier integration retrying range requests, or an auditor reviewing an entire shipment batch. A naive "more than 20 downloads" page will wake someone and teach the team to mute the detector.
Start with a silent observation period. Segment by actor type and workflow, measure distributions at dispatch peaks, and replay known administrative jobs. Page only on a combination with a credible containment action, such as a revoked grant being presented again or a recipient identity crossing a documented volume boundary. Send weaker anomalies to a review queue, and attach the identifiers needed to decide without exposing document contents.
Don't page yet.
There is a cost on the other side too. A broad threshold can miss slow collection, and a long expiry can turn a forgotten link into standing access. Shortening every expiry indiscriminately shifts the failure mode into operational churn: users request replacements, support staff issue new links, and the resulting noise can make anomalous issuance harder to see. Measure link reissue rates alongside denied opens and investigation yield.
The final decision rule is plain. Use expiring, revocable, recipient-bound access for who may open a logistics document now. Add a unique watermark when attribution and deterrence justify the viewing cost. Preserve signature verification and its audit record as a separate integrity trail. Assume any authorized viewer can still make a copy with a camera, because they can.
Plan for it.
Further reading
- ISO 32000-2, Portable Document Format: https://www.iso.org/standard/75839.html
- Box, Watermarking files: https://support.box.com/hc/en-us/articles/360044196413-Watermarking-Files
- Box, Shared link settings: https://support.box.com/hc/en-us/articles/360043697094-Configuring-Individual-Shared-Link-Settings
- Dropbox DocSend, Dynamic watermarking: https://help.docsend.com/hc/en-us/articles/360024057334-Dynamic-Watermarking
- Digify, Dynamic watermark: https://help.digify.com/en/articles/584948-dynamic-watermark
- Adobe Acrobat Sign, Audit reports: https://helpx.adobe.com/sign/using/audit-reports.html
- DocRaptor documentation: https://docraptor.com/documentation
- Gotenberg documentation: https://gotenberg.dev/docs/getting-started/introduction
Top comments (0)