Templates
Create reusable email templates with variable substitution. Templates support HTML content with {{variable}} placeholders that are replaced at send time.
/templatesCreate a new email template. Templates start unpublished (published: false) and must be published before emails can be sent with them.
{
"name": "Welcome Email",
"subject": "Welcome to {{company_name}}, {{first_name}}!",
"html": "<h1>Hello {{first_name}},</h1><p>Welcome to {{company_name}}.</p>",
"text": "Hello {{first_name}}, Welcome to {{company_name}}.",
"variables": ["first_name", "company_name"]
}/templatesList templates with pagination. Filter with search (name) and published=true|false.
{
"data": [
{
"id": 77,
"publicId": "tpl_abc123def456ghi789",
"name": "Welcome Email",
"subject": "Welcome to {{company_name}}, {{first_name}}!",
"published": true,
"version": 3,
"createdAt": "2026-03-23T10:00:00.000Z",
"updatedAt": "2026-03-23T12:00:00.000Z"
}
],
"meta": {
"page": 1,
"perPage": 20,
"total": 12,
"totalPages": 1
}
}/templates/:idRetrieve a single template including its full HTML content and variable list.
{
"template": {
"id": 77,
"publicId": "tpl_abc123def456ghi789",
"name": "Welcome Email",
"subject": "Welcome to {{company_name}}, {{first_name}}!",
"htmlBody": "<h1>Hello {{first_name}},</h1><p>Welcome to {{company_name}}.</p>",
"textBody": "Hello {{first_name}}, Welcome to {{company_name}}.",
"variables": ["first_name", "company_name"],
"published": true,
"version": 3,
"createdAt": "2026-03-23T10:00:00.000Z",
"updatedAt": "2026-03-23T12:00:00.000Z"
}
}/templates/:idUpdate a template. Changing the subject or content bumps its version number. A published template stays published, so the change applies to the next send that uses it.
{
"name": "Welcome Email v2",
"html": "<h1>Hi {{first_name}}!</h1><p>Thanks for joining {{company_name}}.</p>"
}/templates/:idDelete a template. This does not affect emails already sent using this template.
{
"success": true
}/templates/:id/publishPublish a template, making it available for sending. Sending with an unpublished template returns 422.
{
"template": {
"id": 77,
"publicId": "tpl_abc123def456ghi789",
"published": true,
"updatedAt": "2026-03-23T15:00:00.000Z"
}
}/templates/:id/unpublishUnpublish a template. Sends that reference it fail with 422 until it is published again.
{
"template": {
"id": 77,
"publicId": "tpl_abc123def456ghi789",
"published": false
}
}/templates/:id/duplicateCreate a copy of an existing template. The copy is unpublished, starts at version 1, and is named 'Copy of <name>'.
HTTP/1.1 201 Created
{
"template": {
"id": 78,
"publicId": "tpl_new456copy789ghi01",
"name": "Copy of Welcome Email",
"published": false,
"version": 1,
"createdAt": "2026-03-23T16:00:00.000Z"
}
}/templates/presetsList the built-in template presets (welcome, password-reset, invoice, newsletter, announcement, event-invitation, order-confirmation, feedback-request, …). Use one as a starting point.
{
"data": [
{
"id": "welcome",
"name": "Welcome Email",
"subject": "Welcome to {{company_name}}",
"html": "<!DOCTYPE html>...",
"variables": ["company_name", "first_name", "dashboard_url", "unsubscribe_url"]
},
{
"id": "password-reset",
"name": "Password Reset",
"subject": "Reset your password",
"html": "<!DOCTYPE html>...",
"variables": ["company_name", "first_name", "reset_url", "expiry_hours", "unsubscribe_url"]
}
]
}/templates/presets/:presetId/useCreate a new, unpublished template from a preset, addressed by the preset's string id.
HTTP/1.1 201 Created
{
"template": {
"id": 79,
"publicId": "tpl_fromPreset12345678",
"name": "Welcome Email",
"subject": "Welcome to {{company_name}}",
"published": false,
"version": 1,
"createdAt": "2026-03-23T16:00:00.000Z"
}
}Using Templates with Emails
Reference a published template by ID when sending an email. Provide the variable values to populate the placeholders:
await poststack.emails.send({
from: 'you@yourdomain.com',
to: ['alice@example.com'],
template_id: 'tpl_abc123def456ghi789',
variables: {
first_name: 'Alice',
company_name: 'Acme Inc',
},
});{
"from": "you@yourdomain.com",
"to": ["alice@example.com"],
"template_id": "tpl_abc123def456ghi789",
"variables": {
"first_name": "Alice",
"company_name": "Acme Inc"
}
}Unsubscribe Placeholders
A variable with no matching value renders as an empty string. Two placeholders are exempt, because they are not yours to supply: {{unsubscribe_url}} and {{preferences_url}} are filled in per recipient when the message is sent, so leave them out of variables and they resolve to a real signed link. Pass an explicit value only if you host your own opt-out page. Including either one also switches on the RFC 8058 one-click unsubscribe headers for that send.
Conditional Blocks
Templates support flat conditional blocks for inclusion or exclusion of content based on whether a variable is truthy. Useful for optional greetings, footer lines that only appear for certain audiences, or fallback copy when a value is missing:
{{#if first_name}}Hi {{first_name}},{{/if}}
{{#unless first_name}}Hi there,{{/unless}}
Welcome to {{company_name}}.
{{#if support_url}}Need help? Visit {{support_url}}.{{/if}}A variable counts as truthy when present and not the empty string, "0", or "false". Conditionals do not nest in this pass — flat use covers the common cases (greet when known, fall back when unknown, hide CTAs that lack a URL). Conditionals are evaluated before variable substitution, so the inner content can reference the same variables.
Visual Template Builder
The dashboard includes a block-based visual editor as an alternative to hand-written HTML. When you choose Visual Builder at create time, you compose the email from typed blocks — heading, paragraph, button, image, divider — and PostStack renders them to the final HTML on save. The underlying builder_blocks JSON is stored alongside the compiled HTML so the editor can round-trip without lossy re-parsing. Variable placeholders ({{first_name}}) pass through any block's text content untouched and are substituted at send time.
You can switch a template between builder_type "html" and "visual", but going visual → html is one-way: editing the HTML directly discards the block tree. Editing the subject or content bumps the version; renaming does not.
React Email Components
PostStack does not run a React renderer server-side. If you author your emails as React components (for example via react-email), render to HTML at build time or in your application before sending. The TS SDK accepts plain HTML on the html field — bring your own renderer:
import { render } from '@react-email/render';
import { WelcomeEmail } from './emails/welcome';
const html = render(<WelcomeEmail name="Alice" />);
await poststack.emails.send({
from: 'you@yourdomain.com',
to: ['alice@example.com'],
subject: 'Welcome',
html,
});This pattern keeps PostStack's surface small and the SDK runtime fast — the React renderer stays in your application's deployment, not in the email pipeline.