What Are Tag Templates?
Tag Templates are the standard document format in ScopeStack. They use tags β short references enclosed in curly braces β to pull project data into a Word, PowerPoint, or Excel file and produce a finished document for the client.
Tag Templates replaced the older Mail Merge (V1) format. If you are building new templates, use Tag Templates. The legacy format continues to work but is not recommended for new work.
Formatting Options
When you upload a Tag Template, you can enable Formatting for Tag Templates. This option lets you apply date, time, and currency formatting directly in your template using wrapper methods like .to_currency and .to_short_date.
See the Tag Template Formatting article for full details on when to enable this and how it changes the tag syntax.
Viewing Merge Data
Before writing template tags, it helps to see the actual data available for a specific project. ScopeStack includes a Merge Data view for this purpose.
Enabling the permission: The Merge Data view must be enabled in your accountβs role settings before users can access it. Go to Settings > Roles, open the role you want to update, and find the Merge Data permission under the Projects section. Change it from None to View, then save the role.
Accessing the view: Open any project, click the gear icon in the header, and select View Merge Data. Choose V2 data when working with Tag Templates. The panel shows each field name and its current value for that project, letting you confirm exact field paths before adding them to a template.
Use the V2 Merge Data view β not the raw API response β as your reference when building or debugging templates. The render-time data includes computed fields like unique, unique_count, and renamed entities that do not appear in the API.
Debugging Templates: Merge Data View vs. Raw API
The Merge Data view and the raw API look like two ways to access the same information. They are not. Understanding the difference prevents a common class of confusing failures.
The Merge Data view (/projects/{id}/merge_data_visualization?version=2, accessed from the gear menu) shows data after the V2 transformation layer has run. This is what the template engine actually sees at render time. Use this as your reference when building or debugging any Tag Template.
The raw API endpoint (/projects/{id}/merge-data) returns data before that transformation runs. Several fields that exist at render time are absent from the raw API response because they are synthesized during rendering, not stored in the database.
Fields that are present at render time but absent from the raw API include:
uniqueβ the deduplication boolean on each service. Do not use\{#unique?\}in templates; the?suffix crashes the V2 parser.unique_countβ the count of how many times a service name appears across the project.servicesandsubservicesβ V2 renamestaskstoservicesandfeaturestosubservices. The raw API still returns the old names. If the API showstasksbut your template usesservices, that is correct and expected.- The nested
locations > phases > servicespath used in location-first templates. The raw API returns a different data structure. The Merge Data view shows the V2-transformed shape.
If a field appears missing when you browse the API directly but works correctly in a generated document, the V2 transformation is the explanation. Always debug templates against the Merge Data view, not the raw API.
Where Does the Data Come From?
Tag Templates pull from ScopeStackβs structured project data, delivered in JSON format. JSON is the industry standard for document generation β it is text-based, compact, and human-readable.
Here is a simplified example of what JSON data looks like, using the first U.S. Congress as a stand-in:
{
"president": {
"first_name": "George",
"last_name": "Washington",
"state": "Virginia"
},
"vice_president": {
"first_name": "John",
"last_name": "Adams",
"state": "Massachusetts"
},
"representatives": [
{ "first_name": "Fisher", "last_name": "Ames", "state": "Massachusetts" },
{ "first_name": "John Baptista", "last_name": "Ashe", "state": "North Carolina" }
]
}Two concepts matter here:
- Object: A set of named attributes, wrapped in curly braces.
presidentis an object with attributesfirst_name,last_name, andstate. - Array: A collection of objects, wrapped in square brackets.
representativesis an array of individual people.
These two concepts β objects and arrays β drive how sections and loops behave in Tag Templates.
Data Context
Before getting into the specific tags, it helps to understand data context. At any point in a document, the merge process is focused on a particular part of the data β this is the current context. Special tags let you shift the context to zoom in on a specific object or loop through an array.
Section Tags
The core building block of Tag Templates is the section tag pair:
{#path.to.something}
...content...
{/path.to.something}A section always starts with {#...} and ends with {/...}. The path in both tags must match. What happens inside the section depends on what path.to.something points to.
If the path points to an object, the context shifts to that object. Tags inside the section can reference that objectβs attributes directly.
Example:
{#president}
The first President of the United States was {first_name} {last_name} of {state}.
{/president}
{#vice_president}
His Vice President was {first_name} {last_name} from the state of {state}.
{/vice_president}Output:
The first President of the United States was George Washington of Virginia.
His Vice President was John Adams from the state of Massachusetts.If the path points to an array, the section loops β everything inside repeats once for each item in the array.
Example:
The House of Representatives was as follows:
{#representatives}
- {last_name}, {first_name} of {state}
{/representatives}Output:
The House of Representatives was as follows:
- Ames, Fisher of Massachusetts
- Ashe, John Baptista of North Carolina
- ...One caution with loops: any text inside the section repeats for every item. If you include an introductory line inside the loop by mistake, it will repeat for each item in the array.
Conditional Sections
A section tag can also act as a conditional. If the path points to a simple value rather than an object or array, the section renders only when that value is truthy.
You can also use comparisons inside section tags:
{#representatives}
{#state=="Massachusetts"}
- {first_name} {last_name}
{/state=="Massachusetts"}
{/representatives}This renders only the representatives from Massachusetts. Supported comparisons include equal (==), not equal (!=), greater than (>), and less than (<).
HTML Content Tags
Fields that use Markdown β such as service descriptions and language fields β have a formatted_ equivalent in the merge data. Use a double-tilde tag to convert that HTML content into formatted document text:
{~~formatted_service_description}Without the double tilde, the raw HTML markup will appear in the document instead of the formatted text.
The rendered content takes on the paragraph style of the line where the tag sits β that is how you control its font and how lists are styled. See Styling rich text and lists.
Image Tags
To embed an image, use a tag starting with %:
{%project.account_logo_url}Math in Tags
Tags support basic math expressions. For example:
{#services}
{name} will cost {hourly_rate * extended_hours}.
{/services}This lets you calculate totals and derived values directly in the template without pre-processing the data.
Quick Reference
All examples below require V2 merge data. If tags are not replaced in your output, confirm the template is configured for V2 and that the Merge Data view shows the expected fields for your project.
| Purpose | Syntax |
|---|---|
| Insert a text field | \{field\} |
| Conditional block (show when truthy) | \{#field\}β¦\{/field\} |
| Conditional block (show when falsy) | \{^field\}β¦\{/field\} |
| Loop over an array | \{#items\}β¦\{/items\} |
| Rich text / HTML rendering | \{~~formatted_field\} |
| Inline math | \{hourly_rate * extended_hours\} |
| Image embed | \{%project.account_logo_url\} |
| String equality check | \{#field=="value"\}β¦\{/field=="value"\} |
| Negated equality check | \{^field=="value"\}β¦\{/field=="value"\} |
| Array length comparison | \{#items.length > 1\}β¦\{/items.length > 1\} |
For the full set of pipe filters, include-formatting dot accessors, deduplication patterns, and expression recipes, see Template Expression Reference.