Skip to main content
A Document Template is a document layout that a flow fills with data and turns into a PDF: a payslip, an employment certificate, an onboarding form. The flow produces the file, and an upload step after it delivers the file wherever it should go. There are two kinds: Templates are managed under Organization settings → Document templates.

Uploading a template

Drop an .html or .pdf file (at most 10 MB) on the upload area. The template is named after the file, without its extension, and the flow action picks it by that name. Names are unique within an organization: to replace a template, delete it and upload the new version. Each template is checked when it is uploaded, so a broken one is refused there and then instead of failing a flow run later:
  • An HTML template must be valid Liquid. A syntax error is reported with its line number.
  • An AcroForm must be a standard PDF form with at least one fillable field. XFA forms and PDFs without fields are refused.

What a template needs

Click a template in the list to see what a flow has to give it. For an AcroForm, that is the list of its form fields. The flow passes a map with one entry per field name. For an HTML template, the dialog has three parts:
  • Data to supply. The keys the map passed to the template must have, and what each holds: a single value, a map with certain fields, or a list of rows with certain fields.
  • Placeholders. Every value the template reads, written as a path into that map. [] marks a list: rows[].Amount is the Amount of each row. When the template reads a value under another name, that name is shown next to it.
  • Set by the template. Names the template creates itself with {% assign %} or {% capture %}. The flow does not supply these.
Take a payslip template that starts like this:
Its placeholders are rows[].Employee_Name (read as summary.Employee_Name), rows[].Description and rows[].Amount (read as row.Description and row.Amount), and employee.salary_currency (read as currency). summary and currency are set by the template. The flow supplies two keys: employee and rows.
The placeholder list is read from the template’s text, so it is a strong hint rather than a guarantee. A value read in an unusual way, for example through a name built at run time, may not appear in it.

Rendering a template in a flow

Add a Flow Action and pick the Render HTML to PDF step:
  • Template: the HTML template to render.
  • Data assign: the assign holding the map the template reads. Build it with Build Map, one row per key from Data to supply.
  • File name, or File name assign to name each file from the data (payslip-1042.pdf).
  • Fail on missing values: stop when the template reads a value the data does not have, instead of printing it blank. The error names each missing value and its line in the template.
  • Output assign: where the PDF is written, ready for an upload step.
For the payslip above, the Build Map has two rows: employee, taken from the assign holding the employee, and rows, taken from the assign holding the payslip lines (for example the rows read from a CSV file). Fill PDF form works the same way, with a Fields assign holding one entry per form field instead of a data assign. Its Flatten option, on by default, bakes the values into the page so the result can no longer be edited.

Employees and dated properties

An employee from Twine’s System Data can be passed to the template as it is. The template reads its properties by their Twine names, and each one prints the value valid today:
Properties have a history, and a moment after the property name reads another point in it. These are the same moments as @ in expressions: A payslip printed after a change of bank account can show the account valid on the payment date rather than today’s:
Some properties are kept per employment or per salary, such as salary_currency. These are read across all of the employee’s employments, so employee.salary_currency is the currency valid today whichever employment it belongs to. A property the employee has no value for prints blank, or stops the flow when Fail on missing values is on. Use Twine’s property names (bank_iban), not the source system’s: the employee data model lists them.

Writing HTML templates

Templates use Liquid: {{ ... }} prints a value and {% ... %} holds logic such as if and for. Liquid’s standard filters are available, plus one of Twine’s own:
  • money formats a number Swedish style: {{ row.Amount | money }} prints 5838.42 as 5 838,42. Pass the number of decimals to change them: {{ row.Amount | money: 0 }}.
Some things to know:
  • Make the file self-contained. For security, rendering cannot fetch anything from the internet. Put styles in a <style> block and images in the file as data: URIs. An external stylesheet, font or image is left out of the PDF, and the flow run log names each one.
  • Read the first row with rows[0]. rows.first and rows.last are not supported on lists.
  • An empty text value counts as true. {% if row.Unit %} is true for an empty cell. Reading a CSV file turns blank cells into nothing by default, which counts as false.
  • Keep Liquid out of HTML comments. Liquid reads {{ and {% inside <!-- --> too, so a comment containing them is a syntax error.
  • Use print CSS for page layout. @page { size: A4; margin: 20mm; } sets the paper size and margins, and page-break-before: always starts a new page.