OpenAPI spec
One spec, every client. Always in sync with the live API.
# JSON
curl https://api.papermark.com/v1/openapi.json -o openapi.json
# YAML
curl https://api.papermark.com/v1/openapi.yaml -o openapi.yamlGenerate a client in any language
Any OpenAPI generator works. The most common path is openapi-generator-cli, which supports 50+ target languages and frameworks:
openapi-generator-cli generate \
-i https://api.papermark.com/v1/openapi.yaml \
-g typescript-fetch \
-o ./papermark-tsopenapi-generator-cli generate \
-i https://api.papermark.com/v1/openapi.yaml \
-g python \
-o ./papermark-pyopenapi-generator-cli generate \
-i https://api.papermark.com/v1/openapi.yaml \
-g go \
-o ./papermark-goopenapi-generator-cli generate \
-i https://api.papermark.com/v1/openapi.yaml \
-g rust \
-o ./papermark-rustOther 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:
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
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
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 ↗:
npx openapi-typescript https://api.papermark.com/v1/openapi.yaml \
-o ./types/papermark.d.tsThen import the resource types in your code:
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.
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.