dataroom.dev

Engineering

The data room API: a complete developer guide

Everything you need to drive a virtual data room over REST: authentication and token scoping, the six-resource data model, uploads, per-recipient links, page-level analytics, webhooks, error handling, and production patterns, with runnable examples against the Papermark API.

Read full docsFull endpoint reference: canonical API docs
July 6, 2026·6 min read·By dataroom.dev

A data room API turns confidential document distribution into a programmable primitive: provision rooms from your CRM, mint per-recipient watermarked links from a script, stream view events into your warehouse, and revoke everything on close with one call. This guide is the end-to-end tour for engineers: auth, the data model, every major operation with runnable examples, webhooks, and the patterns that matter in production.

The worked example throughout is the Papermark API at https://api.papermark.com/v1. It is the option this site recommends for API-first data room work, for reasons you can verify rather than take on faith: the full surface is public (43 operations across 6 resources, OpenAPI 3.1 spec), the engine behind it is open source, and the same API serves the hosted service and self-hosted deployments, so nothing here locks you in. Where behavior details matter, the canonical source of truth is papermark.com/docs.

Fundamentals

The API is conventional REST: JSON bodies, bearer authentication, resource-shaped URLs, standard status codes. If you have used the Stripe or GitHub APIs, nothing here will surprise you.

curl -sS https://api.papermark.com/v1/datarooms \
  -H "Authorization: Bearer $PAPERMARK_TOKEN"

Two ways to authenticate:

  1. API tokens (pm_live_…), minted at app.papermark.com/settings/tokens. Right for servers, CI, and cron. Store them in your secret manager; scope them to the narrowest team or workspace that the job needs.
  2. OAuth 2.1 device flow, right for CLI tools and anything interactive where a human can approve a browser prompt. Grants are individually revocable and attributed in the audit log. Walkthrough in the device flow guide.

The data model in one pass

Six resources, and every workflow is a composition of them:

Resource What it is Typical operations
Dataroom Container for one deal, raise, or audit create, list, update, delete, analytics
Document A file plus version history upload, list, versioning, delete
Folder Hierarchy within a room create, move, update, delete
Link Access policy with a URL create, update, delete, list views
Visitor An identified external party list, get, view history
View One visit, with per-page timing list, analytics rollups

The design decision that makes the rest of the API composable: documents exist independently of datarooms (upload once, attach anywhere), and links carry the policy (a room has many links, each with its own expiry, watermark, and gating). Model your own integration around those two facts.

Provision a room

API="https://api.papermark.com/v1"

DR_ID=$(curl -sS -X POST "$API/datarooms" \
  -H "Authorization: Bearer $PAPERMARK_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "Series B — diligence", "description": "Confidential"}' \
  | jq -r '.data.id')

for folder in Financials Legal "Cap Table" Product; do
  curl -sS -X POST "$API/datarooms/$DR_ID/folders" \
    -H "Authorization: Bearer $PAPERMARK_TOKEN" \
    -H "Content-Type: application/json" \
    -d "{\"name\": \"$folder\"}"
done

Or with the TypeScript SDK, which is generated from the OpenAPI spec and fully typed:

import { Papermark } from "@papermark/sdk";

const pm = new Papermark(); // reads PAPERMARK_TOKEN

const room = await pm.datarooms.create({
  name: "Series B — diligence",
  description: "Confidential",
});

for (const name of ["Financials", "Legal", "Cap Table", "Product"]) {
  await pm.datarooms.folders.create(room.id, { name });
}

Upload documents

Small files go up as multipart form data. Large files use presigned uploads: request an upload URL, PUT the bytes directly to object storage, then confirm. The presigned path keeps large transfers off the API tier and is the right default above a few tens of megabytes; the full pattern, including parallelism and retry handling, is covered in presigned uploads.

curl -sS -X POST "$API/documents" \
  -H "Authorization: Bearer $PAPERMARK_TOKEN" \
  -F "file=@financial-model.pdf" \
  -F "dataroom_id=$DR_ID"

Documents are versioned: uploading a new version keeps every link pointing at the same document while viewers get the latest content, and the old versions stay retrievable for the audit trail. In a live deal this is the difference between "re-send new links to 20 bidders" and "the room is simply current."

Mint links: where the policy lives

A link is a policy object. Everything that controls access hangs off it:

curl -sS -X POST "$API/links" \
  -H "Authorization: Bearer $PAPERMARK_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "dataroom_id": "'"$DR_ID"'",
    "name": "Acme Capital — round 2",
    "email_protected": true,
    "allow_download": false,
    "expires_at": "2026-08-15T00:00:00Z",
    "enable_watermark": true,
    "watermark_text": "{{email}} · {{date}}"
  }'

One link per recipient is the pattern to internalize: it is what makes engagement attribution, leak attribution, and per-recipient revocation possible. The reasoning and the bulk-minting code are in per-recipient share links. Field names above follow the canonical docs; treat papermark.com/docs/api as the source of truth for the exact schema.

Read the analytics

Every view is recorded with visitor identity (via the email gate), per-page dwell time, IP, and timestamps. The rollups answer the questions deal teams actually ask:

# Who has viewed, and how much
curl -sS "$API/datarooms/$DR_ID/analytics" \
  -H "Authorization: Bearer $PAPERMARK_TOKEN"

# One visitor's full history
curl -sS "$API/visitors/$VISITOR_ID/views" \
  -H "Authorization: Bearer $PAPERMARK_TOKEN"

Because the output is structured JSON rather than a dashboard, you can compute the metrics your process cares about: total engaged minutes per bidder, which pages of the financial model get the most attention, who has gone quiet for a week. The audit log guide covers schema details and warehouse export.

Webhooks

Polling analytics works for digests; webhooks are the push path. Register an endpoint at app.papermark.com/settings/webhooks and you receive events for views, downloads, and link activity as they happen. Verify the signature on every delivery, respond 200 quickly, and process async: the standard webhook hygiene applies. A complete worked receiver, including Slack routing, is in view events to Slack.

Errors and production behavior

The API uses conventional status codes: 401 for a bad or revoked token, 403 for a scope miss, 404 for a resource outside your workspace, 422 for validation failures with a field-level error body, 429 with a Retry-After header when you exceed rate limits.

Production checklist:

  1. Retry idempotently. Retry 429 and 5xx with exponential backoff and jitter. For creates, check-then-create or use client-side deduplication keys so a retried request cannot double-provision a room.
  2. Page everything. List endpoints paginate; loop the cursor to completion before acting on "all links" or "all documents," or your hygiene sweep silently misses page two.
  3. Verify bulk operations. After a 400-document upload, list and count. Distributed uploads fail partially; scripts that assume success corrupt the room's completeness silently.
  4. Scope tokens per job. The CI job that uploads board decks does not need delete permissions on deal rooms. Narrow tokens turn a leaked credential from an incident into an inconvenience.
  5. Keep the audit log external. Stream view events to your warehouse from day one. When the post-close dispute arrives in 2029, you want the log in your infrastructure, on your retention policy.

Beyond raw REST

The same 43-operation surface is exposed three more ways, so pick the interface per job rather than per platform: typed SDKs for TypeScript and Python for application code, the papermark CLI for shell scripts and CI, and the MCP server when the operator is an AI agent rather than a program — covered in depth in MCP data rooms: the complete guide. Everything ends up at the same API with the same tokens and the same audit log.

See also

More in Engineering