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, priceCatalog.

B

Entity type

The machine name of the entity type to query. For example, priceListEntry.

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.

Auxiliary columns

Columns D and onward are available for auxiliary calculations — intermediate values, helper formulas, or notes. The engine does not read these columns as part of the data source declarations.

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:

The _data sheet
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:

The _data sheet with a label row
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:

  1. Rendering — all cells on the _data sheet 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 name dealRegion, a filter like region == '{{$.dealRegion}}' resolves dealRegion from the triggering record before the filter is applied.

    In batch reports, the user’s search criteria are available under the filter key. A filter expression like region == '{{filter.variableValues.region}}' resolves the region the user selected before the data source query runs.

  2. 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:

The _data sheet
A B C

1

recentDeals

deal

{{=}}CONCATENATE("createdAt > '", TEXT(TODAY()-30, "yyyy-mm-dd"), "'")

The three phases in order:

  1. Every cell on the sheet is rendered as a template.

  2. Every formula on the sheet is evaluated, so C1 becomes the text createdAt > '2026-07-02'.

  3. 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 _errors sheet in the output workbook.

The _errors sheet has four columns:

Column Content

Collection Key

The collection key from the _data sheet row that failed.

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.