Concepts
Templates, versions, variables, manifests — the mental model.
This page is the mental model behind Craftkit. Five primitives compose every workflow: templates, versions, variables, loops, and renders.
Quick Start
If you only remember one thing, remember this hierarchy:
Project
└── Template (slug: "invoice")
└── Template version (immutable snapshot, "v3")
├── Variables (manifest)
└── Loops (repeating blocks)
└── Render (one execution against input data)A POST /v1/templates/invoice/render walks the chain: latest published version of invoice → manifest → validate input → enqueue render row.
Templates
A template is a visual blueprint with merge fields. It belongs to a project, has a slug (invoice, contract, ...), and accumulates versions over time.
You design templates in either the Document canvas (paginated, for contracts) or the Simple flow editor (continuous, for receipts).
Versions
Every time you click Publish version, Craftkit snapshots the template's content, compiles it, and stores it as a new immutable templateVersion. The active version is what /render uses by default. Old versions stay on disk forever — past API calls keep working.
Pin a render to a specific version:
{
"data": { "...": "..." },
"options": { "versionNumber": 3 }
}Variables
A variable is a single merge field with a key, a label, a data type, and a required flag. Keys can be dot-pathed (customer.name, order.shipping.country).
| Data type | Description |
|---|---|
text |
Single-line string |
longtext |
Multi-line string |
number |
Finite numeric, optional integer flag |
currency |
Numeric with currency formatting (format: "money:EUR") |
date |
ISO date |
datetime |
ISO datetime |
boolean |
True/false checkbox |
image |
Image asset (URL or upload) |
url |
Hyperlink |
email |
Email address |
When a version is published, Craftkit walks the template, collects every variable and loop, and stores a variable manifest on the version. That manifest is the contract behind:
- The Zod validator that gates incoming render requests.
- The auto-generated JSON Schema shown in the dashboard.
- The cURL snippet on the template detail page.
- The form fields rendered by the embeddable form route.
Loops
A loop is a repeating block (Handlebars-style {{#each items}}...{{/each}}) keyed on an array variable. Each loop has an item shape — its own list of variables. Loops are how you express invoice line items, package contents, attendee lists.
Renders
A render is one execution of one template version against one set of input data. Each render lives as a row in Postgres with:
| Field | Description |
|---|---|
status |
queued → rendering → succeeded | failed | cancelled |
data |
The input payload that was validated against the manifest |
downloadUrl |
The output asset URL once the render reaches a terminal state |
durationMs |
Wall-clock time from enqueue to completion |
source |
What triggered the render: api, form, partner_supplied, or dashboard |
apiKeyId |
The API key (or embed session) that initiated the call |
The dashboard's Renders tab is just a window onto this table.
Glossary
| Term | Meaning |
|---|---|
| Template | A visual blueprint with merge fields, scoped to a project |
| Template version | An immutable snapshot of a template's content + manifest |
| Variable | A single merge field (key + type + required) |
| Loop | A repeating block keyed on an array variable |
| Manifest | The list of variables + loops extracted from a version |
| Render | One execution of one template version against input data |
| API key | A bearer token authenticating one project's API calls |
| Inbound webhook | A signed URL that triggers a render from any external system |
| Embed session | A short-lived JWT that scopes an iframe mount to one tenant + actor |
Related
- Quickstart — see the primitives in action
- POST /v1/templates/:slug/render — the request that uses the manifest
- Architecture: data model — the Postgres schema for these primitives