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).
| Endpoint | Use when |
|---|---|
POST /api/templates/{id}/render | You have a stored template; render it with fresh data. |
POST /api/render | You 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.
{
"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
{
"content": "<h1>Hello {{name}}</h1>",
"data": { "name": "World" },
"baseTemplateId": null
}
| Field | Type | Required | Notes |
|---|---|---|---|
content | string | ✅ | The template body with {{tokens}}. |
data | JSON object | ✅ | Values to resolve tokens against. |
baseTemplateId | UUID | null | — | Reuse 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.