--- title: "POST /mail/v2/validate" slug: "transactional-email-post-mail-v2-validate" description: "Explains the POST /mail/v2/validate endpoint for validating transactional email payloads before sending, including request format, responses, and errors." updated: 2026-10-09T10:06:54Z published: 2026-10-09T10:06:54Z canonical: "academy.insiderone.com/transactional-email-post-mail-v2-validate" --- > ## Documentation Index > Fetch the complete documentation index at: https://academy.insiderone.com/llms.txt > Use this file to discover all available pages before exploring further. # POST /mail/v2/validate Check a template's [Liquid](/v1/docs/liquid-tags-in-email) 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. ```json { "subject": "Hello {{ first_name }}", "content": [ { "type": "text/html", "value": "
Hi {{ first_name }}
" } ], "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. 1. Set your API key in the **INSIDER_API_KEY** environment variable. 2. Copy the sample for your language into a file named **validate.py** or **validate.mjs**. 3. 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**. ```python """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": "Hi {{ first_name }}
"}], "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**. ```node-repl // 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: "Hi {{ first_name }}
" }], 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. ```json { "valid": true, "rendered": [ { "field": "subject", "output": "Hello Alice" }, { "field": "content[0]", "output": "Hi Alice
" } ] } ``` When the syntax is invalid, each problem is listed with the field it came from. ```json { "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](/docs/transactional-email-error-codes). ## Suggested Next Once validation succeeds and you have reviewed any rendered output, see [POST /mail/v2/send](/v1/docs/transactional-email-post-mail-v2-send) to prepare your transactional email send request.