Skip to content

Templates

Templates let you define reusable email layouts with placeholder variables. Store your HTML once, then send personalized emails by passing different data each time.

POST /api/v1/templates

Terminal window
curl -X POST https://api.relaypost.dev/v1/templates \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"name": "Order Confirmation",
"subject": "Your order #{{orderNumber}}",
"html": "<h1>Thanks, {{customerName}}!</h1><p>Your order #{{orderNumber}} totaling {{total}} has been confirmed.</p>",
"text": "Thanks, {{customerName}}! Your order #{{orderNumber}} totaling {{total}} has been confirmed."
}'
FieldTypeRequiredDescription
namestringYesTemplate name
subjectstringYesSubject line (supports {{variables}})
htmlstringNoHTML body (supports {{variables}})
textstringNoPlain text body (supports {{variables}})
variable_schemaarrayNoTyped variable definitions (see Variable Schema below)
{
"data": {
"id": "tmpl_abc123",
"name": "Order Confirmation",
"subject": "Your order #{{orderNumber}}",
"html_body": "<h1>Thanks, {{customerName}}!</h1><p>Your order #{{orderNumber}} totaling {{total}} has been confirmed.</p>",
"text_body": "Thanks, {{customerName}}! Your order #{{orderNumber}} totaling {{total}} has been confirmed.",
"variables": ["orderNumber", "customerName", "total"],
"variable_schema": null,
"is_active": true,
"created_at": "2025-01-15T10:30:00.000Z",
"updated_at": "2025-01-15T10:30:00.000Z"
}
}

Reference the template by ID and pass the variable values using POST /api/v1/emails/send:

Terminal window
curl -X POST https://api.relaypost.dev/v1/emails/send \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"from": { "email": "orders@yourapp.com" },
"to": [{ "email": "customer@example.com" }],
"subject": "Your order #1234",
"template_id": "tmpl_abc123",
"template_data": {
"customerName": "Jane",
"orderNumber": "1234",
"total": "$49.99"
}
}'

GET /api/v1/templates

Terminal window
curl "https://api.relaypost.dev/v1/templates?page=1&limit=20" \
-H "Authorization: Bearer YOUR_API_KEY"
ParameterTypeDefaultDescription
pageinteger1Page number
limitinteger20Results per page (max 100)
{
"data": [
{
"id": "tmpl_abc123",
"name": "Order Confirmation",
"subject": "Your order #{{orderNumber}}",
"html_body": "...",
"text_body": "...",
"variables": ["orderNumber", "customerName", "total"],
"variable_schema": null,
"is_active": true,
"created_at": "2025-01-15T10:30:00.000Z",
"updated_at": "2025-01-15T10:30:00.000Z"
}
],
"pagination": {
"page": 1,
"limit": 20,
"total_count": 5,
"total_pages": 1
}
}

PUT /api/v1/templates/:id

Only include the fields you want to change — everything else stays the same.

Terminal window
curl -X PUT https://api.relaypost.dev/v1/templates/tmpl_abc123 \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"html": "<h1>Updated layout</h1><p>Hello {{customerName}}</p>"
}'
FieldTypeRequiredDescription
namestringNoUpdated template name
subjectstringNoUpdated subject line
htmlstringNoUpdated HTML body
textstringNoUpdated plain text body
variable_schemaarrayNoUpdated variable definitions
is_activebooleanNoActivate or deactivate

At least one field must be provided.

{
"data": {
"id": "tmpl_abc123",
"name": "Order Confirmation",
"subject": "Your order #{{orderNumber}}",
"html_body": "<h1>Updated layout</h1><p>Hello {{customerName}}</p>",
"text_body": "Thanks, {{customerName}}! Your order #{{orderNumber}} totaling {{total}} has been confirmed.",
"variables": ["orderNumber", "customerName", "total"],
"variable_schema": null,
"is_active": true,
"created_at": "2025-01-15T10:30:00.000Z",
"updated_at": "2025-01-15T11:00:00.000Z"
}
}
StatusCodeDescription
400VALIDATION_ERRORNo update fields provided or invalid values
404NOT_FOUNDTemplate not found or belongs to another organization
409TEMPLATE_NAME_CONFLICTTemplate name already exists

DELETE /api/v1/templates/:id

Soft-deletes the template by setting is_active to false. The template remains in the database so historical email records referencing it stay valid. Use PUT /api/v1/templates/:id with is_active: true to reactivate.

Terminal window
curl -X DELETE https://api.relaypost.dev/v1/templates/tmpl_abc123 \
-H "Authorization: Bearer YOUR_API_KEY"
{
"data": {
"id": "tmpl_abc123",
"name": "Order Confirmation",
"subject": "Your order #{{orderNumber}}",
"html_body": "<h1>Thanks, {{customerName}}!</h1>",
"text_body": "Thanks, {{customerName}}!",
"variables": ["orderNumber", "customerName"],
"variable_schema": null,
"is_active": false,
"created_at": "2025-01-15T10:30:00.000Z",
"updated_at": "2025-01-15T12:00:00.000Z"
}
}
StatusCodeDescription
404NOT_FOUNDTemplate not found or belongs to another organization

Templates support an optional variable_schema field that defines typed variable declarations. When provided, template_data is validated against the schema at send time.

Each entry in the array has the following shape:

FieldTypeRequiredDescription
namestringYesVariable name as referenced in template content
typestringYesstring, number, boolean, array, or object
requiredbooleanYesWhether the variable must be provided at send time
default_valueanyNoDefault value when the variable is not provided (must match type)

Example: Creating a template with a schema

Section titled “Example: Creating a template with a schema”
{
"name": "Order Confirmation",
"subject": "Your order #{{orderNumber}}",
"html": "<h1>Thanks, {{customerName}}!</h1><p>Total: {{total}}</p>",
"variable_schema": [
{ "name": "customerName", "type": "string", "required": true },
{ "name": "orderNumber", "type": "string", "required": true },
{ "name": "total", "type": "string", "required": true },
{ "name": "items", "type": "array", "required": false, "default_value": [] }
]
}

Auto-detected variables vs user-defined schema

Section titled “Auto-detected variables vs user-defined schema”

Variables are automatically detected from {{variable}} placeholders in the subject, HTML, and text bodies. The variables field in the response always lists all detected variables, regardless of whether a schema is defined.

When variable_schema is provided:

  • Required variables that are missing at send time return a TEMPLATE_MISSING_VARIABLES error
  • Variables with the wrong type return a TEMPLATE_DATA_INVALID error
  • Variables with required: false and a default_value use the default when not provided

When variable_schema is not provided:

  • No validation is performed on template_data
  • Missing variables render as empty strings

If a {{variable}} appears in the template content but is not declared in variable_schema, the API returns a warning in the response when creating or updating the template:

{
"data": { "...": "..." },
"warnings": ["Undeclared variable: discount"]
}

This is informational only — the template is still created/updated successfully.


POST /api/v1/templates/:id/preview

Preview a rendered template with sample data before sending. Missing variables render as empty strings and are listed in the warnings array. Inactive templates can be previewed.

See the REST API Reference for full request/response details and code examples in all languages.


POST /api/v1/emails/batch

Send the same template to multiple recipients with per-recipient variable data. Maximum 1000 recipients per batch.

See the REST API Reference for full request/response details and code examples in all languages.