Email Templates API
Overview
The Email Templates API provides comprehensive management of reusable email templates with versioning support. Create and maintain email templates with dynamic content, multiple versions, attachments, and tracking capabilities. Templates support both HTML and plain text content with Handlebars templating for dynamic data substitution.
Resource Description
Email templates in Stack9 consist of:
- Template Metadata: Base template configuration including sender information and tracking settings
- Template Versions: Multiple versions of content with independent subject lines and HTML/text bodies
- Dynamic Content: Handlebars-based templating for personalization and dynamic data
- Attachments: Support for file attachments via URL references
- Template Snippets: Reusable content blocks that can be included in templates
Key Features
- Version Control: Maintain multiple versions of each template
- Active Version Management: Designate which version is active for sending
- Dynamic Templating: Use Handlebars syntax for personalization
- Preview Capabilities: Test templates with sample data before sending
- Template Cloning: Duplicate existing templates for faster creation
- Attachment Support: Include files via URL references
Authentication
All endpoints require API key authentication:
X-API-Key: your-api-key-here
Template Management
Create Template
Create a new email template with basic configuration.
POST /api/templates
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Display name for the template |
from_email | string | Yes | Sender email address (must be verified) |
from_name | string | No | Sender display name |
reply_to | string | No | Reply-to email address |
description | string | No | Template description |
type | string | No | Template category/type for grouping |
transactional | boolean | Yes | Whether template is transactional (bypasses unsubscribes) |
open_tracking | boolean | No | Enable open tracking (default: false) |
click_tracking | boolean | No | Enable click tracking (default: false) |
Example Request
curl -X POST \
https://apis.app.stack9.co/api/templates \
-H 'X-API-Key: your-api-key-here' \
-H 'Content-Type: application/json' \
-d '{
"name": "Welcome Email",
"description": "Sent to new users after registration",
"type": "onboarding",
"from_name": "Support Team",
"from_email": "support@example.com",
"reply_to": "support@example.com",
"transactional": false,
"open_tracking": true,
"click_tracking": true
}'
Example Response
{
"id": "550e8400-e29b-41d4-a716-446655440000"
}
List Templates
Retrieve all email templates with optional filtering.
GET /api/templates
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
name | string | No | Filter by template name |
type | string | No | Filter by template type |
search | string | No | Search across template fields |
page | integer | No | Page number (default: 1) |
limit | integer | No | Items per page (default: 20, max: 100) |
Example Request
curl -X GET \
'https://apis.app.stack9.co/api/templates?type=onboarding&page=1&limit=50' \
-H 'X-API-Key: your-api-key-here'
Example Response
{
"results": [
{
"version": 1,
"created_at": "2024-01-15T10:30:00.123Z",
"updated_at": "2024-03-20T14:45:30.456Z",
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Welcome Email",
"description": "Sent to new users after registration",
"type": "onboarding",
"from_name": "Support Team",
"from_email": "support@example.com",
"reply_to": "support@example.com",
"transactional": false,
"open_tracking": true,
"click_tracking": true
}
],
"total": 15,
"totalPages": 1
}
Get Template by ID
Retrieve a specific template by its ID.
GET /api/templates/{id}
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id | uuid | Yes | Template ID |
Example Request
curl -X GET \
https://apis.app.stack9.co/api/templates/550e8400-e29b-41d4-a716-446655440000 \
-H 'X-API-Key: your-api-key-here'
Example Response
{
"version": 2,
"created_at": "2024-01-15T10:30:00.123Z",
"updated_at": "2024-03-20T14:45:30.456Z",
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Welcome Email",
"description": "Sent to new users after registration",
"type": "onboarding",
"from_name": "Support Team",
"from_email": "support@example.com",
"reply_to": "support@example.com",
"transactional": false,
"open_tracking": true,
"click_tracking": true
}
Update Template
Update an existing template's configuration.
PUT /api/templates/{id}
Parameters
Same as Create Template, all fields are required in the update.
Example Request
curl -X PUT \
https://apis.app.stack9.co/api/templates/550e8400-e29b-41d4-a716-446655440000 \
-H 'X-API-Key: your-api-key-here' \
-H 'Content-Type: application/json' \
-d '{
"name": "Welcome Email - Updated",
"from_email": "noreply@example.com",
"from_name": "Example Team",
"transactional": false,
"open_tracking": true,
"click_tracking": false
}'
Delete Template
Delete a template and all its versions.
DELETE /api/templates/{id}
Example Request
curl -X DELETE \
https://apis.app.stack9.co/api/templates/550e8400-e29b-41d4-a716-446655440000 \
-H 'X-API-Key: your-api-key-here'
Template Versions
Each template can have multiple versions with different content. Only one version can be active at a time.
Create Template Version
Add a new version to an existing template.
POST /api/templates/{template_id}/versions
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
template_id | uuid | Yes | Parent template ID |
active | boolean | Yes | Set as active version |
subject | string | Yes | Email subject line (supports Handlebars) |
html_content | string | Yes | HTML email body (supports Handlebars) |
plain_content | string | No | Plain text body |
generate_plain_content | boolean | No | Auto-generate plain text from HTML |
template_builder_content | object | No | Visual builder JSON data |
test_data | object | No | Sample data for previewing |
data_fields_schema | array | No | Field definitions for template |
attachments | array | No | File attachments via URL |
Attachment Object Structure
{
"name": "invoice.pdf",
"type": "application/pdf",
"url": "https://example.com/files/invoice.pdf"
}
Example Request
curl -X POST \
https://apis.app.stack9.co/api/templates/550e8400-e29b-41d4-a716-446655440000/versions \
-H 'X-API-Key: your-api-key-here' \
-H 'Content-Type: application/json' \
-d '{
"active": true,
"subject": "Welcome to {{company_name}}, {{first_name}}!",
"html_content": "<html><body><h1>Welcome {{first_name}}!</h1><p>Thank you for joining {{company_name}}.</p></body></html>",
"plain_content": "Welcome {{first_name}}! Thank you for joining {{company_name}}.",
"test_data": {
"first_name": "John",
"company_name": "Example Corp"
},
"attachments": [
{
"name": "welcome-guide.pdf",
"type": "application/pdf",
"url": "https://example.com/guides/welcome.pdf"
}
]
}'
Example Response
{
"id": "660e8400-e29b-41d4-a716-446655440001",
"version": 1
}
List Template Versions
Get all versions for a template.
GET /api/templates/{template_id}/versions
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
page | integer | No | Page number (default: 1) |
limit | integer | No | Items per page (default: 20) |
Example Request
curl -X GET \
https://apis.app.stack9.co/api/templates/550e8400-e29b-41d4-a716-446655440000/versions \
-H 'X-API-Key: your-api-key-here'
Example Response
{
"results": [
{
"id": "660e8400-e29b-41d4-a716-446655440001",
"template_id": "550e8400-e29b-41d4-a716-446655440000",
"version": 2,
"active": true,
"subject": "Welcome to {{company_name}}, {{first_name}}!",
"html_content": "<html><body><h1>Welcome!</h1></body></html>",
"plain_content": "Welcome!",
"generate_plain_content": false,
"test_data": {
"first_name": "John",
"company_name": "Example Corp"
},
"attachments": []
},
{
"id": "660e8400-e29b-41d4-a716-446655440002",
"template_id": "550e8400-e29b-41d4-a716-446655440000",
"version": 1,
"active": false,
"subject": "Welcome!",
"html_content": "<html><body>Welcome to our service!</body></html>",
"plain_content": null,
"generate_plain_content": true,
"test_data": null,
"attachments": []
}
],
"total": 2,
"totalPages": 1
}
Get Template Version
Retrieve a specific template version.
GET /api/templates/{template_id}/versions/{id}
Example Request
curl -X GET \
https://apis.app.stack9.co/api/templates/550e8400-e29b-41d4-a716-446655440000/versions/660e8400-e29b-41d4-a716-446655440001 \
-H 'X-API-Key: your-api-key-here'
Update Template Version
Update an existing template version.
PUT /api/templates/{template_id}/versions/{id}
Parameters are the same as Create Template Version.
Example Request
curl -X PUT \
https://apis.app.stack9.co/api/templates/550e8400-e29b-41d4-a716-446655440000/versions/660e8400-e29b-41d4-a716-446655440001 \
-H 'X-API-Key: your-api-key-here' \
-H 'Content-Type: application/json' \
-d '{
"active": true,
"subject": "Welcome to {{company_name}}!",
"html_content": "<html><body><h1>Welcome!</h1><p>We are excited to have you.</p></body></html>"
}'
Delete Template Version
Remove a specific template version.
DELETE /api/templates/{template_id}/versions/{id}
Example Request
curl -X DELETE \
https://apis.app.stack9.co/api/templates/550e8400-e29b-41d4-a716-446655440000/versions/660e8400-e29b-41d4-a716-446655440001 \
-H 'X-API-Key: your-api-key-here'
Template Operations
Preview Inline Template
Preview how a template will render with substitution data without saving it.
POST /api/templates/preview/inline
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
substitution_data | object | No | Data for template variables |
content | object | Yes | Template content to preview |
content.from | string | Yes | Sender email address |
content.subject | string | No | Email subject with variables |
content.html | string | Yes | HTML content with variables |
content.text | string | No | Plain text content with variables |
Example Request
curl -X POST \
https://apis.app.stack9.co/api/templates/preview/inline \
-H 'X-API-Key: your-api-key-here' \
-H 'Content-Type: application/json' \
-d '{
"substitution_data": {
"first_name": "Jane",
"product_name": "Stack9 Platform",
"trial_days": 30
},
"content": {
"from": "sales@example.com",
"subject": "Start your {{trial_days}}-day trial of {{product_name}}",
"html": "<html><body><h1>Hi {{first_name}}!</h1><p>Your {{trial_days}}-day trial of {{product_name}} starts today.</p></body></html>"
}
}'
Example Response
{
"subject": "Start your 30-day trial of Stack9 Platform",
"html": "<html><body><h1>Hi Jane!</h1><p>Your 30-day trial of Stack9 Platform starts today.</p></body></html>"
}
Clone Template
Create a copy of an existing template with its active version.
POST /api/templates/clone
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
template_id | uuid | Yes | Source template ID to clone |
name | string | Yes | Name for the new template |
Example Request
curl -X POST \
https://apis.app.stack9.co/api/templates/clone \
-H 'X-API-Key: your-api-key-here' \
-H 'Content-Type: application/json' \
-d '{
"template_id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Welcome Email - Copy"
}'
Example Response
{
"id": "770e8400-e29b-41d4-a716-446655440003",
"version_id": "880e8400-e29b-41d4-a716-446655440004"
}
Template Snippets
Template snippets are reusable content blocks that can be included in multiple templates.
Create Template Snippet
POST /api/template_snippets
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Snippet name |
content | string | Yes | Snippet HTML content |
description | string | No | Snippet description |
Example Request
curl -X POST \
https://apis.app.stack9.co/api/template_snippets \
-H 'X-API-Key: your-api-key-here' \
-H 'Content-Type: application/json' \
-d '{
"name": "footer_social_links",
"description": "Standard footer with social media links",
"content": "<div class=\"footer\"><a href=\"https://twitter.com/example\">Twitter</a> | <a href=\"https://facebook.com/example\">Facebook</a></div>"
}'
List Template Snippets
GET /api/template_snippets
Example Request
curl -X GET \
https://apis.app.stack9.co/api/template_snippets \
-H 'X-API-Key: your-api-key-here'
Get Template Snippet
GET /api/template_snippets/{id}
Update Template Snippet
PUT /api/template_snippets/{id}
Delete Template Snippet
DELETE /api/template_snippets/{id}
Handlebars Templating
Stack9 uses Handlebars for dynamic content in templates.
Basic Variable Substitution
Conditionals
Loops
Helpers
Including Snippets
Best Practices
Template Design
- Mobile Responsive: Ensure templates work on all devices
- Fallback Content: Provide plain text versions for better deliverability
- Image Optimization: Use web-optimized images with alt text
- Preheader Text: Include preview text for inbox display
Version Management
- Test Before Activating: Always test new versions with sample data
- Keep Previous Versions: Maintain history for rollback capability
- Document Changes: Use descriptions to track version changes
- A/B Testing: Create multiple versions for testing
Dynamic Content
- Default Values: Always provide fallbacks for missing data
- Data Validation: Validate test data matches production structure
- Error Handling: Handle missing or null values gracefully
- Preview Testing: Test with various data scenarios
Performance
- HTML Size: Keep HTML under 100KB for best deliverability
- Attachment Limits: Total attachments should not exceed 10MB
- Image Hosting: Use CDN for images rather than attachments
- CSS Inlining: Inline CSS for better email client support
Error Handling
Common Error Responses
400 Bad Request - Invalid Template Data
{
"error": {
"code": "INVALID_TEMPLATE",
"message": "Template validation failed",
"details": {
"field": "from_email",
"message": "Sender email must be from a verified domain"
}
}
}
404 Not Found - Template Not Found
{
"error": {
"code": "TEMPLATE_NOT_FOUND",
"message": "Template with specified ID does not exist",
"details": {
"template_id": "550e8400-e29b-41d4-a716-446655440000"
}
}
}
409 Conflict - Version Conflict
{
"error": {
"code": "VERSION_CONFLICT",
"message": "Cannot delete the only version of a template",
"details": {
"template_id": "550e8400-e29b-41d4-a716-446655440000",
"version_count": 1
}
}
}
422 Unprocessable Entity - Template Rendering Error
{
"error": {
"code": "TEMPLATE_RENDER_ERROR",
"message": "Failed to render template",
"details": {
"error": "Undefined variable: customer_name",
"line": 15
}
}
}
Rate Limits
| Endpoint | Limit | Window |
|---|---|---|
| GET endpoints | 100 requests | 1 minute |
| POST/PUT endpoints | 50 requests | 1 minute |
| DELETE endpoints | 20 requests | 1 minute |
| Preview endpoint | 30 requests | 1 minute |
Migration Guide
When migrating templates from another provider:
- Export Existing Templates: Export HTML and metadata from current provider
- Create Base Templates: Create template records with metadata
- Import Content: Create versions with HTML/text content
- Update Variables: Convert to Handlebars syntax
- Test Rendering: Use preview endpoint to verify rendering
- Update Integrations: Update API calls to use new template IDs
Variable Syntax Migration
| Provider | Original | Stack9 Handlebars |
|---|---|---|
| SendGrid | {{name}} | {{name}} |
| Mailchimp | `* | FNAME |
| Mandrill | `* | NAME |
| Custom | ${name} | {{name}} |
Code Examples
Node.js - Create and Send Template
const axios = require('axios');
// Create template
async function createTemplate() {
const template = await axios.post(
'https://apis.app.stack9.co/api/templates',
{
name: 'Order Confirmation',
from_email: 'orders@example.com',
from_name: 'Example Store',
transactional: true,
open_tracking: true
},
{
headers: {
'X-API-Key': 'your-api-key-here',
'Content-Type': 'application/json'
}
}
);
// Create version
const version = await axios.post(
`https://apis.app.stack9.co/api/templates/${template.data.id}/versions`,
{
active: true,
subject: 'Order #{{order_number}} Confirmed',
html_content: `
<html>
<body>
<h1>Thank you for your order, {{customer_name}}!</h1>
<p>Order #{{order_number}} has been confirmed.</p>
<h2>Order Details:</h2>
<ul>
{{#each items}}
<li>{{name}} - Qty: {{quantity}} - ${{price}}</li>
{{/each}}
</ul>
<p><strong>Total: ${{total}}</strong></p>
</body>
</html>
`,
test_data: {
customer_name: 'John Doe',
order_number: '12345',
items: [
{ name: 'Widget', quantity: 2, price: 10.00 }
],
total: 20.00
}
},
{
headers: {
'X-API-Key': 'your-api-key-here',
'Content-Type': 'application/json'
}
}
);
return { templateId: template.data.id, versionId: version.data.id };
}
Python - Preview Template
import requests
def preview_template(template_data, substitution_data):
url = 'https://apis.app.stack9.co/api/templates/preview/inline'
payload = {
'substitution_data': substitution_data,
'content': {
'from': 'noreply@example.com',
'subject': template_data['subject'],
'html': template_data['html']
}
}
headers = {
'X-API-Key': 'your-api-key-here',
'Content-Type': 'application/json'
}
response = requests.post(url, json=payload, headers=headers)
return response.json()
# Example usage
template = {
'subject': 'Hello {{name}}!',
'html': '<h1>Welcome {{name}}</h1><p>Your account is ready.</p>'
}
data = {
'name': 'Jane Smith'
}
result = preview_template(template, data)
print(result['html']) # <h1>Welcome Jane Smith</h1><p>Your account is ready.</p>