Oakprint: PDFs

Send HTML (or pick a hosted template and send data) and get back a PDF. Rendering uses real Chrome, so modern CSS works: flexbox, grid, web fonts, @page rules, page breaks. Every PDF is logged with its status, stored privately, and handed back as a download link.

Use it for invoices, receipts, proposals, reports, certificates, tickets, or any page of your app you want as a PDF.

Quick start

  1. Get an API key at https://oakstack.dev/dashboard/api-keys and set it as OAKSTACK_API_KEY.
  2. Create a PDF where your app needs it:
import { Oakstack } from "oakstack";
const oakstack = new Oakstack();

// From your own HTML:
const pdf = await oakstack.print.pdfs.create({
  html: "<h1>Hello</h1><p>Rendered by Oakstack.</p>",
  filename: "hello.pdf",
});
pdf.url; // private download link, valid for an hour

// Or from a hosted template:
const invoice = await oakstack.print.pdfs.create({
  template: "invoice",
  data: {
    brand: { name: "Oak & Pine Renovations", color: "#1f6f4a" },
    invoiceNumber: "INV-1042",
    issueDate: "2026-10-04",
    dueDate: "2026-11-03",
    from: { name: "Oak & Pine Renovations", addressLines: ["12 Elm St", "Springfield"] },
    to: { name: "Riley Chen", email: "riley@example.com" },
    items: [
      { description: "Kitchen cabinet install", quantity: 1, unitPrice: 3200 },
      { description: "Hardware", quantity: 24, unitPrice: 12.5 },
    ],
    taxRate: 8.25,
  },
  filename: "invoice-1042.pdf",
  metadata: { orderId: "1042" },
});

const bytes = await oakstack.print.pdfs.download(invoice.id); // Uint8Array

create waits for the PDF (usually 1 to 5 seconds) and returns it. To send the PDF to a user, either redirect them to pdf.url, or download the bytes and attach or store them yourself.

Or with an agent: the MCP tools print_list_templates and print_create_pdf do the same. With curl:

curl https://oakstack.dev/api/v1/print/pdfs \
  -H "Authorization: Bearer $OAKSTACK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"html":"<h1>Hello</h1>","filename":"hello.pdf"}'

# Download it (follows a redirect to the file):
curl -L -o hello.pdf https://oakstack.dev/api/v1/print/pdfs/<id>/content \
  -H "Authorization: Bearer $OAKSTACK_API_KEY"

Hosted templates

Template For
invoice Itemized invoice with tax, discount, totals, payment instructions, and an optional PAID stamp.
proposal Client proposal: summary, scope sections, pricing table, terms, signature lines.
report Status or monthly report: headline metric cards, summary, sections with bullets and tables.

Every template has a JSON Schema for its data and a complete working example:

const templates = await oakstack.print.templates.list(); // [{ id, name, description, schema, example }]
const { example } = await oakstack.print.templates.get("invoice");
await oakstack.print.pdfs.create({ template: "invoice", data: example }); // a full sample invoice

The fastest way to get the fields right is to start from example and change values. Data is checked strictly: a missing required field, a wrong type, or an unknown field (a typo like invoice_number for invoiceNumber) is rejected with a message naming it, and nothing is counted.

Shared fields:

Your own HTML

Send a full HTML document. Tips:

The PDF object

{
  "id": "6f1c...",
  "object": "print.pdf",
  "status": "succeeded",
  "source": "template",
  "template": "invoice",
  "filename": "invoice-1042.pdf",
  "options": {
    "format": "Letter",
    "landscape": false,
    "margin": { "top": "0.5in", "right": "0.5in", "bottom": "0.5in", "left": "0.5in" },
    "printBackground": true,
    "javascript": false
  },
  "metadata": { "orderId": "1042" },
  "sizeBytes": 48211,
  "pages": 1,
  "durationMs": 1830,
  "blocked": [],
  "error": null,
  "url": "https://...signed link...",
  "urlExpiresAt": "2026-10-04T18:30:00.000Z",
  "fileExpiresAt": "2026-10-11T17:30:00.000Z",
  "createdAt": "2026-10-04T17:30:00.000Z",
  "finishedAt": "2026-10-04T17:30:01.830Z"
}

Errors

API reference

All endpoints take Authorization: Bearer <API key>.

Method Path What it does
POST /api/v1/print/pdfs Render a PDF. Body: html or template + data; filename, options, metadata optional.
GET /api/v1/print/pdfs Recent PDFs, newest first. ?status=failed, ?limit= (max 100).
GET /api/v1/print/pdfs/:id One PDF with a fresh download link.
GET /api/v1/print/pdfs/:id/content Redirects to the file (curl -L).
DELETE /api/v1/print/pdfs/:id Delete the file now. Full-access keys only.
GET /api/v1/print/templates Templates with JSON Schema and example.
GET /api/v1/print/templates/:id One template.

POST accepts an Idempotency-Key header (the SDK sends one automatically), so a retried request returns the first PDF instead of rendering twice. metadata is up to 20 string values of your own, returned with the PDF.

Limits

Plan PDFs a month At once Files kept
Free 25 2 1 day
Builder 1,000 5 7 days
Studio 5,000 10 30 days

Per render: 25 seconds, 100 loaded resources, 20 MB of loaded resources, 4 MB of HTML, 20 MB of PDF. Limits reset on the 1st of each month (UTC). Check usage with oakstack.usage().