Render Engine
Docuget's Render Engine is a universal format conversion and template rendering pipeline. It takes data and templates in one format and produces output in another — Typst to PDF, JSON to XLSX, ECharts to SVG, images between formats, and dozens more combinations.
The engine supports three modes:
- Template rendering — process a Nunjucks template with data and convert the output to a target format
- Format conversion — convert data between formats without any template (e.g. JSON to CSV)
- Record rendering — fetch a database record by table and ID, then render it through a template
All modes share the same converter registry, caching layer, and composable rendering pipeline.
Supported Formats
Template Rendering (source_type to output)
| Source Type | Output Formats | Engine |
|---|---|---|
| Typst | PDF, SVG, PNG | Typst compiler |
| ECharts | SVG, HTML, PNG, JSON | ECharts SSR |
| Pintora | SVG, PNG | Pintora renderer |
Data Conversion (input to output)
| Input | Output Formats |
|---|---|
| JSON | CSV, TSV, XLSX, YAML, TOML, XML, Markdown table, HTML table, GeoJSON, DOCX |
| CSV | JSON, TSV |
| TSV | JSON |
| GeoJSON | CSV, SVG, DXF, JSON |
| SVG | PNG, JPG, WebP, HPGL |
| HTML | DOCX |
| Markdown | DOCX |
| PNG/JPG/WebP | PNG, JPG, WebP (image conversion) |
Tip: Use
GET /v1/render/converters/matrixto see the full, live conversion matrix for your instance, including any custom converters.
API Endpoints
All endpoints require authentication (Bearer token).
Synchronous Render
Renders data through a template and converts to the target output format. Returns the output inline by default.
POST /v1/render
Request body:
{
"company_id": "550e8400-e29b-41d4-a716-446655440000",
"template_id": "tpl_01hjd3...",
"data": {
"title": "Q1 Report",
"items": [{ "name": "Widget", "qty": 42 }]
},
"output_format": "pdf",
"output_filename": "report.pdf",
"inline": true,
"cache_ttl": 3600
}
| Field | Type | Description |
|---|---|---|
company_id |
string | Required. Company scope |
template_id |
string | Template ID (provide this or template_key) |
template_key |
string | Template key lookup (alternative to template_id) |
source_table |
string | DB table to fetch record from |
source_record_id |
string | Record ID within source_table |
data |
object | Data passed to the template |
output_format |
string | Required. Target format (pdf, svg, csv, xlsx, etc.) |
output_filename |
string | Suggested filename for the output |
inline |
boolean | Return output directly (default true). When false, returns metadata JSON. |
cache_ttl |
number | Cache duration in seconds. Subsequent identical requests return the cached result. |
curl example:
curl -X POST https://api.docuget.com/v1/render \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"company_id": "550e8400-...",
"template_key": "invoice_pdf",
"data": { "invoice_number": "INV-001", "total": 1500 },
"output_format": "pdf"
}' \
--output invoice.pdf
Response headers include X-Render-Duration-Ms, X-Render-From-Cache, and
optionally X-Render-Job-Id.
Format Conversion
Converts data between formats without any template rendering.
POST /v1/render/convert
Request body:
{
"input": [
{ "name": "Alice", "age": 30 },
{ "name": "Bob", "age": 25 }
],
"input_format": "json",
"output_format": "csv",
"company_id": "550e8400-...",
"options": {
"columns": ["name", "age"],
"bom": true
}
}
curl example:
curl -X POST https://api.docuget.com/v1/render/convert \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"input": [{"name": "Alice", "score": 95}],
"input_format": "json",
"output_format": "xlsx",
"options": {
"sheetName": "Results",
"style": {
"headerStyle": { "bold": true, "fillColor": "#4472C4", "fontColor": "#FFFFFF" },
"alternateRowColor": "#D9E2F3",
"borders": true
}
}
}' \
--output results.xlsx
Render a Database Record
Fetches a record from a database table and renders it through a template.
POST /v1/render/record
{
"company_id": "550e8400-...",
"table": "customer",
"record_id": "customer_01hjd3...",
"template_key": "customer_card",
"output_format": "pdf"
}
curl example:
curl -X POST https://api.docuget.com/v1/render/record \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"company_id": "550e8400-...",
"table": "invoice",
"record_id": "inv_01hjd4...",
"template_key": "invoice_pdf",
"output_format": "pdf"
}' \
--output invoice.pdf
Get Record Render (shorthand)
A GET endpoint for quick record rendering via query parameters.
GET /v1/render/record/:table/:id?out=pdf&template_key=invoice_pdf&company_id=...
curl example:
curl "https://api.docuget.com/v1/render/record/invoice/inv_01hjd4...?out=svg&company_id=550e8400-..." \
-H "Authorization: Bearer $TOKEN" \
--output invoice.svg
Render via Short Reference
Resolves a @code to a template or record and renders it directly.
GET /v1/render/ref/:code?out=svg&company_id=...&width=800&height=400
curl example:
curl "https://api.docuget.com/v1/render/ref/k7f2n?out=png&company_id=550e8400-...&width=1200&height=600" \
-H "Authorization: Bearer $TOKEN" \
--output chart.png
Batch Render
Renders multiple items through a template. Optionally merges all PDF outputs into a single file.
POST /v1/render/batch
Request body:
{
"company_id": "550e8400-...",
"template_key": "invoice_pdf",
"output_format": "pdf",
"merge_pdf": true,
"concurrency": 5,
"items": [
{ "data": { "invoice_number": "INV-001", "total": 1500 } },
{ "data": { "invoice_number": "INV-002", "total": 2300 } },
{ "source_table": "invoice", "source_record_id": "inv_01hjd5..." }
]
}
| Field | Type | Description |
|---|---|---|
items |
array | Required. Array of render inputs (each can override template_id, template_key, output_format) |
merge_pdf |
boolean | When true and output_format is pdf, merges all outputs into a single PDF (default false) |
concurrency |
number | Max parallel renders, 1-20 (default 5) |
curl example:
curl -X POST https://api.docuget.com/v1/render/batch \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"company_id": "550e8400-...",
"template_key": "invoice_pdf",
"output_format": "pdf",
"merge_pdf": true,
"items": [
{ "data": { "invoice_number": "INV-001" } },
{ "data": { "invoice_number": "INV-002" } }
]
}' \
--output batch_merged.pdf
When merge_pdf is false, the response is JSON with per-item status:
{
"total": 2,
"succeeded": 2,
"failed": 0,
"results": [
{
"index": 0,
"status": "fulfilled",
"job_id": "rjob_01...",
"mime_type": "application/pdf",
"size": 14200,
"duration_ms": 320
},
{
"index": 1,
"status": "fulfilled",
"job_id": "rjob_02...",
"mime_type": "application/pdf",
"size": 15800,
"duration_ms": 290
}
]
}
Async Render
Same parameters as POST /v1/render, but returns 202 Accepted immediately
with a job_id. The render runs in the background.
POST /v1/render/async
curl example:
# Start async render
curl -X POST https://api.docuget.com/v1/render/async \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"company_id": "550e8400-...",
"template_key": "annual_report",
"data": { "year": 2026 },
"output_format": "pdf"
}'
# Response: { "job_id": "rjob_01hjd6...", "status": "queued" }
Job Status and Output
GET /v1/render/jobs?company_id=...&status=completed&page=1&pageSize=20
GET /v1/render/job/:id
GET /v1/render/job/:id/output
DELETE /v1/render/job/:id
curl examples:
# List jobs
curl "https://api.docuget.com/v1/render/jobs?company_id=550e8400-...&status=completed" \
-H "Authorization: Bearer $TOKEN"
# Check job status
curl "https://api.docuget.com/v1/render/job/rjob_01hjd6..." \
-H "Authorization: Bearer $TOKEN"
# Download completed output
curl "https://api.docuget.com/v1/render/job/rjob_01hjd6.../output" \
-H "Authorization: Bearer $TOKEN" \
--output report.pdf
Job statuses: pending, processing, completed, cached, failed.
Converter Registry
GET /v1/render/converters
GET /v1/render/converters/matrix
POST /v1/render/converters
PUT /v1/render/converters/:id
DELETE /v1/render/converters/:id
curl examples:
# List all converters
curl "https://api.docuget.com/v1/render/converters" \
-H "Authorization: Bearer $TOKEN"
# Get the full conversion matrix (source -> output mapping)
curl "https://api.docuget.com/v1/render/converters/matrix" \
-H "Authorization: Bearer $TOKEN"
# Register a custom converter
curl -X POST https://api.docuget.com/v1/render/converters \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"source_type": "custom_markup",
"output_format": "html",
"engine": "custom",
"is_enabled": true
}'
Dynamic Data Functions ($fn: Directives)
The render engine supports $fn: directives — special string values in template
data that are resolved to live database query results during Phase 1 (Data
Resolution). This powers the Data Science module's
charts and tabular views.
Available functions: count, sum, avg, min, max, list, table,
group_count, daily_count, monthly_count.
{
"series": [{
"data": [{
"value": "$fn:count(telegram_messages)",
"name": "Messages"
}]
}]
}
After resolution, $fn:count(telegram_messages) becomes the actual row count
(e.g. 4287). All queries are company-scoped and read-only.
See the full Data Science documentation for the complete function reference, security model, and examples.
Nunjucks Filters
The render engine registers several Nunjucks filters for use inside templates. These enable inline format conversion and composable rendering.
render
Renders a template by ID or key and returns the output inline. This is how composable (recursive) rendering works — a template can embed the output of another template.
{{ "tpl_01hjd3..." | render("svg") }}
{{ "invoice_header" | render("html", extra_data) }}
to_yaml
Converts a data object to YAML.
{{ config | to_yaml }}
to_csv
Converts an array of objects to CSV. Optionally pass column names.
{{ rows | to_csv }}
{{ rows | to_csv(["name", "email", "score"]) }}
to_toml
Converts a data object to TOML.
{{ settings | to_toml }}
to_json
Serializes data to JSON with optional indentation.
{{ data | to_json }}
{{ data | to_json(2) }}
to_md_table
Converts an array of objects to a Markdown table. Optionally pass column names.
{{ rows | to_md_table }}
{{ rows | to_md_table(["name", "score"]) }}
render_chart
Renders an ECharts template inline as SVG, PNG, or HTML. Used to embed Data Science charts in other templates.
{{ "sales-chart" | render_chart("svg", 800, 400) }}
{{ "@k7f2n" | render_chart("png", 600, 300, {"year": 2026}) }}
Arguments: format (default "svg"), width, height, data (optional
override object). Respects the MAX_DEPTH = 5 recursion limit.
CLI Usage
The docuget CLI provides a render subcommand for rendering templates and
converting formats from the terminal.
# Render a template to PDF
docuget render --template-key invoice_pdf --out pdf --save invoice.pdf
# Render a specific DB record
docuget render --table invoice --id inv_01hjd4... --template-key invoice_pdf --out pdf
# Render via short reference
docuget render @k7f2n --out svg --save chart.svg
# Convert JSON file to CSV
docuget render --convert input.json --out csv --save output.csv
# Render and print to stdout
docuget render --template-key sales_chart --out svg
| Flag | Description |
|---|---|
--template-key |
Template key to render |
--table |
Source DB table for record rendering |
--id |
Record ID within the source table |
--out |
Output format (pdf, svg, csv, xlsx, etc.) |
--save |
Save output to a file path |
--convert |
Input file for format conversion (no template) |
@code |
Short reference — resolved to a template or record automatically |
Composable Rendering
Templates can embed other templates using the render Nunjucks filter. This
creates a recursive rendering pipeline where the output of one template becomes
part of another.
{# Main report template #}
<h1>{{ title }}</h1>
{# Embed a chart rendered as SVG #}
<div class="chart">
{{ "sales_chart_template" | render("svg", { year: 2026 }) }}
</div>
{# Embed a data table rendered as HTML #}
<div class="table">
{{ rows | to_md_table(["product", "revenue"]) }}
</div>
The engine tracks recursion depth and enforces a maximum of 5 levels
(MAX_DEPTH = 5). If a render chain exceeds this limit, a RenderDepthError is
raised with a 400 status code.
Each nested render call inherits the parent's company_id and increments the
depth counter, preventing infinite loops.
Caching
The render engine supports output caching via the cache_ttl parameter (in
seconds). When set:
- A cache key is computed from the render request parameters (template, data, output format)
- If a cached result exists and has not expired, it is returned immediately
- The response header
X-Render-From-Cacheindicates whether the result was served from cache
{
"cache_ttl": 3600
}
This is useful for expensive renders (large PDFs, complex charts) that are
requested frequently with the same data. Set cache_ttl to 0 or omit it to
disable caching.
XLSX Styling
When converting JSON to XLSX via POST /v1/render/convert, you can pass
XlsxStyleOptions in the options.style field to control the appearance of the
generated spreadsheet.
{
"input": [
{ "name": "Alice", "score": 95 },
{ "name": "Bob", "score": 87 }
],
"input_format": "json",
"output_format": "xlsx",
"options": {
"sheetName": "Results",
"columns": ["name", "score"],
"style": {
"headerStyle": {
"bold": true,
"fillColor": "#4472C4",
"fontColor": "#FFFFFF"
},
"alternateRowColor": "#D9E2F3",
"borders": true
}
}
}
| Option | Type | Description |
|---|---|---|
headerStyle.bold |
boolean | Bold header text (default true) |
headerStyle.fillColor |
string | Header background color as hex (e.g. #4472C4) |
headerStyle.fontColor |
string | Header text color as hex (e.g. #FFFFFF) |
alternateRowColor |
string | Background color for odd data rows as hex (zebra striping) |
borders |
boolean | Apply thin borders to all cells |
Additional XLSX options:
| Option | Type | Description |
|---|---|---|
sheetName |
string | Name of the worksheet (default Sheet1, max 31 characters) |
columns |
string[] | Column order. If omitted, all keys from the first row are used. |
sheets |
array | Multi-sheet mode: [{ name, data, columns }]. Overrides sheetName and root data. |