Templates API

The Templates API provides full CRUD (Create, Read, Update, Delete) operations for managing reusable email templates. You can create, list, view, update, and delete templates programmatically to use across broadcasts, sequences, and transactional emails.

Required Permissions

All endpoints require authentication via an API token with appropriate permissions:

  • Read Permission: Required for GET endpoints (list, show)
  • Write Permission: Required for POST, PATCH, DELETE endpoints

Template Object

The template object contains the following fields:

Field Type Description
`id` integer The unique identifier of the template
`label` string The internal name of the template (must be unique per channel)
`subject` string The subject line of the email
`preheader` string Preview text that appears in email clients
`body` string The content of the email (plain text or HTML)
`html_body` boolean Whether the body contains HTML content (default: false)
`html_body_add_footer` boolean Whether the channel footer (with the unsubscribe link) is appended wherever this template is sent: sequence steps, confirmation emails and transactional emails made from it (default: true). An email with its own unsubscribe link (a footer block that shows the link, or a `{{ unsubscribe_url }}` link that every subscriber sees) never gets the channel footer on top, whatever this value (2.46.0 or later). Without an own link, rich text and block emails always get the channel footer, and HTML emails follow this value.
`content_type` string `rich_text`, `html` or `blocks` (the Drag-and-Drop Email Builder)
`blocks_json` object The block document when `content_type` is `blocks`; `body` holds the rendered HTML
`created_at` datetime When the template was created
`template_purpose` string Either `general` (default) or `confirmation`. Set to `confirmation` to use the template for double opt-in confirmation emails. See Confirmation Pages.
`confirmation_text` string Legacy single-paragraph body for the success state of the confirmation page. Superseded by `confirmation_page_settings` but still honored as a fallback. Only meaningful when `template_purpose` is `confirmation`.
`default_confirmation` boolean When `true`, marks this template as the channel’s default confirmation template. Setting `true` automatically unsets the flag on any other confirmation template in the same channel.
`confirmation_page_settings` object Per-state heading, body and button for the confirmation page. Shape: { "<language>": { "<state>": { "heading": "...", "body": "...", "button_label": "...", "button_url": "..." } } } where `<state>` is one of `confirmed`, `already_confirmed`, `expired`, `not_found`, `confirmation_failed`, `fallback`. Only specified states/fields are applied; the rest fall back to defaults. Unknown keys are ignored with a warning (see Create below).
`updated_at` datetime When the template was last updated

List Templates

GET /api/v1/templates

Retrieve a list of all templates for the authenticated broadcast channel, ordered alphabetically by label.

Query Parameters

  • limit (optional): Maximum number of templates to return, from 1 to 1000
  • offset (optional): Number of templates to skip, 0 or more

A limit or offset outside those ranges, or that is not a whole number, returns 422 Unprocessable Entity.

Request

curl -X GET \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  http://your-domain.com/api/v1/templates

Response

{
  "data": [
    {
      "id": 1,
      "label": "Monthly Newsletter",
      "subject": "Your Monthly Update",
      "preheader": "See what's new this month",
      "body": "<p>Newsletter content here</p>",
      "html_body": true,
      "html_body_add_footer": true,
      "content_type": "html",
      "blocks_json": null,
      "template_purpose": "general",
      "confirmation_text": null,
      "default_confirmation": false,
      "confirmation_page_settings": {},
      "created_at": "2024-01-01T12:00:00Z",
      "updated_at": "2024-01-01T12:00:00Z"
    },
    {
      "id": 2,
      "label": "Subscription Confirmation",
      "subject": "Please confirm your subscription",
      "preheader": "One click to confirm",
      "body": "<p>Click <a href=\"{{ confirmation_url }}\">here</a> to confirm.</p>",
      "html_body": true,
      "html_body_add_footer": true,
      "content_type": "html",
      "blocks_json": null,
      "template_purpose": "confirmation",
      "confirmation_text": "Thanks for confirming!",
      "default_confirmation": true,
      "confirmation_page_settings": {
        "confirmed": { "heading": "You're in!", "body": "Welcome to the list." }
      },
      "created_at": "2024-01-02T10:00:00Z",
      "updated_at": "2024-01-02T10:00:00Z"
    }
  ],
  "total": 2
}

Get Template

GET /api/v1/templates/:id

Retrieve details of a specific template.

Request

curl -X GET \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  http://your-domain.com/api/v1/templates/123

Response

{
  "id": 123,
  "label": "Welcome Email",
  "subject": "Welcome to Our Community!",
  "preheader": "Thanks for joining us",
  "body": "<p>Welcome to the community! We're excited to have you.</p>",
  "html_body": true,
  "html_body_add_footer": true,
  "content_type": "html",
  "blocks_json": null,
  "template_purpose": "general",
  "confirmation_text": null,
  "default_confirmation": false,
  "confirmation_page_settings": {},
  "created_at": "2024-01-01T12:00:00Z",
  "updated_at": "2024-01-01T12:00:00Z"
}

Create Template

POST /api/v1/templates

Create a new email template.

Parameters

Send the parameters inside a template object, as in the request below.

  • label (required): Internal name of the template (must be unique per channel)
  • subject (required): Subject line of the email
  • body (required): Content of the email
  • preheader (optional): Preview text that appears in email clients
  • html_body (optional): Whether the body contains HTML (default: false)
  • content_type (optional): rich_text (default), html or blocks. With blocks, send the design as blocks_json
  • blocks_json (optional): A block document ({ "version": 1, "blocks": [...] }) for the Drag-and-Drop Email Builder
  • html_body_add_footer (optional): Whether the channel footer is appended wherever the template is sent (default: true). An email with its own unsubscribe link (a footer block that shows the link, or a {{ unsubscribe_url }} link that every subscriber sees) never gets the channel footer on top, whatever this value (2.46.0 or later). Without an own link, rich text and block emails always get the channel footer, and HTML emails follow this value. For a confirmation template, a confirmation sent with include_unsubscribe_link: false gets no footer and no unsubscribe link. A value that disagrees with the document is replaced, and the response carries a parameter_overridden warning (see API Response Warnings).
  • template_purpose (optional): general (default) or confirmation. Set to confirmation to use this template for double opt-in confirmation emails.
  • confirmation_text (optional): Legacy success-state body for the confirmation page. Only meaningful when template_purpose is confirmation.
  • default_confirmation (optional): Boolean. When true, this template becomes the channel’s default confirmation template (any previous default is unset).
  • confirmation_page_settings (optional): Per-language, per-state heading, body and button for the confirmation page. See Confirmation Pages for the resolution chain. Shape: json { "en": { "confirmed": { "heading": "...", "body": "...", "button_label": "Back to our website", "button_url": "https://example.com" }, "expired": { "heading": "...", "body": "..." } }, "de": { "confirmed": { "heading": "...", "body": "...", "button_label": "Zurück zur Website", "button_url": "https://example.de" } } }
    • Languages: en, de, fr, es, it, pt, nl, pl, sv, da, nb, fi, ja.
    • States: confirmed, already_confirmed, expired, not_found, confirmation_failed, fallback.
    • Fields: heading, body, button_label and button_url. All are plain text.
    • button_label and button_url (Broadcast 2.46.0 or later) add a button under the text. Send both or neither. button_url must be an absolute http:// or https:// URL, used exactly as written: it takes no variables, so a {{ email }} in it is refused. When already_confirmed has no button of its own, the page shows the confirmed button of the same language. A button with only one of the two, or with any other kind of link (javascript:, data:, a relative path), returns 422 Unprocessable Entity and nothing is saved.
    • The older shape without a language ({ "confirmed": { ... } }) is still accepted, and reads as the channel’s default language.
    • A language, state or field that is not in these lists is ignored, and the response carries an unrecognized_parameter warning that names it, for example template.confirmation_page_settings.confirmed.title (see API Response Warnings).

Request

curl -X POST \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "template": {
      "label": "Welcome Email",
      "subject": "Welcome to Our Community!",
      "preheader": "Thanks for joining us",
      "body": "<p>Welcome to the community! We are excited to have you.</p>",
      "html_body": true
    }
  }' \
  http://your-domain.com/api/v1/templates

Response

{
  "id": 123
}

Notes

  • The label must be unique within your broadcast channel
  • If html_body is set to true, the body should contain valid HTML
  • Line breaks in HTML should use <br> tags, and paragraphs should be wrapped in <p> tags

Update Template

PATCH /api/v1/templates/:id

Update an existing template.

Parameters

All parameters are optional. Send them inside a template object, and only include the fields you want to update:

  • label: Internal name of the template
  • subject: Subject line of the email
  • body: Content of the email
  • preheader: Preview text that appears in email clients
  • html_body: Whether the body contains HTML
  • content_type: rich_text, html or blocks
  • blocks_json: A block document for the drag-and-drop editor
  • html_body_add_footer: Whether the channel footer is appended wherever the template is sent (see above)
  • template_purpose: general or confirmation
  • confirmation_text: Legacy success-state body for the confirmation page
  • default_confirmation: Boolean, set as channel default
  • confirmation_page_settings: Per-state page text and buttons (see Create above for shape)

Request

curl -X PATCH \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "template": {
      "subject": "Updated Subject Line",
      "body": "<p>Updated content</p>"
    }
  }' \
  http://your-domain.com/api/v1/templates/123

Response

{
  "id": 123,
  "label": "Welcome Email",
  "subject": "Updated Subject Line",
  "preheader": "Thanks for joining us",
  "body": "<p>Updated content</p>",
  "html_body": true,
  "html_body_add_footer": true,
  "content_type": "html",
  "blocks_json": null,
  "template_purpose": "general",
  "confirmation_text": null,
  "default_confirmation": false,
  "confirmation_page_settings": {},
  "created_at": "2024-01-01T12:00:00Z",
  "updated_at": "2024-01-01T12:30:00Z"
}

Delete Template

DELETE /api/v1/templates/:id

Delete a template.

Request

curl -X DELETE \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  http://your-domain.com/api/v1/templates/123

Response

{
  "message": "Template deleted successfully"
}

The default confirmation template

2.49+

The channel’s default confirmation template cannot be deleted, as in the app: opt-in forms and double opt-in send it when no other template is chosen. The request returns 422:

{
  "error": "This is the channel's default confirmation template, which opt-in forms and double opt-in send when no other template is chosen, so it cannot be deleted. Make another confirmation template the default first (PATCH /api/v1/templates/:id with template[default_confirmation] = true), then delete this one.",
  "code": "default_confirmation_template"
}

Make another confirmation template the default first with PATCH /api/v1/templates/:id and {"template": {"default_confirmation": true}}. The old template is then an ordinary confirmation template, and you can delete it.

Channel Design (Brand Kit)

GET /api/v1/channel/design returns the resolved brand kit of the token’s channel, as set on Settings → Brand Kit (see Drag-and-Drop Email Builder). It needs the templates read permission (or, from 2.46.0, Channel Settings read). Defaults are filled in, so every field is always present; the logo is a public URL or null. From 2.46.0 you can also change the brand kit with PATCH; see the Brand Kit API.

GET /api/v1/channel/design
bash

curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  http://your-domain.com/api/v1/channel/design
json

{
  "colors": { "page_background": "#f4f4f5", "body_background": "#ffffff", "text": "#18181b",
              "muted": "#71717a", "link": "#2563eb", "accent": "#ff5500", "button_text": "#ffffff" },
  "typography": { "font": "georgia", "font_stack": "Georgia,'Times New Roman',Times,serif" },
  "layout": { "width": 600, "radius": 4 },
  "brand": {
    "logo_url": "https://your-domain.com/files/abc123",
    "logo_width": 180,
    "website_url": "https://acme.example",
    "social_links": [ { "network": "x", "url": "https://x.com/acme" } ],
    "social_icon_style": "dark"
  }
}

Error Responses

If the request is invalid, the response will include an appropriate HTTP status code and error message:

401 Unauthorized

{
  "error": "Unauthorized"
}

This error occurs when: - No authorization header is provided - The token is invalid - The token doesn’t have the required permissions

An expired admin API token returns 401 with {"error": "Token has expired"} instead.

404 Not Found

{
  "error": "Template not found"
}

This error occurs when: - The template ID doesn’t exist - The template belongs to a different broadcast channel

422 Unprocessable Entity

{
  "error": "Label can't be blank, Subject can't be blank, Body can't be blank"
}

This error occurs when: - Required fields are missing - The label is already taken by another template in the same channel - A confirmation page button has a label without a link (or the other way round), or its link is not an http:// or https:// URL, for example "Confirmation page settings Confirmed page (English): the button link must be an http:// or https:// URL" - Validation fails for any other reason

Usage Example: Syncing Templates from External System

Here’s an example of how you might sync templates from an external system:

# First, list existing templates
curl -X GET \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  http://your-domain.com/api/v1/templates

# Create a new template if it doesn't exist
curl -X POST \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "template": {
      "label": "Product Update",
      "subject": "New Product Features",
      "preheader": "Check out what is new",
      "body": "<h1>New Features</h1><p>We have exciting updates to share!</p>",
      "html_body": true
    }
  }' \
  http://your-domain.com/api/v1/templates

# Update an existing template
curl -X PATCH \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "template": {
      "body": "<h1>New Features</h1><p>Updated content with more details!</p>"
    }
  }' \
  http://your-domain.com/api/v1/templates/123

Last updated

Was this page helpful?

Thanks for your feedback!

Thanks for letting us know. We'll work on improving this page.