Sending Transactional Email with the Mailchimp API
Almost every production application needs to send transactional email, e.g. for password resets and notifications. This article will walk you through integrating a Next.js web app with the Mailchimp Transactional Email API.
Sam Magura
There are two main types of automated email: marketing email and transactional email. Most email services, including Mailchimp, offer separate APIs for these two types of email. Here's when you should use each type:
- Marketing email: This one is pretty self-explanatory. Use a marketing email API when you want to send an email blast to a large group of users. Most marketing email services will provide tools to help you market effectively, like the ability to target emails to certain cohorts of users. Marketing email services allow you to send tens of thousands of emails efficiently and cost-effectively.
- Transactional email: Use a transactional email API whenever you want to send an email to a single user. You'll use transactional email for things like email verification, password resets, and user-specific notifications like order confirmations. Transactional email services should generally not be used for sending emails in bulk, since they cost more per message than marketing email services and may have stricter rate limits.
In this article, we're going to build a simple Next.js web app that sends email via the Mailchimp Transactional Email API , with the Mailchimp API key stored in the Zero secrets manager. Our app will show a mock user sign-up form. When the user enters their email address and submits the form, the application's backend will use the Mailchimp Transactional API to send a "please verify your email" message to the user.
🔗 The full code for this example is available in the zerosecrets/examples GitHub repository.
Secure your secrets conveniently
Zero is a modern secrets manager built with usability at its core. Reliable and secure, it saves time and effort.
Setting up our Mailchimp Transactional Account
First, we'll sign up for Mailchimp and add a verified domain to send emails from. Then we'll build the Next.js app and integrate it with Zero and the Mailchimp API.
Prerequisites
To send a real email from your application, you'll need the following:
- ($7+) A domain name and the ability to add DNS records to it. You can buy a domain name from Google , Namecheap , GoDaddy , and many other services. For my project, I purchased
srmagura.de
from Amazon Route 53 for $9. - ($20) One block of transactional email from Mailchimp, which will allow you to send 25,000 emails. Mailchimp Transactional does have a free tier, but it only allows you to send emails to your own verified domains. I went ahead and paid the $20 since this seemed easier than setting up an email address on my newly-purchased domain.
Of course, if you would just like to write the code without seeing your email get delivered, you can skip both of these purchases.
Adding a Verified Sending Domain
You can sign up for a free Mailchimp account here . Once you're logged in to Mailchimp, navigate to https://mandrillapp.com/settings . It's a bit confusing — Mailchimp's transactional email service is provided by Mandrill, a company which Mailchimp recently purchased. As of this writing, the UI for configuring your transactional email account is still in the Mandrill app, which is completely separate from Mailchimp's main admin console.
Now that we're in the Mandrill settings dashboard, let's add our domain name as a verified sending domain by selecting "Domains" > "Sending domains" from the top navigation bar. Then enter your domain name in the text box and click "Add".
At this point, your domain will show up in the Sending Domains table, but you'll see a warning or error in each of the "Verified Domain", "DKIM Settings", and "SPF Settings" columns. You'll need to get a green checkmark in each of these columns before you can send emails. If you're curious, DKIM and SPF are email authentication technologies which show recipients that your email did in fact originate from your domain.
To verify the domain and enable SPF, log in to the service that you bought your domain name from, go to the DNS settings, and create a TXT record. The TXT record should contain two lines, with the first line copied from the "Verified Domain" > "View details" dialog, and the second line copied from the "View SPF settings" dialog. The content of your record will look similar to this:
mandrill_verify.ygz0T4KFtElZclGcPes3_A
v=spf1 include:spf.mandrillapp.com ?all
Wait around 30 seconds after adding the record and then click "Test DNS Settings". You should now see checkmarks under "Verified Domain" and "SPF Settings".
To enable DKIM, follow the instructions shown in the "View DKIM settings" dialog. This time, you'll create a TXT record at mandrill._domainkey.your-domain.com
(replace your-domain.com
with your actual domain name). Some DNS providers might require you to escape the semicolons in the TXT record's content with backslashes. After adding the record, click "Test DNS Settings" again and you should see a green check for DKIM.
For reference, here are the DNS records I added to my domain in the Amazon Route 53 console:
Creating a Mailchimp API Key
Once you have set up a verified sending domain, the rest of the integration with Mailchimp Transactional will be a breeze. The next step is to create an API key.
First, let's log in to Zero and create a new project to house the Mailchimp API key. Upon creating the project, you'll be presented with a Zero token which you should save in a safe location on your PC. Back in the Zero web console, click the "Create new secret" button and select Mailchimp from the dropdown.
Now return to https://mandrillapp.com/settings , click the "Add API key" button, and copy-paste the API key into the "Create new secret" dialog in Zero.
Writing the Next.js Web App
With all of the Mailchimp setup behind us, it's time to create a simple Next.js 13 web application that uses the Mailchimp Transactional Email API to send a demo email. Our app will display a mock user signup form with an email address input. When the user clicks the "Sign up" button, the frontend will pass the email address to our backend API which will send a "Please verify your email address" email via the Mailchimp Transactional API.
To bootstrap a new Next.js project using TypeScript and the experimental app
directory, run:
npx create-next-app@latest mailchimp-webapp --ts --experimental-app
Building the Frontend
Now let's open app/page.tsx
and create a simple form that looks like this:
The React code for the form should look something like this:
const [email, setEmail] = useState('')
/* ... */
return (
<form onSubmit={onSubmit}>
<div className="form-group">
<label htmlFor="emailInput">Email address</label>
<input
id="emailInput"
name="email"
type="email"
required
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
</div>
<button type="submit">Sign up</button>
</form>
)
Remember to place the 'use client'
directive at the top of the file to tell Next.js that this is a Client Component, not a Server Component!
When the user clicks "Sign up", the form will invoke the onSubmit
function which calls our backend API with the email address that was entered by the user:
const [submitted, setSubmitted] = useState(false)
async function onSubmit(e: React.FormEvent<HTMLFormElement>): Promise<void> {
e.preventDefault()
setSubmitted(false)
try {
const response = await fetch('/api/signUp', {
method: 'POST',
body: JSON.stringify({email}),
headers: {
'Content-Type': 'application/json',
},
})
if (!response.ok) {
throw new Error(`Received an error HTTP status code: ${response.status}.`)
}
setSubmitted(true)
} catch (e) {
setSubmitted(false)
console.error(e)
}
}
Building the Backend
If you click the "Sign up" button right now, you'll get a 404 error back from the server because the /api/signUp
API method doesn't exist yet — let's add it now. We can make a new Next.js API route by creating the file pages/api/signUp.ts
:
import type {NextApiRequest, NextApiResponse} from 'next'
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method !== 'POST') {
throw new Error('Only POST is allowed.')
}
const {email} = req.body
if (!email) {
throw new Error('Failed to get the email address from the request.')
}
// TODO Use the Mailchimp API to send an email
res.status(200).send('')
}
Right now, our API method isn't very interesting. To send an email, we'll need get a Mailchimp API client from the @mailchimp/mailchimp_transactional library. You can install this library along with the Zero TypeScript SDK by running
npm install @mailchimp/mailchimp_transactional @types/mailchimp__mailchimp_transactional @zerosecrets/zero
Now we can write a function in the src/util
directory that fetches the Mailchimp API key from Zero and uses it to instantiate the Mailchimp API client:
import Mailchimp from '@mailchimp/mailchimp_transactional'
import {zero} from '@zerosecrets/zero'
let mailchimpClient: Mailchimp.ApiClient | undefined
export async function getMailchimpClient(): Promise<Mailchimp.ApiClient> {
// Reuse the same Mailchimp client if one has already been created, so that we
// don't call Zero on every request
if (mailchimpClient) {
return mailchimpClient
}
if (!process.env.ZERO_TOKEN) {
throw new Error('Did you forget to set the ZERO_TOKEN environment variable?')
}
const secrets = await zero({
token: process.env.ZERO_TOKEN,
pick: ['mailchimp'],
}).fetch()
if (!secrets.mailchimp) {
throw new Error('Did not receive an API key for Mailchimp.')
}
mailchimpClient = Mailchimp(secrets.mailchimp.transactional_api_key)
return mailchimpClient
}
If you've been following along with our other blog posts, this function should look very familiar!
Next, let's return to api/signUp.ts
and replace the // TODO
with the code for sending an email:
const mailchimpClient = await getMailchimpClient()
// You can use this to test that communication with Mailchimp is working:
// const response = await mailchimpClient.users.ping();
const response = await mailchimpClient.messages.send({
message: {
from_email: 'demo@YOUR-DOMAIN.com', // TODO Replace YOUR-DOMAIN.com with your verified sending domain
subject: '[Demo] Please verify your email address',
text: 'If this was a real app, there would be a link right here ;)',
to: [
{
email,
type: 'to',
},
],
},
})
console.log('Mailchimp responded with:')
console.log(response)
At this point, the code is complete. Let's run the app to see if it works. Remember to pass your Zero token in as an environment variable, like this:
ZERO_TOKEN=YOUR-ZERO-TOKEN npm run dev
Once the app launches, enter your real email address into the text input and click "Sign up". Then check the terminal where you ran npm run dev
to see the response that Mailchimp returned. If it worked, the response will look like this:
;[
{
email: 'YOUR-EMAIL@gmail.com',
status: 'sent',
_id: '6c18a64de59345e6919ec1c5c6a17718',
reject_reason: null,
queued_reason: null,
},
]
If you check your inbox, you should see the email! If it's not there even though Mailchimp returned a status of sent
, check you spam folder.
Troubleshooting
Here are some of the reject_reason
s Mailchimp may return, and how to fix them:
reject_reason: 'unsigned'
— you tried to send an email from a domain that has not been verified with Mailchimp. Refer to the "Adding a Verified Sending Domain" section of this article.reject_reason: 'recipient-domain-mismatch'
— you are using the free demo version of Mailchimp Transactional and the domain of the recipient email address did not match the domain of the sender email address. (The demo plan requires that the domain names match.) To send an email to a recipient on a different domain, upgrade your Mailchimp Transactional account to a paid plan by purchasing one block of transactional emails.
Next Steps
Congratulations on integrating your Next.js application with Mailchimp Transactional Email and Zero! At this point, you can apply the coding patterns shown in this article to send transactional email from your real application.
One thing we didn't account for in the demo project which you should handle in a production application is transient failures when attempting to send email. If the Mailchimp API has an outage or your server experiences a blip in the network, it's important to ensure your transactional emails are still delivered.
The most common way to build this resiliency into your application is, when you need to send an email (e.g. in response to an API call from the frontend), add the email to a queue instead of sending it straightaway. Then, you can write a background processor which listens for messages on the queue and sends the emails. If sending the email fails, the background processor can re-enqueue the message with a delay. This approach should eliminate (or at least greatly reduce) the number of transactional emails that get lost. See our earlier article on the Work Queue pattern for details.
Additional Resources
- Send Your First Transactional Email — Mailchimp Docs
Other articles
Prototyping an Ecommerce Application using Braintree and Mailchimp
This quick guide will show you how to integrate multiple 3rd party services into a single flow using Zero.
Use Pusher to Implement Real-Time Notifications
Pusher makes it easy to add pub-sub functionality to your web apps, allowing you to implement chat, notifications, and more.
Secure your secrets
Zero is a modern secrets manager built with usability at its core. Reliable and secure, it saves time and effort.