Track and Respond to Email Activity with Webhooks

At a glance

Webhooks allow your application to receive information about email events as they occur, and respond in a way that you define. You can configure and test webhooks using the Webhooks settings page in your account, or you can do so via the API

You could use webhooks to: 

  • Create your own custom reporting dashboard using webhook data

  • Keep your CRM in sync with events from Mailchimp Transactional Email 

  • Store data about your emails for longer than 30 days

For the purposes of this guide, we run a plant delivery website called Eiffel Flowers. In the course of our business, we send registered users a variety of transactional emails. 

In this guide, we’ll set up a webhook that will trigger when a particular link is clicked in one of our emails, which will allow us to then send a follow-up email advertising a relevant flower sale. We’ll walk through how to create the webhook, and then we’ll write code to handle the incoming webhook data and send a follow-up email in response.

What you’ll need

  • A Mailchimp Transactional Email account

  • A callback URL for your application that can accept HTTP POST requests

  • Your API key

Create a new webhook

First, let’s set up our webhook, which will be triggered when subscribers click a link in one of our plant emails. There are two ways to do this: using the Transactional Email app or through the API. In this guide, we’ll walk through setting it up using the Transactional Email app. 

To set up a webhook: 

  1. Navigate to the Webhooks page of your Transactional Email account

  2. Click Add a Webhook at the top of the page

  3. Choose the events you want to listen for in the “Trigger on Events” section. For this guide, we’ll select Message Is Clicked

  4. In the “Post To URL” field, input the callback URL your application will use to accept the incoming webhook

  5. If you’d like, give your webhook a description in the “Description” field

  6. Click Create Webhook

Note: Your webhook callback URL should be set up to accept POST requests at a minimum. When you provide the URL where you want Mailchimp to POST the event data, a HEAD request will be sent to the URL to check that it exists.

If the URL doesn't exist or returns something other than a 200 HTTP response to the HEAD request, Mailchimp will fall back and attempt a POST request. In this case, the POST request, the mandrill_events parameter will be an empty array, and the POST will be signed with a generic key (with the value test-webhook).

Handling the webhook response in your app

Now that we have the webhook set up, we need to handle the webhook data on the server that the webhook URL we provided points to. 

The body of the webhook request, parsed as JSON, will look something like this:

Response

JSON
[json]
{
  "mandrill_events": [
    {
      "event": "open",
      "msg": {
        "ts": 1365109999,
        "subject": "Roses Are Red, Violets Are On Sale",
        "email": "flowerfriend@example.com",
        "sender": "hello@eiffelflowers.biz",
        "tags": ["violets"],
        "opens": [
          {
            "ts": 1365111111
          }
        ],
        "clicks": [
          {
            "ts": 1365111111,
            "url": "https://www.eiffelflowers.biz/news/ultraviolet-sale"
          }
        ],
        "state": "sent",
        "metadata": {
          "user_id": 111
        },
        "_id": "7761629",
        "_version": "123"
        # trimmed for brevity
      }
    }
  ]
}

Now that we know what the webhook data will look like for our “Message Is Clicked” event, we can work on handling it on our server. 

That code might look like this:

Handling the webhook response in your app

const express = require("express");
const bodyParser = require("body-parser");

const app = express();

// notice that the body will be of type `x-www-form-urlencoded`,
// and needs to be parsed as such
app.use(
  bodyParser.urlencoded({
    extended: false
  })
);

const TARGET_LINK_URL = "https://www.eiffelflowers.biz/news/ultraviolet-sale";

const generateMessage = email => ({
  html:
    "<p>We noticed you were interested in violets! How about we offer you a great deal on a dozen if you buy in the next 72 hours?</p>",
  text:
    "We noticed you were interested in violets! How about we offer you a great deal on a dozen if you buy in the next 72 hours?",
  subject: "Roses Are Red, Violets Are On Sale",
  from_email: "hello@eiffelflowers.biz",
  from_name: "Daisy @ Eiffel Flowers",
  to: [
    {
      email,
      type: "to"
    }
  ]
});

app.post("/", (req, res) => {
  const mandrillEvents = JSON.parse(req.body.mandrill_events);
  mandrillEvents.forEach(event => {
    const {
      clicks: [url],
      email
    } = event.msg;
    if (url === TARGET_LINK_URL) {
      // send follow-up message here using the Mailchimp Transactional API
      // for more details, see https://mailchimp.com/developer/guides/send-your-first-transactional-email
      console.log("Send follow up email with this payload:", generateMessage(email));
    } else {
      // lets us test the webhook using the Mailchimp Transactional UI (see next section)
      console.log("webhook handled successfully!");
    }
  });
  res.send("Done");
});

app.listen(3000, () => console.log("Now listening at http://localhost:3000"));

Test the webhook

Now let’s run a simple test of the webhook without sending any emails. First, head back to the webhooks section of the settings page. Find the entry for your webhook and click the Send Test button. 

If your webhook handler is working, you should find the message webhook handled successfully! in your server logs.

To test the webhook further, you’ll need to send an email containing a link with the TARGET_LINK_URL we used above, then have the recipient click the link. At that point, our handler should trigger, sending our follow-up email.