When to Use a Template
Use a template for emails whose wording changes more often than your code: password resets, order confirmations, invoices, onboarding steps. Use Create Message when the body is built per send, for example a report that is generated on the fly.
Transactional templates are separate from campaign templates, which are starting designs for marketing campaigns under Marketing → Templates.
Create a Template
In the dashboard, open Transactional → Templates and create a template. Choose how to build the design:- Simple Editor: write the email like a document. Its design can only be changed in the dashboard.
- Advanced Editor: design the email in MJML.
- Upload: your own HTML file, or a zip with the HTML and its images (the
htmleditor in the API).
content.mjml) or HTML (content.html). Images and stylesheets must use full https:// URLs; the API cannot upload files. The MJML, the HTML and a custom text version can each be at most 200 KB.
Variables
Write a variable in the design as{{ first_name }}. The templating language is MiniJinja, the same as for Create Message.
- Required or optional. Every variable is required unless the design gives it a fallback, for example
{{ first_name|default("there") }}. The template page and Get Template list each variable withrequired: trueorfalse. - Escaping. In the HTML body, every value is HTML-escaped. To insert a value as HTML, write
{{ content|safe }}in the design. The subject, the headers and the text body get values as they are. - Reserved names. AhaSend supplies
email(the recipient’s address) and, when the design uses it,unsubscribe_url. A value you give for these is replaced. - Subject and headers. A
{{ variable }}in the subject or in a custom header resolves from the same values. A variable with no value becomes empty.
Unsubscribe Link
When the design uses{{ unsubscribe_url }}, each recipient gets its own link, and the message carries List-Unsubscribe and List-Unsubscribe-Post: List-Unsubscribe=One-Click, so the unsubscribe button of mail clients works. The button suppresses the recipient for the sending domain; the link opens a confirmation page first. Most transactional email should not have an unsubscribe link; use it for templates that are optional for the recipient, such as digests.
Draft, Publish, Versions
Every edit, in the dashboard or through the API, goes to the template’s draft. Sends use the published copy.- The dashboard and the API edit the same draft. A publish publishes the whole draft, including dashboard changes. Get Template Draft shows what a publish will publish; Discard Template Draft throws it away.
- Each publish makes a version. A template keeps its 50 most recent versions. Preview any of them on the template page or with Get Template Version, and bring one back with Restore Template Version.
has_drafton Get Template tells you whether unpublished changes are waiting.
Send a Template
Call Create Template Message with the template’s ID. Each recipient gets a separate message with its own values. One request can hold up to 100 recipients.id (null when nothing was sent), a status (queued, scheduled or error) and an error.
What the request can set, and what it cannot:
from: optional. Left out, the template’s default sender is used. When neither is set, the request is refused with400 from is required because the template has no default sender.subject: optional. Left out or empty, the template’s published subject is used. When neither has a subject, the request is refused.reply_to: optional. Left out, the template’s reply-to is used.substitutions: per recipient only. Asubstitutionsobject at the top of the request is refused with400 substitutions go on each recipient, not at the top of the request.- Body: always the template’s, and the message has no AMP part.
html_content,text_contentandamp_contentare refused with400 <field> is not accepted, the template supplies the body. attachments,headers,tags,tracking,retention,schedule,sandbox: work as in Create Message. Sandbox sends are a good way to check your substitutions without using credits.
@ahasend/sdk 0.3.0 and later) and the Go SDK (v0.3.0 and later) cover template sends; see the changelog for the release.
Permissions
Sending a template needs a key that can send from the sender’s domain:messages:send:all or the domain-specific send scope. No templates scope is needed to send. Creating, editing and publishing templates needs templates:write; deleting needs templates:delete. See Permission scopes.
Errors You Will See
Tracking
- Every message sent from a template records the template: Get Message returns its
template_id, and the template page shows delivery statistics for the last 30 days. - Templates are available through the MCP server: an AI agent can create, change, publish and restore templates, and send one.

