Skip to main content

Template

A template is a versioned document blueprint inside a project, addressed by a URL-safe slug (unique per project). You render against a slug: POST /v1/templates/:slug/render.

Version

Publishing a template snapshots an immutable version (currentVersionNumber). Renders use the current published version unless you pin one with options.versionNumber. A template with no published version cannot be rendered (no_published_version).

Variable

A typed merge field. Each variable has a dot-path key (customer.name), a dataType, and a required flag.

Select variables (fixed choices)

A select variable carries an options array of { value, label } choices — value is what is stored and validated, label is display text. A select requires a non-empty options array at publish time (POST/PUT /v1/templates reject a select with no options); option values must be unique, and a defaultValue, if set, must be a string equal to one of the option values. At render time only a declared value is accepted — anything else fails with invalid_input_data, exactly like url/date/email validation. The generated JSON Schema emits it as a string enum.
options is optional and only meaningful for select; clients that ignore it are unaffected. See Variables & loops for dot-path nesting and arrays.

Manifest

The variable manifest is the contract a render payload must satisfy: { variables: VariableDefinition[], loops: LoopDefinition[] }. It is auto-extracted from the template at publish time — you cannot POST a manifest directly. Read it with GET /v1/templates/:slug.
The render endpoint validates data against the manifest and strips any key that is not in the manifest before the worker runs. If a key does not bind, the value is silently dropped — this is the most common cause of a “missing” field. Read the manifest and match it exactly.

Render

A single execution: input data + status + asset. Renders are asynchronous — enqueued on a queue, processed by a worker, uploaded to object storage. Status moves queued → rendering → succeeded | failed. See Render lifecycle.

Render pipelines

Not every template renders the same way. The worker chooses one of three pipelines based on the published version’s content, and they differ in what they can do (loops, dynamic images). This choice drives whether arrays and signature images work — read Render pipelines before authoring a data-heavy template.

Document mode

Block layout via @react-pdf. No loops, static images.

Canvas (Handlebars)

HTML → Puppeteer. Loops and dynamic images.

PDF overlay

Fields stamped onto an uploaded base PDF via pdfme.