The _data Sheet
XLSX templates support a reserved sheet named _data that pulls in additional data sources — records of other entity types, filtered by an expression.
This allows a single generated workbook to combine data from multiple sources.
The _data sheet is available only in XLSX templates.
| For an overview of all XLSX-specific features — typed values, live formulas, cell splitting, and more — see Excel templates. |
When to use the _data sheet
Use the _data sheet when the generated workbook needs data beyond what the triggering record provides.
Common scenarios include:
-
a contract workbook that includes the current price catalog from a directory entity type,
-
a reconciliation report that pulls in payments from a separate entity type alongside the deal data,
-
a campaign summary that combines deal information with display inventory records filtered by region.
Without the _data sheet, an XLSX template can only access fields from the record that triggers the generation and any records linked to it through link fields.
Sheet structure
The _data sheet uses three columns to declare each data source:
| Column | Holds | Content |
|---|---|---|
A |
Collection key |
The name by which this data source will be referenced in placeholders on other sheets.
For example, |
B |
Entity type |
The machine name of the entity type to query.
For example, |
C |
Filter expression |
An expression that filters the records returned. The expression follows the same syntax as expressions elsewhere in the system. Leave empty to return all records of the entity type. |
Every row declares one data source, starting at the first row of the sheet.
A single _data sheet can declare as many data sources as it has rows.
A row whose column A is blank is skipped, so blank rows can be used as separators.
Example
Suppose the system has two entity types: Price List Entry (machine name priceListEntry, with fields itemName, unitPrice, and category) and Digital Display (machine name digitalDisplay, with a field status).
All of these are machine names defined when the administrator created each entity type and its fields.
A _data sheet that pulls in all price list entries for the "outdoor" category and all active displays:
| A | B | C | |
|---|---|---|---|
1 |
priceCatalog |
priceListEntry |
category == 'outdoor' |
2 |
activeDisplays |
digitalDisplay |
status == 'active' |
Declarations start at row 1.
Labelling the columns
The engine reads every row whose column A is not blank.
A header row with "Collection key" typed into A is therefore read as a data source by that name, which fails to load and adds a row to the _errors sheet.
The way to label the columns is to put a comment in column A. A comment renders to nothing, which leaves column A blank, and a row with a blank column A is skipped before columns B and C are ever read — so the rest of the row is free text:
| A | B | C | |
|---|---|---|---|
1 |
{{! Collection key }} |
Entity type |
Filter |
2 |
priceCatalog |
priceListEntry |
category == 'outdoor' |
3 |
activeDisplays |
digitalDisplay |
status == 'active' |
The label stays visible to whoever opens the template and is invisible to the engine. A row whose column A is simply left empty is skipped in the same way, which is how blank separator rows work between groups of declarations.
A #, // or -- typed into column A is read as an ordinary collection key, and produces a failed data source and a row on the _errors sheet.
|
On other sheets in the workbook, the loaded records are available as collections.
Inside the repeating section, placeholders use the machine names of fields on the loaded entity type — here, itemName and unitPrice from the Price List Entry type:
{{#priceCatalog}}
{{itemName}} | {{unitPrice}}
{{/priceCatalog}}
Processing order
The _data sheet is processed in two phases:
-
Rendering — all cells on the
_datasheet are rendered through the template engine first. This means you can use placeholders in the filter expression column. For example, if the triggering record’s entity type has a field with machine namedealRegion, a filter likeregion == '{{$.dealRegion}}'resolvesdealRegionfrom the triggering record before the filter is applied.In batch reports, the user’s search criteria are available under the
filterkey. A filter expression likeregion == '{{filter.variableValues.region}}'resolves the region the user selected before the data source query runs. -
Declaration reading — after rendering, the engine reads columns A, B, and C to extract the data source declarations and loads the requested records.
Each data source is declared and filtered independently of the other data sources on the _data sheet.
After processing, the _data sheet is removed from the output workbook.
It does not appear in the generated file.
Reserved collection keys
Two collection keys are reserved and cannot be used on the _data sheet:
-
items— reserved for the primary record’s nested items (for example, proposal line items). -
filter— holds the search criteria that the user applied when generating the report. The engine uses this key to make filter values available as placeholders —{{filter.name}},{{filter.variableValues.region}}, and so on. See filter values in the template for the full reference and examples.
A row that uses one of them is not silently dropped: the collection comes out empty and the reason is written to the _errors sheet, the same as any other data source that fails to load.
The formula prefix
A cell that starts with {{=}} is treated as an Excel formula: the prefix is stripped, the remainder is rendered as a template, and the result is written into the cell as a formula.
This is different from an expression placeholder ({{=price * quantity}}), which evaluates JavaScript and inserts a static value.
Note where the closing braces sit — that is the whole difference.
The same prefix works on ordinary sheets too; see Excel templates.
On the _data sheet it matters more, because the formulas are evaluated before the declarations are read, which is what lets column C compute a filter from values worked out in the auxiliary columns.
Use the prefix when the filter needs a calculation, such as date arithmetic.
Example — everything created in the last 30 days:
| A | B | C | |
|---|---|---|---|
1 |
recentDeals |
deal |
{{=}}CONCATENATE("createdAt > '", TEXT(TODAY()-30, "yyyy-mm-dd"), "'") |
The three phases in order:
-
Every cell on the sheet is rendered as a template.
-
Every formula on the sheet is evaluated, so C1 becomes the text
createdAt > '2026-07-02'. -
Columns A, B and C are read as a declaration — and C1 now holds a finished filter.
The same works with auxiliary columns: put an intermediate value in column D, and have the formula in column C read D1.
The prefix does not put a formula in front of the reader here.
The _data sheet is removed from the output workbook, so the formula exists only long enough to produce the filter text.
|
Permission checks
The engine checks that the current user has read permission on the entity type declared in each data source. If the user does not have read access to the entity type, that data source is not loaded.
Error handling
A data source that fails to load does not stop the report. If the entity type does not exist, the filter expression is invalid, the collection key is reserved, or a permission check fails, the engine:
-
substitutes an empty array for that collection key (so placeholders referencing it produce no output rather than an error), and
-
creates an
_errorssheet in the output workbook.
The _errors sheet has four columns:
| Column | Content |
|---|---|
Collection Key |
The collection key from the |
Entity Type |
The entity type machine name that was requested. |
Filter |
The filter expression that was used. |
Error |
A description of what went wrong. |
If all data sources load successfully, no _errors sheet is created.
Check the generated workbook for an _errors sheet if any data sections appear empty.
The _errors sheet is the primary diagnostic tool for _data sheet problems.
|
Related pages
-
Entity types — where field machine names are defined
-
Data types — the nine field types and what each one holds
-
Repeating sections — iterating the collections loaded by the
_datasheet -
Expressions and formatting — the formula prefix vs expression placeholders
-
Excel templates — the full set of XLSX-specific features
-
Troubleshooting — when a data source produces no rows