Build on top of Papermark Data Rooms. Documents, links, visitors, and analytics over REST, CLI, or MCP.
Read the docs or jump to the quickstart
Three steps. Create an account, mint a scoped token, make a request. Tokens look like pm_live_... and act as a long-lived bearer credential.
# 1. set your token (mint one in Settings → API Tokens) export PAPERMARK_TOKEN=pm_live_AbCdEfGhIjKlMnOpQrStUvWxYz # 2. list your documents curl https://api.papermark.com/v1/documents \ -H "Authorization: Bearer $PAPERMARK_TOKEN" # → { "data": [ { "id": "doc_pitch_v4", "name": "Pitch Deck.pdf" } ], "next_cursor": null }
# 1. install the CLI $ npm install -g papermark # 2. log in via OAuth device flow $ papermark login ▸ open https://app.papermark.com/device ✓ token stored · ~/.config/papermark/auth.json # 3. list your documents $ papermark documents list --json [ { "id": "doc_pitch_v4", "name": "Pitch Deck.pdf" } ]
// 1. install the SDK // npm install papermark import { Papermark } from "papermark"; const pm = new Papermark({ apiKey: process.env.PAPERMARK_TOKEN }); // 2. list your documents const { data } = await pm.documents.list(); // → [ { id: "doc_pitch_v4", name: "Pitch Deck.pdf" } ]
curl example, the papermark CLI, and @papermark/mcp-server.A small, deliberately boring REST surface: HTTP + JSON + bearer tokens. Data Rooms, documents, folders, links, visitors, and analytics. The full spec lives at /docs/openapi.json and generates every reference page on the site, so nothing drifts.
password, expires_at, email_gate, and download permission.list_visitor_views, and pull page-by-page durations through get_view_analytics.// create a password-gated link to a Data Room const res = await fetch( "https://api.papermark.com/v1/links", { method: "POST", headers: { Authorization: `Bearer ${process.env.PAPERMARK_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ dataroom_id: "dr_8K2m", password: "series-b-2026", expires_at: "2026-06-10T00:00:00Z", email_gate: true, allow_download: false, }), }, ); const { id, url } = await res.json();
# create a password-gated link to a Data Room import os, requests res = requests.post( "https://api.papermark.com/v1/links", headers={ "Authorization": f"Bearer {os.environ['PAPERMARK_TOKEN']}", "Content-Type": "application/json", }, json={ "dataroom_id": "dr_8K2m", "password": "series-b-2026", "expires_at": "2026-06-10T00:00:00Z", "email_gate": True, "allow_download": False, }, ) link = res.json() print(link["url"])
# create a password-gated link to a Data Room curl -X POST https://api.papermark.com/v1/links \ -H "Authorization: Bearer $PAPERMARK_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "dataroom_id": "dr_8K2m", "password": "series-b-2026", "expires_at": "2026-06-10T00:00:00Z", "email_gate": true, "allow_download": false }'
@papermark/mcp-server on npm exposes the full API to any MCP client. Stdio for local clients (Claude Desktop, Claude Code), HTTP at mcp.papermark.com/mcp for browser clients (claude.ai Connectors, ChatGPT Apps). Same 43 tools, same shapes.
{ "mcpServers": { "papermark": { "command": "npx", "args": ["-y", "@papermark/mcp-server"], "env": { "PAPERMARK_TOKEN": "pm_live_..." } } } } // then in Claude: "list my papermark documents" // or: "create a Data Room called Series B and // upload every PDF in ~/acme/"
# remote MCP server (no install required) # works with Cursor, Continue, Claude Code, any MCP client { "papermark": { "url": "https://mcp.papermark.com", "transport": "http", "headers": { "Authorization": "Bearer pm_live_..." } } } # 43 tools available out of the box: # create_dataroom, upload_document, create_link, # search_documents, get_view_analytics, ...
Track who opened which link, which page they spent time on, where they dropped off. Roll up by document, by link, by Data Room, or by an individual view. Real-time, page-level, per-visitor.
GET /v1/visitors/:id/views returns every document opened, with timing and dwell per page.list_link_views filtered by date returns who clicked, who verified, and how far through each viewer got.get_view_analytics(view_id) returns the page-by-page durations. Find where prospects drop off in your deck.{ "view_id": "vw_3m9k", "link_id": "lk_8K2m", "document_id": "doc_pitch_v4", "visitor": { "email": "alex@olivetree.vc", "verified": true }, "engagement": { "pages_viewed": 14, "completed": 0.87, "duration_ms": 412300, "top_page": 12, "page_durations_ms": [ 2100, 14200, 8900, 41300, 28100, 12400, ... ] } }
papermark on npm. One binary, the same token as the API, every command with --json for pipelines and machine-readable error codes. CI-friendly by design.
$ npm install -g papermark # OAuth 2.1 device flow $ papermark login ▸ open https://app.papermark.com/device ▸ enter code: WBKR-FXPL ✓ token stored · ~/.config/papermark/auth.json # or skip the browser and use a long-lived token $ papermark login --token pm_live_... $ papermark whoami leila@papermark.com · team_zenith · scopes: documents.*, datarooms.*
# list every Data Room you can access $ papermark datarooms list --json [ { "id": "dr_3qpL", "name": "Acquisition", "pages": 312 }, { "id": "dr_8K2m", "name": "Series B", "pages": 148 }, { "id": "dr_4nfP", "name": "LP update Q4", "pages": 24 } ] # list documents inside one Data Room $ papermark documents list --dataroom dr_3qpL doc_pitch_v4 Pitch Deck.pdf 2.1 MB doc_model_FY3 FY26 Model.xlsx 1.4 MB doc_cap_v2 Cap Table.pdf 180 KB
# upload a single file $ papermark documents upload ./pitch.pdf ▸ uploading pitch.pdf (2.1 MB) ok ✓ doc_pitch_v5 created # upload a folder into a Data Room $ papermark documents upload ./diligence/ \ --dataroom dr_3qpL --recursive ▸ 12 files queued · 148 pages ▸ uploading model.xlsx ok ▸ uploading nda_v3.pdf ok ▸ uploading cap_table.pdf ok ✓ 12/12 uploaded · dr_3qpL
# create a Data Room from the CLI $ papermark datarooms create \ --name "Series B" --json { "id": "dr_8K2m", "name": "Series B" } # upload every PDF in a folder $ papermark documents upload ./pitch.pdf \ --dataroom dr_8K2m --json ▸ uploading pitch.pdf (2.1 MB) ok ▸ attached to dr_8K2m ok # mint a password-gated link $ papermark links create --dataroom dr_8K2m \ --password series-b-2026 --json ✓ https://papermark.com/view/abc...
Pick the surface that fits your stack. Same models, same auth.
papermark on npm. Single binary. Same token as the API. --json output on every command for pipelines and CI.Documents, folders, Data Rooms, links, visitors, and views all live in the same graph. Scopes, audit logs, and analytics roll up automatically, so the CLI, an agent on MCP, and your own backend all see the same picture.
Open source on GitHub. Hosted on papermark.com. Self-hostable on your own infrastructure.