Templates API
API reference for creating, managing, and previewing email templates.
Create reusable email templates with Handlebars variable substitution. Templates can be referenced by slug or UUID when sending emails.
Create Template
POST /v1/templates
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Display name (max 191 characters) |
slug | string | Yes | Unique identifier (lowercase alphanumeric + hyphens, 2-100 chars) |
description | string | No | Optional description |
subject | string | No | Subject line template (supports {{variables}}) |
html_body | string | No | HTML body template |
text_body | string | No | Plain text body template |
Example Request
curl -X POST https://api.postscale.io/v1/templates \
-H "Authorization: Bearer ps_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"name": "Welcome Email",
"slug": "welcome",
"description": "Sent to new users after signup",
"subject": "Welcome to {{company_name}}, {{first_name}}!",
"html_body": "<h1>Hello {{first_name}}</h1><p>Welcome to {{company_name}}!</p>",
"text_body": "Hello {{first_name}}, Welcome to {{company_name}}!"
}'
Response 201
{
"template": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"org_id": "f1e2d3c4-b5a6-7890-abcd-ef1234567890",
"name": "Welcome Email",
"slug": "welcome",
"description": "Sent to new users after signup",
"subject": "Welcome to {{company_name}}, {{first_name}}!",
"html_body": "<h1>Hello {{first_name}}</h1><p>Welcome to {{company_name}}!</p>",
"text_body": "Hello {{first_name}}, Welcome to {{company_name}}!",
"active": true,
"created_at": "2026-03-16T12:00:00Z",
"updated_at": "2026-03-16T12:00:00Z"
}
}
Errors
| Status | Error | Description |
|---|---|---|
400 | Invalid slug | Slug doesn't match ^[a-z0-9][a-z0-9-]*[a-z0-9]$ |
400 | Invalid template syntax | Subject, html_body, or text_body has a Handlebars parse error |
403 | Template limit reached | Plan limit exceeded |
409 | Slug already exists | Another template in this org uses the same slug |
List Templates
GET /v1/templates
Query Parameters
| Parameter | Type | Description |
|---|---|---|
limit | integer | Number of results (default: 50, max: 100) |
offset | integer | Number of results to skip |
Example Request
curl https://api.postscale.io/v1/templates?limit=50 \
-H "Authorization: Bearer ps_live_your_api_key"
Response 200
{
"templates": [
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"org_id": "f1e2d3c4-b5a6-7890-abcd-ef1234567890",
"name": "Welcome Email",
"slug": "welcome",
"description": "Sent to new users after signup",
"subject": "Welcome to {{company_name}}, {{first_name}}!",
"html_body": "...",
"text_body": "...",
"active": true,
"created_at": "2026-03-16T12:00:00Z",
"updated_at": "2026-03-16T12:00:00Z"
}
],
"total": 1
}
Get Template
GET /v1/templates/:id
The :id parameter accepts either a UUID or a slug.
Example Requests
# By UUID
curl https://api.postscale.io/v1/templates/a1b2c3d4-e5f6-7890-abcd-ef1234567890 \
-H "Authorization: Bearer ps_live_your_api_key"
# By slug
curl https://api.postscale.io/v1/templates/welcome \
-H "Authorization: Bearer ps_live_your_api_key"
Response 200
{
"template": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"org_id": "f1e2d3c4-b5a6-7890-abcd-ef1234567890",
"name": "Welcome Email",
"slug": "welcome",
"subject": "Welcome to {{company_name}}, {{first_name}}!",
"html_body": "<h1>Hello {{first_name}}</h1><p>Welcome to {{company_name}}!</p>",
"text_body": "Hello {{first_name}}, Welcome to {{company_name}}!",
"active": true,
"created_at": "2026-03-16T12:00:00Z",
"updated_at": "2026-03-16T12:00:00Z"
}
}
Update Template
PUT /v1/templates/:id
Partially update a template. Only provided fields are changed. The :id parameter accepts either a UUID or a slug.
Request Body
| Field | Type | Description |
|---|---|---|
name | string | Display name |
slug | string | Unique slug (validated for format and uniqueness) |
description | string | Description |
subject | string | Subject line template |
html_body | string | HTML body template |
text_body | string | Plain text body template |
active | boolean | Whether the template can be used for sending |
All fields are optional. Template syntax is validated on update.
Example Request
curl -X PUT https://api.postscale.io/v1/templates/welcome \
-H "Authorization: Bearer ps_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"subject": "Welcome aboard, {{first_name}}!",
"active": true
}'
Response 200
Returns the full updated template object.
Delete Template
DELETE /v1/templates/:id
Permanently delete a template. The :id parameter accepts either a UUID or a slug.
Example Request
curl -X DELETE https://api.postscale.io/v1/templates/welcome \
-H "Authorization: Bearer ps_live_your_api_key"
Response 200
{
"status": "deleted"
}
Preview Template
POST /v1/templates/:id/preview
Render a template with test variables and return the result. Useful for testing templates before sending. The :id parameter accepts either a UUID or a slug.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
variables | object | No | Key-value pairs to substitute into the template |
Example Request
curl -X POST https://api.postscale.io/v1/templates/welcome/preview \
-H "Authorization: Bearer ps_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"variables": {
"first_name": "Jane",
"company_name": "Acme Corp"
}
}'
Response 200
{
"subject": "Welcome to Acme Corp, Jane!",
"html": "<h1>Hello Jane</h1><p>Welcome to Acme Corp!</p>",
"text": "Hello Jane, Welcome to Acme Corp!"
}
Template Object
| Field | Type | Description |
|---|---|---|
id | string (UUID) | Unique template identifier |
org_id | string (UUID) | Organization that owns this template |
name | string | Display name |
slug | string | Unique slug used to reference the template when sending |
description | string | null | Optional description |
subject | string | Subject line template with {{variable}} placeholders |
html_body | string | HTML body template |
text_body | string | Plain text body template |
active | boolean | Whether the template can be used for sending |
created_at | string (ISO 8601) | Creation timestamp |
updated_at | string (ISO 8601) | Last update timestamp |
Built-in Variables
These variables are automatically injected when rendering templates (they won't overwrite user-provided values):
| Variable | Description |
|---|---|
{{current_year}} | Current year (e.g., 2026) |
{{current_date}} | Current date in YYYY-MM-DD format |
{{unsubscribe_url}} | One-click unsubscribe URL (populated at send time) |
Template Syntax
Templates use Handlebars syntax:
- Variables:
{{variable_name}} - Nested access:
{{user.first_name}} - Conditionals:
{{#if paid}}...{{else}}...{{/if}} - Loops:
{{#each items}}{{this.name}}{{/each}} - Defaults:
{{default first_name "there"}}— returns"there"iffirst_nameis empty or missing
Plan Limits
| Plan | Max Templates |
|---|---|
| Free | 5 |
| Starter | 25 |
| Plus | 50 |
| Pro | 100 |
| Scale | 500 |