> ## Documentation Index
> Fetch the complete documentation index at: https://docs.getveles.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Document Generation

> Generate branded proposals, order forms, and contracts from quote data using DOCX or Google Docs/Slides templates with dynamic merge fields.

Veles generates branded proposals, order forms, and contracts directly from quote data. You build templates in Microsoft Word (DOCX) or Google Docs/Slides, insert merge fields that reference quote variables, and Veles renders the final document with live deal data at generation time.

Templates are managed by admins and available to reps when generating documents from a quote. The output reflects your [brand identity](/get-started/quickstart/account-setup#brand-identity) (logo, colors) and can include pricing tables, terms, CRM data, and any content from the quote's blocks.

***

## 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

<Tabs>
  <Tab title="DOCX (Word)">
    ### Inserting merge fields

    1. Place your cursor where you want the dynamic value.
    2. Press `Ctrl+F9` (Windows) or `Cmd+F9` (Mac) to create field brackets `{ }`.
    3. Type the MERGEFIELD syntax between the brackets.
    4. Press `Cmd+Shift+Option+U` to save the merge field.
    5. Use `Alt+F9` (Windows) or `Option+F9` (Mac) to toggle between field codes and rendered values.

    **Alternative:** Insert → Quick Parts → Field → choose "MergeField" from the list.

    ### 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`  |
  </Tab>

  <Tab title="Google Docs / Slides">
    ### Syntax reference

    Google Docs and Google Slides use the same handlebars-style syntax.

    | Operation             | Syntax                           |
    | --------------------- | -------------------------------- |
    | **Simple value**      | `{{ title }}`                    |
    | **Nested value**      | `{{ owner.name }}`               |
    | **Loop start**        | `{{ #each(options) as option }}` |
    | **Loop variable**     | `{{ option.name }}`              |
    | **Loop end**          | `{{ /each }}`                    |
    | **Conditional start** | `{{ #if(salesforce) }}`          |
    | **Else if**           | `{{ #elseif(primary_option) }}`  |
    | **Else**              | `{{ #else }}`                    |
    | **Conditional end**   | `{{ /if }}`                      |
  </Tab>
</Tabs>

***

## 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](/get-started/quickstart/account-setup#brand-identity). |
| `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](#blocks).                |
| `options`        | Array  | Pricing Option blocks only. See [Options](#options).                                           |
| `primary_option` | Object | The primary option (nullable if none set). Same structure as an option.                        |

### Owner (sales rep)

Access via `owner.*`.

| Variable          | Type   | Description   | Example                                             |
| ----------------- | ------ | ------------- | --------------------------------------------------- |
| `owner.name`      | String | Full name     | "Michael Scott"                                     |
| `owner.email`     | String | Email address | "[michael@company.com](mailto:michael@company.com)" |
| `owner.phone`     | String | Phone number  | "(555) 123-4567"                                    |
| `owner.job_title` | String | Job title     | "Regional Manager"                                  |

### Title block

Access via `title_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

The `blocks` 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).  |

Each block also has `terms_html` (for Terms/Text blocks) and `name` (for Title/Option blocks).

### Options

Access via the `options` 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). |                                  |

### Calculations (periods)

Each option contains a `calculations` 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](#line-items). |                     |
| `calc.one_time_lines`    | Array    | One-time lines only.                           |                     |
| `calc.recurring_lines`   | Array    | Recurring lines only.                          |                     |

#### Formatted totals

Access via `calc.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 via `calc.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 via `calc.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 via `line.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 via `line.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 via `salesforce.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)

```text theme={null}
«=title_block.name»

Prepared for: «=title_block.for»
«=title_block.for_extra»

Prepared by: «=title_block.prepared_by»
«=title_block.prepared_by_extra»

Date: «=title_block.issued_on»
Valid until: «=title_block.valid_until»
```

### Iterating through all blocks (DOCX)

Use the type flags to render each block type differently:

```text theme={null}
«blocks:each(block)»
  «block:if(is_title)»
    «=block.name»
    Prepared by: «=block.prepared_by»
  «block:endIf»

  «block:if(is_option)»
    Option: «=block.name»
    Total: «=block.formatted_total_value»
  «block:endIf»

  «block:if(is_terms)»
    «=block.terms_html»
  «block:endIf»

  «block:if(is_text)»
    «=block.terms_html»
  «block:endIf»
«blocks:endEach»
```

### Pricing options with year-by-year breakdown (DOCX)

```text theme={null}
«options:each(option)»
  «=option.name» — «=option.formatted_contract_length»
  Total Contract Value: «=option.formatted_total_value»

  «option.calculations:each(calc)»
    Year «=calc.year» («=calc.start_date» – «=calc.end_date»)
    Total: «=calc.formatted.total»

    «calc:if(any_one_time)»
      One-Time Fees: «=calc.breakout.formatted_one_time_total»
    «calc:endIf»

    «calc:if(any_recurring)»
      Recurring: «=calc.breakout.formatted_recurring_total»
    «calc:endIf»
  «option.calculations:endEach»
«options:endEach»
```

### Line items table (DOCX)

```text theme={null}
«calc.lines:each(line)»
  «=line.formatted.name»  |  «=line.quantity»  |  «=line.formatted.unit_price»  |  «=line.formatted.price»
«calc.lines:endEach»
```

### Separate one-time and recurring tables (DOCX)

```text theme={null}
«calc:if(any_recurring)»
  Recurring Charges:
  «calc.recurring_lines:each(line)»
    «=line.formatted.name»  |  «=line.quantity»  |  «=line.formatted.price»
  «calc.recurring_lines:endEach»
  Recurring Total: «=calc.breakout.formatted_recurring_total»
«calc:endIf»

«calc:if(any_one_time)»
  One-Time Charges:
  «calc.one_time_lines:each(line)»
    «=line.formatted.name»  |  «=line.formatted.price»
  «calc.one_time_lines:endEach»
  One-Time Total: «=calc.breakout.formatted_one_time_total»
«calc:endIf»
```

### Salesforce customer info with conditional (DOCX)

```text theme={null}
«salesforce:if»
  Customer: «=salesforce.account_name»
  «=salesforce.billing_address.street»
  «=salesforce.billing_address.city», «=salesforce.billing_address.state» «=salesforce.billing_address.postal_code»
«salesforce:endIf»
```

### Full proposal (Google Docs)

```text theme={null}
{{ title_block.name }}

Prepared for: {{ title_block.for }}
Prepared by: {{ title_block.prepared_by }}
Date: {{ title_block.issued_on }}

{{ #each(options) as option }}
Option: {{ option.name }}
Contract Length: {{ option.formatted_contract_length }}
Total: {{ option.formatted_total_value }}

{{ #each(option.calculations) as calc }}
Year {{ calc.year }}:
{{ #each(calc.lines) as line }}
  - {{ line.formatted.name }}: {{ line.quantity }} × {{ line.formatted.unit_price }} = {{ line.formatted.price }}
{{ /each }}
Period Total: {{ calc.formatted.total }}
{{ /each }}
{{ /each }}

{{ #if(salesforce) }}
Customer: {{ salesforce.account_name }}
{{ /if }}
```

***

## Dynamic hash fields

Several variables use dynamic keys (marked as "Hash" in the reference). These include `custom_fields`, `custom_columns`, `summary`, `uom_fields`, and `product_custom_fields`.

Access dynamic fields with dot notation using the field key:

```text theme={null}
«=option.custom_fields.my_custom_field»
«=calc.custom_columns.my_column»
«=salesforce.custom_fields.my_sf_field»
«=line.product_custom_fields.my_product_field»
```

The available keys depend on what custom fields you've configured in the [Object Explorer](/get-started/pricing-and-packaging/product), [Display Options](/get-started/calculator/quote-display-options), and [CRM field mapping](/get-started/quickstart/connect-your-crm#field-mapping).

***

## Tips

**Use `formatted` 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

<CardGroup cols={2}>
  <Card title="Account Setup" icon="gear" href="/get-started/quickstart/account-setup#brand-identity">
    Configure the logo and brand color that appear in generated documents.
  </Card>

  <Card title="Quote Overview" icon="file-lines" href="/get-started/calculator/quote-options">
    Understand the quote structure that document generation renders from.
  </Card>

  <Card title="Display Options" icon="sliders" href="/get-started/calculator/quote-display-options">
    Control what pricing detail appears in generated documents.
  </Card>

  <Card title="Web Portal" icon="globe" href="/get-started/calculator/web-portal">
    Share interactive proposals alongside generated documents.
  </Card>
</CardGroup>
