Sitecore Send - Dynamic Transactional Emails (with Mustache)

Moosend logo


Transactional emails are automated emails, that company sends to notify customer about any transaction happened.

Here there are few examples of transactional emails:

  • account create
  • order submit
  • shipping notification
  • password change

You can check out this article, where Moosend explains how those emails can be sent.

How to send transactional emails via Moosend

  1. Prepare the body of email
  2. Enable and obtain SMTP credentials in Moosend: click here to learn, how to configure it
  3. Send the email: see this example to send emails via Moosend SMTP from Sitecore

Sounds easy... The only question is how to prepare the email body? Where it can be managed?

Templates for emails

Typically when it comes to transactional emails we have a generic email template with some placeholders in it (e.g. {FirstName} or {address.City}) and some dynamic model (which usually can be represented in JSON).

So during preprocess stage email placeholders are replaced with values from JSON model.

Render Email Body Scheme

How this can be done with Moosend?

Dynamic emails with custom model in Moosend

Moosend comes with lots of predefined email templates, but they are designed for static content management: you can edit Email Body in Sitecore Send with no option to use dynamic placeholders together with custom model by default.

Email template can be created and managed in the following areas of Moosend:

  1. Regular email campaign. It's a great tool to work on your email campaigns, the only downside - you can't manage the template as soon, as campaign is send. It's implemented to be used as one-time campaign, which is not very useful for transactional emails - you lose ability to manage campaign with first email sent.
  2. Automation plan campaigns - if the action 'Then send email' is added, then the new email campaign is created internally. That's a good way to use & manage it during any time, but not the best from 'role' perspective: automation plan was not fully designed for it. But it's still the best currently available option.
Campaigns API

Campaigns API documentation

To get all campaigns the following request can be used:

GET /v3/campaigns.json?apikey={ApiKey}

Example response:

    "Code": 0,
    "Error": null,
    "Context": {
        "Paging": {
            "PageSize": 10,
            "CurrentPage": 1,
            "TotalResults": 32,
            "TotalPageCount": 4,
            "SortExpression": null,
            "SortIsAscending": true
        "Campaigns": [
                "ID": "{CampaignId}",
                "Name": "Automation Campaign (02-02-2023 18:16:31) (Automation)",
                "Subject": "Your Order Confirmation #{{Order.Id}}",
                "SiteName": null,
                "ConfirmationTo": null,
                "CreatedOn": "/Date(1675358214843+0000)/",
                "ABHoursToTest": null,
                "ABCampaignType": null,
                "ABWinner": null,
                "ABWinnerSelectionType": null,
                "Status": 3,
                "DeliveredOn": "/Date(1675361225907+0000)/",
                "ScheduledFor": null,
                "ScheduledForTimezone": null,
                "MailingLists": [],
                "TotalSent": 2,
                "TotalOpens": 3,
                "UniqueOpens": 1,
                "TotalBounces": 0,
                "TotalForwards": 0,
                "UniqueForwards": 0,
                "TotalLinkClicks": 0,
                "UniqueLinkClicks": 0,
                "RecipientsCount": 2,
                "IsTransactional": false,
                "TotalComplaints": 0,
                "TotalUnsubscribes": 0,
                "CampaignSource": null

Note. To obtain Api Key use this guide.

To get information about any specific campaign by id the following API endpoint should be used:

GET /v3/campaigns/{CampaignId}/view.json?apikey={ApiKey}


    "Code": 0,
    "Error": null,
    "Context": {
        "ID": "{CampaignId}",
        "Name": "Automation Campaign (02-02-2023 18:16:31) (Automation)",
        "Subject": "Your Order Confirmation #{{Order.Id}}",
        "WebLocation": null,
        "HTMLContent": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"\">\n<html xmlns=\"\"><head> ...",
        "PlainContent": " Order #{{Order.Id}}Dear {{Order.FromUser.FirstName}} {{Order.FromUser.LastName}}, thanks for the order! ...",
        "Sender": {
        "DeliveredOn": "/Date(1675361225907+0000)/",
        "ReplyToEmail": {
        "CreatedOn": "/Date(1675358214843+0000)/",
        "UpdatedOn": "/Date(1675358366913+0000)/",
        "ScheduledFor": null,
        "Timezone": null,
        "FormatType": 0,
        "ABCampaignData": null,
        "MailingLists": [],
        "ConfirmationTo": null,
        "Status": 3,
        "IsTransactional": false

Campaign Template with Placeholders in Sitecore Send

So in Sitecore Send we can configure the template for the campaign like this:

Template with Placeholders in Sitecore Send

You can download this template here.

Tip. You can create custom blocks for reuse in other templates. See documentation.

Mustache + Campaign = Dynamic Transactional Email

As via API we can obtain HTMLContent of the campaign, we can use it as a template for our final email, which will be sent via Sitecore Sent SMTP.

In my example I use Mustache logic-less templates. To integrate it with .NET I used Nustache, but any other library, e.g. Stubble can be used as well.

How it all works together:

Transaction email scheme with Moosend

And via .NET Core:

// from Moosend.Wrappers.CSharpWrapper
private readonly ICampaignsApi _campaignsApi;
// service to send email via SMTP
private readonly IEmailService _emailService;
// read settings from appsettings.json
private readonly IOptions<MoosendOptions> _options;

public async Task<IActionResult> SendFromCampaign([FromBody] OrderModel request)
    var config = _options.Value;
    // get the campaign via API
    var campaign = await _campaignsApi.GettingCampaignDetailsAsync(
    // replace placeholders via Nustache for subject
    var subject = Render.StringToString(campaign.Context.Subject, request);
    // replace placeholders via Nustache for body
    var body = Render.StringToString(campaign.Context.HTMLContent, request);
    // send the email!
    await _emailService.Send(request.Order.FromUser.Email, subject, body, config.Campaigns.OrderConfirmation);
    return new ObjectResult(new
        Success = true,

Full sources are available on Github.


To track campaign analytics in the custom campaign (not the default 'Transactional Email Campaign') the header campaign_guid needs to be added:


public async Task Send(string to, string subject, string html, string campaignId = null)
    var from = _configuration.GetValue<string>("Smtp:From");
    var message = new MailMessage(from, to, subject, html)
        IsBodyHtml = true,
    if (!string.IsNullOrEmpty(campaignId))
        message.Headers.Add("campaign_guid", campaignId);   // <-- here is the trick
    _logger.LogInformation("Send Email '{subject}' to {to} (from '{from}')", subject, to, from);
    using var smtp = CreateClient();
    await smtp.SendMailAsync(message);

Check the Reports dashboards in Moosend:

Custom Moosend transactional campaign analytics

All sources are available on my Git Repo.

Sitecore Send Series