Skip to main content

Template Variables

Keelstone uses the xlsx-template library to merge data into Excel files. This page explains how to structure your Excel templates and your mergeData JSON to produce the output you want.

How merging works

  1. Your Excel template contains placeholder cells like {account.Name}
  2. excelTemplate sends the template (as base64) and your data to POST /api/docs/generate
  3. The server substitutes all placeholders with real values
  4. The merged workbook is returned as base64 and sent to Excel

Basic placeholders

Use curly braces to insert a single value into a cell:

Template cellmergeDataOutput
{account.Name}{ account: { Name: "Acme Corp" } }Acme Corp
{account.BillingCity}{ account: { BillingCity: "San Francisco" } }San Francisco

Placeholders can appear anywhere in a cell — mixed with static text:

Generated for: {account.Name} on {generatedDate}

Array iteration (table rows)

To repeat rows for a list of records, use the {#list}...{/list} syntax in adjacent rows:

| First Name       | Last Name       | Email        | Phone        |
|------------------|-----------------|--------------|--------------|
| {#contacts} | | | |
| {FirstName} | {LastName} | {Email} | {Phone} |
| {/contacts} | | | |

When merged with:

{
"contacts": [
{ "FirstName": "John", "LastName": "Doe", "Email": "john@acme.com", "Phone": "555-0101" },
{ "FirstName": "Jane", "LastName": "Smith", "Email": "jane@acme.com", "Phone": "555-0102" }
]
}

The table expands to two rows automatically, and surrounding rows shift down.

Standard mergeData shape

The accountSearch component produces this structure, which maps directly to the sample template:

{
"account": {
"Id": "001...",
"Name": "Acme Corp",
"Phone": "415-555-0100",
"Website": "https://acme.com",
"BillingCity": "San Francisco",
"BillingState": "CA",
"Owner": {
"Name": "Jane Smith"
}
},
"contacts": [
{
"Id": "003...",
"FirstName": "John",
"LastName": "Doe",
"Title": "CEO",
"Email": "john@acme.com",
"Phone": "555-0101"
}
]
}

Corresponding template placeholders:

{account.Name}
{account.Phone}
{account.BillingCity}, {account.BillingState}
{account.Owner.Name}

Custom mergeData shapes

You are not limited to the account/contacts structure. Build any JSON shape in your flow, then match the placeholders in your template.

Example: an opportunity pipeline report

// In your LWC or flow formula
const mergeData = JSON.stringify({
reportTitle: 'Q4 Pipeline Review',
generatedDate: new Date().toLocaleDateString(),
summary: {
totalAmount: 4250000,
openCount: 23,
closedWonCount: 7
},
opportunities: opportunities.map(o => ({
Name: o.Name,
Amount: o.Amount,
Stage: o.StageName,
CloseDate: o.CloseDate,
OwnerName: o.Owner?.Name
}))
});

Template placeholders:

{reportTitle}
Generated: {generatedDate}

Total Pipeline: {summary.totalAmount}
Open Deals: {summary.openCount}

{#opportunities}
{Name} | {Stage} | {Amount} | {CloseDate}
{/opportunities}

Date formatting

xlsx-template passes values as-is. Salesforce date fields return ISO strings ("2024-01-15"). If you need a formatted date, convert it before building mergeData:

const formatDate = (isoDate) =>
isoDate ? new Date(isoDate).toLocaleDateString('en-US') : '';

const mergeData = JSON.stringify({
opportunities: opps.map(o => ({
Name: o.Name,
CloseDate: formatDate(o.CloseDate) // "1/15/2024"
}))
});

Numeric formatting

Numbers are passed as raw values. Cell formatting in the Excel template controls display — set currency, decimal places, or percentage format directly in the template cell.

Uploading a new template

Templates are standard .xlsx files stored in Salesforce Files:

  1. Build your template in Excel with the placeholders above
  2. Upload it to Salesforce Files
  3. Move it into the Keelstone Templates content library
  4. Copy its ContentDocumentId and use it as templateId in your flow

To retrieve the ID:

ContentDocument doc = [
SELECT Id FROM ContentDocument
WHERE Title = 'My Template Name' LIMIT 1
];

For team-maintained templates, store the ID in a Custom Metadata record so non-developers can update it without code changes.