Templates
A template is an email your team writes once and sends many times: an order confirmation, a password reset, a welcome email. You write the HTML, declare its variables, publish it, and then send it with just its id or alias and the variable values. The API works like Resend Templates.
Before you start
- Managing templates through the API needs a key with
email:write. For a key that only sends with templates, grantemail:send. - The template's
fromaddress, or the one you pass when sending, must be on a verified domain.
Write and publish
Open Email → Templates → New template. The editor has:
- Defaults: sender, subject, Reply-To and folder. A send can override each of them.
- HTML with syntax highlighting, a Preview tab filled with the fallback values, and a Plain text tab. Leave plain text empty and Ha Tang generates it from the HTML.
- Variables: declare every variable the subject and body use, as
stringornumber, with an optional fallback. Declare all adds the ones Ha Tang finds in the content. - Check links reports links that are empty, only
#, on a placeholder domain (example.com,yourdomain.com,localhost…), nothttp(s), unreachable, or answering with an error page. Link problems hold back publishing until you choose Publish anyway. - Send test to your own address, Duplicate and Publish.
Only a published version can be sent. You can keep editing the draft: sends keep using the published version until you publish again. The list shows Unpublished changes when the draft differs from what is published.
Variables
Write variables as {{{NAME}}}, or {{{NAME|default}}} with an inline default:
<p>Hi {{{NAME|there}}}, order {{{ORDER}}} for {{{TOTAL}}} VND is confirmed.</p>
- Keys use letters, digits and
_, up to 50 characters. A template declares at most 50 variables, and each value is at most 2,000 characters. - Values are HTML-escaped when they go into the HTML part.
- A
numbervariable refuses values that are not numbers. - The value comes from the send; if there is none, from the inline default; if there is none, from the declared fallback. A variable with none of these makes the send fail with
422and nothing is sent. - Publishing fails while the content uses a variable that is not declared.
- These names are reserved and filled for each recipient by broadcasts and automations:
{{{FIRST_NAME}}},{{{LAST_NAME}}},{{{EMAIL}}},{{{UNSUBSCRIBE_URL}}}(and its alias{{{RESEND_UNSUBSCRIBE_URL}}}). Contact properties are available as{{{contact.plan}}}; see Contacts, segments and topics. You do not declare these.
Send with a template
- cURL
- Node.js
- Python
- PHP
curl -X POST https://api.hatang.dev/emails \
-H "Authorization: Bearer $HATANG_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": ["[email protected]"],
"template": {
"id": "order-confirmation",
"variables": { "NAME": "Lan", "ORDER": "DH1024", "TOTAL": 450000 }
}
}'
const { data, error } = await hatang.emails.send({
template: { id: "order-confirmation", variables: { NAME: "Lan", ORDER: "DH1024", TOTAL: 450000 } },
});
hatang.emails.send({
"template": {"id": "order-confirmation", "variables": {"NAME": "Lan", "ORDER": "DH1024", "TOTAL": 450000}},
})
$res = $client->post('/emails', [
'headers' => ['Authorization' => 'Bearer ' . getenv('HATANG_API_KEY')],
'json' => [
'template' => ['id' => 'order-confirmation', 'variables' => ['NAME' => 'Lan', 'ORDER' => 'DH1024', 'TOTAL' => 450000]],
],
]);
template.idis the template id (tpl_…) or its alias.from,subjectandreply_toin the request override the template's defaults. They are required when the template has none.- Do not send
htmlortexttogether withtemplate. POST /emails/batchacceptstemplatein each item.- To start a broadcast from a template, pass
template_idtoPOST /broadcasts, or choose Start from a template in the broadcast editor. See Broadcasts.
| Error | Cause |
|---|---|
404 not_found | No template has this id or alias. |
422 validation_error "is not published yet" | Publish the template first. |
422 validation_error "Missing value for template variable" | A variable has no value, no inline default and no fallback. |
422 validation_error "The template has no from" | Pass from in the request or set it on the template. |
Manage templates with the API
curl -X POST https://api.hatang.dev/templates \
-H "Authorization: Bearer $HATANG_API_KEY" -H "Content-Type: application/json" \
-d '{
"name": "Order confirmation",
"alias": "order-confirmation",
"from": "Acme <[email protected]>",
"subject": "Order {{{ORDER}}} is confirmed",
"html": "<p>Hi {{{NAME|there}}}, order {{{ORDER}}} for {{{TOTAL}}} VND is confirmed.</p>",
"variables": [
{ "key": "NAME", "type": "string" },
{ "key": "ORDER", "type": "string" },
{ "key": "TOTAL", "type": "number", "fallback_value": 0 }
]
}'
# { "object": "template", "id": "tpl_…" }
curl -X POST https://api.hatang.dev/templates/order-confirmation/publish -H "Authorization: Bearer $HATANG_API_KEY"
# { "object": "template", "id": "tpl_…", "current_version_id": "tplv_…" }
| Endpoint | Description |
|---|---|
POST /templates | Create a draft: name, html (required), alias, from, subject, reply_to, text, variables, folder. |
GET /templates | List templates. Filters: q (name or alias), folder, status (draft or published), plus limit and after. |
GET /templates/{id or alias} | One template, with status, current_version_id, published_at and has_unpublished_versions. |
PATCH /templates/{id or alias} | Change the draft. |
POST /templates/{id or alias}/publish | Publish the draft. |
POST /templates/{id or alias}/duplicate | Copy it into a new draft named "(copy)". |
DELETE /templates/{id or alias} | Delete it. |
An alias uses lowercase letters, digits, - and _, up to 64 characters, and is unique in your business. The Node.js SDK has the same calls under hatang.templates: create, get, list, update, publish, duplicate and remove.