Chapter 7: Building Your First Templates
Learning by Building
Template development is a craft. Like any craft, it improves with deliberate practice, and the fastest way to learn it is to build real things in increasing order of complexity. Reading about conditional logic is useful; building a template that applies three different state-specific disclosure sections, testing it with records from California, Texas, and Washington, and watching each state's document generate correctly is transformative.
This chapter walks through building three templates that together cover every technique you will use in professional document automation work:
- A personalized welcome letter — field insertion, date formatting, one conditional section
- A state-adaptive compliance document — multi-path conditionals, nested logic, required legal language
- A master-detail financial document — loop structures, calculated totals, conditional sections within loops
Work through each template yourself as you read. Use the sample data provided. By the end of this chapter, you will have the hands-on experience needed to build any document in any of the 15 verticals in Chapter 5.
Before You Build Anything: The Design Sketch
Every professional template starts as a sketch — on paper, in a text file, or in a quick outline — before a single line is written in Word. Skipping this step is the most common source of rework in template development.
A template design sketch answers four questions:
1. What sections does this document contain? List every distinct section: header block, salutation, body paragraphs (each one), signature block, attachments. For complex documents: identify every section that might or might not appear depending on data.
2. What data fields appear in each section? For every data value that will be pulled from the dataset, note the field name and its source table. Every field should trace to a specific place in the data model.
3. Where is the conditional logic? For every section that appears in some versions of the document but not others, write the condition: Appears when: State = CA. For every section that has different content for different values: IF MembershipLevel = Gold, IF MembershipLevel = Silver, ELSE.
4. Where are the loops? For every section that repeats for multiple related records, identify the child table being iterated and the fields displayed in each iteration.
With this sketch in hand, building the template is mechanical. Without it, you are making design decisions while building — which slows you down and creates inconsistency.
Template 1: Personalized Member Welcome Letter
Scenario: A professional membership association wants to send personalized welcome letters to all new members who joined this month. Each letter should address the member by name, confirm their membership level and key dates, include a paragraph specific to how they joined (referred vs. direct enrollment), and provide their next steps.
Data model (what the system has available):
Table: Members
──────────────────────────────────
MemberID
FirstName / LastName
Salutation (Mr., Ms., Dr., etc.)
Email / Phone
MembershipLevel (Professional, Associate, Student, Retired)
JoinDate
RenewalDate
ReferredByMemberName (optional — blank if direct join)
MemberPortalURL (organization-wide constant)
AccountNumber
Design sketch:
- Header: today's date, member name and address
- Salutation: Dear [Salutation] [LastName]
- Paragraph 1: Welcome, confirm membership level
- Paragraph 2: Key dates (join date, renewal date)
- Paragraph 3 (CONDITIONAL): If referred, thank them and name the referrer. If direct, note they found us independently.
- Paragraph 4: Next steps (portal activation)
- Closing and signature
The template:
<<LetterDate>>{{FormatDate:MMMM d, yyyy}}
<<FirstName>> <<LastName>>
<<MemberAddress>>
<<MemberCity>>, <<MemberState>> <<MemberZip>>
Dear <<Salutation>> <<LastName>>,
Welcome to the <<OrgName>> community. We are delighted to confirm
your <<MembershipLevel>> membership, effective
<<JoinDate>>{{FormatDate:MMMM d, yyyy}}.
Your membership is active through <<RenewalDate>>{{FormatDate:MMMM d, yyyy}},
at which point you will receive a renewal notice by email. We want
your first year with us to be engaging and valuable — please do not
hesitate to reach out if you have questions about your member benefits.
{{IF ReferredByMemberName!=}}
We understand you joined through the recommendation of
<<ReferredByMemberName>>, one of our valued existing members. Their
enthusiasm for our organization speaks well of the community you are
joining. We will pass along your thanks.
{{ELSE}}
You found your way to us independently — which tells us you recognized
on your own that <<OrgName>> belongs in your professional life. We
hope to confirm that judgment many times over.
{{ENDIF}}
Your next step is to activate your online member account at
<<MemberPortalURL>>. Use the email address on file (<<Email>>) and
your member number <<AccountNumber>> to create your password. Once
inside, you will find your full member benefits guide, the events
calendar, and the member directory.
We look forward to seeing you at an upcoming event.
Sincerely,
<<ExecutiveDirectorName>>
Executive Director, <<OrgName>>
<<OrgPhone>>{{FormatPhone}}
What to test:
Generate this template against five records: one Professional member with a referral, one Associate member without a referral, one Student member, one Retired member, and one record where ReferredByMemberName contains only whitespace (a common data entry problem that should trigger the else branch). Verify that the conditional displays correctly for each case, that dates format properly, and that the salutation and name combination reads naturally.
The key learning from Template 1: The {{IF FieldName!=}} condition — "if this field is not empty" — is one of the most frequently useful conditions in the entire template language. Any field that is sometimes populated and sometimes blank should be wrapped in this condition to prevent awkward blank spaces or partial sentences in the output.
Template 2: State-Adaptive Lease Agreement (Compliance Document)
Scenario: A property management company manages residential properties in California, Texas, and Washington. They need a lease agreement template that automatically applies the correct legal language for each state: the right late fee rules, the right security deposit limits, the right entry notice requirements, and the required state-specific disclosures. The template must also handle a federal requirement (lead paint disclosure for pre-1978 properties) and an optional section (pet addendum).
This template represents the type of compliance-critical document that creates the deepest client value and the strongest retention. When a client knows that this template will update automatically when state law changes — and that you maintain it — they have a reason to stay that no price-based competitor can address.
Additional fields required (beyond basic tenant/property data):
Table: Properties (additional fields)
──────────────────────────────────────
State (CA / TX / WA)
YearBuilt (triggers lead paint requirement)
LeadPaintDisclosureText (state-specific if applicable)
Table: Leases
──────────────────────────────────────
GracePeriodDays (by state)
LateFee (amount)
SecurityDepositAmount
SecurityDepositMaxByState (reference value for compliance check)
EntryNoticeHours (by state: 24, 48, or 72)
PetsApproved (Yes/No)
PetDescription (if approved)
PetDeposit (if approved)
The state-conditional late fee section:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
SECTION 3: LATE FEES AND GRACE PERIOD
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Rent is due on the 1st day of each month.
{{IF State=CA}}
A late charge of <<LateFee>>{{FormatCurrency}} will be assessed if
rent is not received within <<GracePeriodDays>> days of the due date,
as permitted by California Civil Code §1671. No late fee may be charged
before the grace period expires.
{{ENDIF}}
{{IF State=TX}}
A late charge of <<LateFee>>{{FormatCurrency}} will be assessed if
rent is not received within <<GracePeriodDays>> days of the due date,
per Texas Property Code §92.019. The late fee is reasonable and
represents a fair estimate of the damages caused by late payment.
{{ENDIF}}
{{IF State=WA}}
A late fee of <<LateFee>>{{FormatCurrency}} will be assessed if rent
is not received within <<GracePeriodDays>> days of the due date, per
RCW 59.18.170. Landlord must provide written notice before assessing
a late fee.
{{ENDIF}}
The federal lead paint conditional:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
REQUIRED DISCLOSURES
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
{{IF YearBuilt<1978}}
LEAD-BASED PAINT DISCLOSURE (Federal — Required for All Pre-1978 Properties)
This property was constructed in <<YearBuilt>>. Federal law requires
disclosure of known information on lead-based paint hazards before
renting pre-1978 housing.
Landlord's Disclosure: <<LeadPaintKnowledge>>
Tenant acknowledges receipt of the EPA pamphlet "Protect Your Family
from Lead in Your Home."
Tenant Initials: _______ Date: ___________
{{ENDIF}}
The California-specific additional disclosures block:
{{IF State=CA}}
CALIFORNIA REQUIRED DISCLOSURES
Mold Disclosure (Health & Safety Code §26147): Tenant acknowledges
receipt of the written mold disclosure statement.
Bed Bug Disclosure (Civil Code §1954.603): Landlord has no knowledge
of a current bed bug infestation in the dwelling unit or building.
Smoking Policy: <<SmokingPolicyDescription>>
{{IF PropertyInFloodZone=Yes}}
Flood Hazard Disclosure: This property is located within a Special
Flood Hazard Area as designated by FEMA. Flood insurance may be
required or advisable.
{{ENDIF}}
{{ENDIF}}
The optional pet addendum:
{{IF PetsApproved=Yes}}
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
PET ADDENDUM
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Tenant is approved to keep the following pet(s) on the premises:
<<PetDescription>>
Non-refundable pet fee: <<PetDeposit>>{{FormatCurrency}}, due with
first month's rent.
Tenant agrees to: maintain current vaccinations, keep pets restrained
in common areas, immediately clean up any waste, and accept liability
for any property damage or injury caused by the pet. Approval of one
pet does not constitute approval of additional pets.
{{ENDIF}}
What to test:
Generate this template with at least six test records: a California property built in 1965 with pets approved, a California property built in 2001 with no pets, a Texas property built in 1952, a Texas property built in 2010, a Washington property with a flood zone designation, and a Washington property without. Verify that each document contains exactly the right sections for that record's state and property characteristics — nothing extra, nothing missing.
The key learning from Template 2: State-specific compliance documents are the deepest competitive moat in document automation consulting. Once you have built this template and verified it against current law, you have an asset that clients will pay to maintain indefinitely — because the alternative (manually tracking state law changes and updating Word documents) is both time-consuming and perpetually incomplete.
Template 3: Professional Services Invoice with Master-Detail Line Items
Scenario: A management consulting firm bills clients on a time-and-materials basis. Each invoice contains the firm's header, a line-by-line table of all time entries for the billing period, an optional expense section, a summary with totals and aging, and payment instructions. The number of line items varies from 1 to 40+ per invoice.
Data model:
Table: Clients (Master - Level 1)
──────────────────────────────────
ClientID / ClientName
BillingContactName / BillingContactEmail
BillingAddress / City / State / Zip
PaymentTerms (Net 30, Net 15, Due on Receipt)
PriorBalance (outstanding from previous invoices)
Table: Invoices (Master - Level 2, child of Client)
──────────────────────────────────
InvoiceID / ClientID
InvoiceNumber
InvoiceDate / DueDate
ProjectName / ProjectPhase
PastDue (calculated: Yes if DueDate < today and balance > 0)
ServicesTotalAmount (calculated from LineItems)
ExpensesTotalAmount (calculated from Expenses)
InvoiceTotalAmount (calculated: services + expenses)
TotalAmountDue (calculated: invoice total + prior balance)
Table: LineItems (Detail, child of Invoice)
──────────────────────────────────
LineItemID / InvoiceID
ServiceDate
StaffName / StaffTitle
ProjectPhase / TaskDescription
Hours / HourlyRate
LineItemSubtotal (calculated: hours × rate)
Table: Expenses (Detail, child of Invoice)
──────────────────────────────────
ExpenseID / InvoiceID
ExpenseDate
ExpenseDescription
ExpenseAmount
Reimbursable (Yes/No)
The complete invoice template:
[FIRM LOGO]
INVOICE{{MakeBold}}{{SetFontSize:18}}{{CenterText}}
<<FirmName>> Invoice #: <<InvoiceNumber>>{{MakeBold}}
<<FirmAddress>> Date: <<InvoiceDate>>{{FormatDate:MMMM d, yyyy}}
<<FirmCity>>, <<FirmState>> <<FirmZip>> Due: <<DueDate>>{{FormatDate:MMMM d, yyyy}}
<<FirmPhone>>{{FormatPhone}}
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
BILL TO:
<<ClientName>>
Attn: <<BillingContactName>>
<<BillingAddress>>
<<BillingCity>>, <<BillingState>> <<BillingZip>>
PROJECT: <<ProjectName>>{{MakeBold}} | Phase: <<ProjectPhase>>
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
{{IF PastDue=Yes}}
⚠ NOTICE: A prior balance of <<PriorBalance>>{{FormatCurrency}}
remains outstanding from previous invoices.{{MakeRed}}
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
{{ENDIF}}
PROFESSIONAL SERVICES
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
{{ForEach:LineItems}}
<<LineItems.ServiceDate>>{{FormatDate:MM/dd/yyyy}}
<<LineItems.StaffName>>{{MakeBold}}, <<LineItems.StaffTitle>>
<<LineItems.TaskDescription>>
<<LineItems.Hours>> hrs × $<<LineItems.HourlyRate>>/hr
= <<LineItems.LineItemSubtotal>>{{FormatCurrency}}
{{EndForEach}}
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Services Subtotal: <<ServicesTotalAmount>>{{FormatCurrency}}
{{IF ExpenseCount>0}}
EXPENSES
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
{{ForEach:Expenses}}
<<Expenses.ExpenseDate>>{{FormatDate:MM/dd/yyyy}}
<<Expenses.ExpenseDescription>>: <<Expenses.ExpenseAmount>>{{FormatCurrency}}
{{EndForEach}}
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Expenses Subtotal: <<ExpensesTotalAmount>>{{FormatCurrency}}
{{ENDIF}}
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
This Invoice: <<InvoiceTotalAmount>>{{FormatCurrency}}
{{IF PriorBalance>0}}
Prior Balance: <<PriorBalance>>{{FormatCurrency}}
{{ENDIF}}
TOTAL DUE: <<TotalAmountDue>>{{FormatCurrency}}{{MakeBold}}
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
PAYMENT TERMS: <<PaymentTerms>>
<<IF PaymentTerms=Net 30>>
Payment is due within 30 days of this invoice date.
{{ENDIF}}
{{IF PaymentTerms=Net 15}}
Payment is due within 15 days of this invoice date.
{{ENDIF}}
{{IF PaymentTerms=Due on Receipt}}
Payment is due upon receipt of this invoice.
{{ENDIF}}
Please remit payment to:
<<FirmName>>
<<FirmPaymentAddress>>
ACH/Wire: <<FirmBankDetails>>
Check payable to: <<FirmLegalName>>
Questions? <<BillingContactAtFirm>> | <<BillingEmailAtFirm>>
What to test:
Generate this template against six invoices: one with 1 line item and no expenses, one with 8 line items and 3 expenses, one with 22 line items and no expenses, one with a prior balance, one marked as past due, and one with Net 30 terms (then test Net 15 and Due on Receipt variants). Verify that the line item loop handles all counts cleanly, that the expenses section appears only when expenses exist, that the past-due warning block triggers correctly, and that the totals are accurate for every test case.
The key learning from Template 3: The {{IF Count>0}}...{{ENDIF}} pattern — showing a section only when related records exist — is essential for any document with optional subsections. An invoice without expenses should not show an empty "EXPENSES" section with a $0.00 total. A meeting agenda with no open action items should not show an empty "Action Items" table. Design your loops to handle the zero-record case gracefully.
Common Template Problems and How to Diagnose Them
Problem: A field appears blank in the output
Most likely cause: The field name in the template doesn't exactly match the field name in the data. Template field names are case-sensitive and space-sensitive. <<FirstName>> and <<Firstname>> are different fields; <<First Name>> (with a space) is a different field from <<FirstName>>. Check spelling carefully.
Second most likely cause: The field exists but the value is blank in the data source for that record. Check the actual data, not the template.
Problem: A conditional section appears when it shouldn't (or doesn't appear when it should)
Most likely cause: The condition value in the template doesn't match the data exactly. {{IF State=California}} won't trigger for a record where State contains CA. Decide on your canonical values (use CA, not California) and be consistent throughout both the data and the templates.
Second most likely cause: Extra whitespace in the data field. A State field containing CA (with a trailing space) won't match CA. Run a data cleaning step to trim whitespace from all condition-relevant fields before import.
Problem: A loop generates more iterations than expected
Most likely cause: The loop is iterating over the wrong table, or the relationship filter isn't working correctly. Every loop iterates over all child records of the current master record. If you're seeing child records from other master records appearing, the relationship link between the tables isn't filtering correctly.
Problem: Formatting looks wrong — dates as numbers, currency without symbols
Most likely cause: The formatting function isn't attached to the field placeholder. <<InvoiceDate>>{{FormatDate:MMMM d, yyyy}} works; <<InvoiceDate>> {{FormatDate:MMMM d, yyyy}} (with a space before the function) may not. Formatting functions must be immediately adjacent to the field placeholder they modify.
Building Your Template Library
After your first implementation, you have templates. After your fifth, you have a library. The library is what makes the difference between a consultant who can serve 10 clients and one who can serve 100.
Version control your templates. Keep copies at major milestones: the version you submitted for initial client review, the version after first revisions, the production version at go-live. When a client asks you to restore a feature that existed "a few months ago," you need to be able to find it.
Parameterize for reuse. When a template contains client-specific values (their logo path, their organization name, their fee schedule), separate these into a configuration table rather than hard-coding them in the template. This allows you to deploy the same template for a new client by updating the configuration table rather than editing the template itself.
Document your logic. In complex templates, add commented notes explaining the purpose of conditional blocks: [CONDITION: Applies California Tenant Protection Act language — applies to properties built before 1978 in CA only]. Your future self six months from now, and any resource you eventually bring in to help with implementations, will thank you.
Maintain a test data library. For each vertical, maintain a complete set of test records that covers all the meaningful edge cases for templates in that vertical. When you need to test a template change quickly, you shouldn't have to recreate test data from scratch — it should be ready.
Chapter Summary
- Every template starts as a design sketch that answers four questions: what sections exist, what data appears in each, where the conditional logic is, and where the loops are
- Template 1 (personalized letter) teaches field insertion, date formatting, and the
{{IF FieldName!=}}condition for optional content - Template 2 (state-adaptive compliance document) teaches multi-path conditionals, nested conditionals, and the compliance value that creates deepest client retention
- Template 3 (master-detail invoice) teaches loop structures with conditionals inside loops, optional child-record sections, and the
{{IF Count>0}}pattern for handling zero-record cases - Common problems: blank fields (field name mismatch or blank data), wrong conditional behavior (value mismatch or whitespace), loop over-iteration (broken relationship filter), formatting errors (function not adjacent to field)
- Build a template library: version-controlled, parameterized for reuse, logically documented, tested against a maintained edge-case data library
Next: Chapter 8 — Finding Your First Clients
Chapter 7 | The Document Automation Consultant | datapublisher.io/books