Templates
| Method | Path | Scope | SDK |
|---|---|---|---|
GET | /api/v1/templates | any valid key | templates.list |
GET | /api/v1/templates/:key/schema | any valid key | templates.schema |
PUT | /api/v1/templates/:key | templates:write | templates.setEnabled |
GET | /api/v1/templates/:key/content | any valid key | templates.getContent |
PUT | /api/v1/templates/:key/content | templates:write | templates.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.
| Field | Type | Notes |
|---|---|---|
enabled | boolean | Defaults to true when omitted. |
defaultSubject | string | Used when a send omits subject. |
from | object | null | { email, name? } on a verified domain; null clears it; omit to keep. |
brand | string | Brand 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
| Field | Type | Notes |
|---|---|---|
content | object | Required. The content document. |
publish | boolean | true publishes; omit to save a draft. |
brand | string | Brand 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.