Templates
You'll: design a letterhead template in Word, register it with ScaiScribe, instantiate it ten times for ten different recipients, then finalize to PDF for each. Total wall-clock with a warm cache: under a second per letter.
Step 1 — Author the template#
Open Word (or LibreOffice). Create a normal letterhead. Wherever you want a value substituted, write {{ variable_name }}. Use the docx-templates grammar for loops and conditionals:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | |
Save as letterhead.docx. ScaiScribe doesn't care about styling beyond what your design needs — the template IS the styling.
Note three things about the grammar:
- All commands use
{{ ... }}— no{% ... %}block markers. - Inside
FORloops, the loop variable is$-prefixed ($item, notitem). - Number formatting is done inline via JS (
amount.toFixed(2),amount.toLocaleString('de-DE')). There's nonumberFormatfilter. See the Templates reference for the full grammar and the reason.
Step 2 — Register#
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | |
The placeholder list is what ScaiScribe parsed out. If anything's missing, your variable wasn't recognised — check the marker syntax.
Step 3 — Instantiate per recipient#
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | |
Each call mints a fresh doc_id and applies the variables. The instantiated document is a normal spec — you could PATCH it further if you wanted, but you'll usually want to finalize directly.
Step 4 — Finalize to PDF#
1 2 3 4 5 | |
Or batch both formats:
1 2 3 | |
Step 5 — Download#
The download_url is a presigned S3 link. Stream it:
1 2 3 4 5 6 7 8 9 | |
What just happened#
- The template binary lives once in object storage; ten instantiations don't duplicate it.
- Each instantiation is a tiny database row (a new doc_id + a Spec carrying just the variable bindings + a reference to the template).
- The actual
.docxproduced byfinalizeis generated by docx-templates filling the variables into the binary at render time. PDF goes through Gotenberg. - Every instantiation counts under
documents_countquota. The template itself counts undertemplates_count.
Gotchas#
- Numeric formatting — there is no
numberFormatfilter. Format inline with JS built-ins:{{ amount.toFixed(2) }}or{{ amount.toLocaleString('de-DE', {minimumFractionDigits: 2}) }}. - Loop scope — inside a
{{ FOR }}block, the loop variable is$-prefixed: write{{ $person.name }}, not{{ person.name }}. If you need an outer-scope variable in a loop, just reference it normally ({{ outer.name }}). - Conditionals on empty arrays —
{{ IF items }}is truthy for empty arrays. Use{{ IF items.length > 0 }}if you mean "items has at least one". - Image insertion — docx-templates supports
IMAGEfor dynamic images. The expression must return an image object — for ScaiScribe specifically, pass pre-rendered image bytes in the variables payload (we don't exposeadditionalJsContext). See the reference for the exact shape.
PPTX and XLSX templates#
The same flow works for .pptx and .xlsx templates with simple substitution only (no FOR/IF, no JS expressions). Author the template in PowerPoint/Excel, type {{ recipient.name }} directly where you want the value, register, instantiate:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | |
Differences vs. DOCX:
- Method allowlist —
{{ amount.toFixed(2) }}works for both numbers and strings, but only methods from the per-type allowlist are callable. No arrow functions, nonew, noMath/Dateglobals. - Missing variables render as empty (with a warning in the result), not as a render error. By design — partial fills are common in PPTX/XLSX templates.
IMAGE/LINK/HTML/EXECcommands aren't available in PPTX/XLSX. DOCX still has them via docx-templates.
Looping in PPTX/XLSX#
Both PPTX and XLSX support FOR/IF, but with different placement rules:
PPTX text-frame loop — markers occupy whole paragraphs in one text frame:
1 2 3 4 | |
PPTX slide-level loop — the FOR marker is the first text on a slide, END-FOR is the last. The whole slide is cloned per item:
1 2 3 4 | |
Three companies → slide 2 becomes slides 2, 3, 4. The cover slide and any tail slides pass through unchanged.
XLSX row loop — markers occupy whole rows; the block between replicates with row renumbering and formula-ref rewriting:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | |
For full grammar + the method allowlist + edge cases, see Reference → Templates → PPTX & XLSX (Tier 2).
Where to next#
- Templates concept — the model behind it.
- Tutorials → Ingest + edit — for the reverse flow.
- Reference → API —
/v1/templatesand/v1/documents/from_templateshapes.