Mailgun allows you to store predefined email templates and use them to send messages by simply referencing the template name. Using Mailgun to create, manage, and use your email templates can eliminate costly overhead from your tech stack and centralize your email workflow.
Mailgun caps usage of account-level templates to 100 templates and 40 versions per template. Domain-scoped templates have same limit set on a per sending domain basis.
For comprehensive information about templates, refer to the Template API documentation. To learn about creating templates using the Template Builder interface, see Email Templates.
Templates can either be created under a specific sending domain or for greater flexibility, created at the account level.
Templates stored and scoped per sending domain. The template must be associated with your domain when sending email.
Provides the greatest flexibility allowing sending account level templates across any sending domain under account as well as on behalf of subaccounts. Domain scoped templates can be copied to account-level and account-level templates can be copied to subaccounts for direct subaccount template management and sending.
In place of using template name for sending email users can, alternatively, use the unique template ID associated with your templates. This dev-friendly option also allows account-level templates to be more easily sent across domains and even subaccounts via the X-On-Behalf-Of header.
Regions are isolated, meaning you will need to create distinct account level or domain scoped templates in each region for the account/domains.
You can create templates either through the Teamplate API or using the Template Builder in the Mailgun UI.
curl -X POST -s --user 'api:YOUR_API_KEY' \
https://api.mailgun.net/v3/YOUR_DOMAIN_NAME/templates \
--form-string template='<div class="entry"> <h1>{{title}}</h1> <div class="body"> {{body}} </div> </div>' \
-F name='template.test' \
-F description='Sample template'The response returns stored template information:
{
"template": {
"createdAt": "Wed, 29 Aug 2018 23:31:13 UTC",
"description": "Sample template",
"name": "template.test"
},
"message": "template has been stored"
}Creating an Account-Level Template via API
curl -X POST -s --user 'api:YOUR_API_KEY' \
https://api.mailgun.net/v4/templates \
--form-string template='<div class="entry"> <h1>{{title}}</h1> <div class="body"> {{body}} </div> </div>' \
-F name='template.test' \
-F description='Sample template'The response returns stored template information
{
"message": "template has been stored",
"template": {
"createdAt": "Sat, 12 Nov 1955 06:38:00 UTC",
"createdBy": "user-supplied-value",
"description": "Sample template",
"id": "46565d87-68b6-4edb-8b3c-34554af4bb77",
"name": "template.test",
"version": {
"active": true,
"comment": "Version comment",
"createdAt": "Sat, 12 Nov 1955 06:38:00 UTC",
"headers": {
"From": "from@header.tld",
"Reply-To": "reply-to@header.tld",
"Subject": "Subject Value"
},
"id": "3efd2b85-0f41-4a1d-9898-05d7e7459c4a",
"tag": "tag",
"template": "\u003chtml\u003etemplate content\u003c/html\u003e"
}
}
}Once your template is created, you can send messages using it:
curl -s --user 'api:YOUR_API_KEY' \
https://api.mailgun.net/v3/YOUR_DOMAIN_NAME/messages \
-F from='Excited User <postmaster@YOUR_DOMAIN_NAME>' \
-F to=recipient@example.com \
-F subject='Hello there!' \
-F template="template.test" \
-F t:variables='{"title": "API documentation", "body": "Sending messages with templates"}'When sending MIME messages, pass template variables using the X-Mailgun-Template-Variables header instead of the t:variables parameter.
Mailgun templates use a customized version of Handlebars, a popular templating engine. To provide dynamic values for substitution, use the t:variables parameter or the X-Mailgun-Template-Variables header.
Mailgun supports the following Handlebars block helpers: if, unless, each, with, and equal.
Use the if helper to conditionally render content based on variable values.
Example: Dynamic language selection
{{#if english}}
<p>This text is in the English language.</p>
{{else if spanish}}
<p>Este texto está en idioma español.</p>
{{else if french}}
<p>Ce texte est en langue française.</p>
{{/if}}Pass this data via t:variables:
{"spanish": true}This renders the Spanish version of the text.
The unless helper renders a block only when the condition is false.
Example: Payment reminder
{{#unless paid}}
<h3 class="warning">WARNING: Your account is past due and will be suspended shortly. Please contact our billing department for assistance</h3>
{{/unless}}Pass this data via t:variables:
{"paid": false}This displays the warning when the account is unpaid.
The each helper allows you to iterate over arrays or lists.
Example: Listing scheduled services
{{#each user.services}}
<li>You scheduled {{this.service}} on {{this.date}}</li>
{{/each}}Pass this data via t:variables:
{
"user": {
"services": [
{
"date": "07/30/2019",
"service": "deliverability consultation"
},
{
"date": "08/05/2019",
"service": "sales consultation"
}
]
}
}This renders as:
- You scheduled deliverability consultation on 07/30/2019
- You scheduled sales consultation on 08/05/2019
The with helper shifts the context for a section of your template, making it easier to access nested properties without repeating the parent object name.
Example: Simplifying nested author information
<div class="entry">
<h1>{{title}}</h1>
{{#with author}}
<h2>By {{firstName}} {{lastName}}</h2>
<p>Email: {{email}}</p>
{{/with}}
</div>Pass this data via t:variables:
{
"title": "My first post!",
"author": {
"firstName": "Jean",
"lastName": "Valjean",
"email": "jean@example.com"
}
}This renders as:
<div class="entry">
<h1>My first post!</h1>
<h2>By Jean Valjean</h2>
<p>Email: jean@example.com</p>
</div>The equal helper renders content when a variable exactly matches a specific value. This is useful for displaying different content based on user attributes like subscription tier, account status, or preferences.
Example: Customizing content by subscription tier
{{#equal subscription "free"}}
<p>Upgrade to Pro to unlock advanced features!</p>
<a href="/upgrade">Upgrade Now</a>
{{/equal}}
{{#equal subscription "pro"}}
<p>Thanks for being a Pro member! Enjoy your premium features.</p>
{{/equal}}
{{#equal subscription "enterprise"}}
<p>Welcome back! Your dedicated account manager is here to help.</p>
{{/equal}}Pass this data via t:variables:
{"subscription": "pro"}This displays the Pro member message.
Note: The equal helper converts both values to strings before comparing, so {"count": 5} and {"count": "5"} are treated as equal.
The following example demonstrates all supported Handlebars helpers working together in a single template. This comprehensive template shows how you can combine with, equal, unless, if, and each to create dynamic, personalized email content.
curl -X POST -s --user 'api:YOUR_API_KEY' \
https://api.mailgun.net/v3/YOUR_DOMAIN_NAME/templates \
-F name='comprehensive.test' \
-F description='Complete test template with all Handlebars helpers' \
--form-string template='<!DOCTYPE html>
<html>
<head>
<style>
body { font-family: Arial, sans-serif; }
.warning { color: red; }
.premium { background-color: #f0f8ff; padding: 10px; }
</style>
</head>
<body>
<h1>Order Confirmation</h1>
{{#with customer}}
<p>Hello {{firstName}} {{lastName}},</p>
<p>Email: {{email}}</p>
{{/with}}
{{#equal accountType "premium"}}
<div class="premium">
<p>🎉 Premium Member Perk: You have earned 2x points on this order!</p>
</div>
{{/equal}}
{{#equal accountType "basic"}}
<p>Upgrade to Premium for exclusive benefits and rewards!</p>
{{/equal}}
{{#unless paymentReceived}}
<div class="warning">
<p>⚠️ WARNING: Payment pending. Your order will ship once payment is confirmed.</p>
</div>
{{/unless}}
{{#if paymentReceived}}
<p>✓ Payment confirmed! Your order is being processed.</p>
{{/if}}
<h2>Order Items:</h2>
<ul>
{{#each items}}
<li>{{this.name}} - Quantity: {{this.quantity}} - ${{this.price}}</li>
{{/each}}
</ul>
{{#if spanish}}
<p>Gracias por su compra!</p>
{{else if french}}
<p>Merci pour votre achat!</p>
{{else}}
<p>Thank you for your purchase!</p>
{{/if}}
</body>
</html>' curl -s --user 'api:YOUR_API_KEY' \
https://api.mailgun.net/v3/YOUR_DOMAIN_NAME/messages \
-F from='Order System <orders@YOUR_DOMAIN_NAME>' \
-F to='recipient@example.com' \
-F subject='Your Order Confirmation #12345' \
-F template="comprehensive.test" \
-F t:variables='{
"customer": {
"firstName": "Maria",
"lastName": "Garcia",
"email": "maria@example.com"
},
"accountType": "premium",
"paymentReceived": true,
"items": [
{
"name": "Wireless Headphones",
"quantity": 1,
"price": "79.99"
},
{
"name": "Phone Case",
"quantity": 2,
"price": "24.99"
},
{
"name": "USB Cable",
"quantity": 3,
"price": "12.99"
}
],
"spanish": false,
"french": false
}'You can modify the JSON data to test different template behaviors:
- Change
accountTypeto "basic" to see the upgrade message instead of the premium perk - Set
paymentReceivedto false to display the payment warning - Change
spanishto true to see the Spanish thank you message - Modify the
itemsarray to add or remove products from the order list - Update customer information within the
customerobject to test thewithhelper