---
title: "Automate Fundraising Data Room Setup with the Papermark CLI (2026)"
lang: en
canonical_url: https://www.papermark.com/blog/automate-fundraising-data-room-papermark-cli
last_updated: 2026-07-08
published: 2026-07-03
category: [datarooms, fundraising]
author: "Marc Seitz"
summary: "One shell script turns a local folder into a structured fundraising data room with per-VC links, email gating, and expiry dates. Papermark CLI cookbook with code."
---

# Automate Fundraising Data Room Setup with the Papermark CLI (2026)

One shell script can turn a folder of PDFs on your laptop into a structured fundraising data room with a separate, email-gated, expiring link for every VC on your list. The `papermark` CLI (on npm) wraps the full [Papermark data room API](/blog/data-room-api.md) with machine-readable JSON output, so the whole workflow scripts cleanly: upload, organize, mint links, print the link list. This cookbook gives you the complete script plus the pieces to adapt it, aimed at technical founders who would rather spend fifteen minutes writing bash than a weekend clicking through a dashboard.

## Why script your fundraising data room?

A fundraising process is repetitive by design. Every investor gets the same room but needs their own access: their own link, their own email gate, their own analytics trail so you know who actually read the financials before the partner meeting. Doing that by hand for 20 firms means 20 rounds of identical link configuration, and re-doing pieces of it every time the deck gets a new version.

Scripting fixes both the tedium and the consistency. The link list is generated, so no firm gets forgotten. The security settings are code, so no link accidentally ships without an expiry. And when you raise again in 18 months, the script is the documentation of how your last process worked. The underlying structure follows the standard [fundraising data room](/blog/why-startups-need-virtual-data-room-for-fundraising.md) playbook; we're just making it reproducible.

The hidden cost of the manual approach is not the clicking, it's the drift. By the twelfth firm you configure a link by hand, you have stopped double-checking the expiry toggle and the email gate, and that is exactly when a link ships wide open. A closed round with a live, ungated URL sitting in a partner's inbox is the kind of leak that never shows up until a competitor quotes your numbers. When the settings live in a script, every investor gets byte-for-byte the same access policy, and the policy is reviewable in a pull request before a single link exists. That is data room automation working the way infrastructure-as-code works: the config is the source of truth, not whatever state a dashboard happens to be in.

There is an audit benefit too. A scripted virtual data room leaves you with two artifacts you can commit to the deal repo: the script that built the room and the `links.csv` it emitted. Six months later, when a wire is closing and legal asks who had access to the cap table and when, you answer from version control instead of memory. For a technical founder, that reproducibility is worth more than the fifteen minutes the script saves on setup day.

The CLI is the right tool for this job: it's built for one-off scripts, cron jobs, and CI, with about 50-150 ms per call. If you're building a backend service instead, hit the [REST API](https://www.papermark.com/docs/api) directly, and if you'd rather converse than script, the same workflow runs through [Claude and the MCP server](/blog/how-to-create-data-room-with-claude.md).

## Setup: install and log in

Two commands. You need Node 24+ and a Papermark plan with API access (Business or above; the Data Rooms plan is €99/month with a 7-day free trial).

```bash
npm install -g papermark
papermark login
```

`papermark login` runs an OAuth 2.1 device flow: it prints a code, you approve it in the browser, and the token lands on disk with `0600` permissions. In CI, set the `PAPERMARK_TOKEN` environment variable with a token minted in the dashboard instead. Confirm with `papermark whoami`, and if anything looks off, `papermark doctor` health-checks your config, token, and API reachability.

Two global flags make the CLI scriptable. `--json` forces a stable JSON envelope (`{ "ok": true, "data": ... }`) and auto-enables when output is piped, so `jq` always sees machine-readable output. `--dry-run` prints the HTTP request that would be sent, token redacted, and exits, which is the fastest way to debug a script without touching your account.

## The script: folder in, link list out

Here is the whole thing. It expects your documents organized in local subfolders (the [six-folder fundraising structure](/blog/data-room-folder-structure.md) works well) and a `vcs.txt` with one firm name and email per line.

```bash
#!/usr/bin/env bash
set -euo pipefail

DEAL_DIR=~/deals/acme-series-a
ROOM_NAME="Acme Series A"
EXPIRY="2026-09-30T00:00:00Z"

## 1. Create the data room
DR_ID=$(papermark datarooms create \
  --name "$ROOM_NAME" \
  --description "Series A raise, Q3 2026" \
  --json | jq -r '.data.id')
echo "Created data room: $DR_ID"

## 2. Upload every PDF and attach it to the room
for f in "$DEAL_DIR"/**/*.pdf; do
  DOC_ID=$(papermark documents upload "$f" --json | jq -r '.data.id')
  curl -sX POST "https://api.papermark.com/v1/datarooms/$DR_ID/documents" \
    -H "Authorization: Bearer $PAPERMARK_TOKEN" \
    -H "Content-Type: application/json" \
    -d "{\"documentId\":\"$DOC_ID\"}" > /dev/null
  echo "Uploaded: $(basename "$f")"
done

## 3. One gated, expiring link per VC
echo "" > links.csv
while IFS=, read -r FIRM EMAIL; do
  URL=$(papermark links create \
    --dataroom "$DR_ID" \
    --name "$FIRM" \
    --email-protected \
    --expires "$EXPIRY" \
    --json | jq -r '.data.url')
  echo "$FIRM,$EMAIL,$URL" >> links.csv
done < vcs.txt

echo "Done. Link list written to links.csv"
```

Run it, and `links.csv` is your outreach artifact: firm, contact, personal URL. Every link points at the same room, requires email verification, and dies on September 30 whether or not you remember it exists.

![Fundraising data room created by the Papermark CLI](https://assets.papermark.io/upload/file_35DtVER7SdS1G6unRE8unv-papermark-data-room.png)
_The structured data room and per-investor links the script produces from a local folder._

## Anatomy of the script

Step 1 creates the room and captures its ID from the JSON envelope with `jq`. Everything downstream hangs off `$DR_ID`, so the script is safe to adapt: change the name and directory, and nothing else moves.

Step 2 uploads each PDF and attaches it to the room. Uploads go through S3 presigned URLs under the hood, so there's no file size ceiling to think about. If you re-run the script, list what's already in the room first (`papermark datarooms` subcommands cover this) and skip duplicates; the [CLI getting started guide](https://www.papermark.com/docs/cli/getting-started) shows the idempotent variant.

Step 3 is where fundraising-specific value concentrates. Per-VC links mean per-VC analytics: when Sequoia's link shows 40 minutes on the financial model and Index's shows two minutes on the cover slide, you know exactly where to spend your week. Email gating (`--email-protected`) confirms who behind the firm actually opened it, and expiry enforces process discipline, since a closed round shouldn't have live links floating around inboxes. You can also add `--password` per link if a firm requests it.

## CLI vs REST API vs MCP server vs dashboard

The script above uses the CLI, but the same fundraising data room is reachable four ways, and picking the wrong one wastes an afternoon. Papermark exposes one surface — documents, deal rooms, per-investor links, investor analytics — behind a single token, and the CLI, the REST API, the MCP server, and the dashboard are just different front doors onto it. The question is never which one is "best" but which one fits the task in front of you, so it is worth being explicit about where each earns its place before you commit a workflow to it.

Reach for the CLI when the work is a one-off script, a cron job, or a step in CI: it is optimized for exactly the folder-in, link-list-out shape this article is built around, with about 50–150 ms per call and no boilerplate. Reach for the [REST API](/blog/data-room-api.md) when you are building a backend service that mints links on a schedule or in response to your own app's events, where you want typed responses and full control over retries. Reach for the [MCP server](/blog/papermark-mcp-server.md) when you would rather describe the outcome than write the steps, letting [Claude drive the data room automation](/blog/how-to-create-data-room-with-claude.md) from a prompt. And the dashboard still wins for the things a human should eyeball: skimming a heat map, sanity-checking a room before you send it, or handling the one bespoke link a script would be overkill for.

| Interface | Best for | Setup effort | Reproducibility |
| --- | --- | --- | --- |
| CLI | One-off scripts, cron jobs, CI steps — the fundraising setup in this article | Low: npm install -g papermark, then login | High: the script is the record of what you did |
| REST API | Backend services that mint links on a schedule or from app events | Medium: write and host the integration code | High, if your code is in version control |
| MCP server | Conversational, flexible workflows driven by Claude or ChatGPT | Low: connect the server, then prompt in natural language | Lower: prompts vary run to run |
| Dashboard | Eyeballing analytics, sanity checks, one-off bespoke links | None: log in and click | None: manual, unlogged clicks |

The practical path for most founders is to start with the script here, keep the dashboard open for the visual read on engagement, and graduate to the REST API or MCP server only when a specific problem asks for it. Because all four share one token and one data model, nothing you build on the CLI is throwaway: the room, the links, and the analytics are identical no matter which door you came through.

## Security and access control per investor link

Fundraising is the moment your most sensitive documents — cap table, financial model, customer contracts — leave your control, so the access policy on each investor link matters as much as the deck itself. The advantage of a scripted virtual data room is that security stops being a checkbox you might forget and becomes a set of flags on every `links create` call, applied identically to all 20 firms. Every control the dashboard offers is reachable from the CLI or a curl call, because they all hit the same API, so nothing about scripting forces you to trade convenience for control.

Four flags do most of the work. `--email-protected` gates the room behind a verified email, so you know a real person at the firm opened it rather than a forwarded URL; `--password` adds a shared secret for the firms that request one; `--expires` sets a hard deadline so access dies with the round whether or not you remember it; and download permissions decide whether a viewer can pull the PDFs down or only read them in the browser. The underlying API also supports dynamic watermarking, which stamps each viewer's email across every page so a screenshotted financial model traces straight back to the firm that leaked it. Layer these on the same `links create` call and every investor link ships with the full policy baked in:

```bash
papermark links create \
  --dataroom "$DR_ID" \
  --name "Index Ventures" \
  --email-protected \
  --password "$SHARED_SECRET" \
  --expires "$EXPIRY" \
  --no-download \
  --json | jq -r '.data.url'
```

Revocation is the control founders forget, and it is the one scripting makes trivial. Because each firm has its own link, you can kill one firm's access — a passed investor, a leaked URL — without touching the other nineteen, either by deleting the link or updating it to an already-past expiry. The per-investor model means access control is granular by construction: there is no single shared link whose exposure you have to reason about, just a clean one-to-one map from firm to link to analytics trail that you can audit at any point in the raise.

## Reading investor analytics from the terminal

The reason to script a fundraising data room is not only to build it but to watch it. Once the links are out, the raise becomes an information game, and the investor analytics tell you where you stand before any partner does. A firm that spends twenty minutes in the financial model and comes back twice is running internal diligence; a firm that opened the cover slide once and never returned is a polite pass you have not been told about yet. Reading that signal from the terminal means you never have to open a browser tab to know who is warm, and it means the signal can drive automation instead of just sitting in a dashboard.

The same token that built the room reads it back, so the analytics live one command away from wherever your scripts already run. Aggregate room stats, per-viewer footprints, and per-link view events are all one command each, and because the output is JSON by default when piped, `jq` turns any of them into exactly the field you care about:

```bash
## Aggregate stats for the room
papermark datarooms stats "$DR_ID"

## One firm's footprint
papermark datarooms viewers "$DR_ID" --email partner@sequoia.com

## Raw view events for one link
papermark views list --link link_abcd1234 --json | jq '.data[]'
```

Pipe any of these into `jq` and you have a morning-coffee dashboard in your terminal, or a cron job that posts to Slack. Exit codes are contractual (2 for auth, 3 for validation, 4 for network), so your script can distinguish "token expired" from "typo" without parsing error strings. The [output contract docs](https://www.papermark.com/docs/cli) list every code.

For deeper engagement analysis, the page-by-page data behind these commands is the same [page-level analytics](https://www.papermark.com/docs/api/reference) the dashboard shows: which pages held attention, where readers dropped off, from which device and location.

The real payoff is turning that read into a push. A dozen lines of bash on a cron schedule can check the room every morning and message you only when something changed, so the analytics find you instead of the other way around. The pattern is to pull the view count, diff it against yesterday's, and post to a Slack webhook when a firm crosses a threshold worth reacting to:

```bash
#!/usr/bin/env bash
set -euo pipefail

NEW=$(papermark datarooms views "$DR_ID" --since 24h --json | jq '.data | length')
if [ "$NEW" -gt 0 ]; then
  curl -sX POST "$SLACK_WEBHOOK" \
    -H "Content-Type: application/json" \
    -d "{\"text\":\"$NEW new investor views in the last 24h\"}" > /dev/null
fi
```

Because the exit codes are contractual, a cron job like this fails loudly and correctly: a `2` pages you that the token expired, a `4` stays quiet through a transient network blip and retries next run. You get an investor-analytics feed in the channel you already live in, built on the same CLI that created the room, with no dashboard tab left open and no polling by hand.

## Reusing the script across rounds and entities

A fundraising script earns its keep the second time you run it, not the first. Most companies raise more than once, and the parts that change between a seed and a Series A are exactly the three variables at the top of the file: the deal directory, the room name, and the expiry date. Everything below them — the upload loop, the per-investor link logic, the CSV output — is round-agnostic. Treating the script as a small, parameterized tool rather than a one-time hack means the next raise starts from a known-good baseline instead of a blank terminal, and the same holds for the [startup fundraising](/blog/data-room-for-startups.md) motion at every stage from pre-seed onward.

The pattern generalizes past rounds to entities. If you run SPVs, a fund vehicle, or separate rooms for existing investors versus new ones, the script becomes a loop over a config file: one room per entity, each with its own document set and its own investor list, all built in a single pass. The same reproducibility that guarantees identical security across 20 firms guarantees identical structure across five entities, which is precisely the discipline [VCs expect when they open your data room](/blog/vc-firms-data-room-essentials.md). You stop hand-building rooms and start declaring them.

Versioning the deck is the third reuse case, and it is where the API's document model pays off. When the metrics deck or the model gets a new cut mid-raise, you do not mint a new link and re-send; you push the new file as a fresh version of the existing document, and every investor's link now serves the current copy while the old view analytics stay attached to the room. The link a firm bookmarked in week one still works in week six, pointing at week six's numbers. Keeping the deck current becomes a one-line upload in the same script, not a scramble to figure out who has which version.

## A Series A raise, end to end

Consider a technical founder — call her the CTO-turned-CEO of a seed-stage infra company — opening a Series A. On a Monday she exports her deal folder into the [six-folder structure](/blog/data-room-folder-structure.md), drops a `vcs.txt` with 18 firms into the directory, and runs the script. Ninety seconds later the deal room exists, every PDF is uploaded, and `links.csv` holds 18 email-gated links that expire the day the round is meant to close. She pastes each link into a personalized intro email and sends the batch before lunch. Setup that would have been a Saturday of dashboard clicking is done before her first meeting.

The analytics start earning their keep by Wednesday. A morning `papermark datarooms stats` shows Sequoia has spent 35 minutes across the financial model and cohort retention tab and returned twice; Index opened the deck once, lingered on the cover, and never came back. She reads that exactly right: Sequoia is running real diligence and Index is a soft pass. So she front-loads her follow-up energy on the firms the data says are leaning in, and when Sequoia's partner asks for updated NDR figures on Thursday, she pushes a new version of the model — the same link keeps working, now serving the fresh cut. Two firms she never emailed show up in the viewer list, forwarded in by a warm intro, and because the links are email-gated she can see precisely who they are.

By the following Friday she has a term sheet, and the revocation loop closes the round cleanly: one script run expires all 18 links so no live cap table sits in an inbox after signing. The whole raise ran on the script she wrote in fifteen minutes, and the `links.csv` plus the analytics history are her complete, auditable record of who saw what and when.

## Variations worth stealing

The quarterly refresh: wrap steps 1-2 in a cron job that syncs a Drive-exported folder into the room monthly, so your data room never goes stale mid-raise. Because uploads can skip existing files, the sync is cheap.

The revocation pass: when the round closes, enumerate the room's links and delete them in one loop. This is the one destructive operation in the workflow, and doing it in a script you can read beats hoping you remembered every link. (Keep deletes out of [AI agent hands](/blog/build-due-diligence-agent-claude.md); in a script you wrote, they're fine.)

The CI publish: your metrics deck builds in CI every month anyway, so have the pipeline upload the fresh PDF as a new version and notify your investor update list. A `pm_live_` token with only `documents.write` scope, set as a CI secret, is all it needs.

## Conclusion

Fifteen minutes of bash gives you a fundraising data room that sets itself up, gates itself per investor, and reports its own engagement. The CLI, the [REST API](/blog/data-room-api.md), and the [MCP server](/blog/papermark-mcp-server.md) all expose the same surface with the same token, so start with the script and graduate to whichever interface fits the next problem. The Data Rooms plan is €99/month with a 7-day free trial.

## Introducing the Papermark CLI

## FAQ

### What is the Papermark CLI?

The official command-line client for Papermark, installed with npm install -g papermark. It wraps the same REST API the dashboard uses, with commands for documents, data rooms, links, and view analytics, plus stable JSON output for scripting.

### Do I need to be a developer to use it?

You need basic terminal comfort: running commands and editing a shell script. The complete script in this article is copy-paste ready; you change three variables (folder path, room name, expiry date) and provide a CSV of investor contacts.

### How does authentication work in scripts and CI?

Interactively, papermark login runs an OAuth 2.1 device flow and stores the token securely on disk. In CI or cron jobs, set the PAPERMARK_TOKEN environment variable with a scoped token from the dashboard. One token works across the CLI, API, and MCP server.

### Why create a separate link for each investor?

Analytics and control. Each link has its own view trail, so you can see which firm read what and for how long. Email gating verifies who opened it, and you can expire or revoke one firm's access without touching the others.

### Can I set passwords and watermarks from the CLI?

Yes. Link creation supports passwords, email protection, expiry dates, and download permissions as flags, and the underlying API also supports dynamic watermarking per link. Anything the API supports is reachable from the CLI or a curl call.

### What does this setup cost?

The CLI is free to install from npm. API access comes with Papermark Business plans and above; the Data Rooms plan with unlimited documents, custom domains, and audit logs is €99/month with a 7-day free trial.

### Can I automate this with AI instead of bash?

Yes. The Papermark MCP server exposes the same operations as typed tools for Claude and ChatGPT, so 'create a data room from this folder with per-VC links' works as a single prompt. The bash version is more reproducible; the agent version is more flexible.

### Can I reuse the same script for my next round?

Yes, that is the point. Only three variables change between a seed and a Series A: the deal directory, the room name, and the expiry date. The upload loop, per-investor link logic, and CSV output are round-agnostic, so the next raise starts from a known-good baseline. The same pattern extends to multiple entities or SPVs by looping over a config file.

### How do I revoke investor access when the round closes?

Because each firm has its own link, you can revoke one firm without touching the others by deleting the link or updating it to a past expiry date. To close the whole round, run one loop over the room's links and expire them all, so no live cap table stays in an inbox after signing.

---

_Markdown version of [this article](https://www.papermark.com/blog/automate-fundraising-data-room-papermark-cli) for AI agents and LLMs._
_More Papermark content: [llms.txt](https://www.papermark.com/llms.txt) · [full index](https://www.papermark.com/llms-full.txt)._
