Flutter Mailchimp SDK

Recently, I built a Flutter app for a client. Towards the end of the project, he asked me if I could add a way to collect users' emails and import them into his Mailchimp Audience.

I assumed (probably idiotically) that this wouldn’t be a huge amount of extra work, so I agreed that I would build the page and I set off to find out how to load contacts from a mobile device into an audience. I googled ‘mailchimp mobile sdk’, and immediately ran into a problem: there is no flutter mailchimp mobile sdk. There are sdks for swift and for kotlin, but nothing for dart and flutter.

Hmmmm…

Now Mailchimp has a quite nice, well documented API, and you could just use an API_KEY in your app to access the mailchimp API and do all sorts of magical stuff to your mailchimp account, but this is a TERRIBLE IDEA! Why? Because Mailchimp’s api keys offer full, unfettered access to your account. They're meant to be used in some server you control. If you ship them with your app, an attacker can decompile your code and discover your key. Generally speaking, anything in a mobile app can be reverse engineered! This is why mobile sdks exist that usually have their own keys. They offer much smaller scoped access to your api. Usually access that anyone who downloads your app should rightfully have. Such as simply adding a contact to an audience. If hackers want to download your app, decompile it, and use your sdk key to add a billion emails to your audience, then… well, they’re just weird. But the point is that’s all they’d be able to do even if they got a hold of your mobile SDK key.

So I decided to just reverse engineer the mobile sdk and rewrite it in flutter. After all, Mailchimp published their iOS mobile SDK, so I could just read the code and figure out how it all works.

And lucky for me, the code for the mobile SDK was well written and easy to follow and therefore, it was pretty easy to figure out how it all worked. Let’s start with the SDK token which is set up in Profile->Extras->API Keys:

const myToken = "843THISISAFAKETOKENOKAY5493984-us10";

There are two parts to this. The first is the password (to the left of the dash), and the second is the datacenter (us10 in this case), which is an important component to the api url you’ll use. This is how you use the datacenter field:

String baseToken = token.split('-')[0];
String datacenter = token.split('-')[1];
String basePath = "https://${datacenter}.api.mailchimp.com/";

The api is a json api which uses basic authorization, so you can set up your headers like so:

String authString = "apikey:${baseToken}"; // apikey is the username
String encoded = base64.encode(utf8.encode(authString));
 
Map<String, String> headers = {
  "content-type": "application/json",
  "Authorization": "Basic ${encoded}"
;

And once you have these headers set up, you just have to deal with your request. If you’re hitting the ‘contacts’ api, your request should look like a contact (you can look at the CodingKeys enum to glean the actual fieldnames for your json request).

In my case, the client just wanted to store email, first name, and last name, so my request looked like this:

Map<String, dynamic> jsonRqst = {
  "email_address": emailAddress,
  "merge_fields": {"FNAME": firstName, "LNAME": lastName},
  "status": "subscribed"
};

Then, to actually send the request, you can simply use the api defined here like so:

return post("$clientapi/1.0/contacts",
      headers: headers, body: json.encode(jsonRqst));

And that’s it. You’ve added a contact to the audience associated with your key. Couldn’t be simpler, and just required a little bit of digging into Mailchimp’s iOS mobile SDK. Here’s a full working example (with a fake key, obviously):

(Note: I’m not going to cover the event api in this article, but the api is defined here and can be used very similarly to the contacts api except with an event object.)

import 'package:http/http.dart';
import 'dart:convert';
 
const myToken = "843THISISAFAKETOKENOKAY5493984-us10";

Future<Response> addContact(String emailAddress, String firstName, String lastName) {
  String baseToken = myToken.split('-')[0];
  String datacenter = myToken.split('-')[1];
  String basePath = "https://${datacenter}.api.mailchimp.com/";
 
  String authString = "apikey:${baseToken}"; // apikey is the username
  String encoded = base64.encode(utf8.encode(authString));
 
  Map<String, String> headers = {
    "content-type": "application/json",
    "Authorization": "Basic ${encoded}"
  ;
 
  Map<String, dynamic> jsonRqst = {
    "email_address": emailAddress,
    "merge_fields": {"FNAME": firstName, "LNAME": lastName},
    "status": "subscribed"
  };
 
  return post("${basePath}clientapi/1.0/contacts",
      headers: headers, body: json.encode(jsonRqst));
}

Thanks for reading, and I hope this helped if you were looking to implement simple Mailchimp integration in a flutter app!

Jon Bedard3 Comments