Skip to main content

Templates

MethodPathScopeSDK
GET/api/v1/templatesany valid keytemplates.list
GET/api/v1/templates/:key/schemaany valid keytemplates.schema
PUT/api/v1/templates/:keytemplates:writetemplates.setEnabled
GET/api/v1/templates/:key/contentany valid keytemplates.getContent
PUT/api/v1/templates/:key/contenttemplates:writetemplates.putContent / publish

All five accept an optional ?brand=<slug> (or brand in the body) — enablement, subject, sender, and content are stored per brand. Omit it to use the project default.

GET /api/v1/templates

{
"ok": true,
"templates": [
{ "key": "welcome", "type": "transactional", "enabled": true,
"subject": "Welcome to Acme", "from": { "email": "hello@acme.example", "name": "Acme" } },
{ "key": "sale", "type": "marketing", "enabled": false, "subject": "", "from": null }
]
}

from: null means the brand default sender applies.

GET /api/v1/templates/:key/schema

The variable contract — what data may contain on a send.

{
"ok": true,
"key": "sale-announcement",
"type": "marketing",
"dataSchema": [
{ "path": "percent", "kind": "number", "sample": 15 },
{ "path": "products", "kind": "array" },
{ "path": "products.name", "kind": "string", "sample": "Aria Wireless Earbuds" }
],
"systemVars": ["brandName", "postalAddress", "unsubscribeUrl", "year"]
}

systemVars are injected by the renderer; passing them in data has no effect. Nothing validates data at send time — an unknown path renders empty, so use this endpoint rather than guessing.

404 unknown_template — no such key. 422 no_brand — the project has no brand.

PUT /api/v1/templates/:key

Enable/disable and configure the template for this project/brand.

FieldTypeNotes
enabledbooleanDefaults to true when omitted.
defaultSubjectstringUsed when a send omits subject.
fromobject | null{ email, name? } on a verified domain; null clears it; omit to keep.
brandstringBrand slug.
{ "ok": true, "key": "welcome", "enabled": true }

422 invalid_from — the from address is not on a verified sending domain for this project. Verify the domain first (admin console → Domains), or omit from and inherit the brand default.

GET /api/v1/templates/:key/content

{
"ok": true,
"key": "welcome",
"defaultContent": { "hero": "Welcome aboard" },
"draft": { "version": 4, "published": false, "content": { "hero": "Glad you're here" } },
"published": { "version": 3, "published": true, "content": { "hero": "Welcome aboard" } }
}

draft and published are absent until a version exists. defaultContent is what the registry ships.

PUT /api/v1/templates/:key/content

FieldTypeNotes
contentobjectRequired. The content document.
publishbooleantrue publishes; omit to save a draft.
brandstringBrand slug.
curl -X PUT https://mailroom.example.com/api/v1/templates/welcome/content \
-H "Authorization: Bearer pk_live_…" \
-H "Content-Type: application/json" \
-d '{"content":{"hero":"Glad you'"'"'re here"},"publish":true}'
{ "ok": true, "key": "welcome", "version": 5, "published": true }

Rendering always uses the published version, so drafts are safe to stage.

422 invalid_content — the document failed validation against the template's block schema; the message names the offending field.