Templates

Reusable email templates with variables, drafts and publishing.

Templates hold reusable content. You edit a draft and publish it; sends always render the published copy, so draft edits never affect live email until you publish again. Use a template from POST /v1/emails or from a broadcast. All template endpoints require a full_access key.

Variable syntax

Template
Subject: Your order {{{ORDER_ID}}} has shipped

<p>Hi {{{first_name|there}}},</p>
<p>Track it here: <a href="{{{tracking_url}}}">{{{tracking_url}}}</a></p>
  • {{{NAME}}} inserts a variable; {{{NAME|fallback}}} uses fallback when it is missing or empty. Double braces ({{name}}) work the same way.
  • Names are matched case-insensitively.
  • Values are always HTML-escaped in the HTML body (triple braces do not mean raw HTML), stripped of line breaks in the subject and inserted as-is in the text body. A value placed in a URL attribute (href, src, …) with a javascript:, vbscript: or data: scheme is replaced by #.
  • A variable used without a fallback (inline or declared) must be supplied at send time, otherwise the send fails with 422 validation_error.

Create a template

POST/api/v1/templates

Body

namestringrequired
Internal name, 1–200 characters.
aliasstring
Stable handle you can use instead of the id (e.g. order-shipped): letters, digits, - and _, at most 64 characters, unique in the workspace (409 otherwise).
subjectstring
Default subject (≤ 998 characters). May contain variables.
fromstring
Default sender (≤ 320 characters).
reply_tostring
Default Reply-To (≤ 320 characters).
htmlstring
HTML body with variables.
textstring
Plain-text body with variables.
variables{ key, type, fallback_value }[]
Declared variables (at most 50). key: letters, digits and _, not starting with a digit, ≤ 64 characters. type: "string" (default) or "number". fallback_valueof the same type is used when a send doesn't provide the variable.
# Create a template
curl -X POST 'https://api.bytesms.com/api/v1/templates' \
  -H 'Authorization: Bearer tp_live_xxxxxxxxx' \
  -H 'Content-Type: application/json' \
  -d '{"name":"Order shipped","alias":"order-shipped","subject":"Your order {{{order_id}}} has shipped","html":"<p>Hi {{{first_name|there}}}, order {{{order_id}}} is on its way.</p>","variables":[{"key":"first_name","type":"string"},{"key":"order_id","type":"number"}]}'
Response
{
  "id": "cmg4n0q8d0005l8v9f2h6j3ks",
  "object": "template"
}

Retrieve a template

GET/api/v1/templates/{id_or_alias}

Top-level subject/html/text/variables are the draft; published is the live copy (null until first published).

# Retrieve a template
curl -X GET 'https://api.bytesms.com/api/v1/templates/order-shipped' \
  -H 'Authorization: Bearer tp_live_xxxxxxxxx'
Response
{
  "object": "template",
  "id": "cmg4n0q8d0005l8v9f2h6j3ks",
  "name": "Order shipped",
  "alias": "order-shipped",
  "status": "published",
  "published_at": "2026-09-28T09:00:00.000Z",
  "from": null,
  "subject": "Your order {{{order_id}}} has shipped",
  "reply_to": null,
  "html": "<p>Hi {{{first_name|there}}}, order {{{order_id}}} is on its way.</p>",
  "text": null,
  "variables": [
    {
      "key": "first_name",
      "type": "string"
    },
    {
      "key": "order_id",
      "type": "number"
    }
  ],
  "has_unpublished_changes": false,
  "has_unpublished_versions": false,
  "published": {
    "subject": "Your order {{{order_id}}} has shipped",
    "html": "<p>Hi {{{first_name|there}}}, order {{{order_id}}} is on its way.</p>",
    "text": null,
    "variables": [
      {
        "key": "first_name",
        "type": "string"
      },
      {
        "key": "order_id",
        "type": "number"
      }
    ]
  },
  "created_at": "2026-09-28T08:45:00.000Z",
  "updated_at": "2026-09-28T09:00:00.000Z"
}

List templates

GET/api/v1/templates

Query

status"draft" | "published"
Only drafts (never published) or only published templates.
limitintegerdefault 100
1–500, newest first.
# List published templates
curl -X GET 'https://api.bytesms.com/api/v1/templates?status=published' \
  -H 'Authorization: Bearer tp_live_xxxxxxxxx'

Update a template

PATCH/api/v1/templates/{id_or_alias}

Updates the draft. Same fields as create, all optional; send null to clear an optional field (not name or variables). Returns { id, object: "template" }.

# Update the draft
curl -X PATCH 'https://api.bytesms.com/api/v1/templates/cmg4n0q8d0005l8v9f2h6j3ks' \
  -H 'Authorization: Bearer tp_live_xxxxxxxxx' \
  -H 'Content-Type: application/json' \
  -d '{"subject":"Shipped: order {{{order_id}}}"}'

Publish

POST/api/v1/templates/{id_or_alias}/publish

Snapshots the current draft as the live version. The template needs an HTML or text body. Returns { id, object: "template" }.

Duplicate

POST/api/v1/templates/{id_or_alias}/duplicate

Creates a copy named <name> (copy) and returns the new { id, object: "template" }.

Delete a template

DELETE/api/v1/templates/{id_or_alias}
Response
{
  "object": "template",
  "id": "cmg4n0q8d0005l8v9f2h6j3ks",
  "deleted": true
}

Unpublishing

Taking a template back to draft-only is available in the dashboard (Templates → Unpublish); the public API has no unpublish endpoint.