Supported formats
| Format | Template | Output | Notes |
|---|---|---|---|
| DOCX (Microsoft Word) | .docx file with merge fields | PDF or DOCX | Uses Word’s MERGEFIELD syntax. Most flexible for complex layouts. |
| Google Docs | Google Doc with template tags | PDF or Google Doc | Uses {{ }} handlebars syntax. Easier to collaborate on templates. |
| Google Slides | Google Slide deck with template tags | PDF or Google Slides | Same {{ }} syntax. Best for visual proposals and pitch decks. |
Template syntax
- DOCX (Word)
- Google Docs / Slides
Inserting merge fields
- Place your cursor where you want the dynamic value.
- Press
Ctrl+F9(Windows) orCmd+F9(Mac) to create field brackets{ }. - Type the MERGEFIELD syntax between the brackets.
- Press
Cmd+Shift+Option+Uto save the merge field. - Use
Alt+F9(Windows) orOption+F9(Mac) to toggle between field codes and rendered values.
Syntax reference
| Operation | Syntax |
|---|---|
| Simple value | MERGEFIELD =title \* MERGEFORMAT |
| Nested value | MERGEFIELD =owner.name \* MERGEFORMAT |
| Loop start | MERGEFIELD options:each(option) \* MERGEFORMAT |
| Loop variable | MERGEFIELD =option.name \* MERGEFORMAT |
| Loop end | MERGEFIELD options:endEach \* MERGEFORMAT |
| Conditional start | MERGEFIELD salesforce:if \* MERGEFORMAT |
| Conditional end | MERGEFIELD salesforce:endIf \* MERGEFORMAT |
| Boolean conditional | MERGEFIELD block:if(is_option) \* MERGEFORMAT |
Template variables reference
Every variable below is available in all template formats. Use dot notation to access nested values (e.g.,owner.name, line.formatted.price).
Top-level fields
| Variable | Type | Description |
|---|---|---|
title | String | The quote/opportunity title. |
logo | Image | Company logo from your brand settings. |
terms_html | HTML | Combined HTML from all Terms and Text blocks. Nullable. |
blocks | Array | All quote blocks in order (title, terms, options, text). See Blocks. |
options | Array | Pricing Option blocks only. See Options. |
primary_option | Object | The primary option (nullable if none set). Same structure as an option. |
Owner (sales rep)
Access viaowner.*.
| Variable | Type | Description | Example |
|---|---|---|---|
owner.name | String | Full name | ”Michael Scott” |
owner.email | String | Email address | ”michael@company.com” |
owner.phone | String | Phone number | ”(555) 123-4567” |
owner.job_title | String | Job title | ”Regional Manager” |
Title block
Access viatitle_block.* or when iterating blocks with is_title.
| Variable | Type | Description |
|---|---|---|
title_block.name | String | Quote name. |
title_block.from | String | From company name. |
title_block.prepared_by | String | Preparer name. |
title_block.prepared_by_extra | String | Preparer additional info (title, email). |
title_block.for | String | Recipient company name. |
title_block.for_extra | String | Recipient additional info. |
title_block.issued_on | Date | Issue date (e.g., “January 15, 2025”). |
title_block.valid_until | Date | Expiry date. |
Blocks
Theblocks array contains all blocks in the quote, in display order. Since blocks are polymorphic (they can be different types), use the type flags to conditionally render content.
| Flag | True when |
|---|---|
is_title | Title block (cover page data). |
is_terms | Terms or aggregated terms block. |
is_option | Pricing Option block. |
is_text | Text block (free-form content). |
terms_html (for Terms/Text blocks) and name (for Title/Option blocks).
Options
Access via theoptions array, primary_option, or when iterating blocks with is_option. Each option contains:
| Variable | Type | Description | Example |
|---|---|---|---|
option.name | String | Option name. | ”Enterprise Plan” |
option.frequency | String | Billing frequency. | ”annual”, “monthly”, “quarterly” |
option.year_count | Integer | Number of calculation periods. | 3 |
option.formatted_contract_length | String | Human-readable contract length. | ”3 years” |
option.total_value | String | Raw decimal total. | ”150000.00” |
option.formatted_total_value | Currency | Formatted total. | ”$150,000.00” |
option.currency | String | Currency code. | ”USD” |
option.start_date | Date | First calculation start. | ”January 1, 2025” |
option.end_date | Date | Last calculation end. | ”December 31, 2027” |
option.service_start_date | Date | Service start date (nullable). | |
option.uplift | String | Annual uplift percentage (nullable). | “3%“ |
option.custom_fields | Hash | Option-level custom fields (dynamic keys). | |
option.terms_html | HTML | Terms specific to this option (nullable). | |
option.calculations | Array | One per billing period. See Calculations. |
Calculations (periods)
Each option contains acalculations array with one entry per billing period (year, quarter, month). Access via option.calculations.
| Variable | Type | Description | Example |
|---|---|---|---|
calc.year | Integer | 1-based period number. | 1 |
calc.currency | String | Currency code. | ”USD” |
calc.start_date | Date | Period start. | ”January 1, 2025” |
calc.end_date | Date | Period end. | ”December 31, 2025” |
calc.total_discount | Float | Raw discount amount. | 5000.0 |
calc.credits | String | Formatted credits (abbreviated). | “100K” |
calc.credits_full | String | Full unscaled credits number. | ”100000” |
calc.credit_rate | Currency | Unit price per credit. | |
calc.credit_discount | String | Discount on credits. | |
calc.credit_total | Currency | Credits total formatted. | |
calc.summary | Hash | Summary variables (dynamic keys). | |
calc.uom_fields | Hash | UOM field values (dynamic keys). | {"seats": "100K"} |
calc.custom_columns | Hash | Custom calculation columns (dynamic keys). | |
calc.any_one_time | Boolean | Has one-time lines? | |
calc.any_recurring | Boolean | Has recurring lines? | |
calc.any_transactional | Boolean | Has transactional lines? | |
calc.lines | Array | All line items. See Line Items. | |
calc.one_time_lines | Array | One-time lines only. | |
calc.recurring_lines | Array | Recurring lines only. |
Formatted totals
Access viacalc.formatted.*.
| Variable | Type | Description | Example |
|---|---|---|---|
calc.formatted.total | Currency | Period total. | ”$50,000.00” |
calc.formatted.original_total | Currency | Pre-discount total. | ”$55,000.00” |
calc.formatted.discount | Currency | Discount amount. | ”$5,000.00” |
calc.formatted.discount_total | Currency | Total discount formatted. | |
calc.formatted.system_total | Currency | System-calculated total. | |
calc.formatted.system_discount | Currency | System-calculated discount. |
One-time vs. recurring breakout
Access viacalc.breakout.*. Both raw numbers and formatted strings are available.
| Variable | Type | Description |
|---|---|---|
calc.breakout.one_time_original | Float | Raw one-time original amount. |
calc.breakout.one_time_total | Float | Raw one-time total. |
calc.breakout.recurring_original | Float | Raw recurring original amount. |
calc.breakout.recurring_total | Float | Raw recurring total. |
calc.breakout.formatted_one_time_original | Currency | One-time list price. |
calc.breakout.formatted_one_time_total | Currency | One-time net price. |
calc.breakout.formatted_recurring_original | Currency | Recurring list price. |
calc.breakout.formatted_recurring_total | Currency | Recurring net price. |
calc.breakout.formatted_one_time_list | Currency | One-time list (alternate). |
calc.breakout.formatted_one_time_net | Currency | One-time net (alternate). |
calc.breakout.formatted_recurring_list | Currency | Recurring list (alternate). |
calc.breakout.formatted_recurring_net | Currency | Recurring net (alternate). |
Line items
Access viacalc.lines, calc.one_time_lines, or calc.recurring_lines. Each line represents a single product on the quote.
| Variable | Type | Description | Example |
|---|---|---|---|
line.product_name | String | Product display name. | ”Enterprise License” |
line.product_sku | String | Product SKU (nullable). | |
line.category | String | Product category (nullable). | |
line.quantity | String | Quantity value. | ”50” |
line.metric_value | String | UOM + scale suffix. | ”100K” |
line.unit_price | Float | Raw unit price. | 100.0 |
line.one_time | Boolean | Is this a one-time charge? | |
line.is_transactional | Boolean | Is this a transactional charge? | |
line.prorated | Boolean | Is this prorated? | |
line.custom_columns | Hash | Line-level custom columns (dynamic keys). | |
line.product_custom_fields | Hash | Product custom fields (dynamic keys). |
Formatted line values
Access vialine.formatted.*. These are pre-formatted for display.
| Variable | Type | Description | Example |
|---|---|---|---|
line.formatted.name | String | Display name. | |
line.formatted.original_price | Currency | List price. | ”$5,000.00” |
line.formatted.price | Currency | Net price. | ”$4,500.00” |
line.formatted.discount | Currency | Discount amount. | ”$500.00” |
line.formatted.prorated_price | Currency | Prorated price (if applicable). | |
line.formatted.unit_price | Currency | Formatted unit price. | ”$100.00” |
line.formatted.system_price | Currency | System-calculated price. | |
line.formatted.system_price_discount | Currency | System price discount. |
Unit of Measure
Access vialine.uom.* (nullable — may not be present on all lines).
| Variable | Type | Description | Example |
|---|---|---|---|
line.uom.key | String | UOM key. | ”seats” |
line.uom.label | String | UOM display label. | ”Seats” |
Salesforce data
Available when the quote is linked to Salesforce. Always wrap in a conditional since this is null when Salesforce is not connected.| Variable | Type | Description |
|---|---|---|
salesforce.account_name | String | SF account name. |
salesforce.account_id | String | SF account ID. |
salesforce.account_phone | String | Account phone. |
salesforce.account_website | String | Account website URL. |
salesforce.stage | String | Opportunity stage. |
salesforce.crm_url | String | Link to Salesforce record. |
salesforce.cpq_url | String | Link to Veles CPQ record. |
salesforce.last_sync | String | Last sync timestamp (ISO 8601). |
salesforce.custom_fields.* | Dynamic | Custom fields mapped in Veles. Access via dot notation: salesforce.custom_fields.my_field. |
Addresses
Access viasalesforce.billing_address.* or salesforce.shipping_address.* (both nullable).
| Variable | Type |
|---|---|
*.street | String |
*.city | String |
*.state | String |
*.postal_code | String |
*.country | String |
Template examples
Cover page (DOCX)
Iterating through all blocks (DOCX)
Use the type flags to render each block type differently:Pricing options with year-by-year breakdown (DOCX)
Line items table (DOCX)
Separate one-time and recurring tables (DOCX)
Salesforce customer info with conditional (DOCX)
Full proposal (Google Docs)
Dynamic hash fields
Several variables use dynamic keys (marked as “Hash” in the reference). These includecustom_fields, custom_columns, summary, uom_fields, and product_custom_fields.
Access dynamic fields with dot notation using the field key:
Tips
Useformatted values for display. Most numeric fields have both a raw value (e.g., line.unit_price = 100.0) and a formatted version (e.g., line.formatted.unit_price = "$100.00"). Use the formatted versions in templates to get proper currency formatting without manual work.
Always wrap Salesforce data in conditionals. The salesforce object is null when no CRM is connected. Referencing salesforce.account_name without a conditional will produce an error in the generated document.
Use one_time_lines and recurring_lines for split tables. Instead of looping through all lines and checking the one_time flag on each, use the pre-filtered arrays for cleaner templates.
Test with a real quote. Generate a document from an actual quote to verify all merge fields resolve correctly. Nullable fields, empty arrays, and missing custom fields are the most common causes of template errors.
What’s next
Account Setup
Configure the logo and brand color that appear in generated documents.
Quote Overview
Understand the quote structure that document generation renders from.
Display Options
Control what pricing detail appears in generated documents.
Web Portal
Share interactive proposals alongside generated documents.

