banshee is under active development: collectors will keep changing, and a version you publish will likely need adjustments. For stable use, wait for version 1.0.0.

Cloud

banshee/cloud-collection/cloud/mandrill/https/

sending

The emails sent by a Mailchimp Transactional (Mandrill) account over a period, and the parts that bounced, were rejected or drew spam complaints.

  • cloud
  • mailchimp
  • mandrill
  • transactional
  • email
  • bounces
  • rejects
  • complaints
  • https
  • api

by banshee0 installations1 version

v1Latest

Published on October 27, 2026. First version.

Keen 1.0.0 · 60 lines
// The sending of a Mailchimp Transactional (formerly Mandrill) account, by its API with an API key: over a period,
// the emails sent, and the parts of them that bounced, were rejected or were reported as spam.
// Reads POST https://mandrillapp.com/api/1.4/users/info.
need('protocol:http');
$key = need('protocol:http:token');
$period = variable('mandrill:period', 'choice', array(
    'label'   => 'Period',
    'help'    => 'The window of the statistics, as Mailchimp Transactional counts them: today, the last 7 days or the last 30 days.',
    'choices' => array('today', 'last_7_days', 'last_30_days'),
    'default' => 'last_7_days',
));

output('sent', 'number', array('label' => 'Sent', 'unit' => 'emails'));
output('hard-bounces', 'number', array('label' => 'Hard bounces', 'unit' => '%'));
output('soft-bounces', 'number', array('label' => 'Soft bounces', 'unit' => '%'));
output('rejects', 'number', array('label' => 'Rejected', 'unit' => '%'));
output('complaints', 'number', array('label' => 'Spam complaints', 'unit' => '%'));

watch.above('hard-bounces', 'Hard bounces high', 'hard-bounces', array('warning' => 2, 'critical' => 5));
watch.above('soft-bounces', 'Soft bounces high', 'soft-bounces', array('warning' => 10, 'enabled' => false));
watch.above('rejects', 'Rejects high', 'rejects', array('warning' => 5, 'critical' => 10));
watch.above('complaints', 'Spam complaints high', 'complaints', array('warning' => 0.1, 'critical' => 0.3));

// The account's figures, by users/info. The API takes its key in the JSON body only, and documents no other
// way: the body carries a secret, which would make the whole answer secret. The answer holds the figures of the
// account and never the key, so the call declares it free of tokens, public=true; a part of it that held the
// key would still be secret.
function users_info($key) {
    $answer = http.post('https://mandrillapp.com/api/1.4/users/info', json.write(array('key' => $key)),
        headers=array('Content-Type' => 'application/json'), public=true);
    if ($answer['status'] == 401) {
        fail('misconfigured', 'Mailchimp Transactional refuses the API key, code 401',
            'The key is wrong or revoked, or it may not call users/info.');
    }
    if ($answer['status'] != 200) {
        fail('unreachable', 'Mailchimp Transactional answers users/info with code ' . $answer['status']);
    }
    return json.read($answer['body']);
}

// The part of $count in $of, in percent: 0 when $of is 0, nothing being sent.
function part($count, $of) {
    if ($of == 0) {
        return 0;
    }
    return number.round($count * 100 / $of, 2);
}

// {"stats": {"last_7_days": {"sent": 1200, "hard_bounces": 6, "soft_bounces": 15, "rejects": 3, "complaints": 1,
// …}, …}, …}. A count the API leaves out is 0. A bounce and a complaint follow an email sent; a rejected email
// was never sent, and its part is taken of the emails submitted, sent or rejected.
$info = users_info($key);
$stats = $info['stats'][$period];
$sent = list.get($stats, 'sent', 0);
$rejects = list.get($stats, 'rejects', 0);
value('sent', $sent);
value('hard-bounces', part(list.get($stats, 'hard_bounces', 0), $sent));
value('soft-bounces', part(list.get($stats, 'soft_bounces', 0), $sent));
value('rejects', part($rejects, $sent + $rejects));
value('complaints', part(list.get($stats, 'complaints', 0), $sent));