Mandrill Install and Setup Guide

Man… Who?

Mandrill is a high-throughput email relay and infrastructure service. Launched in 2012, it includes a powerful API along with with superb analytics, reporting, and tracking dashboards.

Here’s how to get started with Mandrill:

  1. Signup for a free account
  2. Generate an API Key
  3. Add authorized sending domains
  4. Add DKIM and SPF text Records
  5. Download and unzip the Mandrill library in a Shared Resource folder
  6. Build a test app

The Backstory on Mandrill

The Mandrill Logo

Mandrill operates as a startup within MailChimp. It’s a “skunkworks” project that’s both complementary and disruptive. In order to launch Mandrill, MailChimp poached their own engineers and isolated them from rest of the company!

Sound interesting? Learn more about their roots on this blog post.

Transactional messages are ad-hoc notification emails sent to your users. They include payment receipts, password recovery, and registration confirmations. Here is the minimal setup for an app that will send transactional messages with Mandrill:

<?php
require_once 'path/to/shared/script/folder/Mandrill.php';
$mandrill = new Mandrill('SECRET_API_KEY');

try{

        $message = array(
                'subject' => 'Subject of Email',
                'text' => 'Contents of email goes here', // or just use 'html' to support HTMl markup 
                'from_email' => 'sender@example.com',
                'from_name' => 'Sender Name', //optional
                'to' => array(
                        array( // add more sub-arrays for additional recipients
                                'email' => 'recipient@example.com',
                                'name' => 'Recipient Name', // optional
                                'type' => 'to' //optional. Default is 'to'. Other options: cc & bcc
                                )
                ),

                /* Other API parameters (e.g., 'preserve_recipients => FALSE', 'track_opens => TRUE',
                  'track_clicks' => TRUE) go here */
        );

    $result = $mandrill->messages->send($message);
    print_r($result); //only for debugging

} catch(Mandrill_Error $e) {

    echo 'A mandrill error occurred: ' . get_class($e) . ' - ' . $e->getMessage();

    throw $e;
}

Side Notes & Observations

  1. API’s plug into your existing apps. So, don’t hard-code any dynamic information such as sender and recipient information as shown above. These items should be stored in a variable and then sent to the API
  2. Line 25: You’ll want to remove print_r($result); for live deployments
  3. Lines 12-17: The Mandrill ‘to’ array is an array of arrays. Each sub-array represents a recipient that has keys called email, name, and type. However only email is required.
    • By default all recipients’ email addresses are exposed in the ‘to’ field. You may disable this through the dashboard or by adding the following within the message array:
      'preserve_recipients' => FALSE,
  4. The Mandrill FAQs say that the API does not support CC fields. However this is simply not true. Our own testing as well as the API documentation confirm that setting 'type' => 'cc' permits the use of CC fields

Send HTML Emails With Open & Click Tracking

   'html' => '<p>Message contents with HTML mark-up</p>',

   'track_opens' => TRUE,
   'track_clicks' => TRUE,
   'auto_text' => TRUE, // auto-converts html formatted emails to text

Since we’re pulling ad-hoc user information from a database, lets proceed as follows:

  1. STEP 1: Use PDO::FETCH_ASSOC to store our database result-set in an n-length, 2-dimensional associative array called $user_arr:
    $user_arr = $resultset->fetchAll(PDO::FETCH_ASSOC);
    
    return $user_arr;
    
  2. What are Associative Arrays?

    Associative arrays use string-based keys (i.e., indexes) to establish “a strong association between keys and values”. “Regular” arrays use numeric-based indexes and are usually zero-indexed (i.e. they start at “0”).

  3. STEP 2: Sanitize (i.e., parse and format) the database result-set into keys (i.e., email, name, and to) and values that Mandrill understands:
    1. Use foreach to parse and format the contents of $user_arr. Then store this sanitized data into an array called $deliver_to_arr
      
       foreach ( $user_arr as $parse_email_name_set ) {
      
          $deliver_to_arr[] = array(
                          'email' => $parse_email_name_set['email_address'],
                          'name' => $parse_email_name_set['first_name'] . ' ' . $parse_email_name_set['last_name']
                          'type' => 'to' //hard-coded for now
                          );
      
      }
      
  4. STEP 3: Finally, send the $deliver_to_arr array to the Mandrill API. Simply assign it to the 'to' parameter as you would a simple variable:
    
         'to' => $deliver_to_arr,
    
    

Dedicated IP Addresses for Sending Emails

An interesting Mandrill feature is the availability dedicated IP addresses. We were unsure of the benefits of this feature, so we tweeted the Mandrill team to make sure this section was accurate. Here is their first reply:

So, even though you’re using Mandrill’s infrastructure, the IP address of your application’s server (the originating IP) will always appear in email headers — dedicated IP or not. So, we went back to the Mandrill team for their take on the benefits of a dedicated IP:

They pointed us to one of their knowledge base articles called Should I use a dedicated IP?. It concisely sums up the pros and cons of the dedicated IP feature. Basically, you should consider a dedicated IP only if you’re sending at least 5000 emails per day, at least 3 days per week.

Below is the full-blown API for sending transactional emails. Mandrill’s message send API docs includes a descriptions of each of the parameters that you may use.

<?php
try {
    $mandrill = new Mandrill('YOUR_API_KEY');
    $message = array(
        'html' => '<p>Example HTML content</p>',
        'text' => 'Example text content',
        'subject' => 'example subject',
        'from_email' => 'message.from_email@example.com',
        'from_name' => 'Example Name',
        'to' => array(
            array(
                'email' => 'recipient.email@example.com',
                'name' => 'Recipient Name',
                'type' => 'to'
            )
        ),
        'headers' => array('Reply-To' => 'message.reply@example.com'),
        'important' => false,
        'track_opens' => null,
        'track_clicks' => null,
        'auto_text' => null,
        'auto_html' => null,
        'inline_css' => null,
        'url_strip_qs' => null,
        'preserve_recipients' => null,
        'view_content_link' => null,
        'bcc_address' => 'message.bcc_address@example.com',
        'tracking_domain' => null,
        'signing_domain' => null,
        'return_path_domain' => null,
        'merge' => true,
        'global_merge_vars' => array(
            array(
                'name' => 'merge1',
                'content' => 'merge1 content'
            )
        ),
        'merge_vars' => array(
            array(
                'rcpt' => 'recipient.email@example.com',
                'vars' => array(
                    array(
                        'name' => 'merge2',
                        'content' => 'merge2 content'
                    )
                )
            )
        ),
        'tags' => array('password-resets'),
        'subaccount' => 'customer-123',
        'google_analytics_domains' => array('example.com'),
        'google_analytics_campaign' => 'message.from_email@example.com',
        'metadata' => array('website' => 'www.example.com'),
        'recipient_metadata' => array(
            array(
                'rcpt' => 'recipient.email@example.com',
                'values' => array('user_id' => 123456)
            )
        ),
        'attachments' => array(
            array(
                'type' => 'text/plain',
                'name' => 'myfile.txt',
                'content' => 'ZXhhbXBsZSBmaWxl'
            )
        ),
        'images' => array(
            array(
                'type' => 'image/png',
                'name' => 'IMAGECID',
                'content' => 'ZXhhbXBsZSBmaWxl'
            )
        )
    );
    $async = false;
    $ip_pool = 'Main Pool';
    $send_at = 'example send_at';
    $result = $mandrill->messages->send($message, $async, $ip_pool, $send_at);
    print_r($result);
    /*
    Array
    (
        [0] => Array
            (
                [email] => recipient.email@example.com
                [status] => sent
                [reject_reason] => hard-bounce
                [_id] => abc123abc123abc123abc123abc123
            )
    
    )
    */
} catch(Mandrill_Error $e) {
    // Mandrill errors are thrown as exceptions
    echo 'A mandrill error occurred: ' . get_class($e) . ' - ' . $e->getMessage();
    // A mandrill error occurred: Mandrill_Unknown_Subaccount - No subaccount exists with the id 'customer-123'
    throw $e;
}
To a naive observer, it seems insane that tiny companies bootstrapped with no money and made up of a bunch of friends from college could ever build products that rival the output of huge companies.

References:
  1. https://mandrillapp.com/api/docs/messages.php.html
  2. https://mandrillapp.com/api/docs/index.php.html
  • industriaexpansion

    Excelente explicacion, el tutorial es muy claro y me ayudo mucho en lo que necesitaba implementar.

    Muchas gracias.

    • Code Sport I/O

      Anytime.. feel free to post if you have any questions

  • Allan Perez

    @treebright:disqus how to track open/click inside my web app?

    • Code Sport I/O

      @lhanpp:disqus In your mandrill-api-caller.php file include the line ‘track_clicks’ => TRUE

      Then you log into your Mandrill account and you see tracked clicks in the dasboard