Templates
Templates let you define the HTML, plain text, and subject of an email once, then send it with different data each time. ScaiSend templates are Handlebars/Mustache-compatible — the same syntax SendGrid dynamic templates use.
Endpoints: GET/POST/PATCH/DELETE /v3/templates, GET/POST/PATCH/DELETE /v3/templates/{id}/versions
Auth: templates.read for reads, templates.write for writes, templates.delete for deletes.
The two-level model#
A template is a named container. A template version is an actual renderable document (subject + HTML + plain text). One template has many versions; exactly one is active at a time. Sending with a template uses the active version.
1 2 3 4 | |
Version switches let you A/B test subject lines, roll out changes atomically, and roll back fast.
Creating a template#
1 2 3 4 | |
1 2 3 4 5 6 7 8 9 10 | |
1 2 3 4 5 6 7 8 9 | |
Fields:
| Field | Type | Notes |
|---|---|---|
name |
string (required) | Human-readable name; unique within tenant |
generation |
string | dynamic (default; uses Handlebars) or legacy (uses SendGrid's older {{ }} substitutions) |
Response:
1 2 3 4 5 6 7 8 | |
The id starts with d- for dynamic templates. This is what you pass as template_id when sending.
Adding a version#
1 2 3 4 5 6 7 8 9 10 | |
Fields:
| Field | Type | Notes |
|---|---|---|
name |
string (required) | 1–255 chars; identifies the version |
subject |
string | Template-rendered subject line (≤ 1000 chars) |
preheader |
string | Preview text shown in the inbox list (≤ 500 chars); supports variables |
html_content |
string | HTML body |
plain_content |
string | Plain-text body (generated from HTML if omitted, but provide one — it's a deliverability signal) |
editor |
string | UI editor type (typically "design" or "code"; informational) |
active |
0 or 1 | Set to 1 to immediately make this version the active one |
Every new version defaults to active: 0. You can activate later with:
1 2 | |
Sending with a template#
Once a version is active, send without content:
1 2 3 4 5 6 7 8 9 10 11 12 13 | |
dynamic_template_data becomes the context passed to the template engine. {{name}} in the template renders to Ada, {{plan}} to Pro.
Template syntax#
ScaiSend uses chevron under the hood — a Mustache implementation extended with Handlebars-style helpers.
Variables#
1 | |
Output is HTML-escaped by default. To output raw HTML (dangerous; only use with trusted content):
1 | |
Conditionals#
1 2 3 4 5 | |
Iteration#
1 2 3 4 5 | |
Inside an {{#each}}, this refers to the current item. For primitive arrays (e.g., ["red", "blue"]), use {{.}} to render the value directly.
Helpers#
ScaiSend ships a set of common helpers:
| Helper | Usage | Output |
|---|---|---|
uppercase |
{{uppercase name}} |
Upper-cased string |
lowercase |
{{lowercase name}} |
Lower-cased string |
truncate |
{{truncate text 50}} |
First 50 chars |
default |
{{default name "Guest"}} |
name if non-empty, else "Guest" |
length |
{{length items}} |
Length of array or string |
formatDate |
{{formatDate date "%Y-%m-%d"}} |
Formatted date |
equals |
{{#equals status "active"}}...{{/equals}} |
Block helper (equality) |
greaterThan |
{{#greaterThan count 5}}...{{/greaterThan}} |
Block helper |
lessThan |
{{#lessThan count 5}}...{{/lessThan}} |
Block helper |
and |
{{#and a b}}...{{/and}} |
Block helper (logical AND) |
or |
{{#or a b}}...{{/or}} |
Block helper (logical OR) |
Example#
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | |
With data:
1 2 3 4 5 6 7 8 9 10 11 | |
Renders as:
1 2 3 4 5 6 7 8 | |
Preview text (preheader)#
The preheader field becomes the text shown next to the subject in the inbox list. Set it explicitly — otherwise email clients use the first ~100 chars of the body, which is often unhelpful.
1 2 3 4 5 6 | |
Preheader supports variables. A typical pattern: use the preheader to summarize the CTA, since it's the first thing the recipient reads.
Managing versions#
List versions of a template:
1 2 | |
Update a version (cannot modify an active version without deactivating first):
1 2 3 4 | |
Duplicate a version (useful for starting a new variant from the current active):
1 2 3 4 | |
The duplicate is inactive by default.
Deactivate:
1 2 | |
A template with no active version can't be used for sending — /v3/mail/send with that template_id returns 400.
Rendering errors#
Template rendering happens in the Worker service, not the API. A rendering error (undefined variable accessed strictly, broken syntax, helper misuse) does not cause a 4xx on /v3/mail/send. Instead:
- The message lands in
FAILEDstatus. - A
droppedevent is recorded with reasontemplate_render_error. - The message's
error_messagefield contains the rendering exception.
Test your templates before relying on them. Send a test email with representative data via a test key — sandbox rendering catches the error the same way a real send would.
Listing templates#
1 2 | |
Query parameters:
| Parameter | Notes |
|---|---|
generation |
dynamic or legacy; filter by template style |
Deleting#
Deleting a template deletes all its versions. Active sends that reference the template mid-flight complete normally (they've already been snapshot-rendered). Future sends with the template_id return 400.
1 2 | |
Prefer deactivation (remove the active version) over deletion when you might want the template back.
What's next#
- Sending Mail — using
template_idon a send. - Attachments and Images — embedding images in templates.
- Templates Reference — exhaustive endpoint reference.