Sendry Docs
API Reference

Templates

Create and manage reusable email templates with variable substitution.

Templates let you define reusable HTML email layouts with {{variable}} placeholders. When you send an email using template_id, Sendry substitutes the variables before delivery.

Templates can be grouped with an optional folder label. A folder is a plain string, not a resource: it exists as long as at least one template carries it and disappears when the last one moves out. Folders are a single level; slashes are rejected.


Create template

POST /v1/templates

Requires full_access scope.

Request body

{
  "name": "Welcome Email",
  "folder": "Onboarding",
  "subject": "Welcome to {{company_name}}, {{name}}!",
  "html": "<h1>Hi {{name}}</h1><p>Thanks for joining {{company_name}}.</p>",
  "text": "Hi {{name}}, thanks for joining {{company_name}}."
}
FieldTypeRequiredDescription
namestringYesHuman-readable template name.
folderstringNoFolder to group the template under. Max 64 characters; slashes and control characters are rejected. Surrounding whitespace is trimmed and internal runs are collapsed to a single space. Omit it, or send null or "", to leave the template unfiled.
subjectstringYesSubject line with optional {{variables}}.
htmlstringNo*HTML body. At least one of html or text required.
textstringNo*Plain-text body.

Response

{
  "id": "tmpl_abc123",
  "name": "Welcome Email",
  "folder": "Onboarding",
  "subject": "Welcome to {{company_name}}, {{name}}!",
  "created_at": "2025-03-12T09:00:00Z",
  "updated_at": "2025-03-12T09:00:00Z"
}

Get template

GET /v1/templates/:id

Requires read_only scope.


List templates

GET /v1/templates

Requires read_only scope.

Query parameters

ParameterTypeDefaultDescription
limitnumber50Results per page, 1 to 100.
cursorstringnext_cursor from the previous response.
folderstringReturn only templates in this folder. Pass the literal value none to return only templates that are in no folder. Omit it to return every template.

Response

{
  "data": [
    {
      "id": "tmpl_abc123",
      "name": "Welcome Email",
      "folder": "Onboarding",
      "subject": "Welcome to {{company_name}}, {{name}}!",
      "created_at": "2025-03-12T09:00:00Z",
      "updated_at": "2025-03-12T09:00:00Z"
    }
  ],
  "has_more": false,
  "next_cursor": null
}

folder is null on templates that are not in a folder.


List folders

GET /v1/templates/folders

Requires read_only scope. Returns every folder currently in use, with the number of templates in it, sorted alphabetically. Templates with no folder are not counted here.

Response

{
  "data": [
    { "folder": "Onboarding", "count": 4 },
    { "folder": "Receipts", "count": 2 }
  ]
}

List starter templates

GET /v1/templates/starters

Requires read_only scope. Returns the built-in starter templates, including their default subject, HTML, and available variables.

Response

{
  "data": [
    {
      "id": "welcome",
      "name": "Welcome Email",
      "description": "Clean welcome email with logo, greeting, and CTA.",
      "subject": "Welcome to {{company_name}}, {{name}}!",
      "html": "<h1>Hi {{name}}</h1><p>Thanks for joining {{company_name}}.</p>",
      "variables": ["name", "company_name"],
      "engine": "react"
    }
  ]
}

engine is always react for starter templates.


List visual starters

GET /v1/templates/visual-starters

Requires read_only scope. Returns the available visual-editor starter templates.

Response

{
  "data": [
    {
      "id": "vs-welcome",
      "name": "Welcome Email",
      "description": "Clean welcome email with logo, greeting, and CTA.",
      "category": "transactional"
    }
  ]
}

category is one of transactional, marketing, or notification.


Get visual starter

GET /v1/templates/visual-starters/:starterId

Requires read_only scope. Returns one visual starter including its full block design.

Response

{
  "id": "vs-welcome",
  "name": "Welcome Email",
  "description": "Clean welcome email with logo, greeting, and CTA.",
  "category": "transactional",
  "design": {
    "blocks": []
  }
}

Update template

PATCH /v1/templates/:id

Requires full_access scope.

Request body

All fields are optional — only include fields you want to change.

{
  "subject": "Updated subject: welcome {{name}}!",
  "html": "<h1>Updated body for {{name}}</h1>"
}

Moving a template between folders

Send only folder. Pass a name to file the template, or null to take it out of every folder.

{ "folder": "Receipts" }
{ "folder": null }

PUT /v1/templates/:id is the full-replace variant of PATCH /v1/templates/:id. It accepts the same body and behaves identically; every field stays optional and omitted fields are left unchanged, so the two verbs are interchangeable.


Delete template

DELETE /v1/templates/:id

Requires full_access scope.


Duplicate template

POST /v1/templates/:id/duplicate

Requires full_access scope. Creates a copy of the template and returns it.

Response

Returns the new template object, same shape as Create template.


List template versions

GET /v1/templates/:id/versions

Requires read_only scope. Returns the version history for a template, newest first.

Response

{
  "data": [
    {
      "id": "tv_abc123",
      "version": 3,
      "subject": "Welcome to {{company_name}}, {{name}}!",
      "created_at": "2025-03-12T09:00:00Z"
    }
  ]
}

Publish template version

POST /v1/templates/:id/publish/:versionId

Requires full_access scope. Publishes a specific version, making it the template's current content.

Response

Returns the updated template object, same shape as Get template.


Render template (preview)

POST /v1/templates/:id/render

Requires read_only scope. Returns the rendered HTML and subject with variables substituted — useful for previewing before sending.

Request body

{
  "variables": {
    "name": "Alice",
    "company_name": "Acme Corp"
  }
}

Response

{
  "subject": "Welcome to Acme Corp, Alice!",
  "html": "<h1>Hi Alice</h1><p>Thanks for joining Acme Corp.</p>",
  "text": "Hi Alice, thanks for joining Acme Corp."
}

Render ad-hoc template HTML

POST /v1/templates/render

Requires sending_access scope. Renders inline HTML passed in the request with variable substitution, without saving a template. Use this for previews of unsaved content; to render a saved template by ID, use Render template (preview).

Request body

FieldTypeRequiredDescription
htmlstringYesRaw HTML to render with variable substitution.
enginestringNoTemplate engine: html, react, or visual.
variablesobjectNoVariable name/value pairs to substitute. Values are strings.

Example request

{
  "html": "<h1>Hello {{name}}</h1>",
  "variables": {
    "name": "Alice"
  }
}

Response

{
  "html": "<h1>Hello Alice</h1>",
  "text": "Hello Alice"
}

Compile visual blocks

POST /v1/templates/compile-blocks

Requires sending_access scope. Compiles a visual block design to email-safe HTML. Used for live preview in the visual editor.

Request body

FieldTypeRequiredDescription
designobjectYesVisual block design JSON to compile.
variablesobjectNoVariable values substituted during compilation.

Example request

{
  "design": {
    "blocks": []
  },
  "variables": {
    "company_name": "Acme Corp"
  }
}

Response

{
  "html": "<html><body></body></html>",
  "text": ""
}

Using templates when sending

Pass template_id and variables when calling the send endpoint:

await sendry.emails.send({
  from: "hello@acme.com",
  to: "user@example.com",
  template_id: "tmpl_abc123",
  variables: {
    name: "Alice",
    company_name: "Acme Corp",
  },
});

template_id overrides the html and text fields.


Plan limits

PlanTemplates
Free5
Pro25
BusinessUnlimited
EnterpriseUnlimited

On this page