How to Add Per-Recipient Watermarks, Then Track Every Read
Confidential documents leak two ways: forwarded quietly, or screenshotted loudly. Watermarks handle the second — a name in the footer makes every screenshot traceable. But a watermark can't tell you the document is circulating before the leak, and tracking alone can't deter what it only observes.
So do both, in one pipeline: every recipient gets a copy stamped with their own name, behind their own tracked link. Deterrence on the page, visibility on the wire.
Understand the four-call chain
The whole pipeline is API results feeding API inputs — no downloads in between:
POST /api/pdf/file/create HTML in → personal stamp page (async job)
POST /api/pdf/page/overlay base + stamp in → watermarked copy URL
POST /api/docs that URL in → tracked document
POST /api/docs/{id}/links name + email in → the URL you send
The elegance is in step three: processing results are hosted URLs, and the documents endpoint ingests by URL — so the watermarked copy flows straight into tracking without ever touching your disk.
Run the pipeline over your list
#!/usr/bin/env bash
# Per-recipient watermark + tracked link, one pipeline per name.
API="https://apdf.io/api"
H=(-H "Authorization: Bearer $API_TOKEN" -H "Accept: application/json")
while IFS=, read -r NAME EMAIL; do
# 1. generate the personal stamp page (async job)
STAMP="<html><body style=\"margin:0;width:100%;height:100vh;position:relative;overflow:hidden;\">
<div style=\"position:absolute;top:50%;left:50%;transform:translate(-50%,-50%) rotate(-35deg);font-family:Arial;font-size:64px;font-weight:bold;color:rgba(200,30,30,0.18);white-space:nowrap;letter-spacing:6px;\">CONFIDENTIAL</div>
<div style=\"position:absolute;bottom:24px;left:0;right:0;text-align:center;font-family:Arial;font-size:11px;color:rgba(120,120,120,0.55);\">Copy licensed to $NAME · not for distribution</div>
</body></html>"
JOB=$(curl -s "${H[@]}" -X POST "$API/pdf/file/create" \
--data-urlencode "html=$STAMP" -d "format=a4" \
-d "margin_top=0" -d "margin_bottom=0" -d "margin_left=0" -d "margin_right=0" | jq -r .job_id)
sleep 1
until STAMP=$(curl -s "${H[@]}" -X POST "$API/job/status/check" -d "id=$JOB" | jq -re '.result.file') 2>/dev/null; do sleep 2; done
# 2. overlay it onto every page of the report
sleep 1
STAMPED=$(curl -s "${H[@]}" -X POST "$API/pdf/page/overlay" \
--data-urlencode "file=$BASE_PDF" --data-urlencode "overlay=$STAMP" -d "repeat=1" | jq -r .file)
# 3. upload the personal copy as a tracked document and mint the link
sleep 1
DOC=$(curl -s "${H[@]}" -X POST "$API/docs" \
--data-urlencode "file=$STAMPED" --data-urlencode "name=Board Report — $NAME" \
-d "is_public=0" | jq -r .data.doc_id)
sleep 1
URL=$(curl -s "${H[@]}" -X POST "$API/docs/$DOC/links" \
--data-urlencode "name=$NAME" --data-urlencode "email=$EMAIL" | jq -r .data.url)
echo "$NAME → $URL"
sleep 1
done < <(tail -n +2 recipients.csv)
$ BASE_PDF=https://your-site.example/board-report.pdf API_TOKEN=... ./stamp-and-track.sh
Miriam Wolfe → https://docs.apdf.io/studio/e6a74-8bc24-77c31?v=JbWCV5aQ
Viktor Chen → https://docs.apdf.io/studio/a42a6-f7ca0-9bf31?v=EPNDFGmS
We pulled Miriam's finished copy back down and checked: every page carries “Copy licensed to Miriam Wolfe · not for distribution” under the diagonal stamp. Viktor's says Viktor.
What each copy buys you
- Deterrence — a screenshot or printout of any page names its owner. Forwarding stops being free.
- Per-person analytics — each copy is its own tracked document, so “Board Report — Miriam”'s sessions are Miriam's reading: every open, page and dwell time, plus the full history when you need it.
- Surgical shutdown — one person's access ends by deactivating their link or archiving their document; nobody else notices. The revocation tutorial has the mechanics.
Keep the free plan's 3-tracked-documents limit in mind: per-recipient copies consume a document each, so this pattern is for the small, sensitive circle — board packs, term sheets, due-diligence material — not the hundred-lead campaign (that's per-recipient links on one document).
Where to go from here
Each half of this crossover has its own deep-dive.
Related tutorials
See If Clients Actually Read Your Monthly Reports
The report nobody reads is the retainer nobody renews. Tracked links per client sort the portfolio in one sweep — both Meridian stakeholders read cover to cover, Brightpath flicked for 15 seconds, Okafor never opened — and per-page data tells you whether your decision request was ever seen.
Mint PDF Tracking Links from ChatGPT
The share decision happens mid-conversation, not in a dashboard tab. The MCP server speaks to any client — including ChatGPT — so 'share the pricing guide with Elif' mints her tracked link inside the draft you were writing, and the same chat checks who read and kills a leaver's old link.
Track Who Reads Your Press Kit
Twenty pitches, twenty attached kits, and a silence that could mean anything. Per-journalist links end the blindness: the full read plus asset download says she's writing, the fact-sheet return visit says offer the interview now, and the never-opened rows say re-pitch — not follow up.