PaperStudio/Plantillas
UsuarioSoporteDeveloper

Templates

The Templates module is the central content engine in Docuget. Every email body, PDF invoice, chart visualization, video script, and data export is a template — stored in a single templates table, rendered by Nunjucks, and routed to the appropriate output format by source_type.

For a high-level architecture overview, see Template System. For email-specific usage, see Email Templates.


Template Fields

Field Type Default Description
id string auto TypeID (prefix: template)
company_id UUID required Multi-tenant scope
description string Display name
template_key string null Unique per company, for programmatic lookup (e.g. calendar_event_email)
template_category string null Purpose grouping: email, invoice, echart, data_science, video, report, system
source_type string "markdown" Rendering engine — see Source Types
dest_type string "html" Default output format
dest_filename string "template.html" Suggested output filename
content JSONB null Template content. Text-based: {"text":"...markup..."}. Structured (echart, json): raw JSON object. Supports {{ placeholder }} syntax via Nunjucks
replacer string "nunjucks" Templating engine (currently only Nunjucks)
seed_json JSON null Default values merged with caller data
source_json JSON null Schema definition for structured input data
lang_code string "en" Language code
is_active boolean true Soft-delete flag
ai_template_id string null FK to ai_prompt_template for AI content generation
readme string null Markdown documentation for the template — purpose, data binding, authorship, links, AI notes. Edited in the Readme tab; not used at render time
webhook_on_create string null Webhook triggered after render
webhook_on_update string null Webhook triggered on template update
team_id string null Optional team scope

Source Types

Each template's source_type determines how it is rendered and which output formats are available.

Source Type Engine Available Outputs Use Case
html Nunjucks HTML, PDF Emails, web content
text Nunjucks Text, CSV, HTML Plain text, logs
markdown Nunjucks + Marked HTML, PDF, Markdown, Text Documentation, rich content
typst Nunjucks + Typst compiler PDF, PNG, SVG Invoices, certificates, reports
json Nunjucks JSON, HTML, CSV, SVG, Text API responses, data export
xml Nunjucks XML, HTML, PDF, Text CFDI (Mexican tax), integrations
svg Nunjucks SVG, PNG, PDF Icons, simple graphics
latex Nunjucks PDF Academic documents
echart Nunjucks + ECharts SSR SVG, PNG, PDF, HTML Charts and visualizations
tabular Nunjucks + data converters CSV, XLSX, JSON, HTML, Markdown Tabular data views
remotion Nunjucks + Remotion MP4, WebM, GIF Video rendering

Template Editor

The template editor is a split-panel UI in the frontend:

  • Left panel — filterable list of templates with icons by dest_type, search, and create/delete
  • Right panel — tabbed editor for the selected template

Editor Tabs

Tab Description
Editor CodeMirror content editor (Markdown mode) with source type and dest type selectors
Preview For echart/echart-js templates: server-side render preview (SVG inline). Calls POST /v1/templates/:id/render
Seed JSON JSON editor for default values (seed_json) — merged with caller data at render time
Source JSON JSON editor for input schema (source_json) — defines the expected data structure
Webhook (Create) Webhook picker — select a webhook to trigger after render
Webhook (Update) Webhook picker — select a webhook to trigger on template update

Form Fields

The editor toolbar exposes:

  • Description — template display name
  • Source Type — dropdown: html, text, markdown, typst, json, xml, svg, latex, echart, remotion
  • Dest Type — context-aware dropdown (changes based on source type)
  • Dest Filename — output filename
  • Replacer — templating engine (default: nunjucks)
  • Active — toggle on/off

AI Integration

Templates have built-in AI assist. The earlier split Generate / Enhance buttons were replaced by a single unified panel.

AI Sidecar

One toggle opens the AI Sidecar — a CodeMirror request box on the right of the editor. You describe what you want in plain language; if the open record is empty the assistant creates, if it has content it edits — no separate mode. It sends the record's source_type, dest_type and current content as context (so you don't re-enter them) and streams the result back live (SSE, so long generations don't hit the edge timeout). Apply / Undo / Save-now gate the result. Uses the template_assistant prompt. Create a blank record with New.

Draft README with AI

The Readme tab has a ✨ Draft with AI button that documents the template from its own record (description, source_type, body/layers, form_fields, seed_json) via the readme_writer prompt. It streams markdown into the field (with a pre-draft Undo), writes in the operator's current app language, and preserves human-written notes (authorship/links) when refining an existing readme.

AI Prompt Templates

The AI system behind the Sidecar and Draft-README is configurable. Admins can manage prompt templates at /ai/prompt-templates (Intelligence Labs > AI > Prompt Templates).

Each AI prompt template has:

Field Description
key Unique identifier (e.g. template_assistant, readme_writer, echart_generator)
system_prompt System role instructions for Claude
user_prompt_template Nunjucks template for the user message — receives user inputs as variables
model Claude model to use
max_tokens Token limit
temperature Creativity level (0-1)
input_fields Dynamic form fields shown to the user (text, textarea, select, number, toggle, json)
output_format Expected output: text, typst, json, markdown, html

The AI Prompt Template Manager has 5 tabs: General, System Prompt, User Prompt, Input Fields, and Test (with embedded live testing).

Seeded AI Templates

Platform-global AI prompt templates (company_id NULL — available to every tenant) provided out of the box:

Key Purpose
template_assistant Create or edit a template from one free-form request (powers the AI Sidecar)
readme_writer Draft a template's README from its own record, in the operator's language
flow_generator Generate a runnable workflow (FlowPack) from a description
echart_generator Generate ECharts option JSON from chart type, description, and data sample

The legacy template_generator / template_enhancer prompts are superseded by template_assistant.


Nunjucks Filters

All templates use the same Nunjucks environment with these filters available.

Date & Time

Filter Input Output Example
localdate Date Localized string (es-MX) {{ start | localdate }} → "21 de marzo de 2026"
isodate Date ISO 8601 string {{ start | isodate }} → "2026-03-21T14:30:00-06:00"
dddd Date YYYY-MM-DD {{ date | dddd }} → "2026-03-21"
hhmm Date HH:MM:SS {{ time | hhmm }} → "14:30:00"
todate "mm/dd/yyyy" ISO Date {{ date | todate }}
ddmmyyyy_to_iso "dd/mm/yyyy" ISO Date {{ date | ddmmyyyy_to_iso }}
timestamp prefix string prefix + current date {{ "Generated: " | timestamp }}

Numbers & Currency

Filter Input Output Example
money2number "$22,345.00" "22345.00" {{ total | money2number }}
iva "1000" "160.00" {{ subtotal | iva }} (16% Mexican tax)
number any passthrough Identity filter for numeric context

Strings & Encoding

Filter Input Output Example
encodebase64 string Base64 string {{ data | encodebase64 }}
emailname "user@domain.com" "user" {{ email | emailname }}
contains string boolean {{ text | contains("search") }} (case-insensitive)
toJSON any JSON string {{ value | toJSON }}
object object JSON string {{ config | object }}

Images & Media

Filter Input Output Example
qrcode URL string SVG markup {{ url | qrcode }}
img_url_b64 image URL Base64 string {{ png_url | img_url_b64 }}
typst_png_image image URL Typst #image(...) code {{ photo_url | typst_png_image }}

Maps & Links

Filter Input Output Example
osm_map_link {latitude, longitude} OpenStreetMap URL {{ location | osm_map_link }}
google_map_link {latitude, longitude} Google Maps URL {{ location | google_map_link }}
link_new_window "url|text" HTML <a> tag {{ "https://example.com|Click" | link_new_window }}

Short References

Filter Input Output Example
ref_id @code + company_id Full TypeID {{ "@k7f2n" | ref_id(company_id) }}

Composable Rendering

These filters call the render engine recursively (max depth: 5).

Filter Input Output Example
render template ID, @shortref, or key Rendered content {{ "invoice_header" | render("html") }}
render_key template key + company_id Rendered content {{ "monthly_chart" | render_key("svg", company_id) }}
render_chart template key or @code Chart SVG/PNG/HTML {{ "sales-chart" | render_chart("svg", 800, 400) }}

For binary outputs (PNG, PDF), these filters return a base64 data URI.

Data Format Conversion

Filter Input Output Example
to_yaml object YAML string {{ config | to_yaml }}
to_csv array of objects CSV string {{ rows | to_csv(["name", "score"]) }}
to_toml object TOML string {{ settings | to_toml }}
to_json any JSON string {{ data | to_json(2) }}
to_md_table array of objects Markdown table {{ rows | to_md_table }}

Rendering Pipeline

When a template is rendered, the system follows this pipeline:

1. Lookup     → Fetch template by ID or (company_id, template_key)
2. Merge      → seed_json defaults + caller data (caller wins)
3. Resolve    → @shortref codes → full IDs
4. Resolve    → $fn: directives → live query results
5. Render     → Nunjucks processes {{ placeholders }}
6. Route      → source_type dispatcher (HTML passthrough, Marked, Typst, ECharts SSR, etc.)
7. Convert    → Optional format conversion (e.g. SVG → PNG, JSON → XLSX)
8. Cache      → Optional output caching (cache_ttl)
9. Deliver    → Return inline content, file download, or metadata

Steps 3-4 only apply when rendering through the universal Render Engine. Direct template rendering (POST /v1/templates/:id/render) skips to step 5.

Seed JSON Merging

The seed_json field provides default values. When rendering, these are deep-merged with caller-provided data, with caller values winning:

// seed_json (defaults)
{ "title": "Untitled", "color": "#000", "show_footer": true }

// caller data
{ "title": "Q1 Report", "items": [...] }

// merged result (passed to Nunjucks)
{ "title": "Q1 Report", "color": "#000", "show_footer": true, "items": [...] }

Special seed_json keys:

  • _text_template — plain-text email fallback (rendered separately)
  • width, height — ECharts chart dimensions
  • widget_config — ChartWidget display settings (auto-refresh, format)
  • style — XLSX styling options (headerStyle, alternateRowColor, borders)

API Reference

All endpoints require authentication (Bearer token).

Template CRUD

GET    /v1/templates                    List templates
GET    /v1/templates/:id                Get by ID
GET    /v1/templates/by-key/:key        Get by key (requires ?company_id=)
POST   /v1/templates                    Create template
PUT    /v1/templates/:id                Update template
DELETE /v1/templates/:id                Delete template

List filters:

Parameter Type Description
company_id string Filter by company
template_category string Filter by category
source_type string Filter by source type
dest_type string Filter by destination type
is_active boolean Filter active/inactive
search string Search description and key
page number Page number
pageSize number Results per page

Render

POST   /v1/templates/:id/render         Render with data (direct)
POST   /v1/templates/:id/preview-data   Preview tabular data (resolve $fn:)

curl examples:

# Create a Typst invoice template
curl -X POST https://api.docuget.com/v1/templates \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "company_id": "550e8400-...",
    "description": "Standard Invoice",
    "template_key": "invoice_standard",
    "template_category": "invoice",
    "source_type": "typst",
    "dest_type": "pdf",
    "dest_filename": "invoice.pdf",
    "content": "#set page(paper: \"letter\")\n= Invoice {{ invoice_number }}\n\nDate: {{ date }}\nTotal: ${{ total }}",
    "seed_json": {
      "invoice_number": "",
      "date": "",
      "total": "0.00"
    }
  }'

# Render it
curl -X POST https://api.docuget.com/v1/templates/template_01hjd3.../render \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "invoice_number": "INV-2026-001",
    "date": "2026-03-21",
    "total": "1,500.00"
  }'

# List email templates
curl "https://api.docuget.com/v1/templates?company_id=550e8400-...&template_category=email" \
  -H "Authorization: Bearer $TOKEN"

# Lookup by key
curl "https://api.docuget.com/v1/templates/by-key/calendar_event_email?company_id=550e8400-..." \
  -H "Authorization: Bearer $TOKEN"

AI Prompt Templates

GET    /v1/ai/templates                  List AI prompt templates
GET    /v1/ai/templates/:id              Get by ID
POST   /v1/ai/templates                  Create
PUT    /v1/ai/templates/:id              Update
DELETE /v1/ai/templates/:id              Delete
POST   /v1/ai/completions               Execute a prompt (returns AI response)
GET    /v1/ai/logs                       Completion logs

Webhooks

Templates can optionally trigger webhooks on two events:

Field Trigger
webhook_on_create After a template is rendered (e.g. POST the rendered output to an external service)
webhook_on_update When the template itself is modified (e.g. notify a CMS of content changes)

Webhooks are configured via the Webhook tabs in the template editor. See Webhooks for full webhook documentation.

Note: Automatic webhook execution after render is not yet wired — webhooks are currently stored as references for manual triggering.


Caching

Templates use a multi-layer caching strategy:

Layer Scope TTL Cleared on
Template list cache In-memory (BoundedCache, 500 entries) 1 hour Create, update, delete
Template by-ID cache In-memory 1 hour Update, delete
Render output cache render_job table Configurable (cache_ttl) Cache expiry or manual invalidation
ECharts chart cache chart_cache table (optional) Configurable Template update

Examples

HTML Email Template

<div style="font-family: sans-serif; max-width: 600px;">
  <h2>{{ title }}</h2>
  <p>{{ date | localdate }}</p>
  {% if location %}<p>Location: {{ location }}</p>{% endif %}
  {% if description %}<p>{{ description }}</p>{% endif %}
  <img src="{{ event_url | qrcode }}" width="150" />
</div>

Typst Invoice

#set page(paper: "letter", margin: 2cm)
#set text(font: "Inter", size: 10pt)

= Invoice {{ invoice_number }}

*Date:* {{ date | dddd }}
*Due:* {{ due_date | dddd }}

#table(
  columns: (1fr, auto, auto, auto),
  [*Item*], [*Qty*], [*Price*], [*Total*],
  {% for item in line_items %}
  [{{ item.name }}], [{{ item.qty }}], [${{ item.price }}], [${{ item.total }}],
  {% endfor %}
)

#align(right)[*Total: ${{ total }}*]

ECharts Visualization

{
  "xAxis": { "type": "category" },
  "yAxis": { "type": "value" },
  "series": [{
    "type": "bar",
    "data": "$fn:group_count(invoices, status, 10)",
    "itemStyle": { "color": "#4472C4" }
  }],
  "tooltip": { "trigger": "axis" }
}

Composable Template (HTML embedding a chart and table)

<h1>{{ title }}</h1>

<div class="chart">
  {{ "sales-chart" | render_chart("svg", 800, 400) }}
</div>

<h2>Details</h2>
{{ rows | to_md_table(["product", "revenue", "units"]) }}

<footer>Generated {{ "" | timestamp }}</footer>