oneVcard Template Engine
The oneVcard Template Engine is available at selected locations, allowing you to enrich your content with dynamic data.
Simple Placeholders
A field name in double curly braces is replaced by its corresponding value.
{{placeholder}}Example:
Data:
{ "firstName": "Max", "lastName": "Doe" }Template:
Hello {{firstName}} {{lastName}}!Result:
Hello Max Doe!Missing Values
If a placeholder finds no value in the data, it is replaced by an empty string.
Hello {{name}}! → Hello !Path Resolution
Placeholders can access nested fields in objects and arrays.
Nested Objects
Use a dot (.) as a separator:
{{user.address.city}}Data:
{ "user": { "address": { "city": "Munich" } }}Result: Munich
Arrays by Index
Arrays are accessed using zero-based indices:
{{contacts[0].name}}{{contacts[1].email}}Data:
{ "contacts": [ { "name": "Anna", "email": "anna@example.com" }, { "name": "Bob", "email": "bob@example.com" } ]}Result: Anna / bob@example.com
Array Filters
You can filter an array by a specific field and then access the result:
{{contacts(type=billing_address)[0].name}}This filters the contacts array for all entries where type === "billing_address" and returns the first match.
Optional Blocks
Optional blocks are written in double square brackets: [[ ... ]]
[[Street: {{address.street}}]][[ZIP: {{address.zipCode}}]]Data:
{ "address": { "street": "Sample Street 1" } }Result:
Street: Sample Street 1The ZIP block is omitted entirely because address.zipCode is not present.
Multiple Placeholders in One Block
All placeholders in a block must be present for the block to be rendered:
[[{{firstName}} {{lastName}}]]Only when both fields are present will the block appear.
Conditions
Conditions allow you to show or hide content depending on the data.
Basic Structure
{#if condition} Content{/if}{#if condition} Content{:else} Fallback{/if}{#if condition1} Content 1{:else if condition2} Content 2{:else} Fallback{/if}Truthy/Falsy Values
When simply checking for the presence or truthiness of a field:
| Value | Result |
|---|---|
true, "true", non-empty string | true |
| non-empty array | true |
false, "false", 0, "" | false |
missing / null / undefined | false |
{#if user.active}Account is active{:else}Account locked{/if}Comparison Operators
| Operator | Meaning |
|---|---|
== | equal |
!= | not equal |
> | greater than |
< | less than |
>= | greater than or equal |
<= | less than or equal |
{#if user.age >= 18}Adult{:else}Minor{/if}{#if user.role == 'admin'}Administrator access{/if}{#if status != 'locked'}Access allowed{/if}Strings in conditions are enclosed in single or double quotes:
{#if country == "US"}Welcome to the United States{/if}{#if country == 'GB'}Welcome to the United Kingdom{/if}Logical Operators
Conditions can be combined with && (AND) and || (OR):
{#if active && age >= 18}Access granted{/if}{#if role == 'admin' || role == 'manager'}Management{/if}{#if a || b || c}At least one applies{/if}Grouping
Use parentheses to define the order of evaluation:
{#if (role == 'admin' || role == 'editor') && active}Active editor{/if}{#if age >= 18 && (member || promo)}Discount available{/if}Block Functions
Block functions wrap content and transform or control its output.
{{#function attribute="value"}} Content{{/function}}join
Joins multiple optional blocks with a delimiter. Blocks whose placeholders have no value are automatically skipped.
Attributes:
| Attribute | Description | Default |
|---|---|---|
delimiter | Separator between blocks | "" |
prefix | Text before the result | "" |
suffix | Text after the result | "" |
Example - address line:
{{#join delimiter=", "}} [[{{street}}]] [[{{zipCode}} {{city}}]] [[{{country}}]]{{/join}}Data:
{ "street": "Sample Street 1", "city": "Munich" }Result: Sample Street 1, Munich
zipCode and city are missing. Their blocks are skipped — no double commas.
Example - with prefix and suffix:
{{#join delimiter=" · " prefix="[" suffix="]"}} [[{{tag1}}]][[{{tag2}}]][[{{tag3}}]]{{/join}}Data:
{ "tag1": "Invoice", "tag3": "Urgent" }Result: [Invoice · Urgent]
Line break as delimiter:
Use \n for a line break:
{{#join delimiter="\n"}}[[{{line1}}]][[{{line2}}]][[{{line3}}]]{{/join}}uppercase
Converts all content to uppercase.
{{#uppercase}}{{salutation}} {{lastName}}{{/uppercase}}Data:
{ "salutation": "Mr.", "lastName": "Miller" }Result: MR. MILLER
format
Formats a value according to a specific style.
Attributes:
| Attribute | Description |
|---|---|
style | Formatting style: "phoneNumber" or "date" |
Phone Number Formatting (style="phoneNumber")
Formats a phone number into international format.
| Attribute | Description | Default |
|---|---|---|
country | Country code (ISO 3166-1) for numbers without a prefix | none |
{{#format style="phoneNumber"}}{{phoneNumber}}{{/format}}Numbers with an international prefix (+49, +1, +44, …) are automatically assigned to the correct country — no country attribute needed:
+491782367141 → +49 178 2367141+12025550123 → +1 202 555 0123+447911123456 → +44 7911 123456Numbers without a prefix require the country attribute:
{{#format style="phoneNumber" country="US"}}{{phoneNumber}}{{/format}}
2025550123 → +1 202 555 0123If the number cannot be parsed, the original value is returned unchanged.
Date Formatting (style="date")
Formats a date according to a pattern or locale.
| Attribute | Description | Default |
|---|---|---|
pattern | Output format (token pattern, see below) | Locale-dependent |
locale | Language/region format for output without pattern | "de-DE" |
inputFormat | Input format when the value is not an ISO date (token pattern) | automatic |
Pattern Tokens
| Token | Description | Example |
|---|---|---|
yyyy | 4-digit year | 2025 |
yy | 2-digit year | 25 |
MM | Month (2 digits) | 06 |
dd | Day (2 digits) | 15 |
HH | Hour (24h, 2 digits) | 14 |
mm | Minute (2 digits) | 30 |
ss | Second (2 digits) | 00 |
MMMM | Month name (written out) | June |
EEE | Day of week (short) | Mon. |
EEEE | Day of week (written out) | Monday |
Examples:
{{#format style="date" pattern="MM/dd/yyyy"}}{{createdAt}}{{/format}}Input: 2025-06-15 → Output: 06/15/2025
{{#format style="date" pattern="MM/dd/yyyy HH:mm"}}{{createdAt}}{{/format}}Input: 2025-06-15T14:30:00 → Output: 06/15/2025 14:30
{{#format style="date" pattern="EEEE, MMMM dd, yyyy" locale="en-US"}}{{date}}{{/format}}Input: 2025-06-15 → Output: Sunday, June 15, 2025
Input with Custom Format (inputFormat)
If your data does not contain an ISO date but e.g. 06/15/2025, use inputFormat:
{{#format style="date" inputFormat="MM/dd/yyyy" pattern="yyyy-MM-dd"}}{{date}}{{/format}}Input: 06/15/2025 → Output: 2025-06-15
Inputs are parsed in this order:
- Custom
inputFormat(if specified) - ISO 8601 (
2025-06-15or2025-06-15T14:30:00Z) - Unix timestamp in milliseconds
replace
Replaces all occurrences of a text with another.
Attributes:
| Attribute | Description |
|---|---|
from | The text to be replaced |
to | The replacement text |
{{#replace from="Inc." to="Corp."}}{{company}}{{/replace}}Data:
{ "company": "Sample Inc." }Result: Sample Corp.
Deleting text (replacement with empty string):
{{#replace from=" " to=""}}{{productCode}}{{/replace}}Input: AB 1234 CD → Output: AB1234CD
each
Iterates over an array or object and outputs the content for each element.
Attributes:
| Attribute | Description | Default |
|---|---|---|
data | Path to the array or object | - |
as | Name of the loop variable | entry |
Array of Objects
{{#each data=users as="user"}} - {{user.firstName}} {{user.lastName}}{{/each}}Data:
{ "users": [ { "firstName": "Anna", "lastName": "Smith" }, { "firstName": "Bob", "lastName": "Miller" } ]}Result:
- Anna Smith - Bob MillerConditions within each
You can use {#if} inside {{#each}}. The condition is evaluated with the data of the current element:
{{#each data=users as="user"}} {#if user.active}✓ {{user.firstName}}{:else}✗ {{user.firstName}}{/if}{{/each}}Optional Blocks within each
Optional blocks [[ ]] also work inside the loop:
{{#each data=contacts as="k"}} [[{{k.firstName}} {{k.lastName}}]]{{/each}}Only contacts where both fields are present will appear in the output.
Iterating Objects
For objects, each key-value pair is passed as a two-part element:
entry.0→ the keyentry.1→ the value
{{#each data=properties as="e"}} {{e.0}}: {{e.1}}{{/each}}Data:
{ "properties": { "color": "Blue", "size": "XL" } }Result:
color: Blue size: XLAccessing Outer Data
Inside the loop, you also have access to all fields outside the array:
{{#each data=articles as="a"}} {{a.name}} - Currency: {{currency}}{{/each}}Pipe Syntax
The pipe syntax is a more compact alternative to block functions when you want to transform a single value. Multiple pipes can be chained.
{{placeholder | function}}{{placeholder | function(attribute="value")}}{{placeholder | function1 | function2}}Available Pipe Functions
| Function | Description |
|---|---|
uppercase | Converts to uppercase |
format(...) | Formats (date, phone number) |
replace(...) | Replaces text |
Examples:
{{name | uppercase}}max doe → MAX DOE
{{phoneNumber | format(style="phoneNumber")}}+12025550123 → +1 202 555 0123
{{date | format(style="date" pattern="MM/dd/yyyy")}}2025-06-15 → 06/15/2025
{{articleName | replace(from=" " to="_") | uppercase}}Article Name → ARTICLE_NAME
Pipes in Optional Blocks
Pipes also work inside [[ ]] blocks. Block validity is determined by the source value, not the formatted result:
[[{{phoneNumber | format(style="phoneNumber")}}]]The block is omitted if phoneNumber is missing or empty, regardless of what the formatting would return.
Attributes in Pipe Functions
As with block functions, you can pass attributes by name or positionally (see Attributes - Named vs. Positional):
{{date | format(style="date" pattern="MM/dd/yyyy")}} ← named{{date | format("date" pattern="MM/dd/yyyy")}} ← style positional{{text | replace("old", "new")}} ← both positionalGlobal Variables
The system provides some built-in variables under the g. prefix.
g.now - Current Date and Time
g.now returns the current date and time at the moment the template is evaluated.
Formatting the output:
{{g.now | format(style="date" pattern="MM/dd/yyyy")}}Output: 04/17/2026
{{g.now | format(style="date" pattern="MM/dd/yyyy HH:mm")}}Output: 04/17/2026 09:45
Using in conditions (see next section):
{#if g.now < "2025-12-31"}Promotion still running{:else}Promotion ended{/if}Date Comparisons
Date values can be compared directly in conditions. The system automatically recognizes ISO 8601 date strings and compares them correctly as points in time, not as strings.
Supported Date Formats in Conditions
| Format | Example |
|---|---|
| ISO date | "2025-12-31" |
| ISO date with time | "2025-12-31T23:59:59" |
| ISO date with timezone | "2025-12-31T23:59:59Z" |
Examples
Checking a promotion period:
{#if g.now >= "2025-01-01" && g.now <= "2025-12-31"} Promotion 2025 is active{/if}Checking an expiry date from data:
{#if contract.validUntil >= g.now} Contract is valid{:else} Contract has expired{/if}Comparing two date fields:
{#if delivery.arrival < delivery.planned} Delivered early{:else if delivery.arrival == delivery.planned} Delivered on time{:else} Delayed{/if}Attributes - Named vs. Positional
Attributes in block functions and pipes can be specified in two ways.
Named Attributes
The attribute name is specified explicitly. Order does not matter.
{{#format style="date" pattern="MM/dd/yyyy"}}{{date}}{{/format}}{{#format pattern="MM/dd/yyyy" style="date"}}{{date}}{{/format}} ← equivalentPositional Attributes
The value is specified in quotes without a key name. The order determines which parameter the value is assigned to.
{{#format "phoneNumber"}}{{phoneNumber}}{{/format}}{{#format "date" pattern="MM/dd/yyyy"}}{{date}}{{/format}}Positional attributes can be mixed with named ones. Named attributes always take precedence.
Positional Order per Function
| Function | Position 0 | Position 1 | Position 2 |
|---|---|---|---|
format | style | locale / country | pattern |
replace | from | to | - |
Short form (pipe):
{{phoneNumber | format("phoneNumber")}}{{date | format("date" pattern="MM/dd/yyyy")}}{{text | replace("old", "new")}}Known Limitations
Nested {{#each}} Blocks
Multiple nested {{#each}} blocks are currently not supported. Only the outermost level is processed correctly.
{{#each data=categories as="cat"}} {{#each data=cat.articles as="art"}} ← does not work {{art.name}} {{/each}}{{/each}}{#if} Outside of {{#each}} Content
If a condition references fields that only exist inside a {{#each}} loop, the condition must be placed inside the {{#each}} block, which is supported. Conditions that reference loop variables from outside are not possible.
Quick Reference
Syntax Overview
| Syntax | Description |
|---|---|
{{field}} | Simple placeholder |
{{obj.field}} | Nested field |
{{arr[0].field}} | Array access by index |
{{arr(key=val)[0].field}} | Array filter |
[[...{{field}}...]] | Optional block (omitted if empty) |
{#if cond}...{/if} | Condition |
{#if cond}...{:else}...{/if} | Condition with else |
{#if cond}...{:else if cond2}...{:else}...{/if} | Multi-condition |
{{#join delimiter=","}}...{{/join}} | Join blocks |
{{#uppercase}}...{{/uppercase}} | Uppercase |
{{#format style="date" pattern="..."}}...{{/format}} | Date formatting |
{{#format style="phoneNumber"}}...{{/format}} | Phone number formatting |
{{#replace from="x" to="y"}}...{{/replace}} | Replace text |
{{#each data=list as="item"}}...{{/each}} | Loop |
{{field | function}} | Pipe |
{{field | function | function2}} | Pipe chain |
g.now | Current date/time |
Comparison Operators
| Operator | Meaning |
|---|---|
== | equal |
!= | not equal |
> | greater than |
< | less than |
>= | greater than or equal |
<= | less than or equal |
&& | logical AND |
|| | logical OR |
Date Tokens (Selection)
| Token | Output |
|---|---|
yyyy | 2025 |
MM | 06 |
dd | 15 |
HH:mm | 14:30 |
MM/dd/yyyy | 06/15/2025 |
MMMM yyyy | June 2025 |
EEEE, MMMM dd, yyyy | Sunday, June 15, 2025 |