You've got variables merging and conditions branching — this article covers the power tools: formatting filters, reusable values, repeating table rows, client name templates, and what to do when a letter won't generate.
Filters
A filter reshapes a value before it prints. Chain it after the variable with a pipe:
{{ service.price | usd }}
Fidu's filters
Filter | Example | Prints |
|
| $1,234.50 |
|
| 21st |
usd is the right way to print any money amount — never rely on the raw number. ordinalize shines in legal boilerplate:
Executed on this {{ engagement.closing_day | ordinalize }} day of {{ engagement.closing_month }}.
Standard Liquid filters
Fidu's letter generation runs real Liquid, so the standard filters work too. The most useful:
Filter | Example | Prints |
|
| March 5, 2026 |
|
| ACME HOLDINGS LLC |
|
| Llc — be careful with acronyms |
|
| Falls back when the first value is blank |
|
| Matter No. 2026-014 |
|
| Strips a suffix |
Common date patterns: %B %-d, %Y → March 5, 2026 · %m/%d/%Y → 03/05/2026 · %A → Thursday. Always verify date output with a test letter — the stored answer format determines what the filter can parse.
Filters chain left to right: {{ client.legal_name | default: client.name | upcase }}.
Reusable values with assign and capture
Compute a value once, use it many times:
{% assign fee = service.price | usd %}
The fee of {{ fee }} is due upon execution. … A late charge applies to any portion of the {{ fee }} not received…
capture does the same for a block of text:
{% capture full_address %}{{ client.address }}, {{ client.city }}, {{ client.province }} {{ client.postal_code }}{% endcapture %}
{{ full_address }}
Place assign/capture tags on their own line near the top of the document; each renders nothing itself.
Repeating table rows (Word templates only)
Word templates can repeat a table row per item in a list. The row-loop tags live in their own otherwise-empty table rows, using the {%tr … %} form:
Row 1 (tag only):
{%tr for user in users %}Row 2 (the template row):
{{ user.name }}in one cell,{{ user.email }}in the nextRow 3 (tag only):
{%tr endfor %}
The rows holding the {%tr%} tags disappear in the generated letter, leaving one populated row per item. One tag per row, nothing else in that row. Google Docs templates don't support table-row loops.
Client name templates
When your signup form has no Business/Client Name field (typical for individual clients), Fidu builds each new client record's name from the client name template — a small Liquid template on the letter template's Signup Form tab. The default is:
{{ user_first_name }} {{ user_last_name }}Available variables: any named question on the signup form (by its bare name — {{ user_first_name }}, not the engagement. form), plus {{ external_name }} and {{ internal_name }} for the service's names. For example, to name records like "Nguyen — Estate Plan":
{{ user_last_name }} — Estate Plan
Authoring rules by document type
Both formats
A tag must not contain a line/paragraph break in the middle.
Use straight quotes inside tags (watch for Word/Google autocorrect).
Blocks must be balanced (
if/endif,for/endfor).Money math belongs in your data and prices; templates only format values.
Google Docs
Every tag must be uniformly styled — one font, weight, and size across the whole tag. Content between tags can be styled freely.
Loop bodies can't span multiple paragraphs, and table-row loops aren't supported.
Tags must be plain text — no inline images or page-number fields inside a tag.
Word
Table-row loops are supported via the
{%tr%}form (above).Within a tag, styling can vary; across paragraphs it cannot (a tag can't contain a hard break).
Troubleshooting
A variable printed nothing. Either the name is misspelled (copy it from the variables list) or the underlying value is genuinely blank (an optional answer, a missing phone number). Use default: or an {% if %} wrapper for values that may legitimately be empty. Test letters report which variables came out unresolved.
The letter failed to generate with a template error. The error message names the offending tag. The usual suspects:
an unclosed or mismatched block (
{% if %}with no{% endif %}),curly "smart" quotes inside a tag,
a tag split across lines or (in Google Docs) styled inconsistently,
a misspelled tag or filter name.
Fix the tag it names and regenerate.
A condition never fires. Compare against the exact answer label, capitalization included (== "Yes"), and check the quotes are straight.
Stray blank lines where a condition was false. Put the {% if %} and {% endif %} on their own lines wrapping whole paragraphs — paragraph-level conditions that come up empty are removed cleanly, inline ones leave the rest of their line.
When in doubt, generate a test letter. It runs the exact production merge with sample data and is always the fastest diagnostic.
