Docs

Three ways in: a 30-second quickstart, the live REST reference, or the typed TypeScript SDK.

Quickstart

Get an API key from app.paperjet.dev, then render your first PDF.

  1. 1. Render a PDF with curl

    curl -X POST https://api.paperjet.dev/v1/render \
      -H "Authorization: Bearer $PAPERJET_API_KEY" \
      -H "Content-Type: application/json" \
      -o hello.pdf \
      -d '{"source":"= Hello #data.name","data":{"name":"World"}}'
    
    # 200 OK · application/pdf · 11 KB · 75 ms.
  2. 2. Inject JSON data into Typst

    Whatever you pass under data shows up inside the template as a top-level Typst binding called data. It's a real Typst dictionary — use .foo dot access, arrays with .at(0), branching with if, mapping with .map().

    // source field of the request body, or stored as a template:
    = Hello #data.user.name
    
    // loops + branching are real:
    #for item in data.cart [
      - #item.title (#item.qty × #item.price €)
    ]
  3. 3. Reuse a template across calls

    Stable templates live in your account's R2 bucket. Reference them by id; send fresh data on every request.

    # 1. Upload the template once
    npx wrangler r2 object put paperjet-templates/templates/invoice-v1/main.typ \
      --file ./invoice-v1.typ --remote
    
    # 2. Render against it as often as you want
    curl -X POST https://api.paperjet.dev/v1/render \
      -H "Authorization: Bearer $KEY" \
      -d '{"template_id":"invoice-v1","data":{...}}'
  4. 4. Make retries safe

    Pass Idempotency-Key on POSTs. A retry within 24 h with the same key + same body replays the cached PDF — no double render, no double bill.

    curl -X POST https://api.paperjet.dev/v1/render \
      -H "Authorization: Bearer $KEY" \
      -H "Idempotency-Key: order-12345" \
      -d '{"template_id":"invoice-v1","data":{...}}'

TypeScript SDK

@paperjet/sdk wraps fetch with auth, structured errors, and typed responses. Works in Node 20+, Bun, Deno, and Cloudflare Workers.

// npm install @paperjet/sdk   (or bun add, pnpm add, …)

import { Paperjet, PaperjetError } from "@paperjet/sdk";

const pj = new Paperjet({ apiKey: process.env.PAPERJET_API_KEY! });

try {
  const pdf = await pj.render(
    { template_id: "invoice-v1", data: invoice },
    { idempotencyKey: `order-$+invoice.id`} },
  );
  await Bun.write("out.pdf", pdf);
} catch (err) {
  if (err instanceof PaperjetError) {
    console.error(err.code, err.message, err.requestId);
  }
}

The full method surface — pj.renders.list, pj.templates.list, pj.keys — is in the SDK README and the REST reference.

Looking for something specific?

The interactive API reference covers every endpoint, request/response shape, and error code.

Open the REST reference →