Skip to content

Templating (Safe Placeholder Subset)

Templates support placeholders inside string values using a strict safe grammar.

Allowed Syntax

Only these forms are supported:

  1. Variable path (dot notation):
json
{ "type": "text", "value": "Hello {{ customer.name }}" }
  1. Allowed filter chain:
json
{ "type": "text", "value": "{{ issued_at|date('%d/%m/%Y') }}" }

Supported filters:

  • date(format?)
  • datetime(format?)
  • currency(symbol?, decimals?)
  • concat(value?)

Table Loop Alias (item)

Dynamic table columns run in a per-row context with item:

json
{
  "type": "table",
  "source": "lines",
  "columns": [
    { "label": "Concept", "value": "{{ item.concept }}" },
    { "label": "Qty", "value": "{{ item.quantity }}" }
  ]
}

When source resolves to an empty array, renderer inserts one row using empty_message.

Static table modes are also supported:

  • table with data: no loop, cells are rendered directly (placeholders still resolve).
  • table with columns and no source: renderer creates a single row from columns[].value.

Examples

json
{
  "elements": [
    { "type": "text", "value": "{{ total|currency('€', 2) }}" },
    { "type": "text", "value": "{{ prefix|concat(suffix) }}" },
    { "type": "text", "value": "{{ created_at|datetime('%Y-%m-%d %H:%M') }}" }
  ]
}

Not Allowed

These are rejected with actionable issues (for example PLACEHOLDER_UNSAFE_EXPRESSION):

  • function calls in expressions
  • control flow tags ({% ... %})
  • comments ({# ... #})
  • arbitrary expression syntax outside the safe placeholder subset

Example rejected payload:

json
{ "type": "text", "value": "{{ cycler.__init__.__globals__.os.popen('id').read() }}" }

Missing Data Behavior

  1. POST /api/public/validate
  • Missing placeholders become PLACEHOLDER_MISSING warnings.
  1. POST /api/public/render
  • Missing placeholders are replaced with empty string.

Data Payload

When rendering with data:

json
{
  "template": { "elements": [ { "type": "text", "value": "{{ total }}" } ] },
  "data": { "total": 123.45 }
}