Versioning
Manage template versions
A version is a numbered revision of a template. Numbers are assigned server-side as one above the highest the template has, so they never repeat and never fill a gap left by a deletion.
A version holds no subject and no body
The renderable content lives one level further down, in the version’s localizations. A single-language template is one with a single localization — there is no separate simple case. A version carries only the settings that apply to all of its languages: which stylesheet the content was written against, and the sample data a preview renders with.
List
GET /api/v1/templates/{templateId}/versions
GET /api/v1/templates/{templateId} returns the same list alongside the template, which is usually the call you want.
Create
POST /api/v1/templates/{templateId}/versions
Both fields are optional, and an empty body is valid — that is how you start a version whose content you are about to write:
{
"stylesheet_id": "0198f6a1-3c7e-7b21-9f4d-2a5c8e0b1d33",
"sample_data": "{\"name\": \"Alice\"}"
}
sample_data is a JSON string, not an object. The response is 201:
{
"version": {
"id": "0198f6a1-3c80-7c44-b6e1-9d2f7a0c5188",
"version": 2,
"created_at": "2026-01-15T00:00:00Z"
}
}
Two clients creating a version at the same instant can land on the same number. One of them is refused with 409 and
the message says to retry — the second attempt reads the new maximum and gets the number after it.
Update
PATCH /api/v1/templates/{templateId}/versions/{versionId}
Changes the same two settings. To change what the version renders, edit its localizations .
Activate
POST /api/v1/templates/{templateId}/activate/{versionId}
This is the publish step, and it is the only thing that changes what live sends resolve. The response carries the
template with its new active_version_id.
Delete
DELETE /api/v1/templates/{templateId}/versions/{versionId}
Returns 204. The active version is refused with 409 — activate another one first, so a template is never left
sendable-by-name but with nothing to send.
A release, end to end
POST .../versionsto open a draft. The live version keeps sending throughout.PUT .../localizationsfor each language you support.POST .../versions/{versionId}/previewwith representative data, and read the rendered HTML.POST /api/v1/templates/{id}/send-testto a real mailbox, because a preview is a browser and a mail client is not.POST .../activate/{versionId}.
Step 5 is atomic and takes effect on the next send. If something is wrong with the new copy, activating the previous version rolls it back just as fast — the old version was never modified, only overtaken.