Template System
Docuget uses a unified template system backed by a single templates table.
All templates — emails, PDFs, charts, videos — live in the same table, rendered
by the same Nunjucks engine, and distinguished by two key fields:
source_type— determines how the template is rendered (HTML, Typst, EChart, Remotion, etc.)template_category— determines what purpose the template serves (email, invoice, report, video, etc.)
This means one Templates module manages everything, with category-based filtering to keep things organized.
For the full module reference — editor UI, all Nunjucks filters, AI integration, webhooks, and caching — see Templates.
Two Template Systems
Docuget has two complementary template systems:
| System | Table | Purpose | Rendering |
|---|---|---|---|
| Templates | templates |
Deterministic rendering (emails, PDFs, charts) | Nunjucks — fast, no AI cost |
| AI Prompt Templates | ai_prompt_template |
AI-powered content generation | Claude API — dynamic, creative |
Templates can optionally link to an AI Prompt Template via ai_template_id —
for example, an invoice email template might link to an AI prompt that generates
a personalized message body before the template renders the final layout.
Template Fields
Each template record has these key fields:
| Field | Description |
|---|---|
template_key |
Unique identifier per company (e.g. calendar_event_email). Used for programmatic lookup. |
template_category |
Purpose grouping: email, invoice, echart, video, report, system |
source_type |
Rendering engine: html, typst, markdown, echart, remotion, etc. |
content |
JSONB template content. Text-based templates: {"text":"..."}. Structured templates (echart, json): raw JSON object |
seed_json |
Default values merged with caller-provided data |
ai_template_id |
Optional link to an AI Prompt Template for content generation |
Source Types
Each template has a source_type that determines how it's rendered:
| Source Type | Output | Use Case |
|---|---|---|
html |
HTML string | Emails, web content |
text |
Plain text | Email fallback, logs |
markdown |
HTML (via Marked) | Documentation, rich content |
typst |
PDF (via Typst compiler) | Invoices, certificates, reports |
json |
JSON string | API responses, data export |
xml |
XML string | CFDI, integrations |
svg |
SVG markup | Icons, simple graphics |
latex |
LaTeX string | Academic documents |
echart / echart-js |
SVG/PNG/PDF/HTML | Charts and visualizations |
tabular |
CSV/XLSX/JSON/HTML/Markdown | Tabular data views |
remotion |
TSX (for Remotion) | Video rendering |
Categories
The template_category field groups templates by business purpose:
| Category | Description | Typical Source Type |
|---|---|---|
email |
Email bodies (calendar, invoices, notifications) | html |
invoice |
Invoice and billing PDFs | typst |
echart |
Charts and data visualizations | echart |
data_science |
Data Science charts and tabular views | echart, tabular |
video |
Video generation pipelines | remotion |
report |
Business reports and exports | typst, markdown |
system |
System-wide shared templates (headers, footers, wrappers) | varies |
Categories are free-form strings — you can create new categories as needed without schema changes.
Placeholder Syntax
All template types use the same Nunjucks syntax:
Variables
{{ company_name }}
{{ invoice.total }}
{{ items[0].name }}
Conditionals
{% if discount > 0 %}
Discount applied: {{ discount }}%
{% endif %}
Loops
{% for item in line_items %}
{{ item.name }} — ${{ item.price }}
{% endfor %}
Filters
Filters transform values using the pipe (|) syntax:
{{ start_date | localdate }}
{{ total | money2number }}
{{ url | qrcode }}
See the Templates page for the full filter reference.
Template Lookup
Templates can be fetched two ways:
- By ID —
GET /v1/templates/:id— for direct references (e.g. invoice → template_id FK) - By Key —
GET /v1/templates/by-key/:key?company_id=...— for programmatic/route-based lookup (e.g. email route looks upcalendar_event_email)
The template_key is unique per company, so the same key can exist in different
companies with different content.
Architecture
Frontend (template_data)
↓
Route handler (email, invoice, video, etc.)
↓
template_getbykey(company_id, key) or template_getbyid(id)
↓
Merge: seed_json defaults + template_data
↓
templates_render(template, merged_data)
↓
Nunjucks renders {{ placeholders }} based on source_type
↓
Output delivered (email, PDF download, chart embed, etc.)
The rendering pipeline:
- Lookup: Template fetched by
(company_id, template_key)or by ID - Merge:
seed_jsondefaults merged with provided data (caller values win) - Route:
source_typedetermines the rendering handler (Nunjucks, Marked, ECharts, etc.) - Render: Template content processed with merged data
- Post-process: Output may be further transformed (e.g., Typst → PDF, EChart → SVG)
- Deliver: Final output sent to destination (email, file download, API response, video pipeline)
API Reference
| Endpoint | Method | Description |
|---|---|---|
/v1/templates |
GET | List templates (filter by template_category, source_type, dest_type) |
/v1/templates/:id |
GET | Get template by ID |
/v1/templates/by-key/:key |
GET | Get template by key (requires company_id param) |
/v1/templates |
POST | Create template |
/v1/templates/:id |
PUT | Update template |
/v1/templates/:id |
DELETE | Delete template |
/v1/templates/render/:id |
POST | Render template with provided data |