Templates
A template is a pre-formatted .docx containing {{ variable }} markers that get replaced at instantiation time with structured data. Use templates when you want a designer-controlled visual artefact (letterhead, contract, branded report) that produces one document per request without the AI/agent needing to know about the visual design at all.
This is different from themes: a theme controls styling that applies on top of any spec; a template is a specific document skeleton you fill in.
End-to-end flow#
1 2 3 4 5 6 7 8 9 10 11 | |
The template lives in object storage; the database keeps a row with the placeholder catalogue and metadata. Each instantiation produces a brand-new document (with its own doc_id) — templates are immutable; the instantiation is the variable thing.
Register a template#
1 2 3 4 5 6 7 8 | |
The server parses the binary, extracts every {{ name }} marker, and returns the list. If your template references {{ recipient.address.street }}, the placeholder list reports recipient.address.street — nested paths are first-class.
Instantiate#
1 2 3 4 5 6 7 8 9 | |
The response is a normal DocumentEnvelope — same shape create_document returns. From here you can finalize, preview, or patch the spec like any other doc.
Templating engine#
Under the hood, ScaiScribe uses docx-templates with cmdDelimiter: ["{{", "}}"]. Every command lives inside {{ ... }} — there is no {% ... %} block syntax. The grammar at a glance:
| Marker | Effect |
|---|---|
{{ name }} |
Substitute the value. |
{{ user.email }} |
Nested path access. |
{{ INS expression }} or {{ = expression }} |
Insert (explicit / shorthand). |
{{ IF active }}…{{ END-IF }} |
Conditional block. |
{{ FOR row IN rows }}…{{ $row.cell }}…{{ END-FOR row }} |
Loop block. Note the $ prefix on the loop variable inside the body. |
{{ ALIAS x INS something }} … {{ *x }} |
Reusable inline alias. |
{{ LINK ({url, label}) }} |
Hyperlink. |
The expression inside any command is JavaScript — filtering, arithmetic, formatting via toFixed, ternaries all work inline.
For the complete grammar (every command, every quirk, the placeholder extraction story, the limitations vs. upstream docx-templates) see the Templates reference.
Format coverage#
| Format | Substitution | Expressions | FOR / IF | Slide / row replication | Image insertion |
|---|---|---|---|---|---|
| DOCX | ✓ (docx-templates) | ✓ full JS | ✓ | ✓ table-row replication | ✓ via IMAGE (inlined image objects) |
| PPTX | ✓ | ✓ safe subset | ✓ text-frame level | ✓ slide-level | ✗ — roadmap |
| XLSX | ✓ | ✓ safe subset | ✓ row level | ✓ row replication with formula refs | n/a |
DOCX goes through the docx-templates library and gets the full grammar (loops, conditionals, aliases, JS expression evaluation in a VM sandbox).
PPTX and XLSX use a hand-rolled engine in workers/node/lib/ooxml-template-fill.js (Tier 2):
- Expressions — safe subset of JS via a recursive-descent parser. No
eval, nonew, no arrow functions, no arbitrary globals. Supports path access, method chains (with a per-type allowlist: strings gettoUpperCase/toLowerCase/slice/replace/…, numbers gettoFixed/toLocaleString/…, arrays getlength/join/slice/…), arithmetic, comparisons, logical ops, and ternaries. {{ FOR x IN list }} … {{ END-FOR x }}— replicate paragraphs (PPTX) or rows (XLSX) per item. Inside the body,$xis the current item and$idxthe 0-based index. PPTX has TWO FOR scopes: text-frame level (within one shape) and slide level (a whole slide cloned per item).{{ IF expr }} … {{ END-IF }}— gate content on a truthy expression.- Missing variables render as empty strings (not errors) + emit
TEMPLATE_VARIABLE_MISSINGwarning. - Run-span coalescing preserves a token across multiple
<a:r>/<w:r>runs when PowerPoint or Excel splits it during authoring.
What's NOT supported in PPTX/XLSX expressions (DOCX still has these via docx-templates):
- Arrow functions / lambdas (
items.filter(x => x.urgent)) - The
IMAGE,LINK,HTML,EXEC,ALIAScommands - Arbitrary globals (
Math,Date,JSON, etc.)
What you can't do#
- Use a template AND a theme together for the body content. The template controls every visual aspect — it IS the styling. The theme isn't consulted during template instantiation. (Themes still apply to spec-driven documents you create separately.)
- Patch a template-instantiated document and re-render against the template. Once instantiated, the document is a normal spec — the template binding is lost. The instantiation is one-shot.
- Use FOR/IF in PPTX or XLSX templates. v1.x — see the matrix above. DOCX gets the full grammar today.
Quotas#
The templates_count axis (default 20 per tenant) limits how many templates you can register. Instantiation doesn't have a separate quota — every instantiated doc is just a normal doc, counted under documents_count.
When templates are the right tool#
Use templates when:
- The visual design is owned by a non-technical stakeholder
- You produce the same document shape over and over with different data
- The design is more elaborate than themes alone can express (specific tables, header/footer art, complex page geometry)
- You need pixel-stable output across runs
Don't use templates when:
- The document structure varies per request (build the spec dynamically instead)
- An agent is supposed to author content semantically (let the agent compose elements via MCP tools)
- You need to edit the produced document after generation (you can, but the template binding is one-shot)
Where to next#
- Tutorials → Templates — concrete walkthrough end-to-end.
- Themes — when to use a theme instead.
- Reference → API —
/v1/templatesand/v1/documents/from_templateshapes.