Check a template's Liquid syntax, and optionally render it with sample data, without sending any email. Use this endpoint to test a template before it goes live, so mistakes are caught up front rather than surfacing in a real send.
Endpoint and headers
POST https://email.useinsider.com/mail/v2/validate
Header | Value | Purpose |
|---|---|---|
X-INS-AUTH-KEY | API token, format INS.{id}.{secret} | Authentication |
Content-Type | application/json | Request format |
Body parameters
Below are the parameters for this request.
Parameter | Description | Data type | Required |
|---|---|---|---|
subject | Subject template to validate. | String | No* |
content | Content blocks to validate (type, value). | Array | No* |
fields | Optional sample variables used to render valid templates. | Object | No |
* Provide at least one of subject or content.
Request example
Below is a sample body for this request.
{
"subject": "Hello {{ first_name }}",
"content": [
{
"type": "text/html",
"value": "<p>Hi {{ first_name }}</p>"
}
],
"fields": {
"first_name": "Alice"
}
}Code samples
These samples are provided as is, as a starting point for your integration. Insider One does not guarantee that they work in every environment, so test them in your own environment before you use them in production.
Each sample validates a subject and an HTML block with sample fields, then prints the rendered output or each syntax error.
Set your API key in the INSIDER_API_KEY environment variable.
Copy the sample for your language into a file named validate.py or validate.mjs.
Run the file. A valid template prints each field with its rendered output, such as subject: Hello Alice.
Python
Requires Python 3.8 or later and the requests package (pip install requests). Run python validate.py.
"""Check a template's Liquid syntax with POST /mail/v2/validate, without sending.
Sample code, provided as is. Test it in your own environment before you use it in production.
Requires Python 3.8+ and the requests package: pip install requests
"""
import os
import time
import requests
URL = "https://email.useinsider.com/mail/v2/validate"
# OAuth 2.0: call https://gw.useinsider.com/api/em/v2/tx/validate with "Authorization: Bearer {token}" instead.
HEADERS = {"X-INS-AUTH-KEY": os.environ["INSIDER_API_KEY"], "Content-Type": "application/json"}
def call(method, url, payload=None, retries=3):
"""Send the request. On 429 or 5xx, wait and retry up to `retries` times."""
for attempt in range(retries + 1):
response = requests.request(method, url, json=payload, headers=HEADERS, timeout=30)
if (response.status_code != 429 and response.status_code < 500) or attempt == retries:
return response
wait = float(response.headers.get("Retry-After", 2 ** attempt)) # 1, 2, 4 s without Retry-After
print(f"HTTP {response.status_code}: retrying in {wait:.0f}s")
time.sleep(wait)
payload = {
"subject": "Hello {{ first_name }}",
"content": [{"type": "text/html", "value": "<p>Hi {{ first_name }}</p>"}],
"fields": {"first_name": "Alice"}, # Optional: returns the rendered output
}
response = call("POST", URL, payload)
if response.status_code != 200:
raise SystemExit(f"Validation request failed ({response.status_code}): {response.text[:300]}")
result = response.json()
for item in result.get("rendered", []):
print(f"{item['field']}: {item['output']}")
for error in result.get("errors", []):
print(f"Invalid {error['field']}: {error['message']}")Node.js
Requires Node.js 18 or later. No packages are needed. Run node validate.mjs.
// Check a template's Liquid syntax with POST /mail/v2/validate, without sending.
// Sample code, provided as is. Test it in your own environment before you use it in production.
// Requires Node.js 18+ (built-in fetch). No dependencies.
const URL = "https://email.useinsider.com/mail/v2/validate";
// OAuth 2.0: call https://gw.useinsider.com/api/em/v2/tx/validate with "Authorization: Bearer {token}" instead.
const HEADERS = { "X-INS-AUTH-KEY": process.env.INSIDER_API_KEY, "Content-Type": "application/json" };
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
// Send the request. On 429 or 5xx, wait and retry up to `retries` times.
async function call(method, url, payload, retries = 3) {
for (let attempt = 0; ; attempt++) {
const response = await fetch(url, {
method,
headers: HEADERS,
body: payload === undefined ? undefined : JSON.stringify(payload),
signal: AbortSignal.timeout(30000),
});
if ((response.status !== 429 && response.status < 500) || attempt === retries) return response;
const wait = Number(response.headers.get("Retry-After") ?? 2 ** attempt); // 1, 2, 4 s without Retry-After
console.warn(`HTTP ${response.status}: retrying in ${wait}s`);
await sleep(wait * 1000);
}
}
const payload = {
subject: "Hello {{ first_name }}",
content: [{ type: "text/html", value: "<p>Hi {{ first_name }}</p>" }],
fields: { first_name: "Alice" }, // Optional: returns the rendered output
};
const response = await call("POST", URL, payload);
if (response.status !== 200) {
throw new Error(`Validation request failed (${response.status}): ${(await response.text()).slice(0, 300)}`);
}
const result = await response.json();
for (const item of result.rendered ?? []) console.log(`${item.field}: ${item.output}`);
for (const error of result.errors ?? []) console.log(`Invalid ${error.field}: ${error.message}`);Sample responses
The endpoint always returns 200; the outcome is in the body. When the syntax is valid and sample fields are provided, the rendered output is returned.
{
"valid": true,
"rendered": [
{
"field": "subject",
"output": "Hello Alice"
},
{
"field": "content[0]",
"output": "<p>Hi Alice</p>"
}
]
}When the syntax is invalid, each problem is listed with the field it came from.
{
"valid": false,
"errors": [
{
"field": "content[0]",
"message": "unterminated \"for\" block"
}
]
}Keep in mind
Validation checks Liquid syntax. Fields appear in a stable order: subject first, then content[0], content[1].
Run validation as the last step before a template goes live. It is the intended way to confirm a template is well-formed without sending a real request.
For the full outcome shapes, refer to the Error Codes.
Suggested Next
Once validation succeeds and you have reviewed any rendered output, see POST /mail/v2/send to prepare your transactional email send request.