Sending

Templates

Create reusable emails with variables and fallbacks, publish them, and send them by ID or alias with template.id and template.variables.

A template is a reusable email with placeholders. You write it once, publish it, and then send it by ID or alias with different variable values each time. Templates belong to a project.

Variable syntax

Put a variable in the subject, the HTML or the text with triple braces:

FormMeaning
{{{NAME}}}Replaced with the value of NAME.
{{{ NAME }}}The same. Spaces inside the braces are ignored.
{{{NAME|fallback}}}Replaced with the value of NAME, or with fallback when no value is available.
  • Names may contain letters, digits, underscore, dot and hyphen, and are matched without regard to case, so {{{first_name}}} and {{{FIRST_NAME}}} are the same variable.
  • A variable that is missing, or whose value is an empty string, uses the inline fallback. Without a fallback it becomes an empty string.
  • A fallback is plain text and cannot contain a closing brace.
  • In the HTML body, values are HTML-escaped, so a value such as <b> shows up as text. In the subject and the plain-text body, values are inserted as they are. Put markup in the template, not in the values.
html
<p>Hi {{{FIRST_NAME|there}}},</p>
<p>Your order {{{ORDER_ID}}} ships on {{{SHIP_DATE}}}.</p>

Declare variables

A template can declare its variables. Each one has a key (letters, digits and underscore, up to 50 characters), a type of string or number, and an optional fallback_value. A template can declare up to 50.

When you send, every declared variable must have a value in the request or a fallback_value. If neither exists, the send fails with 422 validation_error and the message "Missing template variable KEY." This protects you from sending an email with a blank name or order number. Variables you pass that are not declared are still substituted wherever they appear in the template.

An inline fallback such as {{{NAME|there}}} does not satisfy a declared variable. Give the variable a fallback_value instead. When you pass variables, use the same capitalization as the declared key.

Create a template

Shell
curl -X POST https://api.sooner.dev/templates \
  -H "Authorization: Bearer $SOONER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Order shipped",
    "alias": "order-shipped",
    "from": "Acme <orders@mail.yourcompany.com>",
    "subject": "Your order {{{ORDER_ID}}} has shipped",
    "html": "<p>Hi {{{FIRST_NAME|there}}},</p><p>Order {{{ORDER_ID}}} is on its way.</p>",
    "text": "Hi {{{FIRST_NAME|there}}}, order {{{ORDER_ID}}} is on its way.",
    "variables": [
      { "key": "ORDER_ID", "type": "string" },
      { "key": "FIRST_NAME", "type": "string", "fallback_value": "there" }
    ]
  }'
JSON
{ "object": "template", "id": "jt5n7c2x9m4q8r1v6w3y0z2b" }

name and html are required. alias is optional, unique within the project, and may use lowercase letters, digits, hyphen and underscore, up to 100 characters. The template also stores optional defaults for from, subject and reply_to. You can also build templates in the dashboard under Templates.

Draft and published versions

A template has a draft and, once published, a published version.

  • Creating and editing a template changes the draft.
  • POST /templates/{id}/publish copies the draft to the published version.
  • Sending uses the published version only. A template that has never been published cannot be sent, and the request is rejected.
  • After you edit a draft, sends keep using the previous published version until you publish again. The has_unpublished_versions field on the template tells you when the draft differs from what is live.
  • A publish can take up to about 30 seconds to be used by sends.
Shell
curl -X POST https://api.sooner.dev/templates/order-shipped/publish \
  -H "Authorization: Bearer $SOONER_API_KEY"

Send with a template

Pass template instead of html, text and, if the template sets them, from and subject. template.id is the template's ID or its alias.

Shell
curl -X POST https://api.sooner.dev/emails \
  -H "Authorization: Bearer $SOONER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "to": ["ada@example.com"],
    "template": {
      "id": "order-shipped",
      "variables": { "ORDER_ID": "1042", "FIRST_NAME": "Ada" }
    }
  }'
JSON
{ "id": "01jcz8w3f2x9k4m7n6p5q2r1s0" }

The same with fetch:

TypeScript
const res = await fetch("https://api.sooner.dev/emails", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.SOONER_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    to: ["ada@example.com"],
    template: { id: "order-shipped", variables: { ORDER_ID: "1042", FIRST_NAME: "Ada" } },
  }),
});
console.log(await res.json());

How the request and template combine:

  • from and reply_to from the request win. Otherwise the template's values are used.
  • subject from the request wins and is used as is. Otherwise the template's subject is rendered with your variables.
  • The HTML and text bodies always come from the template. html and text in the request are ignored.
  • You still need a from and a subject from one place or the other. If both are missing, the send fails with missing_required_field.
  • Variable values can be strings or numbers.
  • Everything else works as in Send an email: recipients, scheduled_at, tags, headers, attachments and topic_id. Templates also work in batch sending.

An unknown template ID or alias returns 404 not_found.

Manage templates

The templates API reference covers listing, retrieving, updating, deleting and duplicating templates. A duplicate is created as a draft named "(copy)" and has no alias, so it is never sent until you publish it.