Skip to main content

Getting started — for developers

Everything in the Paperwright platform is backed by an HTTP API. This page gets you from zero to your first rendered PDF. For the full endpoint catalogue and the versioning policy, see the API Reference.

Base URL

http://localhost:5110 # local development
https://api.paperwright.io # production (planned — not yet live)

All paths below are relative to the base URL. The current API surface is v1 — see versioning.

Authentication: two lanes

LaneHeaderWhoUse it for
JWTAuthorization: Bearer <token>Humans / admin sessionsSign-up, login, managing templates & API keys, workspace admin
API keyX-Api-Key: <key>Machines / your backendRendering from server-side code

The render endpoints are the only ones that accept both lanes. Everything else (template CRUD, workspaces, keys) is JWT-only. This keeps your production backend from ever holding a user credential.

1. Get a token

curl -X POST http://localhost:5110/api/auth/login \
-H "Content-Type: application/json" \
-d '{ "email": "you@example.com", "password": "••••••••" }'

You'll get back a session JWT. Use it as Authorization: Bearer <token> for the next calls.

2. Mint an API key (for machine rendering)

curl -X POST http://localhost:5110/api/api-keys \
-H "Authorization: Bearer <jwt>" \
-H "Content-Type: application/json" \
-d '{ "name": "production-backend" }'

The key is shown once — store it as a secret. From now on your backend renders with X-Api-Key and never needs a JWT.

3. Create a template

curl -X POST http://localhost:5110/api/templates \
-H "Authorization: Bearer <jwt>" \
-H "Content-Type: application/json" \
-d '{
"name": "Invoice",
"content": "<h1>Invoice {{invoiceNumber}}</h1><p>Total: {{total}}</p>"
}'

The response includes the template's id (a UUID). Hold onto it.

4. Render it

Render a stored template with fresh data — this accepts either auth lane:

curl -X POST http://localhost:5110/api/templates/<id>/render \
-H "X-Api-Key: <key>" \
-H "Content-Type: application/json" \
-d '{ "data": { "invoiceNumber": "INV-1024", "total": "$4,200.00" } }' \
--output invoice.pdf

Prefer not to store a template? Render inline in one shot:

curl -X POST http://localhost:5110/api/render \
-H "X-Api-Key: <key>" \
-H "Content-Type: application/json" \
-d '{
"content": "<h1>Hello {{name}}</h1>",
"data": { "name": "World" }
}' \
--output hello.pdf

That's the whole loop: template + data → PDF.

The data model in one minute

  • A Template has a name and HTML-ish content with {{token}} placeholders.
  • At render time you pass a data JSON object; tokens resolve against it.
  • Special tokens ({{$today}}, {{$now}}, {{$page}}, {{$pages}}) resolve at render time.
  • Templates are workspace-scoped: a key or token only ever sees its own workspace's templates. Cross-tenant access returns 404 (never 403 — we don't confirm a resource exists across tenants).

The SDK (roadmap)

A first-party .NET SDK (Paperwright.Sdk) is planned so a render becomes a single typed call instead of a hand-rolled HTTP request:

// Planned API — subject to change
var pdf = await paperwright.RenderAsync(templateId, new { invoiceNumber = "INV-1024", total = "$4,200.00" });

Until then, the HTTP API above is the integration surface.


What's next