Mugo Web main content.

How to send customized emails to tens of thousands of users with Bronto and eZ Platform

By: Thiago Campos Viana | December 21, 2017 | Business solutions, Case study, Web solutions, email, campaign, bronto, and marketing

In an effort to engage its audience, our client, FindaTopDoc, wanted to integrate a custom Q&A platform into its site. They chose Oracle + Bronto as the accompanying marketing automation tool. Here we outline how we integrated Bronto with eZ Platform to build a robust Q&A and newsletter system with the capacity to send customized e-mails to tens of thousands of users.

The Q&A feature

First we built FindaTopDoc's Q&A platform, which allows website visitors to submit medical questions. The questions are then parsed and e-mailed to member doctors based on their specialty. Doctors can log in to their accounts and see a list of questions available for them to answer.

 

FindaTopDoc Expert Questions Dashboard

FindaTopDoc Expert Questions Dashboard

Our next step was to create an e-mail system that sent relevant questions to each doctor. We needed to be able to send one question per week to each doctor respecting several criteria, for example:

  • A doctor should not receive the same question twice;
  • Questions should be related to each doctor's specialty;
  • We should spread the questions out evenly among doctors;

With those criteria in mind, we needed to get information about the doctors and the questions, and perform database queries based on that information. The best solution was to create a script to synchronize the relevant Bronto contact information to the CMS on daily basis, creating new users and marking the ones that were deleted from Bronto.

Bronto: Schedule questions

Bronto: Schedule questions

Bronto API PHP integration

Bronto offers two webservice APIs (SOAP and REST) that can be used to perform several tasks programatically. If you're a developer wondering how to fetch the contact data from Bronto, here is sample code using the SOAP API:

<?php
$client = getBrontoClient();
$specialtyField = 'ContactFieldID';
$usernameField = 'AnotherContactFieldID';

$contactParams = array(
    'pageNumber' => 1,
    'includeLists' => false,
    'filter' => array(),
    'fields' => array(
        $specialtyField, $usernameField
    )
);

// Fetch all bronto contacts data creating new ones for those that are not in the system yet
while( $contact_result = $client->readContacts( $contactParams )->return )
{
    foreach( $contact_result as $contact )
    {
// getting basic bronto data: email, status, specialty, uid
        $contactData = [ 'email' => $contact->email, 'status' => $contact->status ];
        foreach( $contact->fields as $contactField )
        {
            if( $contactField->fieldId == $specialtyField )
            {
                $contactData['specialty'] = $contactField->content;
            }
            else if( $contactField->fieldId == $usernameField )
            {
                $contactData['uid'] = $contactField->content;
            }
        }
        ...  

Since we are dealing with tens of thousands of users, we needed to perform several optimizations to make this process efficient. We needed to:

  • Make sure the script would recover from where it stopped in the incidence of failures: We needed to select the most relevant question for a certain user taking into account all the necessary data for the weekly questions e-mail campaign. We developed the code to select a question to be sent to each user. We needed not only to optimize the code, but also to ensure it could recover from failures, so we built a queue system. 
  • Make sure we optimized question scheduling in Bronto: At first we were scheduling the deliveries individually, but this process turned out to be ineffective as we needed to call the API over 60,000 times during the script run just to schedule the deliveries, which took 16 hours! Fortunately, the API offers a way to schedule multiple deliveries per call, so with a bit of adaptation we were able to batch the deliveries and complete the process in only a couple of hours.

Here is some sample code on how to schedule multiple deliveries with the SOAP API:

<?php
$deliveries = [ ];

// Add API token here
$token = $this->getToken();

$session_header = new SoapHeader( "http://api.bronto.com/v4", 'sessionHeader', array( 'sessionId' => $this->batchSessionID ) );
$client->__setSoapHeaders( array( $session_header ) );

foreach( $batch as $batchItem )
{
        ...
        $delivery = array();
        $delivery['start'] = $now;
        $delivery['messageId'] = $id;
        $delivery['fromName'] = $fromEmail;

        if( $replyTracking )
        {
            $delivery['replyTracking'] = true;
        }
        $deliveryRecipientObject = array( 'type' => 'contact',
            'id' => $contact_write_result->results[0]->id );
        $delivery['recipients'] = array( $deliveryRecipientObject );
        $delivery['type'] = "transactional";

        foreach( $fields as $field => $value )
        {
            $delivery['fields'][] = array( 'name' => $field, 'type' => 'html', 'content' => $value );
        }
        $deliveries[] = $delivery;
}
$parameters = array( 'deliveries' => $deliveries );
$res = $client->addDeliveries( $parameters )->return;

Creating content via e-mail

Another interesting feature we developed was a solution that allows users to reply to the e-mail directly. If the user selects "Reply," he or she can send their answer back via e-mail and we will save it in the CMS. This way users can create CMS content without actually logging in to the site.

In order to accomplish this, we used Google Apps Script to parse e-mails sent to certain addresses. Then we sent web requests to an end point we created, passing on all the necessary information to validate and create the answer objects from the user's e-mail reply.

Updating Bronto profiles and conversion data

Every time the user replies to a question, we also update a field in Bronto with the total number of questions the user has answered, so that FindaTopDoc can create segments and reports with that information using the Bronto platform:

Bronto: Question email sample

Bronto: Question email sample

 

<?php
// Add API token here
$token = $this->getToken();

// The session will end if there is no transaction for 20 minutes.
$sessionId = $client->login( array( 'apiToken' => $token ) )->return;

$session_header = new SoapHeader( "http://api.bronto.com/v4", 'sessionHeader', array( 'sessionId' => $sessionId ) );
$client->__setSoapHeaders( array( $session_header ) );

$AnswersCounterFieldID = $ini->variable( $this->iniBlock, 'AnswersCounterFieldID' );

$contact = array(
    'email' => $email,
    'fields' => array(
        array( 'fieldId' => $AnswersCounterFieldID, 'content' => $count )
    )
);

$contact_write_result = $client->updateContacts(array($contact))->return;
......

FindaTopDoc also tracks conversions on certain e-mails, so when a user clicks a link in the e-mail and performs a certain action on the site, we send a message to Bronto with data about the conversion. Here is sample REST code on how to do that:

<?php
public function addOrder( $email, $tid )
{
    $url = "https://rest.bronto.com/orders?createContact=false&ignoreInvalidTid=false&ignoreInvalidTracking=false";
    $headers = [ "Content-Type: application/json", "Authorization: Bearer {$this->tokenData['access_token']}" ];
    $params = [
        'customerOrderId' => $email . '-' . time(),
        "status" => "PROCESSED",
        'emailAddress' => $email,
        'tid' => $tid,
    ];
    $result = FATDHelper::postData( $url, json_encode( $params ), $headers );
    eZLog::write( "{$email} - $tid\n{$result}", 'bronto_order.log' );
    return $result;
}

Conclusion

Bronto is very competitive in terms of features and API, but it has one issue that proved critical: there is no sandbox area for testing. You need to ask specifically for a secondary account to perform more robust tests, which leads to extra code to map all the necessary attributes from one account to another.

After all was said and done, we ended up with a robust internal API to manage Bronto and eZ Platform integrations. It can be adapted to work with other platforms as well, so if you're thinking about integrating Bronto into your next project, please feel free to contact us and ask for more information.

 

loading form....
Thanks for reaching out. We will get back to you right away.