Templates
HTML/CSS render templates for the render_template op: create, version, import/export
7 endpoints under /api/v1/templates. What every call shares is on the REST overview, and what they are for — with the calls for every SDK, the CLI and MCP — on Templates. * marks a required field.
List templates
GET /api/v1/templates
Paged — page and perPage, or cursor, in; meta out
Built-in templates first, then the caller's own, most recently updated first, a page at a time. Each row is the current version without its html and css — read one with GET /api/v1/templates/{id}, which is also the document POST /api/v1/templates/import takes back.
FILTER: omit for both, builtin for the shipped ones only, user for your own only.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| filter | query | string | 'builtin' or 'user'; omit for bothone of builtin · user |
Returns 200 — data is TemplateSummary[]
One page of templates; meta says whether there is another
Errors
| Status | Code and when |
|---|---|
| 400 |
|
Create a template
POST /api/v1/templates
Accepts an Idempotency-Key
Creates version 1 and answers 201 with it. name, html, width and height are required. html ≤ 256 KB and css ≤ 128 KB (UTF-8 bytes), width / height 1–4096 px; anything outside is 400 invalid_param naming the field. variables defaults to the {{ … }} names found in the html and css. Each account may own at most 200 templates (422 resource_limit_exceeded).
Rendering is POST /api/v1/jobs {"op": "render_template", "templateId": …, "items": [...]}, or one row now with POST /api/v1/images/render.
Request body — TemplateData
Returns 201 — data is TemplateData
The template, at version 1
Errors
| Status | Code and when |
|---|---|
| 400 |
|
| 422 |
|
Import templates
POST /api/v1/templates/import
Accepts an Idempotency-Key
Takes a list of template documents — what GET /api/v1/templates/{id} returns, one per template — and creates each as a new template at version 1: history and slug are not carried over (the slug is made from the name, with a numeric suffix when it is taken). The list rows of GET /api/v1/templates carry no html, so they are not an export.
Each entry is checked like a create; one that fails is reported in errors and the rest still land, so a 201 can carry refusals — read errors. The whole import is refused (422 resource_limit_exceeded) if it would take the account past 200 templates.
Request body — TemplateData[]
Returns 201 — data is BatchImportResultDTOTemplateData
What landed, and one line per entry that did not
Errors
| Status | Code and when |
|---|---|
| 400 |
|
| 422 |
|
Get a template (or one frozen version of it)
GET /api/v1/templates/{id}
{id} is a template id (or your own slug) for the current version, or {id}@{version} for one specific version — the same spelling render_template accepts as templateId, so what a job rendered with can always be read back. Built-ins have exactly one version.
An earlier version comes back as a document whose id is {id}@{version}, without slug, description or updatedAt — those belong to the template, not to a version of it.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| id (required) | path | string | Template id, slug, or id@version |
Returns 200 — data is TemplateData
The template document
Errors
| Status | Code and when |
|---|---|
| 400 |
|
| 404 |
|
Update a template (new version)
PUT /api/v1/templates/{id}
Accepts an Idempotency-Key
Send only what changes: the body is merged over the current version, and a field it leaves out keeps its value ({"width": 1080, "height": 1080} is a whole update); "" empties css or description. variables, when not sent, stay as they are unless html or css changes, and are then derived from the new markup. Same limits as create. Never edits in place: the template moves to version + 1 — every PUT does, a rename included — and the previous bytes stay readable as {id}@{version}, so a job or automation that pinned a version keeps rendering exactly that. Built-in ids are 403; to start from one, create your own with its html.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| id (required) | path | string | Template id or your own slug |
Request body — TemplateData
Returns 200 — data is TemplateData
The template, at its new version
Errors
| Status | Code and when |
|---|---|
| 400 |
|
| 403 |
|
| 404 |
|
Delete a template
DELETE /api/v1/templates/{id}
Accepts an Idempotency-Key
Deletes the template and every version of it. A render job already rendering keeps the bytes it was handed; one still queued behind the account's other jobs fails unstarted (invalid_state). Built-in ids are 403.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| id (required) | path | string | Template id or your own slug |
Returns 204 — no body
Deleted; no body
Errors
| Status | Code and when |
|---|---|
| 403 |
|
| 404 |
|
List a template's versions
GET /api/v1/templates/{id}/versions
Every version ever saved, newest first, each as a full document — html and css included — whose id is {id}@{version} and which has no slug, description or updatedAt. Not paged: a template's history is one answer. A built-in answers with its one current document.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| id (required) | path | string | Template id or your own slug — not id@version |
Returns 200 — data is TemplateData[]
Every version, newest first
Errors
| Status | Code and when |
|---|---|
| 404 |
|
Objects
Each described once; a linked type is another object on this page.
BatchImportResultDTOTemplateData
What an import did: the entries that landed, and one line for each that did not
| Field | Type | Description |
|---|---|---|
| data | TemplateData[] | The created resources, as saved, in the order sent |
| errors | string[] | One line per refused entry, naming its index and why ( Preset at index 2 'Hero': …). Absent when every entry landed. |
| importedCount | integer (int32) | How many entries were created — the length of data |
TemplateData
HTML/CSS render template with {{ var }} placeholders; the input of the render_template op
| Field | Type | Description |
|---|---|---|
| builtIn | boolean | Whether this is a built-in template (read-only; PUT and DELETE are 403) |
| createdAt | string (date-time) | When the template — or, read as id@version, that version — was created |
| css | string | Stylesheet (≤ 128 KB as UTF-8). Like the html, it loads nothing but data: URIs and this product's own asset host; any other @import or url() is blocked at render time. |
| description | string | Free text for people; not rendered. "" on a PUT empties it. |
| height | integer (int32) | Viewport / output height in px (required on create; 1–4096)e.g. 1080 |
| html | string | Body HTML (required on create; ≤ 256 KB as UTF-8). Scripts never run; every request it makes is blocked at render time except data: URIs and this product's own asset host. |
| id | string | Template id ( tpl_…; a built-in's is builtin-template-…). A version read by id@version carries that spelling here. |
| name | string | Display name (required on create; at most 200 characters)e.g. "Quote card" |
| slug | string | URL-safe handle, unique per account: lowercase letters, digits and hyphens. Generated from the name when omitted; one you send must be free. builtin- is reserved. |
| updatedAt | string (date-time) | When the current version was saved; absent on a version read as id@version |
| variables | string[] | Placeholder names (at most 100, each letters, digits, _ and .). Derived from the html/css when omitted. A render fills {{ name }} HTML-escaped and {{{ name }}} raw; a name the row does not carry renders as nothing. |
| version | integer (int32) | Current version; every PUT increments it and keeps the old one readable as id@version |
| width | integer (int32) | Viewport / output width in px (required on create; 1–4096)e.g. 1080 |
TemplateSummary
A template as it is listed: everything but its html and css, which GET /api/v1/templates/{id} returns
| Field | Type | Description |
|---|---|---|
| builtIn | boolean | Whether this is a built-in template (read-only) |
| createdAt | string (date-time) | When the template was created |
| description | string | Free text for people |
| height | integer (int32) | Output height in px |
| id | string | Template id ( tpl_…, or builtin-template-…); id@version addresses one frozen version |
| name | string | Display name |
| slug | string | URL-safe slug |
| updatedAt | string (date-time) | When its current version was saved — the order your own rows are listed in |
| variables | string[] | Placeholder names a render fills in |
| version | integer (int32) | Current version |
| width | integer (int32) | Output width in px |