dataroom.dev

Cookbook

Per-recipient share links: one dataroom, N watermarked URLs

Generate one share link per recipient with per-recipient watermarks, per-recipient policy, and CRM write-back: the single most useful pattern in programmable data rooms. Worked example uses the Papermark API.

Read full docsMint share links via the API: full Links reference
April 18, 2026·7 min read·By dataroom.dev

If you only remember one pattern from this entire site, make it this: one share link per recipient, watermarked with the recipient's identity. Not one link that everyone shares. The per-recipient pattern gives you four wins simultaneously, each of which would justify the small extra setup work on its own:

  1. Engagement attribution. Every view is attributable to a known recipient by name and organization. Not the useless "someone at this fund" or "an anonymous viewer." The signal compounds: 47 views distributed across 30 named investors tells you which 8 are warm leads. 47 views on a single shared link tells you basically nothing actionable.
  2. Leak attribution. Watermarks are forensic, not preventative. They don't stop a determined leak, but they do identify it. When a confidential financial model lands in a competitor's hands or on Twitter, the watermark tells you which recipient's link produced the screenshot. In practice, this deters about 80% of casual leakage simply because recipients know they're identified.
  3. Per-recipient policy. This bidder gets download disabled; that one gets a 30-day expiry; the third gets no password because they're a board member who hates friction; the fourth gets a 7-day expiry because they're a late-stage tire-kicker you don't fully trust yet. One shared link with uniform policy cannot do this. N links each with their own policy can.
  4. Clean revocation. When a recipient drops out, leaves the firm, or becomes adversarial, you cut their link without nuking everyone else's. A single revocation call. Audit log stays intact. No "we're rotating the link, here's a new one" email that broadcasts your operational immaturity.

This article is the complete pattern with code, edge cases, and the policy templates that map to common use cases.

The mechanics

A share link is a server-side policy attached to a dataroom (or a single document). Many links can point to the same content. Each link carries its own:

  1. Password: optional, applies before email gate.
  2. Expiry: optional, hard cutoff at a specific timestamp.
  3. Email gate: requires the visitor to enter and verify an email before accessing.
  4. Download permission: true allows direct download; false enforces viewer-only.
  5. Watermark template: substitutes {{email}}, {{name}}, {{timestamp}}, {{ip}}, {{country}}, and custom variables at view time. Rendered server-side, not stripped by client tampering.
  6. Folder filter: restricts visible documents to a subset of the dataroom's folders.
  7. Allowed countries: geofence to a country list (rare but useful for export-controlled materials).
  8. Notes: free-text metadata for your own record-keeping. Visible only to you.

So "send to N recipients" = "create N links with the same dataroom_id."

Worked example: investor outreach

You have a CSV of investors:

name,email,fund,role,priority
Alice Chen,alice@acme-pe.com,Acme PE,Partner,tier1
Bob Patel,bob@bravo.vc,Bravo Capital,Principal,tier2
Carla Singh,carla@carbon.holdings,Carbon Holdings,Partner,tier1
Dan Williams,dan@deltavc.com,Delta VC,Associate,tier3
Eve Lin,eve@epsilonpartners.com,Epsilon Partners,Partner,tier1

Generate one link per investor and write the URL back to your CRM:

import { Papermark } from "@papermark/sdk";
import { parse } from "csv-parse/sync";
import { readFileSync } from "node:fs";
import { addDays } from "date-fns";

const pm = new Papermark();
const DATAROOM_ID = "dr_acme_seed";

const investors = parse(readFileSync("investors.csv"), { columns: true });

// Tier-based policy — higher-priority investors get longer expiry
const tierExpiry: Record<string, number> = {
  tier1: 90, // partners at target funds: 90-day window
  tier2: 60, // principals + warm intros: 60 days
  tier3: 30, // associates + cold intros: 30 days
};

for (const inv of investors) {
  const link = await pm.links.create({
    dataroomId: DATAROOM_ID,
    requireEmail: true,
    allowDownload: false,
    watermark: `${inv.name} · ${inv.fund} · {{timestamp}}`,
    expiresAt: addDays(new Date(), tierExpiry[inv.priority] ?? 30),
    notes: `Generated for ${inv.email} (${inv.priority}) on ${new Date().toISOString()}`,
  });

  console.log(`${inv.email} → ${link.url}`);

  // Write back to CRM
  await crm.updateContact(inv.email, {
    papermark_link_url: link.url,
    papermark_link_id: link.id,
    papermark_minted_at: new Date(),
  });
}

For 30 investors, this runs in 10-15 seconds end-to-end. Your outreach email then references the per-investor URL via merge field. From the investor's perspective, the experience is indistinguishable from 1:1 outreach because mechanically it is. They just don't know their colleague got a different link with a different policy.

CLI variant

If you'd rather pipe a CSV through the shell without writing Node or Python:

tail -n +2 investors.csv | while IFS=, read -r NAME EMAIL FUND ROLE PRIORITY; do
  # Pick expiry based on priority
  case "$PRIORITY" in
    tier1) DAYS=90 ;;
    tier2) DAYS=60 ;;
    *)     DAYS=30 ;;
  esac
  EXPIRES=$(date -u -v+${DAYS}d +%Y-%m-%dT%H:%M:%SZ)

  URL=$(papermark links create \
    --dataroom dr_acme_seed \
    --require-email \
    --watermark "$NAME · $FUND · {{timestamp}}" \
    --expires "$EXPIRES" \
    --notes "Generated for $EMAIL ($PRIORITY)" \
    --json | jq -r '.data.url')

  echo "$EMAIL,$URL,$PRIORITY"
done > investor-links.csv

The output is a CSV ready to merge into your CRM or feed into a mail-merge tool.

Policy templates by use case

The right defaults vary substantially by what you're sharing and who you're sharing with. Six templates that cover most B2B sharing situations:

Template 1: m&A bidders (competitive auction)

  1. Password: generated per-bidder, sent via a separate channel (text message to the bidder's known phone).
  2. Email gate: on, with verification.
  3. Download: disabled.
  4. Expiry: typically 60 days from mint, with renewal each round.
  5. Watermark: Bidder · {{email}} · CONFIDENTIAL · {{timestamp}}. Visible enough to deter, not so aggressive it ruins readability.
  6. Folder filter: round-specific (round 1 sees teaser + financials; round 2 sees full data tape).

Template 2: fundraising investors

  1. Password: none (VCs refuse to enter passwords, full stop).
  2. Email gate: on, with verification.
  3. Download: disabled (no need for them to keep a copy).
  4. Expiry: 45-90 days depending on round velocity.
  5. Watermark: {{name}} · {{fund}} · {{timestamp}}. Friendly, not adversarial.
  6. Folder filter: none for tier-1; possibly restricted for tier-3 cold leads.

Template 3: board members (recurring access)

  1. Password: none (recurring access; password fatigue defeats the purpose).
  2. Email gate: on, with verification.
  3. Download: disabled (compliance posture, not because directors would leak).
  4. Expiry: 14 days post-meeting (long enough for post-meeting review, short enough that stale links don't pile up).
  5. Watermark: {{director_name}} · BOARD CONFIDENTIAL · {{timestamp}}.
  6. Folder filter: scoped to current meeting cycle.

Template 4: external counsel (work-product sharing)

  1. Password: yes. Counsel are used to it and the access scope is high-stakes.
  2. Email gate: on, with verification against the firm's domain.
  3. Download: enabled (counsel needs to work offline; trust is presumed).
  4. Expiry: matches engagement timeline, typically 90-180 days.
  5. Watermark: {{name}} · COUNSEL · PRIVILEGED & CONFIDENTIAL · {{timestamp}}.
  6. Folder filter: matter-specific.

Template 5: clinical trial investigators

  1. Password: yes, with strong complexity requirements.
  2. Email gate: on, with allowlist verification against the investigator roster.
  3. Download: disabled.
  4. Expiry: trial-protocol-bound, often multi-year.
  5. Watermark: {{investigator_name}} · Study {{study_id}} · {{timestamp}}.
  6. Folder filter: study-protocol-specific.

Template 6: internal due-diligence response (sell-side teams)

  1. Password: yes.
  2. Email gate: on.
  3. Download: disabled, except for documents specifically marked downloadable (typically NDAs and basic metadata).
  4. Expiry: rolling 30-day with auto-renewal as long as the process is active.
  5. Watermark: {{name}} · {{firm}} · CONFIDENTIAL · {{timestamp}}.
  6. Folder filter: workstream-specific (e.g., financial-only vs full diligence).

Revoking one without disturbing the others

When a recipient drops out, an employment relationship ends, or a fund declines:

papermark links revoke lnk_acme_pe_alice

That single link returns 410 Gone on the next request, even on already-loaded browser tabs (the viewer revalidates on every page load). The other 29 investors' links keep working unaffected.

Bulk revocation at close

When the round closes, the deal dies, or the engagement ends, kill every outstanding link in one pipe:

papermark datarooms list-links dr_acme_seed --json | \
  jq -r '.data[].id' | \
  xargs -I{} papermark links revoke {} --confirm

For a dataroom with 40 active links, this completes in 5-10 seconds. The audit log of who-saw-what stays intact indefinitely.

Watermark design: six rules

Common watermark mistakes that defeat the purpose:

  1. Don't make it invisible. A pale gray 8pt watermark in the corner doesn't deter anyone. Make it visible. Diagonal, centered, semi-transparent (10-15% opacity), 18-24pt.
  2. Don't make it aggressive. Black text at 60% opacity that obscures the underlying content makes the document unreadable, which makes recipients hate you, which means they don't take your meeting. Find the line between "clearly there" and "ruins the document."
  3. Don't include sensitive information. Watermarks are visible to recipients. Don't include internal deal codes, bidder rankings, or competitive analysis snippets. The watermark is identification, not exfiltration of your own internal context.
  4. Don't use strings long enough to wrap. A 90-character watermark looks broken on rendered output. Stick to 40-60 characters max.
  5. Test on every page format. Landscape and portrait, with margins, without margins, image-heavy and text-heavy. A watermark that looks fine on a deck slide can look terrible on a tax return scan.
  6. Don't trust client-side rendering. Server-rendered watermarks (which Papermark uses) are baked into the served bytes. Client-rendered watermarks can be stripped by a determined user with browser dev tools. Always verify the implementation renders server-side.

A good default for most use cases: {{recipient_name}} · {{recipient_org}} · {{timestamp}} · CONFIDENTIAL. Diagonal, centered, 20pt, 12% opacity, dark gray.

What if the recipient forwards the link?

The email gate catches this. When a recipient forwards their link and a colleague tries to access it with a different email, the colleague sees the email-gate prompt with a different verification flow. The original recipient's link works for them but not for the forwarded recipient.

For higher-stakes scenarios, enable the single_use_email option. Once the link has been bound to an email, it stops accepting other emails entirely. This is the right setting for board materials and M&A.

See also

More in Cookbook