diff --git a/docs/environments/environments-secrets.md b/docs/environments/environments-secrets.md index b10ddd0..b2780dd 100644 --- a/docs/environments/environments-secrets.md +++ b/docs/environments/environments-secrets.md @@ -101,6 +101,8 @@ This is the key difference from Custom Variables, which transfer with Blueprints Navigate to [Configuration > Environments](https://portal.epilot.cloud/app/settings/environments) to manage your variables. +![Environments & Secrets UI](../../static/img/environments/environments-secrets-list-view.png) + From this page you can: - **Create** new variables with a key, type, value, and optional description diff --git a/docs/templates/template-variables.md b/docs/templates/template-variables.md index cb8b87f..359b30f 100644 --- a/docs/templates/template-variables.md +++ b/docs/templates/template-variables.md @@ -9,461 +9,183 @@ sidebar_position: 0 The Template Variables API handles variable discovery and substitution for email and document templates using [Handlebars](https://handlebarsjs.com/). -## Template Variables API +## Overview -The API discovers available variables and executes substitution. When a template renders, the API fetches entity data and other context to resolve each variable. - -## Computed metadata fields - -Some entity fields store identifiers (slugs or UUIDs). The API expands these into human-readable values using the `:` suffix. - -### Tags - -Access resolved tag names from the `_tags` slug array: +Template variables allow you to insert dynamic content into your templates. When a template renders, the API fetches entity data and resolves each variable to its actual value. ```handlebars -{{._tags:name}} -{{opportunity._tags:name}} -{{_tags:name}} -``` - -### Purpose +Hello {{contact.first_name}}, -Access resolved purpose names from the `_purpose` UUID array: - -```handlebars -{{._purpose:name}} -{{opportunity._purpose:name}} -{{_purpose:name}} +Your order {{order._title}} has been confirmed. +Total: {{asCurrency order.total "EUR"}} ``` -If a purpose ID cannot be resolved, the raw ID is returned. +For a complete list of available variables and helpers, see the [Variable Reference](/docs/templates/variable-reference). -## Variable Picker +## Quick Start -The variable picker UI lets users search and explore available variables. +### Basic Entity Variables -## Variable Builder - -Create custom variables in **Configuration > Templates > Variable Builder** ([direct link](https://portal.epilot.cloud/app/variable-builder)). - -![Variable Builder UI](../../static/img/templates/variable-builder.png) - -### Custom Variables - -![Custom Variables](../../static/img/templates/custom-variables.png) - -Custom variables combine free text with any [Handlebars helper](#custom-handlebars-helpers). Reference them in templates with: - -```handlebars -{{custom_variable_name}} -``` - -:::tip -You can use Handlebars helpers directly in templates. Custom variables are useful for encapsulating reusable logic. -::: - -### Order Table Variable - -![Order Table Variable](../../static/img/templates/order-table.png) - -Order tables display line items from an [Order](/docs/pricing/orders). They support [Custom Variables](#custom-variables) and [Handlebars Helpers](#custom-handlebars-helpers). - -![Order Table Column Config](../../static/img/templates/order-table-2.png) -![Order Table Column Config 2](../../static/img/templates/order-table-3.png) - -Customize column order, styling, visibility, headers, and footers. - -![Order Table Column Config 3](../../static/img/templates/order-table-4.png) - -Reference an order table variable in templates with: +Access entity attributes using the entity slug as a prefix: ```handlebars -{{~~custom_table_key}} +{{contact.first_name}} +{{contact.last_name}} +{{order._title}} +{{opportunity.billing_address}} ``` -## Custom Handlebars Helpers - -### Standard Handlebars Helpers - -All helpers from the [`handlebars-helpers`](https://github.com/helpers/handlebars-helpers) library are available: `math`, `number`, `date`, `comparison`, `array`, `string`, `object`, `url`, and more. +### System Variables ```handlebars -{{add 1 2}} +{{system.date}} +{{system.time}} ``` -### formatAddress +### Environment Variables -Formats an address object into a single-line string. Returns empty string for invalid input. +Reference organization-level configuration. See [Environments & Secrets](/docs/environments/environments-secrets). ```handlebars -{{formatAddress 'billing_address.0'}} +{{env.portal.domain}} +{{env.erp_api.base_url}} ``` -### calculateColspan - -Calculates the colspan for table cells based on the table configuration. Used with [Order Table Variables](#order-table-variable). - -```handlebars -{{calculateColspan table_config}} -``` - -### calculatePeriodColspan - -Calculates the colspan for period-related table columns. Used with [Order Table Variables](#order-table-variable). - -```handlebars -{{calculatePeriodColspan table_config}} -``` - -### calculateSummaryColspan - -Calculates the colspan for summary sections based on enabled columns. Used with [Order Table Variables](#order-table-variable). - -```handlebars -{{calculateSummaryColspan table_config}} -``` - -### isColumnEnabled - -Returns `true` if the specified column ID is enabled in the table configuration. Used with [Order Table Variables](#order-table-variable). - -```handlebars -{{isColumnEnabled table_config 'price'}} -``` - -### shouldDisplayDetails - -Returns `true` if the column's `showDetails` property is set. Used with [Order Table Variables](#order-table-variable). - -```handlebars -{{shouldDisplayDetails table_config 'description'}} -``` - -### isSummaryVisible - -Returns `true` if at least one non-draggable column is enabled. Used with [Order Table Variables](#order-table-variable). - -```handlebars -{{isSummaryVisible table_config}} -``` - -### isExternalFeesMetadataVisible - -Returns `true` if `external_fees_metadata` is enabled in the table footer. Used with [Order Table Variables](#order-table-variable). - -```handlebars -{{isExternalFeesMetadataVisible table_config}} -``` - -### gt - -Compares two numbers and returns `true` if the first is greater than the second. +## Variable Picker -```handlebars -{{gt 5 3}} -``` +The variable picker UI in the template editor lets users search and explore available variables. Use it to discover entity attributes, helpers, and custom variables. -### lt +![Variable Builder UI](../../static/img/templates/variable-builder.png) -Compares two numbers and returns `true` if the first is less than the second. +## Custom Variables -```handlebars -{{lt 3 5}} -``` +Create reusable template logic in **Configuration > Templates > Variable Builder** ([direct link](https://portal.epilot.cloud/app/variable-builder)). -### eq +![Custom Variables](../../static/img/templates/custom-variables.png) -Compares two numbers and returns `true` if they are equal. +Custom variables combine free text with Handlebars helpers. Reference them in templates with: ```handlebars -{{eq 5 5}} +{{custom_variable_name}} ``` -### blockHelperMissing - -Returns an empty string when a block helper is missing. +For details, see [Custom Variables](/docs/templates/custom-variables). -```handlebars -{{#missingBlock}}content{{/missingBlock}} -``` +### Order Table Variables -### helperMissing +Order table variables display line items from an [Order](/docs/pricing/orders) with configurable columns, styling, and footers. -Returns an empty string when a helper is not found. - -### makeStyle +![Order Table Variable](../../static/img/templates/order-table.png) -Converts a table configuration object into a CSS style string. Used with [Order Table Variables](#order-table-variable). +Reference order tables with the `~~` prefix: ```handlebars -{{makeStyle table_config.style}} +{{~~custom_table_key}} ``` -### ```.``` +## Computed Metadata Fields -Dynamically registered from schema attributes (e.g., `main.email`, `contact.name`). Retrieves a property value from context data, prioritizing items tagged as "primary" or the first array element. Address properties are automatically formatted as full addresses. - -```handlebars -{{main.email}} -``` +Some entity fields store identifiers (slugs or UUIDs). The API expands these into human-readable values using the `:` suffix. -### email +### Tags -Retrieves the email from context, prioritizing `email` then `billing_email`. Returns the first element if the value is an array. +Access resolved tag names from the `_tags` slug array: ```handlebars -{{email "contact"}} +{{opportunity._tags:name}} +{{_tags:name}} ``` -### billing_email +### Purpose -Retrieves the billing email from context, prioritizing `billing_email` then `email`. Returns the first element if the value is an array. +Access resolved purpose names from the `_purpose` UUID array: ```handlebars -{{billing_email "contact"}} +{{opportunity._purpose:name}} +{{_purpose:name}} ``` -### phone +## Handlebars Helpers -Retrieves the phone number from context, prioritizing `phone` then `billing_phone`. Returns the first element if the value is an array. +epilot provides a rich set of Handlebars helpers for formatting, calculations, and logic. Here are the most commonly used: -```handlebars -{{phone "contact"}} -``` - -### billing_phone - -Retrieves the billing phone from context, prioritizing `billing_phone` then `phone`. Returns the first element if the value is an array. +### Date Formatting ```handlebars -{{billing_phone "contact"}} +{{formatDate "2025-05-13" "dd.MM.yyyy"}} +{{formatDateTime order.created_at}} +{{dateMath order.date "+14d"}} ``` -### address - -Formats an address from context, searching for addresses tagged `billing` or `primary`. Falls back to the first address. +### Currency Formatting ```handlebars -{{address}} +{{asCurrency 1000 "EUR"}} +{{asCurrency order.total "EUR" "de"}} ``` -### billing_address - -Formats an address from context, searching for addresses tagged `billing`, `shipping`, or `primary`. Falls back to the first address. +### Address Formatting ```handlebars +{{formatAddress 'billing_address.0'}} {{billing_address}} -``` - -### shipping_address - -Formats an address from context, searching for addresses tagged `shipping`, `billing`, or `primary`. Falls back to the first address. - -```handlebars {{shipping_address}} ``` -### delivery_address - -Formats an address from context, searching for addresses tagged `delivery` or `primary`. Falls back to the first delivery address. - -```handlebars -{{delivery_address}} -``` - -### additional_address - -Formats an address from context, searching for addresses tagged `primary`. - -```handlebars -{{additional_address}} -``` - -### withTag - -Retrieves a value from an array by tag (defaults to `primary`) and optional attribute. - -```handlebars -{{withTag items tag="primary" attribute="email"}} -``` - -### yn - -Converts a boolean-like value to a localized "Yes" or "No" string. Accepts optional custom `success`/`failure` values. - -```handlebars -{{yn true}} -``` - -### xif - -Returns `"x"` if the input is truthy, otherwise an empty string. - -```handlebars -{{xif true}} -``` - -### customOrderTableVariable - -Renders an [Order Table Variable](#order-table-variable). - -```handlebars -{{~~custom_table_key}} -``` - -### formatDateTime - -Formats a date/time string with a specified pattern. Defaults to short date-time format. - -```handlebars -{{formatDateTime "2025-05-13" "yyyy-MM-dd HH:mm"}} -``` - -### formatDate - -Formats a date string with a specified pattern. Defaults to date-only format. - -```handlebars -{{formatDate "2025-05-13" "yyyy-MM-dd"}} -``` - -### dateMath - -Performs date arithmetic (add/subtract days, months, etc.). Accepts `inputDate`, `expression`, `inputFormat`, and `format` parameters. +### Boolean Formatting ```handlebars -{{dateMath "2025-05-13" "+1d"}} +{{yn is_active}} +{{yn is_active "On" "Off"}} ``` -### padStart - -Pads a string to a target length with a specified character (defaults to space). - -```handlebars -{{padStart "5" 3 "0"}} -``` - -### generateJourneyLink - -Generates a signed journey link. Pass parameters as space-separated key-value pairs. - -**Required**: `journey_id` - -**Optional**: -- `custom_url` -- custom domain for the URL -- `expires_in` -- token expiration ([ms format](https://github.com/vercel/ms)) -- `nonce` -- add a nonce to the payload (boolean) - -Automatically includes `initial_submission_id` from context when available. +### Tag-Based Filtering ```handlebars -{{generateJourneyLink hash.journeyId="123"}} +{{withTag contacts tag="billing" attribute="email"}} ``` -### asCurrency - -Formats a number as a currency string using [@epilot/pricing](https://github.com/epilot-dev/pricing). Accepts `currency` (default: `EUR`), `locale` (default: `de`), and `displayZeroAmount` (default: `false`). - -```handlebars -{{asCurrency 100.50 "EUR"}} -``` +For the complete helper reference, see [Variable Reference - Handlebars Helpers](/docs/templates/variable-reference#handlebars-helpers). ## Excel-like Formulas -Use the `calc` helper for Excel-like formulas in templates. +Use the `calc` helper for Excel-like formulas: ```handlebars -Price: {{price}} -Qty: {{qty}} - -Total (rounded 2): {{ calc "ROUND(price * qty, 2)" }} -Discounted: {{ calc "ROUND((price * qty) * (1 - discount), 2)" }} +{{calc "ROUND(price * quantity, 2)"}} +{{calc "IF(qty > 10, 10, qty)"}} +{{calc "SUM(price1, price2, price3)"}} +{{calc "DATEADD(order.date, 14, \"days\")"}} +``` -Caps with IF: {{ calc "IF(qty > 10, 10, qty)" }} +### Available Functions -Using named args override: -{{ calc "ROUND(a + b, 0)" a=fee b=shipping }} +| Category | Functions | +|----------|-----------| +| **Math** | `ABS`, `CEIL`, `FLOOR`, `ROUND`, `SUM`, `AVERAGE`, `MIN`, `MAX`, `RAND` | +| **Logic** | `IF`, `AND`, `OR`, `NOT` | +| **Date** | `NOW`, `TODAY`, `DATEADD`, `DATEDIFF`, `YEAR`, `MONTH`, `DAY`, `HOUR`, `MINUTE`, `SECOND`, `WEEKDAY`, `WEEKNUM` | -Min/Max: -{{ calc "MAX(price1, price2, price3)" }} -``` +For detailed formula documentation, see [Variable Reference - Excel-like Formulas](/docs/templates/variable-reference#excel-like-formulas). -## Available formulas - -### Arithmetic operators - -- `+` - Addition -- `-` - Subtraction -- `*` - Multiplication -- `/` - Division -- `^` - Exponentiation -- `%` - Modulus (remainder of division) -- `()` - Parentheses for grouping expressions -- `-number` - Negation (unary minus) - -### Formula functions - -- `ABS(number)` - Returns the absolute value of a number. -- `AND(condition1, condition2, ...)` - Returns `true` if all conditions are truthy. -- `AVERAGE(number1, number2, ...)` - Returns the average of a set of numbers. -- `CEIL(number)` - Rounds a number up to the nearest integer. -- `FLOOR(number)` - Rounds a number down to the nearest integer. -- `IF(condition, value_if_true, value_if_false)` - Returns one value if condition is truthy and another value if it's falsey. -- `MAX(number1, number2, ...)` - Returns the largest number in a set of numbers. -- `MIN(number1, number2, ...)` - Returns the smallest number in a set of numbers. -- `NOT(condition)` - Reverses the value of its argument. Returns `true` if its argument is truthy and `false` if its argument is falsey. -- `OR(condition1, condition2, ...)` - Returns `true` if any condition is truthy. -- `ROUND(number, [places])` - Rounds a number to a specified number of decimal places. If places is omitted, it defaults to 0. -- `SUM(number1, number2, ...)` - Returns the sum of a set of numbers. -- `RAND()` - Returns a random number between 0 (inclusive) and 1 (exclusive). - -### Date functions - -All date functions work with ISO 8601 date strings (e.g., "2025-09-26" or "2025-09-26T12:00:00Z") and use UTC by default with optional timezone support. - -#### Current time -- `NOW([timezone])` - Returns the current date and time in ISO format (e.g., "2025-09-26T12:00:00.000Z") -- `TODAY([timezone])` - Returns the current date in ISO format (e.g., "2025-09-26") - -#### Date arithmetic -- `DATEADD(date, value, unit, [timezone])` - Adds/subtracts time from a date - - Units: "years", "quarters", "months", "weeks", "days", "hours", "minutes", "seconds" -- `DATEDIFF(start_date, end_date, unit, [timezone])` - Calculates difference between dates - - Returns floor of the difference (e.g., 1.9 months = 1) - -#### Date components -- `YEAR(date, [timezone])` - Extract year (e.g., 2025) -- `MONTH(date, [timezone])` - Extract month (1-12) -- `DAY(date, [timezone])` - Extract day of month (1-31) -- `HOUR(date, [timezone])` - Extract hour (0-23) -- `MINUTE(date, [timezone])` - Extract minute (0-59) -- `SECOND(date, [timezone])` - Extract second (0-59) -- `WEEKDAY(date, [type], [timezone])` - Get day of week - - `type=1`: Sunday=1, Saturday=7 (Excel default) - - `type=2` or omitted: Monday=1, Sunday=7 (ISO) - - `type=3`: Monday=0, Sunday=6 - -#### Week calculations -- `WEEKNUM(date, [type], [timezone])` - Get ISO week number of the year - -#### Examples +## Where Variables Are Supported -```handlebars - -Delivery date: {{ calc "DATEADD(order.date, 14, \"days\")" }} +Template variables are supported across epilot: - -Age: {{ calc "DATEDIFF(birthDate, TODAY(), \"years\")" }} years old +| Feature | Description | +|---------|-------------| +| **Email templates** | Subject lines and body content | +| **Document templates** | Word documents (.docx) with variable placeholders | +| **Webhooks** | URL, headers, and payload content | +| **Automation actions** | Action parameters and payloads | +| **Entity mapping** | Transformation rules | +| **Customer Portal** | Dynamic content and links | +| **ERP integrations** | Configuration and data transformation | - -Year: {{ calc "YEAR(invoice.date)" }} -Month: {{ calc "MONTH(invoice.date)" }} +## Related Documentation - -Due date: {{ calc "DATEADD(invoice.date, payment.terms, \"days\")" }} -Overdue: {{ calc "DATEDIFF(invoice.dueDate, TODAY(), \"days\") < 0" }} - - -Local date: {{ calc "TODAY(\"Europe/Helsinki\")" }} -``` +- [Variable Reference](/docs/templates/variable-reference) - Complete list of variables and helpers +- [Custom Variables](/docs/templates/custom-variables) - Create reusable template logic +- [Email Templates](/docs/templates/email-templates) - Email template configuration +- [Document Templates](/docs/templates/document-templates) - Document generation +- [Environments & Secrets](/docs/environments/environments-secrets) - Organization-level variables diff --git a/docs/templates/variable-reference.md b/docs/templates/variable-reference.md new file mode 100644 index 0000000..7be2638 --- /dev/null +++ b/docs/templates/variable-reference.md @@ -0,0 +1,547 @@ +--- +sidebar_position: 1 +--- + +# Variable Reference + +This page provides a complete reference for all variables and Handlebars helpers available in epilot templates. + +## Variable Syntax + +Variables use [Handlebars](https://handlebarsjs.com/) syntax with double curly braces: + +```handlebars +{{variable_name}} +{{entity.attribute}} +{{helper_name argument1 argument2}} +``` + +## Entity Variables + +Access entity data using the entity slug as a prefix: + +| Syntax | Description | +|--------|-------------| +| `{{contact.first_name}}` | Contact's first name | +| `{{order._title}}` | Order title | +| `{{opportunity.billing_address}}` | Opportunity's billing address | +| `{{_title}}` | Title of the main entity in context | + +### Nested Attributes + +Access nested attributes with dot notation: + +```handlebars +{{order.line_items.0.product.name}} +{{contact.payment.0.data.iban}} +``` + +### Computed Metadata Fields + +Some fields store IDs that are resolved to human-readable values using the `:` suffix: + +```handlebars +{{opportunity._tags:name}} +{{opportunity._purpose:name}} +``` + +## System Variables + +| Variable | Description | +|----------|-------------| +| `{{system.date}}` | Current date (formatted for the org locale) | +| `{{system.time}}` | Current time | + +## Environment Variables + +Reference organization-level environment variables in templates using the `{{ env.* }}` prefix. Environment variables are resolved at runtime, allowing the same template to work across sandbox and production environments. + +```handlebars +{{env.portal.domain}} +{{env.erp_api.base_url}} +{{env.n8n.metering_webhook_url}} +``` + +### Using Environment Variables in Templates + +Environment variables are supported anywhere template variables work: + +**Email subject:** +```handlebars +Your order confirmation from {{env.company.name}} +``` + +**Email body:** +```html +

+ View your order in the + customer portal. +

+``` + +**Document templates:** +```handlebars +Contact us at {{env.support.email}} or visit {{env.company.website}} +``` + +:::tip +Use [Environments & Secrets](/docs/environments/environments-secrets) to manage your organization's environment variables. Variables set there can be referenced with `{{ env.key }}` in any template. +::: + +### Variable Types + +| Type | In Templates | In Webhooks | +|------|:------------:|:-----------:| +| **String** | Yes | Yes | +| **SecretString** | No | Yes | + +`SecretString` values are only available in trusted backend contexts (webhooks, integrations) and are never exposed in user-facing templates. + +For full details on creating and managing environment variables, see [Environments & Secrets](/docs/environments/environments-secrets). + +## Organization Variables + +| Variable | Description | +|----------|-------------| +| `{{organization.name}}` | Organization name | +| `{{organization.website}}` | Organization website URL | +| `{{organization.email}}` | Organization email | +| `{{organization.phone}}` | Organization phone number | +| `{{organization.address}}` | Full formatted address | +| `{{organization.signature}}` | Email signature | + +## User Variables + +| Variable | Description | +|----------|-------------| +| `{{user.display_name}}` | Current user's display name | +| `{{user.email}}` | Current user's email | +| `{{user.phone}}` | Current user's phone number | +| `{{user.department}}` | Current user's department | + +## Portal & Link Variables + +| Variable | Context | Description | +|----------|---------|-------------| +| `{{consent.optInLink}}` | Email | Double opt-in confirmation link | +| `{{unsubscribe_url}}` | Email | Unsubscribe link | +| `{{portalUser.confirmationLink}}` | Email | Portal user email confirmation link | +| `{{portalUser.forgotPasswordLink}}` | Email | Portal password reset link | +| `{{customerPortal.invitationLink}}` | Email | Customer portal invitation link | +| `{{installerPortal.invitationLink}}` | Email | Installer portal invitation link | +| `{{customerPortal.newDocumentLink}}` | Email | Link to new document in customer portal | +| `{{customerPortal.entityLink}}` | Email | Link to entity in customer portal | +| `{{customerPortal.userEmailsOnEntity}}` | Email | Portal user emails associated with the entity | + +--- + +## Handlebars Helpers + +### Standard Library Helpers + +All helpers from [`handlebars-helpers`](https://github.com/helpers/handlebars-helpers) are available, including: + +- **math**: `add`, `subtract`, `multiply`, `divide`, `ceil`, `floor`, `round`, `abs` +- **number**: `bytes`, `addCommas`, `toFixed`, `toPrecision` +- **comparison**: `eq`, `ne`, `lt`, `lte`, `gt`, `gte`, `and`, `or`, `not` +- **array**: `first`, `last`, `after`, `before`, `join`, `sort`, `length`, `map`, `filter` +- **string**: `capitalize`, `lowercase`, `uppercase`, `trim`, `truncate`, `replace`, `split` +- **date**: `moment` (Moment.js formatting) +- **object**: `get`, `keys`, `values`, `extend`, `merge` +- **url**: `encodeURI`, `decodeURI`, `urlResolve`, `urlParse` + +```handlebars +{{add 1 2}} +{{multiply price quantity}} +{{uppercase "hello"}} +{{truncate description 100}} +``` + +--- + +### Address Helpers + +#### formatAddress + +Formats an address object into a single-line string. + +```handlebars +{{formatAddress 'billing_address.0'}} +``` + +#### address + +Formats the primary address from context (searches for `billing` or `primary` tags). + +```handlebars +{{address}} +{{address "contact"}} +``` + +#### billing_address + +Formats the billing address (searches for `billing`, `shipping`, or `primary` tags). + +```handlebars +{{billing_address}} +``` + +#### shipping_address + +Formats the shipping address (searches for `shipping`, `billing`, or `primary` tags). + +```handlebars +{{shipping_address}} +``` + +#### delivery_address + +Formats the delivery address (searches for `delivery` or `primary` tags). + +```handlebars +{{delivery_address}} +``` + +#### additional_address + +Formats an additional address tagged as `primary`. + +```handlebars +{{additional_address}} +``` + +--- + +### Contact Helpers + +#### email + +Retrieves the email address, prioritizing `email` then `billing_email`. + +```handlebars +{{email}} +{{email "contact"}} +``` + +#### billing_email + +Retrieves the billing email, prioritizing `billing_email` then `email`. + +```handlebars +{{billing_email}} +``` + +#### phone + +Retrieves the phone number, prioritizing `phone` then `billing_phone`. + +```handlebars +{{phone}} +{{phone "contact"}} +``` + +#### billing_phone + +Retrieves the billing phone, prioritizing `billing_phone` then `phone`. + +```handlebars +{{billing_phone}} +``` + +--- + +### Date & Time Helpers + +#### formatDate + +Formats a date string. Defaults to `dd.MM.yyyy`. + +```handlebars +{{formatDate "2025-05-13"}} +{{formatDate order.created_at "yyyy-MM-dd"}} +``` + +#### formatDateTime + +Formats a date/time string. Defaults to `dd.MM.yyyy HH:mm`. + +```handlebars +{{formatDateTime "2025-05-13T14:30:00Z"}} +{{formatDateTime order.created_at "yyyy-MM-dd HH:mm:ss"}} +``` + +#### dateMath + +Performs date arithmetic. + +**Parameters:** +- `inputDate` - The starting date +- `expression` - Arithmetic expression like `+1d`, `-2w`, `+1M` +- `inputFormat` (optional) - Input date format +- `format` (optional) - Output date format + +```handlebars +{{dateMath "2025-05-13" "+14d"}} +{{dateMath order.date "-1M"}} +{{dateMath "2025-05-13" "+1y" format="dd.MM.yyyy"}} +``` + +**Expression units:** `d` (days), `w` (weeks), `M` (months), `y` (years) + +--- + +### Currency Helpers + +#### asCurrency + +Formats a number as currency using [@epilot/pricing](https://github.com/epilot-dev/pricing). + +**Parameters:** +- `amount` - The number to format +- `currency` (default: `EUR`) - Currency code +- `locale` (default: `de`) - Locale for formatting +- `displayZeroAmount` (default: `false`) - Show zero amounts + +```handlebars +{{asCurrency 1000}} +{{asCurrency 99.99 "USD" "en"}} +{{asCurrency order.total "EUR" "de" true}} +``` + +#### fromCents + +Converts a cent value to decimal. Handles both raw integers and formatted strings. + +```handlebars +{{fromCents 23562}} +{{fromCents "9.999,00 €"}} +{{multiply (fromCents product.price.amount_total) 1.1}} +``` + +#### toNumber + +Parses a locale-formatted number string to a number. + +```handlebars +{{toNumber "1,50"}} +{{toNumber "1.50"}} +``` + +--- + +### Comparison Helpers + +#### gt + +Returns `true` if the first value is greater than the second. + +```handlebars +{{#if (gt quantity 10)}}Bulk discount!{{/if}} +``` + +#### lt + +Returns `true` if the first value is less than the second. + +```handlebars +{{#if (lt stock 5)}}Low stock{{/if}} +``` + +#### eq + +Returns `true` if two values are equal. + +```handlebars +{{#if (eq status "approved")}}Approved!{{/if}} +``` + +--- + +### Boolean Helpers + +#### yn + +Converts a boolean value to localized "Yes" or "No". + +```handlebars +{{yn true}} +{{yn false}} +{{yn is_active "Active" "Inactive"}} +``` + +#### xif + +Returns `"x"` if truthy, empty string if falsy. Useful for checkboxes in documents. + +```handlebars +{{xif is_selected}} +``` + +--- + +### Tag & Filter Helpers + +#### withTag + +Retrieves a value from an array by tag. + +**Parameters:** +- `arr` - Array to search +- `tag` (default: `primary`) - Tag to match +- `attribute` (optional) - Attribute to extract + +```handlebars +{{withTag contacts tag="billing" attribute="email"}} +{{withTag addresses tag="delivery"}} +``` + +**Special tags:** +- `*` - Get values from all items (comma-separated) +- `...tagname` - Get all items matching the tag (spread) + +```handlebars +{{withTag items tag="*" attribute="name"}} +{{withTag contacts tag="...customer" attribute="email"}} +``` + +--- + +### String Helpers + +#### padStart + +Pads a string to a target length with a character. + +```handlebars +{{padStart "5" 3 "0"}} +{{padStart invoice_number 8 "0"}} +``` + +--- + +### Journey Link Helpers + +#### generateJourneyLink + +Generates a signed journey link with optional parameters. + +**Required:** +- `journey_id` - The journey ID + +**Optional:** +- `custom_url` - Custom domain for the URL +- `expires_in` - Token expiration ([ms format](https://github.com/vercel/ms)) +- `nonce` - Add a nonce to the payload + +```handlebars +{{generateJourneyLink hash.journeyId="abc123"}} +{{generateJourneyLink hash.journeyId="abc123" hash.expires_in="7d"}} +{{generateJourneyLink hash.journeyId="abc123" hash.custom_url="portal.example.com"}} +``` + +--- + +### Order Table Helpers + +These helpers are used internally by [Order Table Variables](/docs/templates/custom-variables#order-table-variable). + +| Helper | Description | +|--------|-------------| +| `calculateColspan` | Calculates colspan for table cells | +| `calculatePeriodColspan` | Calculates colspan for period columns | +| `calculateSummaryColspan` | Calculates colspan for summary sections | +| `isColumnEnabled` | Checks if a column is enabled | +| `shouldDisplayDetails` | Checks if column details should display | +| `isSummaryVisible` | Checks if summary section is visible | +| `isExternalFeesMetadataVisible` | Checks if external fees are visible | +| `makeStyle` | Converts config to CSS style string | + +--- + +## Excel-like Formulas + +The `calc` helper supports Excel-like formulas for calculations. + +```handlebars +{{calc "ROUND(price * quantity, 2)"}} +{{calc "IF(qty > 10, 10, qty)"}} +{{calc "MAX(price1, price2, price3)"}} +{{calc "ROUND(a + b, 0)" a=fee b=shipping}} +``` + +### Arithmetic Operators + +| Operator | Description | +|----------|-------------| +| `+` | Addition | +| `-` | Subtraction | +| `*` | Multiplication | +| `/` | Division | +| `^` | Exponentiation | +| `%` | Modulus | +| `()` | Grouping | +| `-n` | Negation | + +### Formula Functions + +| Function | Description | +|----------|-------------| +| `ABS(n)` | Absolute value | +| `AND(a, b, ...)` | Logical AND | +| `AVERAGE(a, b, ...)` | Average of values | +| `CEIL(n)` | Round up | +| `FLOOR(n)` | Round down | +| `IF(cond, t, f)` | Conditional | +| `MAX(a, b, ...)` | Maximum value | +| `MIN(a, b, ...)` | Minimum value | +| `NOT(a)` | Logical NOT | +| `OR(a, b, ...)` | Logical OR | +| `ROUND(n, places)` | Round to decimal places | +| `SUM(a, b, ...)` | Sum of values | +| `RAND()` | Random number 0-1 | + +### Date Functions + +All date functions work with ISO 8601 strings and use UTC by default. + +| Function | Description | +|----------|-------------| +| `NOW([tz])` | Current datetime | +| `TODAY([tz])` | Current date | +| `DATEADD(date, val, unit, [tz])` | Add/subtract time | +| `DATEDIFF(start, end, unit, [tz])` | Difference between dates | +| `YEAR(date, [tz])` | Extract year | +| `MONTH(date, [tz])` | Extract month (1-12) | +| `DAY(date, [tz])` | Extract day (1-31) | +| `HOUR(date, [tz])` | Extract hour (0-23) | +| `MINUTE(date, [tz])` | Extract minute (0-59) | +| `SECOND(date, [tz])` | Extract second (0-59) | +| `WEEKDAY(date, [type], [tz])` | Day of week | +| `WEEKNUM(date, [type], [tz])` | Week number | + +**DATEADD units:** `years`, `quarters`, `months`, `weeks`, `days`, `hours`, `minutes`, `seconds` + +```handlebars +{{calc "DATEADD(order.date, 14, \"days\")"}} +{{calc "DATEDIFF(start_date, TODAY(), \"months\")"}} +{{calc "YEAR(invoice.date)"}} +``` + +--- + +## Where Variables Are Supported + +| Feature | Variables | Environment Variables | Secrets | +|---------|:---------:|:--------------------:|:-------:| +| Email templates | Yes | Yes | No | +| Document templates | Yes | Yes | No | +| Webhooks | Yes | Yes | Yes | +| Automation actions | Yes | Yes | Yes | +| Entity mapping | Yes | Yes | Yes | +| Journey configurations | Yes | Yes | No | +| Customer Portal | Yes | Yes | No | +| ERP integrations | Yes | Yes | Yes | + +:::note +**Secrets** (`SecretString` environment variables) are only available in trusted backend contexts like webhooks and integrations. They are never exposed in templates or the frontend. +::: diff --git a/static/img/environments/environments-secrets-list-view.png b/static/img/environments/environments-secrets-list-view.png new file mode 100644 index 0000000..27aaa29 Binary files /dev/null and b/static/img/environments/environments-secrets-list-view.png differ