Template syntax
Templates are HTML/CSS documents with Handlebars placeholders. The fields you reference become the data you send to /v1/generate.
Placeholders & loops
<h1>Invoice {{invoice_number}}</h1>
<p>Bill to: {{customer.name}}</p>
<table>
{{#each items}}
<tr>
<td>{{this.description}}</td>
<td>{{currency this.amount ../currency}}</td>
</tr>
{{/each}}
</table>
{{#if notes}}<p class="note">{{notes}}</p>{{/if}}Helpers
The following helpers are available for formatting (they never compute business logic):
{{currency total "USD"}}: format a number as currency{{date issue_date "medium"}}: format a date (short,medium,long,full, oriso){{number qty 2}}: format a number with fixed decimals- comparisons (for
{{#if (eq a b)}}):eq,ne,gt,gte,lt,lte,and,or
Full helper list: currency, number, date, eq, ne, gt, gte, lt, lte, and, or, code128.
Templates format, your code computes
Keep business math out of templates: pass computed values like subtotal, tax and total in your data. This keeps generation deterministic and avoids rounding bugs.
Every template is a typed API
On publish, Doclinth derives a Draft 2020-12 JSON Schema from the Handlebars structure and sample data. Fetch it from GET /v1/templates/:id/schema, or add ?format=typescript, zod, or pydantic. Set options.validate: true on generation to reject missing or mistyped data with field-level errors before rendering.
Save named payload fixtures for your own edge cases, then run the document-test suite to combine them with automatically generated layout stress cases.
Page size, fonts & images
- Set the page size with CSS
@page { size: A4 }(A6 and Letter are also supported). doclinth adds a sensible default if you omit it. - Use
page-break-inside: avoidon table rows so long tables paginate cleanly. - Use system/standard font stacks or self-hosted
@font-face. Reference a logo via a{{logo_url}}placeholder or adata:URI.
The fastest way to build one is the AI authoring editor: describe the document and refine the result live.