Uploaded Merge Templates
Print a form from an uploaded Word merge template: choose a print mode, upload a .docx, read the lint report, preview, and export a PDF
What This Feature Does
Every form has one print mode. You either design the PDF in the built-in Document Templates editor, or you upload a Word (.docx) merge template that uses merge tags such as {$site_location}. Switching modes deactivates the other template but keeps the file — you can switch back without re-uploading or rebuilding it.
When the form is in uploaded mode, Preview and Export fill those tags from the submission and return a PDF. Tag mistakes never block an export: a broken tag is printed as written. Only a corrupt file or a system failure stops the merge.
Authoring details — syntax, every supported modifier, and lint codes — live in Merge Tag Authoring.
Choosing a Print Mode
- Open the form in the Form Builder
- Open the Templates tab
- Choose one of the two cards:
| Card | What it uses | When to pick it |
|---|---|---|
| In-app editor | The built-in page editor (${field_name} placeholders) | New forms, or layouts you want to design in Pay |
| Uploaded merge template | A Word .docx you upload ({$field_name} tags) | Existing Word templates you want to keep |
A form can only print from one of these at a time. The other artefact stays on the form.
Switching when the other mode already has content
If the other mode already has a template, a confirmation dialog appears:
The existing template is deactivated but retained. You can switch back anytime without re-uploading or rebuilding it.
Click Switch to change modes, or Cancel to stay put.
You cannot switch to Uploaded merge template until a file has been uploaded for that form. Save the form first if it is new.
Uploading a Template
- Save the form (the dropzone is disabled until the form has an id)
- Select Uploaded merge template
- Drop a
.docxfile on the dropzone, or click the dropzone to browse - Wait for the upload to finish
- Read the lint report that appears under the dropzone
Available variables
The uploaded-template panel lists every tag this form can fill. System tags are {$id}, {$date}, {$title}, and {$submitted_at} (ISO timestamp — use with date_format; the other date keys are display strings). Project, client, and workpack tags ({$project_name}, {$client_name}, {$workpack_name}, and their number variants) are empty when the submission has no linked project, client, or workpack, so {if $project_name} and |default:'…' behave as expected. Form fields use the field Name from the builder ({$scope_notes}). Names may contain hyphens (auto-generated names like textarea-6c5 are valid). Click a tag to copy it.
Tag with Milo reads the uploaded Word letterhead, proposes which existing slots should get which tags, and waits for you to apply. It does not invent new sections.
Accepted files
- Extension:
.docx - Size: 25 MB or smaller
- The file must be a real Word document (a zip with
[Content_Types].xml) - PDF files are refused: merge templates are Word documents, and fillable (AcroForm) PDF templates are not supported
Uploading a new file deactivates the previous upload and stores the new one. Switching back to the in-app editor does not delete the uploaded file.
Reading the Lint Report
After upload (and again under Preview), Pay lists every tag it found, plus any problems.
Errors describe tags that will print verbatim in the PDF (the merge still succeeds):
| Code | What it means for you |
|---|---|
MISSING_SIGIL | The tag is missing $. {site} will print as {site}; write {$site}. |
SMART_QUOTES | Word curled the quotes. {$id|pad:5:”0”} will print as written; retype with straight " or '. |
UNKNOWN_MODIFIER | That modifier is not in the supported list. The whole tag prints as written. |
UNKNOWN_FOREACH_MODIFIER | The modifier on a {foreach} source is not in the supported list. The loop prints nothing; the finding shows the opening tag to search for. |
UNBALANCED_BLOCK | An {if} / {foreach} is missing its matching close, or a close/else has no open. Those tags print as written. |
BAD_ARG | A modifier argument could not be read (often a smart quote inside the argument). The tag prints as written. |
UNPARSEABLE | The tag could not be understood at all. It prints as written. |
Warnings never change the PDF:
| Code | What it means for you |
|---|---|
EMPTY_TAG | An empty {} was found. Harmless, but remove it. |
SPLIT_TAG_RESIDUE | Word split the tag across differently formatted runs. The tag may print as fragments. Re-type the whole tag in one style. |
UNRESOLVED_PATH | Shown on preview/export when a path is missing from the sample or submission data. The merge still succeeds. |
Lint is advisory. Fix errors so the PDF looks right; they will not stop Preview or Export.
Previewing
- With an uploaded template in place, click Preview PDF
- Pay fills the template with sample data built from this form (text fields get
Sample <label>, numbers42, dates today, table fields two sample rows, signature fields a bundled signature image) - The merged PDF opens in the preview pane
- Any render-time lint findings appear under the preview
Preview and export use the same merge payload shape — the same field keys, column slugs, display strings, and signature data URLs. Preview is not a layout mock; it is the real merge pipeline with sample answers instead of a submission.
The merged Word document is converted to PDF on our servers, so page breaks and fonts can differ slightly from what desktop Word shows.
What Happens on Export
When the form's print mode is Uploaded merge template:
- Open the submission (or select submissions and choose Export to PDF)
- Pay builds a merge payload from the submission:
- One key per form field Name (not the label, not the field id). Hyphens in auto-generated names (e.g.
textarea-6c5) are valid. - Table / repeat fields become arrays of row objects. Each row is keyed by the column header in lower snake case (
Used for whole shift→used_for_whole_shift). Nested sub-tables repeat the same pattern. Internal row ids (_rowId,_rowIndex,_createdAt) are not exposed. - Cell values are display strings (Resource fields show the name, booleans show
Yesor nothing, date cells stay ISO sodate_formatworks). A header that starts with a digit gets a leading underscore (1st Shift→_1st_shift). Signature and drawing answers aredata:URLs — use{$field|insert_image:W:H}to embed them; a bare{$field}prints the raw URL as text. - Reserved extras:
{$id}(submission id),{$date}/{$submission_date}(formatted submitted-at display strings),{$submitted_at}(raw ISO timestamp — use withdate_format), and{$title}(form title). Do not name a form fieldid,date,title, orsubmitted_at.
- One key per form field Name (not the label, not the field id). Hyphens in auto-generated names (e.g.
- The active uploaded template is merged
- The result is converted to PDF and downloaded (or prepared in the background for larger jobs)
When the form's print mode is In-app editor, export uses the Document Templates path unchanged.
A broken or unresolvable tag never fails the export. The tag is left as written (or, for a missing field, rendered as blank — see passthrough). Only a corrupt template file or an infrastructure failure (timeout, storage, converter) fails the job.
Switching Back (Rollback)
- Open the form → Templates tab
- Click In-app editor
- Confirm the switch
The uploaded file stays on the form, deactivated. Click Uploaded merge template later to reactivate the same file — no re-upload.
Switching the other way keeps the in-app editor template as well.
Limitations
- One print mode per form. A form prints from either its uploaded Word template or the in-app editor. Switching keeps both.
- Uploads are Word
.docxfiles and the output is PDF. PDF, Excel, PowerPoint and HTML files are not accepted as form print templates; fillable PDF forms need rebuilding as Word merge templates. {assign},{math}, and{include}are not supported. If they appear, they print as written.
Troubleshooting
The dropzone says to save the form first
Create and save the form, then return to the Templates tab. Uploads need a form id.
I cannot switch to Uploaded merge template
Upload a .docx first. Switching to uploaded mode with no file on the form is rejected.
The lint report shows SMART_QUOTES
Word replaced "0" with ”0”. Delete the quotes and type straight " characters, then re-upload.
The lint report shows MISSING_SIGIL
A tag is missing $. {crew_lead} must be {$crew_lead}.
Preview is empty or the PDF looks wrong
- Confirm the print mode card is Uploaded merge template
- Confirm the field Name in the form builder matches the tag path
- Read the lint report — errors print verbatim
- Re-type any tag Word split across bold/italic runs (
SPLIT_TAG_RESIDUE)
Export still uses the in-app editor layout
The form is still in In-app editor mode. Switch to Uploaded merge template (this requires an uploaded file).
My table renders nothing
- Confirm the
{foreach}wraps a whole table row and the open/close tags sit in the first and last cells. - Row tags must use the column header in lower snake case, not the label as typed in Word. Example: header
Used for whole shift→{$row.used_for_whole_shift}, not{$row.Used for whole shift}. - Copy column tags from the uploaded-template panel — they match the payload keys.
- For nested sub-tables, repeat
{foreach $nested_field as $subrow}{$subrow.<column>}{/foreach}inside the parent row.
My signature prints as text
Signature and drawing answers arrive as data:image/… URLs (photo and file-upload answers are file names, not images). A bare {$sign} tag prints that URL as text. Use {$sign|insert_image:200:80} (adjust width and height in pixels). The uploaded-template panel advertises the recommended tag for image field types.
What's Next
- Author tags and modifiers: Merge Tag Authoring
- In-app editor templates: Document Templates
- Exporting submissions: Submissions

