<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>MailerSend Archives - Codersee blog- Kotlin on the backend</title>
	<atom:link href="https://blog.codersee.com/tag/mailersend/feed/" rel="self" type="application/rss+xml" />
	<link></link>
	<description>Kotlin &#38; Backend Tutorials - Learn Through Practice.</description>
	<lastBuildDate>Wed, 16 Apr 2025 04:49:52 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	

<image>
	<url>https://blog.codersee.com/wp-content/uploads/2025/04/cropped-codersee_logo_circle_2-32x32.png</url>
	<title>MailerSend Archives - Codersee blog- Kotlin on the backend</title>
	<link></link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Sending Transactional Emails Using Ktor, Kotlin, and MailerSend</title>
		<link>https://blog.codersee.com/sending-transactional-emails-ktor-kotlin-mailersend/</link>
					<comments>https://blog.codersee.com/sending-transactional-emails-ktor-kotlin-mailersend/#comments</comments>
		
		<dc:creator><![CDATA[Piotr]]></dc:creator>
		<pubDate>Thu, 02 Feb 2023 10:58:21 +0000</pubDate>
				<category><![CDATA[Ktor]]></category>
		<category><![CDATA[Email]]></category>
		<category><![CDATA[Kotlin]]></category>
		<category><![CDATA[MailerSend]]></category>
		<guid isPermaLink="false">https://codersee.com/?p=5504278</guid>

					<description><![CDATA[<p>In this, hands-on tutorial I will show you how to send transactional emails using Ktor, Kotlin, and MailerSend.</p>
<p>The post <a href="https://blog.codersee.com/sending-transactional-emails-ktor-kotlin-mailersend/">Sending Transactional Emails Using Ktor, Kotlin, and MailerSend</a> appeared first on <a href="https://blog.codersee.com">Codersee blog- Kotlin on the backend</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Hello and welcome to my next, hands-on tutorial 🙂 This time, I would like to show you <strong>how to send transactional emails using Ktor, Kotlin, and MailerSend</strong>.</p>
<p>Sooner or later, every developer will have to implement email-sending functionality in their application and it&#8217;s worth checking out various solutions, to pick the one that will best meet our needs.</p>
<p>And although the term &#8220;transactional emails&#8221; sounds complicated, in this tutorial I will prove how easily we can use them with Ktor and Kotlin.</p>
<h2>Video Tutorial</h2>
<p>If you prefer <strong>video content</strong>, then check out my video:</p>
<div style="text-align: center; width: 80%; margin-left: 10%;">
<a href="https://blog.codersee.com/sending-transactional-emails-ktor-kotlin-mailersend/"><img decoding="async" src="https://blog.codersee.com/wp-content/plugins/wp-youtube-lyte/lyteCache.php?origThumbUrl=%2F%2Fi.ytimg.com%2Fvi%2F29xM0SpzEEg%2Fhqdefault.jpg" alt="YouTube Video"></a><br /><br /></p>
</div>
<p>&nbsp;</p>
<p>If you find this content useful,<strong> please leave a subscription </strong> 😉</p>
<h2>Prerequisites</h2>
<p>Before we start, let&#8217;s see what exactly we will need for this tutorial.</p>
<p>First of all, we will need a <a href="https://www.mailersend.com?ref=fmebcn1eb2vc" target="_blank" rel="noopener">MailerSend account</a>. The free version will be fair enough for the purpose of learning and even simple projects.</p>
<blockquote><p>Note: the above link is a referral and if you will purchase any paid plan, I&#8217;ll get a small commission for that. To be clear: this fact affects the merits of this article in no way 🙂</p></blockquote>
<p>Additionally, <strong>you will need to have a domain, which you can verify</strong>. This is necessary, in order to work with any transactional email providers.</p>
<h2>Configure Domain in MailerSend</h2>
<p>I won&#8217;t go into much detail in this paragraph, because DNS settings will be dependent on your domain registrar. Nevertheless, let&#8217;s take look at how to add a new domain to MailerSend.</p>
<p>So, assuming we created our account and verified the email along with their policies, we should see the following:</p>
<p>&nbsp;</p>
<p><img fetchpriority="high" decoding="async" class="size-full wp-image-5504282 aligncenter" src="http://blog.codersee.com/wp-content/uploads/2023/01/mailersend_1.png" alt="Screenshot presents MailerSend dashboard." width="1427" height="738" srcset="https://blog.codersee.com/wp-content/uploads/2023/01/mailersend_1.png 1427w, https://blog.codersee.com/wp-content/uploads/2023/01/mailersend_1-300x155.png 300w, https://blog.codersee.com/wp-content/uploads/2023/01/mailersend_1-1024x530.png 1024w, https://blog.codersee.com/wp-content/uploads/2023/01/mailersend_1-768x397.png 768w" sizes="(max-width: 1427px) 100vw, 1427px" /></p>
<p>&nbsp;</p>
<p>As the next step, let&#8217;s click the <strong>Start</strong> button next to the <em>&#8220;Add a sending domain&#8221;</em>:</p>
<p>&nbsp;</p>
<p><img decoding="async" class="alignnone size-full wp-image-5504283" src="http://blog.codersee.com/wp-content/uploads/2023/01/mailersend_2.png" alt="Screenshot presents a modal, which allows user to enter a domain name, which we would like to register for transactional emails sending." width="1291" height="618" srcset="https://blog.codersee.com/wp-content/uploads/2023/01/mailersend_2.png 1291w, https://blog.codersee.com/wp-content/uploads/2023/01/mailersend_2-300x144.png 300w, https://blog.codersee.com/wp-content/uploads/2023/01/mailersend_2-1024x490.png 1024w, https://blog.codersee.com/wp-content/uploads/2023/01/mailersend_2-768x368.png 768w" sizes="(max-width: 1291px) 100vw, 1291px" /></p>
<p>&nbsp;</p>
<p>Right here, let&#8217;s specify the domain name- <em>test.codersee.com</em> in my case- and confirm with the <strong>Add domain</strong> button.</p>
<p>Nextly, we should see the Domain verification modal:</p>
<p>&nbsp;</p>
<p><img decoding="async" class="size-full wp-image-5504284 aligncenter" src="http://blog.codersee.com/wp-content/uploads/2023/01/mailersend_3.png" alt="Screenshot presents domain verification modal with SPF, DKIM and RETURN-PATH settings to copy" width="891" height="1095" srcset="https://blog.codersee.com/wp-content/uploads/2023/01/mailersend_3.png 891w, https://blog.codersee.com/wp-content/uploads/2023/01/mailersend_3-244x300.png 244w, https://blog.codersee.com/wp-content/uploads/2023/01/mailersend_3-833x1024.png 833w, https://blog.codersee.com/wp-content/uploads/2023/01/mailersend_3-768x944.png 768w" sizes="(max-width: 891px) 100vw, 891px" /></p>
<p>&nbsp;</p>
<p>As we can see, in order to verify our domain, we need to navigate to our domain registrar and modify the DNS settings using the values specified in the modal. When it&#8217;s done, let&#8217;s click the <strong>Check/Re-check</strong> now button.</p>
<blockquote><p>Note: DNS propagation can take some time, so don&#8217;t worry if you see the error messages (just like above). In such a case, give it some time and please get back to this page. MailerSend will send you an e-mail when everything is verified, as well.</p></blockquote>
<p>If everything succeeded, we should see the following page:</p>
<p>&nbsp;</p>
<p><img loading="lazy" decoding="async" class="size-full wp-image-5504285 aligncenter" src="http://blog.codersee.com/wp-content/uploads/2023/01/mailersend_4.png" alt="" width="1410" height="706" srcset="https://blog.codersee.com/wp-content/uploads/2023/01/mailersend_4.png 1410w, https://blog.codersee.com/wp-content/uploads/2023/01/mailersend_4-300x150.png 300w, https://blog.codersee.com/wp-content/uploads/2023/01/mailersend_4-1024x513.png 1024w, https://blog.codersee.com/wp-content/uploads/2023/01/mailersend_4-768x385.png 768w" sizes="auto, (max-width: 1410px) 100vw, 1410px" /></p>
<p>&nbsp;</p>
<p>One <strong>important note</strong> before we head to the next part. At this point, we <strong>will be able to send emails only to addresses and domains we previously verified</strong>&#8211; the e-mail address we use for registration and all addresses in a verified domain in our case.</p>
<p>Nevertheless, <strong>if you would like to send emails outside the domain, you will need to get approval</strong>.</p>
<h2>Generate API Key</h2>
<p>In order to send transactional emails with MailerSend in our Ktor app, we will need to generate a new <strong>API key</strong>.</p>
<p>To do so, let&#8217;s navigate to our domains (Email -&gt; Domains in the left sidebar) and click the manage button:</p>
<p>&nbsp;</p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-5504288 size-full" src="http://blog.codersee.com/wp-content/uploads/2023/01/mailersend_5.png" alt="Screenshot presents the test.codersee.com domain details page." width="1397" height="766" srcset="https://blog.codersee.com/wp-content/uploads/2023/01/mailersend_5.png 1397w, https://blog.codersee.com/wp-content/uploads/2023/01/mailersend_5-300x164.png 300w, https://blog.codersee.com/wp-content/uploads/2023/01/mailersend_5-1024x561.png 1024w, https://blog.codersee.com/wp-content/uploads/2023/01/mailersend_5-768x421.png 768w" sizes="auto, (max-width: 1397px) 100vw, 1397px" /></p>
<p>&nbsp;</p>
<p>On this page, we can manage API tokens and configure plenty of other things, like webhooks or tracking.</p>
<p>Anyway, let&#8217;s click the Generate new token button:</p>
<p>&nbsp;</p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-5504289 size-full" src="http://blog.codersee.com/wp-content/uploads/2023/01/mailersend_6.png" alt="Screenshot presents a MailerSend modal, where we can set a name for the API token and permission level for it." width="618" height="581" srcset="https://blog.codersee.com/wp-content/uploads/2023/01/mailersend_6.png 618w, https://blog.codersee.com/wp-content/uploads/2023/01/mailersend_6-300x282.png 300w" sizes="auto, (max-width: 618px) 100vw, 618px" /></p>
<p>&nbsp;</p>
<p>As we can see, right here we can specify the name and access granted to the new token. And although for simplicity we will stick with &#8220;Full access&#8221;, in real-life scenarios we should always follow the principle of least privilege.</p>
<p>Finally, let&#8217;s click the Create token:</p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-5504290 size-full" src="http://blog.codersee.com/wp-content/uploads/2023/01/mailersend_7.png" alt="Screenshot shows a modal with a censored API token value " width="637" height="537" srcset="https://blog.codersee.com/wp-content/uploads/2023/01/mailersend_7.png 637w, https://blog.codersee.com/wp-content/uploads/2023/01/mailersend_7-300x253.png 300w" sizes="auto, (max-width: 637px) 100vw, 637px" /></p>
<p>&nbsp;</p>
<p>As can be seen, the API token was created successfully and it&#8217;s time to save the value.</p>
<blockquote><p>Note: please persist this value securely and never share it with anyone!</p></blockquote>
<h2>Generate Ktor Project</h2>
<p>With all of that being done in MailerSend, we can finally switch to the Ktor &amp; Kotlin part of our tutorial about transactional emails.</p>
<p>As always, let&#8217;s navigate to <a href="https://start.ktor.io/" target="_blank" rel="noopener">https://start.ktor.io/</a> and specify desired project settings:</p>
<p>&nbsp;</p>
<p><img loading="lazy" decoding="async" class="size-full wp-image-5504291 aligncenter" src="http://blog.codersee.com/wp-content/uploads/2023/01/mailersend_8.png" alt="The image shows Ktor Project Generator page, which we use to generate app skeleton, which then will be used to implement transactional emails with MailerSend." width="702" height="852" srcset="https://blog.codersee.com/wp-content/uploads/2023/01/mailersend_8.png 702w, https://blog.codersee.com/wp-content/uploads/2023/01/mailersend_8-247x300.png 247w" sizes="auto, (max-width: 702px) 100vw, 702px" /></p>
<p>Following, let&#8217;s click the Add plugins (we don&#8217;t need anything else), generate the project, and import it to our IDE.</p>
<p>Nextly, let&#8217;s add the <strong>Ktor client</strong> and <strong>Jackson</strong> dependencies inside the build.gradle.kts:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="raw">implementation("io.ktor:ktor-client-core-jvm:$ktor_version")
implementation("io.ktor:ktor-client-okhttp:$ktor_version")

implementation("io.ktor:ktor-client-content-negotiation:$ktor_version")
implementation("io.ktor:ktor-serialization-jackson:$ktor_version")
</pre>
<p>Of course, Jackson is not the only option here, and GSON, or kotlinx.serialization are well supported in Ktor, as well.</p>
<p>&nbsp;</p>
<p><a href="https://codersee.com/newsletter/"><img loading="lazy" decoding="async" class="aligncenter wp-image-3002956 size-large" src="http://blog.codersee.com/wp-content/uploads/2022/05/join_newsletter-1024x576.png" alt="Image shows two ebooks people can get for free after joining newsletter" width="800" height="419" /></a></p>
<h2>Configure Client</h2>
<p>As the next step, let&#8217;s navigate to the<em> application.conf</em> file and add a custom MailerSend config:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="raw">mailersend {
  token = ${MAILERSEND_TOKEN}
  baseUrl = ${MAILERSEND_BASE_URL}
}
</pre>
<p>This way, we can pass the token and URL values securely through environment variables. And although the token will be unique, for the baseUrl, let&#8217;s set <strong>https://api.mailersend.com/v1/</strong>.</p>
<p>Nextly, let&#8217;s create the <strong>MailerSendClient.kt</strong> file and put the following:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">fun Application.configureClient() {

  val token = environment.config.property("mailersend.token").getString()
  val baseUrl = environment.config.property("mailersend.baseUrl").getString()

  val mailerSendClient = HttpClient {
    install(ContentNegotiation) {
      jackson()
    }
  }

  runBlocking {
    // To be done in the next paragraphs
  }
}
</pre>
<p>As we can see, the above function reads configuration properties and installs the <span style="white-space: pre-wrap;"><em>ContentNegotiation</em> plugin using <em>Jackson</em>. Moreover, we&#8217;ve prepared the <em>runBlocking</em> block, where we will invoke our future functions. </span></p>
<p>Lastly, let&#8217;s navigate to the <em>Application.kt</em> file and make use of our config:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">fun Application.module() {
  configureClient()
}
</pre>
<p>At this point, we can run our application and verify that it is working, as expected. If we forget to set environment variables correctly, we should see something like this:</p>
<blockquote><p>Exception in thread &#8220;main&#8221; com.typesafe.config.ConfigException $ UnresolvedSubstitution: application.conf @ [&#8230;] Could not resolve substitution to a value: ${MAILERSEND_BASE_URL}</p></blockquote>
<h2>Send Simple Email</h2>
<p>With all of that done, we can finally start sending transactional emails. In this paragraph, we will learn how to send a simple email with MailerSend.</p>
<h3>Implement EmailRequest</h3>
<p>As the first step, let&#8217;s create a new data class called <strong>EmailRequest</strong>:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">@JsonInclude(JsonInclude.Include.NON_NULL)
data class EmailRequest(
    val from: Recipient,
    val to: List&lt;Recipient&gt;,
    val subject: String,
    val text: String,
    val html: String,
    val variables: List&lt;Variable&gt;? = null
) {
    data class Recipient(
        val email: String,
        val name: String
    )

    data class Variable(
        val email: String,
        val substitutions: List&lt;Substitution&gt;
    ) {
        data class Substitution(
            @JsonProperty("var") val variable: String,
            val value: String
        )
    }
}
</pre>
<p>Basically, objects of this class will be serialized into JSONs when querying MailerSend API.</p>
<p>The <strong>@JsonInclude</strong> annotation states that if the value is set to null, then it should not be added to the JSON payload (so we won&#8217;t send <code class="EnlighterJSRAW" data-enlighter-language="raw">"whatever": null</code>) . Additionally, MailerSend requires us to send the &#8220;var&#8221; field inside the substitution. Nevertheless, <strong>var</strong> is a reserved keyword in Kotlin, so we have to make use of the <strong>@JsonProperty</strong>.</p>
<h3>Use Ktor Client to Perform Requests</h3>
<p>Following, let&#8217;s add a new function called <em>sendSingleEmail</em>:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">suspend fun sendSingleEmail(
  mailerSendClient: HttpClient,
  url: String,
  token: String,
  emailRequest: EmailRequest
): HttpResponse =
    mailerSendClient.post(url) {
      headers {
        append(HttpHeaders.Authorization, "Bearer $token")
      }
      contentType(ContentType.Application.Json)
      setBody(emailRequest)
    }
</pre>
<p>As we can see, this one is responsible for <strong>sending POST requests to the passed URL </strong>with the <strong>Authorization header</strong> containing the <strong>Bearer token</strong>.</p>
<p>Moreover, the function takes the EmailRequest as an argument, which makes it reusable.</p>
<h3>Send Transactional Client with Ktor Client</h3>
<p>Lastly, let&#8217;s implement the <em>sendSimpleMessage</em>:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">suspend fun sendSimpleMessage(
    mailerSendClient: HttpClient,
    baseUrl: String,
    token: String
) {
    val subject = "Hello from {\$company}!"
    val html = """
        Hello &lt;b&gt;{${'$'}name}&lt;/b&gt;, nice to meet you!
    """.trimIndent()
    val text = """
        Hello {${'$'}name}, nice to meet you!
    """.trimIndent()

    val emailRequest = EmailRequest(
        from = EmailRequest.Recipient(
            email = "example@test.codersee.com",
            name = "Codersee"
        ),
        to = listOf(
            EmailRequest.Recipient(
                email = "example@test.codersee.com",
                name = "Pjoter"
            )
        ),
        subject = subject,
        html = html,
        text = text,
        variables = listOf(
            EmailRequest.Variable(
                email = "example@test.codersee.com",
                substitutions = listOf(
                    EmailRequest.Variable.Substitution(
                        variable = "company",
                        value = "Codersee"
                    ),
                    EmailRequest.Variable.Substitution(
                        variable = "name",
                        value = "Piotr"
                    )
                )
            )
        )
    )

    val response = sendSingleEmail(
        mailerSendClient = mailerSendClient,
        url = "$baseUrl/email",
        token = token,
        emailRequest = emailRequest
    )

    if (response.status == HttpStatusCode.Accepted)
        println("Email sent successfully.")
    else
        println("Error when sending email.")
}
</pre>
<p>And although the above code snippet consists of a lot of lines, it&#8217;s definitely easier than it looks.</p>
<p>Firstly, we prepare the EmailRequest instance, which after serialization, will become this JSON:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">{
    "from": {
        "email": "example@test.codersee.com",
        "name": "Codersee"
    },
    "to": [
        {
            "email": "example@test.codersee.com",
            "name": "Pjoter"
        }
    ],
    "subject": "Hello from {$company}!",
    "html": "Hello &lt;b&gt;{$name}&lt;/b&gt;, nice to meet you!"
    "variables": [
        {
            "email": "example@test.codersee.com",
            "substitutions": [
                {
                    "var": "company",
                    "value": "Codersee"
                },
                {
                    "var": "name",
                    "value": "Piotr"
                }
            ]
        }
    ]
}
</pre>
<p>Whereas most of the fields a pretty descriptive, let&#8217;s take a look at one, important thing- <strong>variables</strong>.</p>
<p>As we can see, we can inject variables into the email subject, or content by simply specifying them with a dollar sign inside curly braces, for example: <em>{$someVariable}</em>. This way, we can specify dynamic values for each recipient, like name and company in our example.</p>
<p>In the next lines, we simply send the request to the MailerSend API, and if the response status code is <strong>202 Accepted</strong>, then the email was sent successfully:</p>
<p>&nbsp;</p>
<p><img loading="lazy" decoding="async" class="size-full wp-image-5504295 aligncenter" src="http://blog.codersee.com/wp-content/uploads/2023/02/mailersend_9.png" alt="Screenshot shows an example email send with MailerSend API using Ktor client." width="446" height="260" srcset="https://blog.codersee.com/wp-content/uploads/2023/02/mailersend_9.png 446w, https://blog.codersee.com/wp-content/uploads/2023/02/mailersend_9-300x175.png 300w" sizes="auto, (max-width: 446px) 100vw, 446px" /></p>
<p>&nbsp;</p>
<p>Of course, in order to make it work, let&#8217;s invoke our function inside the <em>configureClient</em>:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">runBlocking {
  sendSimpleMessage(mailerSendClient, baseUrl, token)
}
</pre>
<h2>Send Emails With Templates</h2>
<p>At this point, we already know how to send transactional emails with Ktor and MailerSend. Moreover, we&#8217;ve seen how to send HTML content, which is a great way to customize the template.</p>
<p>Nevertheless, if you&#8217;d like to learn how to <strong>make your life a bit easier</strong> and use a <strong>graphic builder for templates</strong>, then you&#8217;re gonna like this paragraph.</p>
<h3>Prepare Template Using MailerSend Builder</h3>
<p>As the first step, let&#8217;s navigate to the templates page: <a href="https://app.mailersend.com/templates" target="_blank" rel="noopener">https://app.mailersend.com/templates</a>.</p>
<p>Nextly, let&#8217;s click <strong>Create template</strong> button and select <strong>Drag &amp; drop</strong> editor option:</p>
<p>&nbsp;</p>
<p><img loading="lazy" decoding="async" class="size-full wp-image-5504296 aligncenter" src="http://blog.codersee.com/wp-content/uploads/2023/02/mailersend_10.png" alt="Screenshot presents 3 options we can select when creating a new template: drag &amp; drop editor, rich-text editor and HTML editor." width="980" height="687" srcset="https://blog.codersee.com/wp-content/uploads/2023/02/mailersend_10.png 980w, https://blog.codersee.com/wp-content/uploads/2023/02/mailersend_10-300x210.png 300w, https://blog.codersee.com/wp-content/uploads/2023/02/mailersend_10-768x538.png 768w" sizes="auto, (max-width: 980px) 100vw, 980px" /></p>
<p>&nbsp;</p>
<p>This way, we&#8217;re redirected to the <strong>Template gallery</strong>, which contains ~50 predefined templates, which we can adjust to our needs. In this tutorial, we will select the <strong>Reset password</strong>:</p>
<p>&nbsp;</p>
<p><img loading="lazy" decoding="async" class="size-full wp-image-5504297 aligncenter" src="http://blog.codersee.com/wp-content/uploads/2023/02/mailersend_11.png" alt="Image presents MailerSend template gallery with highlited Reset passoword template." width="1250" height="763" srcset="https://blog.codersee.com/wp-content/uploads/2023/02/mailersend_11.png 1250w, https://blog.codersee.com/wp-content/uploads/2023/02/mailersend_11-300x183.png 300w, https://blog.codersee.com/wp-content/uploads/2023/02/mailersend_11-1024x625.png 1024w, https://blog.codersee.com/wp-content/uploads/2023/02/mailersend_11-768x469.png 768w" sizes="auto, (max-width: 1250px) 100vw, 1250px" /></p>
<p>&nbsp;</p>
<p>After we click <strong>Choose</strong>, we&#8217;re redirected to the builder page, where we can make adjustments:</p>
<p>&nbsp;</p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-5504298" src="http://blog.codersee.com/wp-content/uploads/2023/02/mailersend_12.png" alt="Screenshot shows MailerSend template builder and edited template." width="500" height="563" srcset="https://blog.codersee.com/wp-content/uploads/2023/02/mailersend_12.png 662w, https://blog.codersee.com/wp-content/uploads/2023/02/mailersend_12-266x300.png 266w" sizes="auto, (max-width: 500px) 100vw, 500px" /></p>
<p>&nbsp;</p>
<p>As we can see, when using the builder, we need to encapsulate variables with <strong>double curly brackets</strong>.</p>
<p>When we finish, let&#8217;s click the <strong>Save &amp; Publish</strong> button and <strong>write down the Template ID</strong> on the next page- we will need it later.</p>
<h3>Adjust EmailRequest Class</h3>
<p>With that being done, let&#8217;s get back to the Ktor project and edit the EmailRequest class:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">@JsonInclude(JsonInclude.Include.NON_NULL)
data class EmailRequest(
    val from: Recipient,
    val to: List&lt;Recipient&gt;,
    val subject: String,
    @JsonProperty("template_id") val templateId: String,
    val personalization: List&lt;CustomPersonalization&gt;
) {
    data class Recipient(
        val email: String,
        val name: String
    )

    data class CustomPersonalization(
        val email: String,
        val data: PersonalizationData
    ) {
        data class PersonalizationData(
            val name: String,
            @JsonProperty("my_super_generated_code") val code: String
        )
    }
}
</pre>
<p>This time, instead of specifying the content manually, we simply pass the <strong>template identifier</strong> and a <strong>list of personalizations</strong>.</p>
<p>In our case, the desired JSON looks, as follows:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="json">{
  "from": {
    "email": "example@test.codersee.com",
    "name": "Codersee"
  },
  "to": [
    {
      "email": "example@test.codersee.com",
      "name": "Pjoter"
    }
  ],
  "subject": "Please Reset Your Password",
  "templateId": "v69oxl587pzl785k",
  "personalization": [
    {
       "email": "example@test.codersee.com",
       "data": {
         "name": "Pjoter",
         "code": "f7fcc37c-4b7a-4919-aa91-a5ac7edd55d7"
       }
     }
  ]
}

</pre>
<h3>Implement Sending Functionality</h3>
<p>With all of that prepared, let&#8217;s implement the <em>sendMessageUsingTemplate</em>:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">suspend fun sendMessageUsingTemplate(
    mailerSendClient: HttpClient,
    baseUrl: String,
    token: String
) {
    val subject = "Please Reset Your Password"

    val emailRequest = EmailRequest(
        from = EmailRequest.Recipient(
            email = "example@test.codersee.com",
            name = "Codersee"
        ),
        to = listOf(
            EmailRequest.Recipient(
                email = "example@test.codersee.com",
                name = "Pjoter"
            )
        ),
        subject = subject,
        templateId = "v69oxl587pzl785k",
        personalization = listOf(
            EmailRequest.CustomPersonalization(
                email = "example@test.codersee.com",
                data = EmailRequest.CustomPersonalization.PersonalizationData(
                    name = "Pjoter",
                    code = UUID.randomUUID().toString()
                )
            )
        )
    )

    val response = sendSingleEmail(
        mailerSendClient = mailerSendClient,
        url = "$baseUrl/email",
        token = token,
        emailRequest = emailRequest
    )

    if (response.status == HttpStatusCode.Accepted)
        println("Email sent successfully.")
    else
        println("Error when sending email.")
</pre>
<p>As we can see, the logic itself remains almost exactly the same and the only thing, which changes, is the payload we send.</p>
<p>Lastly, let&#8217;s put the following in the <em>configureClient </em>and rerun the application:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">runBlocking {
  sendSimpleMessage(mailerSendClient, baseUrl, token)
}</pre>
<p>As a result, we should see the message &#8220;Email sent successfully.&#8221; and the following email in our inbox:</p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-5504301" src="http://blog.codersee.com/wp-content/uploads/2023/02/mailersend_12-1.png" alt="Image shows an example email from inbox." width="550" height="556" srcset="https://blog.codersee.com/wp-content/uploads/2023/02/mailersend_12-1.png 786w, https://blog.codersee.com/wp-content/uploads/2023/02/mailersend_12-1-297x300.png 297w, https://blog.codersee.com/wp-content/uploads/2023/02/mailersend_12-1-768x777.png 768w" sizes="auto, (max-width: 550px) 100vw, 550px" /></p>
<h2>Handling ErrorsEmails With Templates</h2>
<p>I simply couldn&#8217;t finish this article about sending transactional emails with Ktor and MailerSend without showing <strong>how to handle errors</strong>.</p>
<p>And although we try our best to avoid them, sooner or later they will pop up. Thankfully, MailerSend API informs us in a pretty neat way about any issues. For example:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="json">{
    "message": "The template_id must be an integer. (and 2 more errors)",
    "errors": {
        "template_id": [
            "The template_id must be an integer."
        ],
        "to.0.email": [
            "The to.0.email field is required."
        ],
        "variables.0.email": [
            "The variables.0.email field does not exist in to.*.email."
        ]
    }
}
</pre>
<h3>Create ErrorResponse</h3>
<p>As the first step, let&#8217;s implement the ErrorResponse class:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">data class ErrorResponse(
  val message: String,
  val errors: Map&lt;String, List&lt;String&gt;&gt;
)
</pre>
<p>We will use this simple data class to deserialize JSON response into the object.</p>
<h3>Implement handleError</h3>
<p>Nextly, let&#8217;s add the <em>handleError</em> function:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">suspend fun handleError(response: HttpResponse) {
    val statusCode = response.status.value
    val errorBody =  response.body&lt;ErrorResponse&gt;()
    println("Email sending failed with status code $statusCode and message '${errorBody.message}'. Errors:")

    errorBody.errors.forEach { (fieldName, fieldErrors) -&gt;
        println("  * field name: [$fieldName]. Messages:")
        fieldErrors.forEach { error -&gt; println("    - $error") }
    }
}

</pre>
<p>As we can see, this function takes the HttpResponse from MailerSend API and parses the JSON into the ErrorResponse instance. Thanks to that we can iterate and display all the errors.</p>
<p>Of course, as the last step, we need to modify our conditional statement, to make use of this snippet:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">if (response.status == HttpStatusCode.Accepted)
  println("Email sent successfully.")
else 
  println("Error when sending email.")
</pre>
<h2>Sending Emails With Ktor And MailerSend Summary</h2>
<p>And that&#8217;s all for this article about <strong>how to send transactional emails using Ktor, Kotlin, and MailerSend</strong>.</p>
<p>As always, you can find the source code in this <a href="https://github.com/codersee-blog/kotlin-ktor-mailersend-transactional-emails" target="_blank" rel="noopener">GitHub repository.</a></p>
<p>Happy to hear your <strong>thoughts, feedback, or ideas in the comments</strong> 🙂</p>
<p>The post <a href="https://blog.codersee.com/sending-transactional-emails-ktor-kotlin-mailersend/">Sending Transactional Emails Using Ktor, Kotlin, and MailerSend</a> appeared first on <a href="https://blog.codersee.com">Codersee blog- Kotlin on the backend</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.codersee.com/sending-transactional-emails-ktor-kotlin-mailersend/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
	</channel>
</rss>

<!--
Performance optimized by W3 Total Cache. Learn more: https://www.boldgrid.com/w3-total-cache/?utm_source=w3tc&utm_medium=footer_comment&utm_campaign=free_plugin

Page Caching using Disk: Enhanced 

Served from: blog.codersee.com @ 2026-06-06 15:11:10 by W3 Total Cache
-->