Variables & Dynamic Content
Variables are the heart of FormaMail’s template system, allowing you to create dynamic, personalized emails and attachments. This comprehensive guide covers everything you need to know about using variables effectively.
Table of Contents
- Overview
- Variable Types
- Constant Variables
- System-Provided Global Constants
- Calculated Variables
- Using Variables in Templates
- Data Tables & Columns
- Formatting Options
- Best Practices
- Examples
Overview
Variables allow you to insert dynamic content into your templates. When sending an email or generating an attachment, you provide actual values for these variables, and FormaMail substitutes them into your template.
Key Concepts
Variables: Placeholders for dynamic data (e.g., {{firstName}}, {{order.total}})
All variable types are stored in a single unified variables array with different characteristics:
- Input Variables: Required data provided via API (
required: true) - Constant Variables: Fixed values with defaults, not requiring user input (
required: false,isCalculated: false, hasdefaultValue) - Calculated Variables: Computed values derived from other variables (
isCalculated: true, hasexpression)
Variable Syntax
Variables use Handlebars syntax with double curly braces:
{{variableName}}
{{user.email}}
{{order.items[0].name}}Important: Handlebars syntax ({{}}) is used for:
- Inserting variables in templates
- Referencing data sources in tables and loops
Direct references (without {{}}) are used for:
- Calculated variable expressions
- Table column formulas
- Conditional expressions
Variable Types
FormaMail supports six variable types, each with specific characteristics and validation rules.
1. String Variables
Text data of any length.
Definition:
{
"name": "firstName",
"type": "string",
"required": true,
"defaultValue": "Guest",
"description": "Customer's first name"
}Properties:
format: Optional format validation (email,url,uuid,date,datetime,phone)minLength: Minimum string lengthmaxLength: Maximum string length
Examples:
{
"name": "email",
"type": "string",
"format": "email",
"required": true
}
{
"name": "companyWebsite",
"type": "string",
"format": "url",
"defaultValue": "https://example.com"
}
{
"name": "description",
"type": "string",
"minLength": 10,
"maxLength": 500
}Usage in template:
Hello {{firstName}},
Your email is {{email}}.
Visit us at {{companyWebsite}}.2. Number Variables
Numeric data (integers or decimals).
Definition:
{
"name": "quantity",
"type": "number",
"required": true,
"defaultValue": 1,
"min": 0,
"max": 1000
}Properties:
min: Minimum valuemax: Maximum valueformat: Formatting options for display
Formatting Options:
{
"name": "price",
"type": "number",
"format": {
"type": "number",
"preset": "CURRENCY",
"currency": "USD",
"decimals": 2
}
}Format Presets:
DECIMAL: Standard decimal number (e.g., 1234.56)CURRENCY: Currency with symbol (e.g., $1,234.56)PERCENT: Percentage (e.g., 75.5%)CUSTOM: Custom format string
Supported Currencies:
USD, EUR, GBP, JPY, CNY, INR, CAD, AUD
Examples:
{
"name": "total",
"type": "number",
"format": {
"type": "number",
"preset": "CURRENCY",
"currency": "USD",
"decimals": 2
}
}
{
"name": "discountPercent",
"type": "number",
"format": {
"type": "number",
"preset": "PERCENT",
"decimals": 1
}
}Usage in template:
Quantity: {{quantity}}
Price: {{price}}
Total: {{total}}
Discount: {{discountPercent}}3. Date Variables
Date and time values.
Definition:
{
"name": "orderDate",
"type": "date",
"required": true,
"description": "Order placement date"
}Accepted Formats:
- ISO 8601 string:
"2025-01-15T10:30:00Z" - Date object:
new Date() - Unix timestamp:
1705320600000
Formatting Options:
{
"name": "deliveryDate",
"type": "date",
"format": {
"type": "date",
"preset": "US"
}
}Date Format Presets:
ISO: 2025-01-15 (YYYY-MM-DD)US: 01/15/2025 (MM/DD/YYYY)EU: 15/01/2025 (DD/MM/YYYY)LONG: January 15, 2025SHORT: Jan 15, 2025CUSTOM: Custom format string
Custom Format Tokens:
yyyy: 4-digit year (2025)yy: 2-digit year (25)MMMM: Full month name (January)MMM: Short month name (Jan)MM: 2-digit month (01)M: Month without leading zero (1)dd: 2-digit day (15)d: Day without leading zero (15)HH: 24-hour format (14)hh: 12-hour format (02)mm: Minutes (30)ss: Seconds (45)a: AM/PM
Examples:
{
"name": "invoiceDate",
"type": "date",
"format": {
"type": "date",
"preset": "US"
}
}
{
"name": "appointmentTime",
"type": "date",
"format": {
"type": "date",
"preset": "CUSTOM",
"customFormat": "MMMM dd, yyyy at hh:mm a"
}
}Usage in template:
Order placed on {{orderDate}}
Delivery scheduled for {{deliveryDate}}
Appointment: {{appointmentTime}}4. Boolean Variables
True/false values.
Definition:
{
"name": "isPremiumUser",
"type": "boolean",
"defaultValue": false
}Usage in Conditionals:
{{#if isPremiumUser}}
Welcome, Premium Member!
{{else}}
Upgrade to Premium for exclusive benefits.
{{/if}}5. Object Variables
Structured data with nested properties.
Definition:
{
"name": "customer",
"type": "object",
"properties": {
"name": { "type": "string", "required": true },
"email": { "type": "string", "format": "email", "required": true },
"phone": { "type": "string" },
"address": {
"type": "object",
"properties": {
"street": { "type": "string" },
"city": { "type": "string" },
"state": { "type": "string" },
"zipCode": { "type": "string" }
}
}
}
}Nesting Limit: Maximum 5 levels deep
Usage in template:
Customer: {{customer.name}}
Email: {{customer.email}}
Address: {{customer.address.street}}, {{customer.address.city}}, {{customer.address.state}} {{customer.address.zipCode}}Usage in API:
{
"customer": {
"name": "John Doe",
"email": "john@example.com",
"phone": "+1-555-0123",
"address": {
"street": "123 Main St",
"city": "San Francisco",
"state": "CA",
"zipCode": "94105"
}
}
}6. Array Variables
Lists of items (useful for tables and loops).
Definition:
{
"name": "orderItems",
"type": "array",
"itemType": "object",
"properties": {
"name": { "type": "string", "required": true },
"quantity": { "type": "number", "required": true },
"unitPrice": { "type": "number", "required": true },
"total": { "type": "number", "required": true }
}
}Item Types:
string: Array of stringsnumber: Array of numbersdate: Array of datesboolean: Array of booleansobject: Array of objects (most common for tables)
Usage in Tables:
{
"type": "table",
"props": {
"dataSource": "{{orderItems}}",
"columns": [
{ "header": "Item", "field": "name" },
{ "header": "Qty", "field": "quantity" },
{ "header": "Price", "field": "unitPrice" },
{ "header": "Total", "field": "total" }
]
}
}Usage in Loops:
{{#each orderItems}}
- {{name}}: {{quantity}} x {{unitPrice}} = {{total}}
{{/each}}Usage in API:
{
"orderItems": [
{
"name": "Widget A",
"quantity": 2,
"unitPrice": 25.00,
"total": 50.00
},
{
"name": "Widget B",
"quantity": 1,
"unitPrice": 75.00,
"total": 75.00
}
]
}Constant Variables
Constants are fixed values that don’t change per email. They’re useful for company information, tax rates, or other static data. In the unified variable system, constants are defined as regular variables with required: false, isCalculated: false, and a defaultValue.
Defining Constants
Constants are defined within the variables array:
{
"variables": [
{
"id": "const_company",
"name": "COMPANY_NAME",
"type": "string",
"required": false,
"isCalculated": false,
"defaultValue": "Acme Corporation",
"description": "Company name (constant)"
},
{
"id": "const_taxrate",
"name": "TAX_RATE",
"type": "number",
"required": false,
"isCalculated": false,
"defaultValue": 0.08,
"description": "Tax rate (constant)"
},
{
"id": "const_support",
"name": "SUPPORT_EMAIL",
"type": "string",
"required": false,
"isCalculated": false,
"defaultValue": "support@acme.com",
"description": "Support email (constant)"
},
{
"id": "const_freeship",
"name": "FREE_SHIPPING_THRESHOLD",
"type": "number",
"required": false,
"isCalculated": false,
"defaultValue": 50.00,
"description": "Free shipping threshold (constant)"
}
]
}Naming Convention: UPPER_CASE with underscores is recommended but not enforced.
Constant Characteristics:
required: Set tofalse(constants don’t require user input)isCalculated: Set tofalse(constants are not computed)defaultValue: Must provide a default valuetype: Any type is allowed:string,number,boolean,date,object,array
Using Constants in Templates
Thank you for choosing {{COMPANY_NAME}}!
Contact us at {{SUPPORT_EMAIL}}
Tax ({{TAX_RATE}}%): {{calculatedTax}}
🎁 Free shipping on orders over ${{FREE_SHIPPING_THRESHOLD}}!Using Constants in Calculated Variables
Constants can be referenced in calculated variable expressions:
{
"id": "calc_total",
"name": "totalWithTax",
"type": "number",
"required": false,
"isCalculated": true,
"expression": "subtotal * (1 + TAX_RATE)"
}{
"id": "calc_freeship",
"name": "qualifiesForFreeShipping",
"type": "boolean",
"required": false,
"isCalculated": true,
"expression": "total >= FREE_SHIPPING_THRESHOLD"
}System-Provided Global Constants
FormaMail automatically provides system-level global constants that are available in all templates without definition. These use double-underscore prefix/suffix (__NAME__) to distinguish them from user-defined variables.
Date/Time Constants
| Constant | Type | Description | Example |
|---|---|---|---|
__CURRENT_DATE__ | string | Current date (YYYY-MM-DD) | "2025-12-02" |
__CURRENT_YEAR__ | number | Current year | 2025 |
__CURRENT_MONTH__ | string | Current month name | "December" |
__CURRENT_MONTH_NUMBER__ | number | Current month (1-12) | 12 |
__CURRENT_DAY__ | number | Current day of month | 2 |
__CURRENT_DAY_NAME__ | string | Current day name | "Monday" |
User/Team Constants
| Constant | Type | Description |
|---|---|---|
__CURRENT_USER_ID__ | string | Current user’s ID |
__CURRENT_USER_NAME__ | string | Current user’s name |
__CURRENT_USER_EMAIL__ | string | Current user’s email |
__CURRENT_TEAM_ID__ | string | Current team’s ID |
__CURRENT_TEAM_NAME__ | string | Current team’s name |
Company Constants
These are automatically populated from your Company Settings:
| Constant | Type | Description |
|---|---|---|
__COMPANY_NAME__ | string | Company display name (trade name or legal name) |
__COMPANY_LEGAL_NAME__ | string | Company legal/registered name |
__COMPANY_TRADE_NAME__ | string | Company trade/brand name |
__COMPANY_EMAIL__ | string | Company contact email |
__COMPANY_PHONE__ | string | Company phone number |
__COMPANY_WEBSITE__ | string | Company website URL |
__COMPANY_ADDRESS_LINE1__ | string | Address line 1 |
__COMPANY_ADDRESS_LINE2__ | string | Address line 2 |
__COMPANY_CITY__ | string | City |
__COMPANY_STATE__ | string | State/Province |
__COMPANY_POSTAL_CODE__ | string | Postal/ZIP code |
__COMPANY_COUNTRY__ | string | Country |
__COMPANY_GSTIN__ | string | GST Identification Number |
__COMPANY_PAN__ | string | PAN Number |
Template Constants
| Constant | Type | Description |
|---|---|---|
__TEMPLATE_ID__ | string | Current template’s ID |
__TEMPLATE_NAME__ | string | Current template’s name |
Using System Constants in Templates
Copyright Footer:
© {{__CURRENT_YEAR__}} {{__COMPANY_NAME__}}. All rights reserved.Company Address Block:
{{__COMPANY_NAME__}}
{{__COMPANY_ADDRESS_LINE1__}}
{{__COMPANY_CITY__}}, {{__COMPANY_STATE__}} {{__COMPANY_POSTAL_CODE__}}
{{__COMPANY_COUNTRY__}}Contact Information:
Contact us:
Email: {{__COMPANY_EMAIL__}}
Phone: {{__COMPANY_PHONE__}}
Website: {{__COMPANY_WEBSITE__}}Template Information:
Generated from template: {{__TEMPLATE_NAME__}}
Date: {{__CURRENT_DATE__}}System constants are read-only and automatically populated at render time. You don’t need to define these in your template’s variables - they’re always available.
Company constants require Company Settings to be configured. If not set up, these values will be empty.
Calculated Variables
Calculated variables are computed from other variables using JavaScript expressions. They’re perfect for deriving values without requiring the API caller to compute them. In the unified variable system, calculated variables have isCalculated: true and an expression field.
Defining Calculated Variables
Calculated variables are defined within the variables array with isCalculated: true:
{
"variables": [
{
"id": "calc_total",
"name": "totalWithTax",
"type": "number",
"required": false,
"isCalculated": true,
"expression": "subtotal * (1 + TAX_RATE)",
"description": "Subtotal including tax"
}
]
}Required Properties:
name: Variable name (must follow JavaScript naming rules)isCalculated: Must betruefor calculated variablesexpression: JavaScript expressiontype: Expected return type (string,number,date,boolean,array,object)
Optional Properties:
required: Should befalse(calculated values are computed, not provided)description: Human-readable description
Naming Rules
✅ Valid names:
- Must start with letter or underscore:
total,_temp,Total - Can contain letters, numbers, underscores:
totalPrice,item_1,qty2
❌ Invalid names:
- Can’t start with number:
2total - Can’t use reserved keywords:
if,for,while,function,class, etc. - Can’t use special characters:
total-price,qty!,price@
Expression Syntax
Allowed Operations:
-
Arithmetic:
subtotal + tax // Addition total - discount // Subtraction quantity * unitPrice // Multiplication total / quantity // Division amount % 10 // Modulo price ** 2 // Exponentiation -
Comparison:
quantity > 10 // Greater than price >= 100 // Greater than or equal stock < minimum // Less than age <= 18 // Less than or equal status === 'active' // Equality type !== 'guest' // Inequality -
Logical:
isPremium && isActive // AND isNew || isFeatured // OR !isExpired // NOT -
Ternary (Conditional):
status ? 'Active' : 'Inactive' quantity > 10 ? price * 0.9 : price isPremium ? 'Gold' : (isActive ? 'Silver' : 'Bronze') -
String Operations:
firstName + ' ' + lastName // Concatenation email.toLowerCase() // Method call message.substring(0, 100) // Substring text.replace('old', 'new') // Replace name.trim() // Trim whitespace -
Math Functions:
Math.round(price) // Round Math.floor(value) // Floor Math.ceil(value) // Ceiling Math.abs(difference) // Absolute value Math.min(a, b, c) // Minimum Math.max(a, b, c) // Maximum Math.sqrt(number) // Square root Math.pow(base, exponent) // Power -
String Methods:
text.toUpperCase() // Upper case text.toLowerCase() // Lower case text.trim() // Trim text.substring(start, end) // Substring text.slice(start, end) // Slice text.replace(search, replace) // Replace text.split(',') // Split text.includes('substring') // Includes text.startsWith('prefix') // Starts with text.endsWith('suffix') // Ends with -
Array Operations:
items.length // Array length items[0] // Array access items.join(', ') // Join -
Object Property Access:
customer.name // Dot notation order['total'] // Bracket notation user.address.city // Nested access
Important Security Restrictions:
❌ Forbidden operations (for security):
eval() // Code execution
Function() // Function constructor
require() // Module loading
import // ES6 imports
process // Node.js process
global // Global scope
window // Browser window
document // DOM access
localStorage // Storage access
fetch() // Network requestsExpression Examples
Basic Math:
{
"name": "total",
"expression": "quantity * unitPrice"
}
{
"name": "discount",
"expression": "total * 0.10"
}
{
"name": "finalPrice",
"expression": "total - discount"
}With Constants:
{
"name": "totalWithTax",
"expression": "total * (1 + TAX_RATE)"
}
{
"name": "shippingCost",
"expression": "total >= FREE_SHIPPING_THRESHOLD ? 0 : 9.99"
}String Manipulation:
{
"name": "fullName",
"expression": "firstName + ' ' + lastName"
}
{
"name": "displayEmail",
"expression": "email.toLowerCase()"
}
{
"name": "shortDescription",
"expression": "description.substring(0, 100) + '...'"
}Conditional Logic:
{
"name": "tierLabel",
"expression": "points >= 1000 ? 'Gold' : (points >= 500 ? 'Silver' : 'Bronze')"
}
{
"name": "stockStatus",
"expression": "inventory > 10 ? 'In Stock' : (inventory > 0 ? 'Low Stock' : 'Out of Stock')"
}Date Operations:
{
"name": "isExpired",
"expression": "new Date(expiryDate) < new Date()"
}
{
"name": "daysSinceOrder",
"expression": "Math.floor((new Date() - new Date(orderDate)) / (1000 * 60 * 60 * 24))"
}Complex Calculations:
{
"name": "volumeDiscount",
"expression": "quantity > 100 ? 0.20 : (quantity > 50 ? 0.15 : (quantity > 20 ? 0.10 : 0))"
}
{
"name": "loyaltyPoints",
"expression": "Math.floor(total * (isPremium ? 2 : 1) * POINTS_MULTIPLIER)"
}Common Pitfalls
❌ Don’t use Handlebars syntax in expressions:
{
"name": "total",
"expression": "{{quantity}} * {{unitPrice}}" // WRONG
}✅ Use direct variable references:
{
"name": "total",
"expression": "quantity * unitPrice" // CORRECT
}❌ Don’t use reserved keywords:
{
"name": "for", // WRONG - 'for' is reserved
"expression": "quantity * 2"
}✅ Use descriptive non-reserved names:
{
"name": "doubleQuantity", // CORRECT
"expression": "quantity * 2"
}Using Variables in Templates
Email Templates
Text Components:
Hello {{firstName}},
Your order #{{order.id}} has been confirmed.
Total: {{total}}Button Components:
{
"type": "button",
"props": {
"text": "View Order",
"url": "{{dashboardUrl}}/orders/{{order.id}}"
}
}Image Components:
{
"type": "image",
"props": {
"src": "{{user.avatarUrl}}",
"alt": "{{user.name}}"
}
}Conditional Components:
{
"type": "conditional",
"props": {
"condition": "{{isPremium}}",
"children": [
{
"type": "text",
"props": { "content": "Thank you for being a Premium member!" }
}
]
}
}Attachment Templates (PDF/Excel)
Variables work the same way in attachment templates:
Invoice #{{invoice.number}}
Date: {{invoice.date}}
Customer: {{customer.name}}
Total: {{total}}Charts with Variables:
{
"type": "chart",
"props": {
"chartType": "bar",
"dataSource": "{{salesData}}",
"xField": "month",
"yField": "revenue"
}
}Data Tables & Columns
Tables are powerful components for displaying structured data. They work with array variables and support various column types, formulas, and formatting.
Basic Table Structure
{
"type": "table",
"props": {
"dataSource": "{{orderItems}}",
"columns": [
{ "header": "Item", "field": "name" },
{ "header": "Quantity", "field": "quantity" },
{ "header": "Price", "field": "unitPrice" },
{ "header": "Total", "field": "total" }
]
}
}Column Types
1. Simple Field Column
Display a field from the data source directly.
{
"header": "Product Name",
"field": "name",
"width": "40%",
"align": "left"
}Properties:
header(required): Column header textfield(required): Property name from data sourcewidth: Column width ("40%","120px",120)align: Text alignment ("left","center","right")format: Formatting options (see Formatting Options)
2. Calculated Column
Compute values using a JavaScript expression.
{
"header": "Total",
"isCalculated": true,
"expression": "quantity * unitPrice",
"format": {
"type": "number",
"preset": "CURRENCY",
"currency": "USD",
"decimals": 2
}
}Properties:
header(required): Column header textisCalculated(required): Must betrueexpression(required): JavaScript expressionformat: Formatting options
Expression Context: In table column expressions, you can reference:
- Other fields in the current row:
quantity,unitPrice,discount - Constants:
TAX_RATE,SHIPPING_COST
Examples:
// Subtotal with discount
{
"header": "Subtotal",
"isCalculated": true,
"expression": "quantity * unitPrice * (1 - discount)"
}
// Tax amount
{
"header": "Tax",
"isCalculated": true,
"expression": "subtotal * TAX_RATE"
}
// Conditional pricing
{
"header": "Price",
"isCalculated": true,
"expression": "quantity > 10 ? unitPrice * 0.9 : unitPrice"
}
// Status label
{
"header": "Status",
"isCalculated": true,
"expression": "stock > 10 ? 'Available' : (stock > 0 ? 'Low Stock' : 'Out of Stock')"
}3. Calculated Column
Use JavaScript expressions for calculated columns. Works for both PDF and Excel attachments.
{
"header": "Total",
"formula": "quantity * unitPrice"
}Properties:
header(required): Column header textformula(required): Simple expression
Supported Operators:
+, -, *, /, (), . (property access)
Examples:
// Basic calculation
{
"header": "Total",
"formula": "quantity * unitPrice"
}
// With property access
{
"header": "Discounted Total",
"formula": "item.quantity * item.price * (1 - item.discount)"
}4. Component-Based Column (Email Templates Only)
Render custom components in table cells (buttons, images).
{
"header": "Actions",
"renderType": "component",
"component": {
"type": "button",
"props": {
"text": "View Details",
"url": "https://example.com/products/{{id}}"
},
"contentField": "id"
}
}Supported Component Types:
- Email templates:
button,image,text - Attachment templates:
image,text
Component Structure:
{
"type": "button" | "image" | "text",
"props": {
// Component-specific props
},
"contentField": "fieldName" // Field to use for dynamic content
}Button Example:
{
"header": "Action",
"renderType": "component",
"component": {
"type": "button",
"props": {
"text": "Download",
"url": "{{downloadUrl}}",
"backgroundColor": "#007bff",
"textColor": "#ffffff"
},
"contentField": "downloadUrl"
}
}Image Example:
{
"header": "Product Image",
"renderType": "component",
"component": {
"type": "image",
"props": {
"src": "{{imageUrl}}",
"alt": "{{name}}",
"width": 80,
"height": 80
},
"contentField": "imageUrl"
}
}Column Formatting
Apply formatting to column values for better display.
Number Formatting
{
"header": "Price",
"field": "price",
"format": {
"type": "number",
"preset": "CURRENCY",
"currency": "USD",
"decimals": 2
}
}Number Format Presets:
DECIMAL: 1234.56CURRENCY: $1,234.56PERCENT: 75.5%CUSTOM: Custom format
Properties:
decimals: Number of decimal places (0-4)currency: Currency code (USD,EUR,GBP, etc.)
Date Formatting
{
"header": "Order Date",
"field": "orderDate",
"format": {
"type": "date",
"preset": "US"
}
}Date Format Presets:
ISO: 2025-01-15US: 01/15/2025EU: 15/01/2025LONG: January 15, 2025SHORT: Jan 15, 2025CUSTOM: Custom format with tokens
Custom Format Example:
{
"format": {
"type": "date",
"preset": "CUSTOM",
"customFormat": "MMMM dd, yyyy"
}
}Column Styling
Customize column appearance with styling options.
{
"header": "Status",
"field": "status",
"backgroundColor": "#28a745",
"textColor": "#ffffff",
"fontWeight": "bold",
"align": "center"
}Styling Properties:
backgroundColor: Background color (hex, rgb, color name)textColor: Text color (hex, rgb, color name)fontWeight: Font weight ("normal","bold","100"-"900")align: Horizontal alignment ("left","center","right")
Table Styling
Style the entire table with header and row colors.
{
"type": "table",
"props": {
"dataSource": "{{orderItems}}",
"headerBackgroundColor": "#343a40",
"headerTextColor": "#ffffff",
"rowBackgroundColor": "#ffffff",
"rowTextColor": "#212529",
"borderColor": "#dee2e6",
"columns": [...]
}
}Column Editor Interface
When editing tables in the designer, the Column Editor dialog provides a visual interface for configuring columns.
Column Editor Features:
- Add Column button - Add new columns
- Reorder - Drag handles to reorder columns
- Delete - Remove columns
- Column Properties Panel:
- Header text input
- Field selector dropdown (populated from data source)
- Width input (pixels or percentage)
- Alignment selector (left/center/right)
- Format type selector
- Format options (currency, decimals, date preset)
Calculated Column UI:
- Toggle “Calculated Column” checkbox
- Expression editor with syntax highlighting
- Variable reference autocomplete (use
row.fieldNameto access row data) - Real-time expression validation
- Preview with sample data
Formatting Options
Number Formatting
Decimal:
{
"type": "number",
"preset": "DECIMAL",
"decimals": 2
}Output: 1234.56
Currency:
{
"type": "number",
"preset": "CURRENCY",
"currency": "USD",
"decimals": 2
}Output: $1,234.56
Percent:
{
"type": "number",
"preset": "PERCENT",
"decimals": 1
}Output: 75.5%
Custom:
{
"type": "number",
"preset": "CUSTOM",
"customFormat": "#,##0.00"
}Date Formatting
ISO:
{
"type": "date",
"preset": "ISO"
}Output: 2025-01-15
US:
{
"type": "date",
"preset": "US"
}Output: 01/15/2025
EU:
{
"type": "date",
"preset": "EU"
}Output: 15/01/2025
Long:
{
"type": "date",
"preset": "LONG"
}Output: January 15, 2025
Custom:
{
"type": "date",
"preset": "CUSTOM",
"customFormat": "MMMM dd, yyyy 'at' hh:mm a"
}Output: January 15, 2025 at 02:30 PM
Best Practices
Variable Naming
✅ Do:
- Use descriptive names:
firstName,orderTotal,deliveryDate - Use camelCase for variables:
userName,phoneNumber - Use UPPER_CASE for constants:
TAX_RATE,COMPANY_NAME
❌ Don’t:
- Use vague names:
x,temp,data1 - Use reserved keywords:
if,for,class - Mix naming conventions:
FirstName,last_name,EMAIL
Variable Defaults
Always provide defaults for optional variables:
{
"id": "var_greeting",
"name": "greeting",
"type": "string",
"required": false,
"defaultValue": "Hello"
}Benefits:
- Templates work even if variable is missing
- Better user experience
- Fewer API errors
Required Variables
Mark critical variables as required:
{
"name": "email",
"type": "string",
"format": "email",
"required": true
}FormaMail validates required variables at:
- Template design time - Warns if required variable used but not defined
- API send time - Rejects request if required variable missing
Variable Documentation
Always add descriptions to variables:
{
"name": "orderTotal",
"type": "number",
"required": true,
"description": "Total order amount including tax and shipping",
"format": {
"type": "number",
"preset": "CURRENCY",
"currency": "USD",
"decimals": 2
}
}Benefits:
- Helps other developers understand usage
- Shows in API documentation
- Appears in designer tooltips
Calculated Variables
Use calculated variables to simplify API calls:
❌ Without calculated variables (API must compute):
// API caller must compute these
{
"subtotal": 100,
"taxAmount": 8,
"total": 108,
"taxRate": 0.08
}✅ With calculated variables (computed automatically):
// API only provides:
{
"subtotal": 100
}
// Template variables include:
{
"id": "calc_tax",
"name": "taxAmount",
"type": "number",
"required": false,
"isCalculated": true,
"expression": "subtotal * TAX_RATE"
},
{
"id": "calc_total",
"name": "total",
"type": "number",
"required": false,
"isCalculated": true,
"expression": "subtotal + taxAmount"
}Nesting Depth
Limit object nesting to improve readability:
✅ Good (2-3 levels):
{
"customer": {
"name": "John Doe",
"address": {
"city": "San Francisco",
"state": "CA"
}
}
}❌ Avoid (5+ levels):
{
"data": {
"customer": {
"profile": {
"address": {
"primary": {
"location": {
"city": "..."
}
}
}
}
}
}
}Maximum Allowed: 5 levels
Expression Complexity
Keep expressions simple and readable:
✅ Simple (preferred):
{
"name": "discount",
"expression": "total * 0.10"
}✅ Moderate (acceptable):
{
"name": "shippingCost",
"expression": "total >= FREE_SHIPPING_THRESHOLD ? 0 : 9.99"
}❌ Complex (avoid):
{
"name": "complexCalculation",
"expression": "((quantity > 100 ? price * 0.8 : (quantity > 50 ? price * 0.9 : price)) * quantity) * (isPremium ? 0.95 : 1) + (total > 1000 ? 0 : 15)"
}If expression is too complex:
- Break into multiple calculated variables
- Compute on server and pass as regular variable
- Use constants to make expression more readable
Examples
Example 1: Order Confirmation Email
Variables:
{
"variables": [
{
"id": "var_customer",
"name": "customerName",
"type": "string",
"required": true
},
{
"id": "var_orderno",
"name": "orderNumber",
"type": "string",
"required": true
},
{
"id": "var_orderdate",
"name": "orderDate",
"type": "date",
"required": true,
"format": {
"type": "date",
"preset": "LONG"
}
},
{
"id": "var_items",
"name": "orderItems",
"type": "array",
"itemType": "object",
"schema": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": { "type": "string", "required": true },
"quantity": { "type": "number", "required": true },
"unitPrice": { "type": "number", "required": true }
}
}
}
},
{
"id": "var_subtotal",
"name": "subtotal",
"type": "number",
"required": true
},
{
"id": "const_taxrate",
"name": "TAX_RATE",
"type": "number",
"required": false,
"isCalculated": false,
"defaultValue": 0.08,
"description": "Tax rate (constant)"
},
{
"id": "const_company",
"name": "COMPANY_NAME",
"type": "string",
"required": false,
"isCalculated": false,
"defaultValue": "Acme Store",
"description": "Company name (constant)"
},
{
"id": "calc_tax",
"name": "taxAmount",
"type": "number",
"required": false,
"isCalculated": true,
"expression": "subtotal * TAX_RATE",
"description": "Tax amount (calculated)"
},
{
"id": "calc_total",
"name": "total",
"type": "number",
"required": false,
"isCalculated": true,
"expression": "subtotal + taxAmount",
"description": "Total with tax (calculated)"
}
]
}Template Usage:
Hello {{customerName}},
Your order #{{orderNumber}} has been confirmed!
Order Date: {{orderDate}}
Items:
{{#each orderItems}}
- {{name}}: {{quantity}} x ${{unitPrice}}
{{/each}}
Subtotal: ${{subtotal}}
Tax: ${{taxAmount}}
Total: ${{total}}
Thank you for shopping at {{COMPANY_NAME}}!API Call:
POST /api/emails/send
{
"templateId": "order-confirmation",
"to": ["customer@example.com"],
"variables": {
"customerName": "John Doe",
"orderNumber": "ORD-12345",
"orderDate": "2025-01-15T10:30:00Z",
"orderItems": [
{ "name": "Widget A", "quantity": 2, "unitPrice": 25.00 },
{ "name": "Widget B", "quantity": 1, "unitPrice": 50.00 }
],
"subtotal": 100.00
}
}Example 2: Invoice PDF with Table
Variables:
{
"variables": [
{
"id": "var_invno",
"name": "invoiceNumber",
"type": "string",
"required": true
},
{
"id": "var_invdate",
"name": "invoiceDate",
"type": "date",
"required": true,
"format": { "type": "date", "preset": "US" }
},
{
"id": "var_customer",
"name": "customer",
"type": "object",
"schema": {
"type": "object",
"properties": {
"name": { "type": "string", "required": true },
"email": { "type": "string", "format": "email", "required": true },
"address": {
"type": "object",
"properties": {
"street": { "type": "string" },
"city": { "type": "string" },
"state": { "type": "string" },
"zip": { "type": "string" }
}
}
}
}
},
{
"id": "var_items",
"name": "lineItems",
"type": "array",
"schema": {
"type": "array",
"items": {
"type": "object",
"properties": {
"description": { "type": "string", "required": true },
"quantity": { "type": "number", "required": true },
"rate": { "type": "number", "required": true }
}
}
}
},
{
"id": "const_taxrate",
"name": "TAX_RATE",
"type": "number",
"required": false,
"isCalculated": false,
"defaultValue": 0.08,
"description": "Tax rate (constant)"
},
{
"id": "const_company",
"name": "COMPANY_NAME",
"type": "string",
"required": false,
"isCalculated": false,
"defaultValue": "Acme Corp",
"description": "Company name (constant)"
},
{
"id": "const_address",
"name": "COMPANY_ADDRESS",
"type": "string",
"required": false,
"isCalculated": false,
"defaultValue": "123 Main St, San Francisco, CA 94105",
"description": "Company address (constant)"
}
]
}Table Component:
{
"type": "table",
"props": {
"dataSource": "{{lineItems}}",
"headerBackgroundColor": "#343a40",
"headerTextColor": "#ffffff",
"columns": [
{
"header": "Description",
"field": "description",
"width": "50%",
"align": "left"
},
{
"header": "Qty",
"field": "quantity",
"width": "15%",
"align": "center"
},
{
"header": "Rate",
"field": "rate",
"width": "15%",
"align": "right",
"format": {
"type": "number",
"preset": "CURRENCY",
"currency": "USD",
"decimals": 2
}
},
{
"header": "Amount",
"isCalculated": true,
"expression": "quantity * rate",
"width": "20%",
"align": "right",
"format": {
"type": "number",
"preset": "CURRENCY",
"currency": "USD",
"decimals": 2
}
}
]
}
}Example 3: Subscription Renewal Email with Conditionals
Variables:
{
"variables": [
{
"id": "var_user",
"name": "userName",
"type": "string",
"required": true
},
{
"id": "var_tier",
"name": "subscriptionTier",
"type": "string",
"required": true
},
{
"id": "var_renewal",
"name": "renewalDate",
"type": "date",
"required": true,
"format": { "type": "date", "preset": "LONG" }
},
{
"id": "var_premium",
"name": "isPremium",
"type": "boolean",
"required": true
},
{
"id": "var_monthly",
"name": "monthlyPrice",
"type": "number",
"required": true
},
{
"id": "var_annual",
"name": "annualPrice",
"type": "number",
"required": true
},
{
"id": "calc_savings",
"name": "annualSavings",
"type": "number",
"required": false,
"isCalculated": true,
"expression": "(monthlyPrice * 12) - annualPrice",
"description": "Annual savings amount (calculated)"
},
{
"id": "calc_percent",
"name": "savingsPercent",
"type": "number",
"required": false,
"isCalculated": true,
"expression": "Math.round((annualSavings / (monthlyPrice * 12)) * 100)",
"description": "Savings percentage (calculated)"
}
]
}Template with Conditionals:
Hello {{userName}},
Your {{subscriptionTier}} subscription will renew on {{renewalDate}}.
{{#if isPremium}}
Thank you for being a Premium member! You're getting the best value with:
- Unlimited emails
- Priority support
- Advanced features
{{else}}
Consider upgrading to Premium for more features!
{{/if}}
💰 Save {{savingsPercent}}% by switching to annual billing!
- Monthly: ${{monthlyPrice}}/month (${{monthlyPrice * 12}}/year)
- Annual: ${{annualPrice}}/year (save ${{annualSavings}}!)
[Upgrade to Annual Billing]Related Resources
- Email Designer - Design email templates
- Attachment Designer - Create PDF/Excel templates
- Templates Guide - Template management
- API Reference - Sending emails via API
Need help? Contact support@formamail.com or check our FAQ