dataroom.dev

SDKs

Type-safe clients, generated from one spec.

The Papermark API ships an OpenAPI 3.1 contract. The recommended path today is to generate your own client; first-party TypeScript and Python SDKs are on the roadmap.

Read full docsGenerate a typed client. Full OpenAPI + API reference

OpenAPI spec

One spec, every client. Always in sync with the live API.

bash
# JSON
curl https://api.papermark.com/v1/openapi.json -o openapi.json

# YAML
curl https://api.papermark.com/v1/openapi.yaml -o openapi.yaml

Generate a client in any language

Any OpenAPI generator works. The most common path is openapi-generator-cli, which supports 50+ target languages and frameworks:

TypeScript (typescript-fetch)bash
openapi-generator-cli generate \
  -i https://api.papermark.com/v1/openapi.yaml \
  -g typescript-fetch \
  -o ./papermark-ts
Python (httpx-based)bash
openapi-generator-cli generate \
  -i https://api.papermark.com/v1/openapi.yaml \
  -g python \
  -o ./papermark-py
Gobash
openapi-generator-cli generate \
  -i https://api.papermark.com/v1/openapi.yaml \
  -g go \
  -o ./papermark-go
Rustbash
openapi-generator-cli generate \
  -i https://api.papermark.com/v1/openapi.yaml \
  -g rust \
  -o ./papermark-rust

Other supported targets include Ruby, Java, C#, PHP, Swift, Kotlin, Dart, Elixir, and Scala. See the openapi-generator generator list ↗.

TypeScript via fetch (no SDK required)

If you don't want a generated client, the raw API is reachable from any HTTP library. The example below uses Node 18+ built-in fetch with no dependencies:

papermark.tsts
const API = "https://api.papermark.com/v1";
const TOKEN = process.env.PAPERMARK_TOKEN!;

async function api<T>(
  method: string,
  path: string,
  body?: unknown,
): Promise<T> {
  const r = await fetch(`${API}${path}`, {
    method,
    headers: {
      Authorization: `Bearer ${TOKEN}`,
      "Content-Type": "application/json",
    },
    body: body ? JSON.stringify(body) : undefined,
  });
  if (!r.ok) throw new Error(`${method} ${path} failed: ${r.status}`);
  return r.json() as Promise<T>;
}

// Usage
const dataroom = await api<{ data: { id: string } }>("POST", "/datarooms", {
  name: "Series A. Acme Inc.",
});

const link = await api<{ data: { url: string } }>("POST", "/links", {
  dataroom_id: dataroom.data.id,
  password: "pelican-42",
  require_email: true,
});

console.log(link.data.url);

Edge runtimes

app/api/share/route.ts (Next.js edge)ts
export const runtime = "edge";

export async function POST(req: Request) {
  const { dealName } = await req.json();

  const r = await fetch("https://api.papermark.com/v1/datarooms", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.PAPERMARK_TOKEN}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ name: dealName }),
  });

  const { data } = await r.json();
  return Response.json({ id: data.id });
}

Python via httpx

papermark.pypython
import os, httpx

API = "https://api.papermark.com/v1"
TOKEN = os.environ["PAPERMARK_TOKEN"]

client = httpx.Client(
    base_url=API,
    headers={"Authorization": f"Bearer {TOKEN}"},
    timeout=30,
)

# Create a dataroom
r = client.post("/datarooms", json={"name": "Series A. Acme"})
r.raise_for_status()
dataroom_id = r.json()["data"]["id"]

# Upload a document
with open("deck.pdf", "rb") as f:
    r = client.post("/documents", files={"file": f}, data={"dataroom_id": dataroom_id})
    r.raise_for_status()

# Mint a link
r = client.post("/links", json={
    "dataroom_id": dataroom_id,
    "password": "pelican-42",
    "require_email": True,
})
r.raise_for_status()
print(r.json()["data"]["url"])

Type definitions

If you only need types (not a full client) in TypeScript, generate them from the OpenAPI spec with openapi-typescript ↗:

bash
npx openapi-typescript https://api.papermark.com/v1/openapi.yaml \
  -o ./types/papermark.d.ts

Then import the resource types in your code:

types-onlyts
import type { paths, components } from "./types/papermark";

type Dataroom = components["schemas"]["Dataroom"];
type Document = components["schemas"]["Document"];
type Link = components["schemas"]["Link"];
type CreateLinkRequest = paths["/links"]["post"]["requestBody"]["content"]["application/json"];
type CreateLinkResponse = paths["/links"]["post"]["responses"]["200"]["content"]["application/json"];

Retries & idempotency

The recommended retry policy: exponential backoff with jitter on 5xx and 429. Respect the Retry-After header on 429. Cap at 4-5 attempts.

minimal retry wrapper (TS)ts
async function withRetry<T>(fn: () => Promise<Response>, maxAttempts = 4): Promise<Response> {
  let lastErr: unknown;
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
    try {
      const r = await fn();
      if (r.ok) return r;
      if (r.status === 429) {
        const retryAfter = r.headers.get("Retry-After");
        await sleep(retryAfter ? +retryAfter * 1000 : 2 ** attempt * 1000);
        continue;
      }
      if (r.status >= 500 && r.status < 600) {
        await sleep(2 ** attempt * 1000 + Math.random() * 500);
        continue;
      }
      return r; // 4xx. Don't retry
    } catch (e) {
      lastErr = e;
      await sleep(2 ** attempt * 1000);
    }
  }
  throw lastErr;
}

const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));

For writes you want exactly-once on, the API accepts an Idempotency-Key header (typically a UUID). Verify support and retention window in the canonical docs before relying on it for high-stakes operations.

Roadmap

Public SDK status (subject to change. Verify on papermark.com/docs ↗):

  • CLI (papermark). Published on npm, current.
  • MCP server (@papermark/mcp-server). Published on npm, current.
  • First-party TypeScript SDK. Planned. Until released, generate from OpenAPI or call with fetch.
  • First-party Python SDK. Planned. Same status.