Skip to main content

Rendering

Tag: Templates · Version: v1 · Stability: 🟢 Stable

The render endpoints turn template + data into a PDF. They are the only endpoints that accept either auth lane — a JWT (for previews from the platform) or an X-Api-Key (for your backend).

EndpointUse when
POST /api/templates/{id}/renderYou have a stored template; render it with fresh data.
POST /api/renderYou want a one-off render without persisting a template.

:::tip Performance baseline ~9.5 renders/sec sustained on a single box, p50 ~625 ms, p95 ~1.1 s, p99 ~1.3 s (measured on a laptop). Plenty of headroom for typical workloads. :::

Render a stored template

POST /api/templates/{id}/render · JWT or API key · {id} is a UUID.

Request — RenderTemplateBody
{
"data": {
"invoiceNumber": "INV-1024",
"total": "$4,200.00",
"lineItems": [{ "name": "Design", "qty": 1, "price": "$4,200.00" }]
}
}

data is required — a free-form JSON object whose keys resolve the template's {{tokens}}. Returns the rendered PDF.

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

Render inline (no stored template)

POST /api/render · JWT or API key

Request — RenderInlineBody
{
"content": "<h1>Hello {{name}}</h1>",
"data": { "name": "World" },
"baseTemplateId": null
}
FieldTypeRequiredNotes
contentstringThe template body with {{tokens}}.
dataJSON objectValues to resolve tokens against.
baseTemplateIdUUID | nullReuse a stored template's base PDF as the background of this inline render.

Token grammar

Tokens resolve against the data object at render time:

{{field}} plain substitution
{{total | number}} typed format
{{issuedAt | date:"medium"}} date preset
{{issuedAt | date:"dd/MM/yyyy"}} custom .NET-style format
{{$today | date:"long"}} current date at render time
{{$now | datetime:"short"}} current time at render time
{{$page}} / {{$pages}} page number / total

Security model

The renderer runs with JavaScript disabled and default-deny network egress (no SSRF) — markdown and icon SVGs are sanitised. AI-authored or user-authored content renders safely through the same hardened pipeline.

Metering

Every render is metered (best-effort — a metering failure never fails a render). Renders are tagged by source (api vs preview); see Usage.