GOTV Organizing Technology: Mobile Commons
(also in this series: getting everybody on your lists, automating daily invite emails, texting voters their nearest early vote location)
You can do a lot of cool shit with Mobile Commons!
Mobile Commons is a platform with which a campaign can run a short-code text message service, i.e. text VOTE to 30330, or send blasts out to users who’ve opted in.
Let’s explore how to do some example useful GOTV things: geographic event invites, yard sign lookup, custom variables for fundraising asks, ActBlue integration, voter information lookup, and arbitrarily complex dialogs.
Background context: “mdata”
“mdata” is a Mobile Commons term for something that helps you build a dynamic short-code flow. There are two types:
- With CSV mdata, you upload a CSV with two columns: first column is the incoming user input, and second column is the text to send back to the user. When user a texts in something, Mobile Commons looks in your CSV for a row that matches this incoming user input to the first column, and sends back the second column.
- With webhook mdata, you set up a webserver with an endpoint that accepts a user input string and returns text to send back to the user. When a user texts in something, Mobile Commons pings your webserver with the user input and sends back to the user what your webserver returns.
Event search
Let’s say we want to tell people to text their zip code to our short code to find their nearest event or two. For example, if you text DEBATE to 30330, it could respond “Reply with your ZIP CODE to find a debate watch party near you!”, and then we can set up an mdata to receive the user’s zip code, and respond with info about the upcoming nearby events.
There is an mdata setup kicking around that surely you’ve seen, that uses the Mobilize America API as the mdata webhook URL. This setup has several opportunities for improvement:
- You can’t localize start times to the local timezone :-/ (This is because the Mobilize America API response doesn’t include a formatted start time, and Liquid is not advanced enough to produce one from the timestamp and timezone.)
- You can search among events of a single event type, or any event type, but not more than one event type :-/ (This is because of a Mobile Commons bug in how it parses webhook URLs.)
- You might not be excluding events that are already full.
- You might not be setting a utm_source on the Mobilize America URL.
You can solve these points by setting up your own very thin wrapper around the Mobilize America API. Example code is here.
Example request:
You’ll want to set the final argument, ‘zipcode’, to the user input. The other parameters:
- ts_start, ts_end: unix timetstamp of earliest and latest start time to consider in the search. To figure these out, go to epochconverter.com, choose “Local time”, put in, say, 7am ET on the first day you want to search, click “Human Date to Timestamp”, then copy the “Epoch timestamp”. This will be the value of ts_start. Do the same for, say, 11pm the last day you want to search.
- event_types: Comma-separated list of event types to search. Note that “Watch Party” must be “DEBATE_WATCH_PARTY” here. There are some other event types with suprising names here, as well, so be careful. You may wish to search over multiple event types, in case, for example, different states have used different event types for the events you’re recruiting for; or if you are recruiting intentionally for different types of events.
- max_dist: (optional) search radius in miles, defaults to 50.
- utm_source: (optional) if set, response will add this utm_source param to all returned events’ ‘browser_url’.
The response is identical to Mobilize America event search API response, with a single additional field ‘formatted_time’ added to each ‘timeslot’, with value like ‘Thu, Aug 20 at 6:00 PM’. Each ‘browser_url’ will also have a utm_source param set, if given.
For all searches, the endpoint will return at most 3 events and exclude full events.
You can use this mdata webhook endpoint with the same Liquid you used before; just change the formatting of the start time to use ‘timeslot.formatted_time’.
Here is the code for this endpoint, easily deployed with AWS Lambda (reach out if I can help!). As implemented, the formatted start time does not include explicit timezone (e.g. “CST”), but this can be trivially added
Yard sign find, and other per-zip-code lookups
Let’s say we want users to text us their zipcode, and we’ll return the address of the nearest field office where they can pick up a yard sign! This is easy with the CSV mdata feature, i.e., you don’t need a webserver.
We’ll make a CSV file with one column all zipcodes, and the nearest office, if present, to each zipcode.
Here is an example Redshift query for producing this CSV file — it assumes you have one table with all zip codes, and one table with details of all offices with yard signs
In general, per-zip-code CSV mdata is a very useful pattern. Another cool example is texting in your zipcode to get the latest minivan canvass list, relational organizing app link, GOTV staging location, and much more. All you need is a query that computes what the response should be for each possible zip code, usually easily prepared by an analyst.
Custom profile fields for fundraising asks
It can be very useful and effective to have aggregated fundraising statistics available on Mobile Commons profiles for targeted fundraising asks. This can be used for targeting (e.g. the Mobile Commons manager can easily target a blast to only people with total donation greater than certain amount), or can be used within the ask message itself.
The goal is to store fundraising aggregates as custom fields on each Mobile Commons profile. For example, let’s say we wish to add a ‘donation_total’ to each Mobile Commons profile.
As a baseline, we assume that every night or every hour we sync all Mobile Commons profiles, with their associated custom fields values, into Redshift.
First, we’ll add custom field with name ‘donation_total’ and Number type.
Second, we’ll write a Redshift query that computes what ‘donation_total’ *should* be for each Mobile Commons profile.
Third, we’ll write another query that selects all Mobile Commons profiles whose existing ‘donation_total’ field does not match what the donation total should be based on the previous query.
Finally, we’ll write a program that loops over each such Mobile Common profile and uses the ‘profile_update’ endpoint to set ‘donation_total’ to what it should be. This program can be run frequently, like hourly.
Now Mobile Commons managers can use this ‘donation_total’ custom field for targeting and for donation asks. Other useful variables you can set up this way are last donation date, total merch order amount, average donation, total donation amount minus merch orders, total Q3 donation amount, overall HPC, Q3 HPC, etc.
Polling place lookup
If it makes sense, you can offer to search for voting options based on zip code. With clever implementation, the webhook mdata can return not only details on where/how to vote, but a link to a webpage with a unique ID that shows immediately information for the user’s address.
If you put SMS opt-in language on to your web voter information flow, you can also hook up the web flow to add users to Mobile Commons and send this text, to ensure they have the address and other information handy on their phone.
Automatically add new donors to Mobile Commons
Assuming your ActBlue forms have SMS opt-in language, you can automatically add new donors to your SMS list. If you use the ‘profile_update’ endpoint, and specify an opt-in path ID,
One thing to be careful of: if your campaign gets a big burst of donations, ActBlue may delay notifying your webhook until the middle of the night. In this case, the webhook should notice that it’s 3am Eastern, and schedule a text to be sent later in the day.
Arbitrarily complex dialog
Did you know, Mobile Commons mdata is just flexible enough, that with some creative use of webhook mdata, you can use your short code to engage supporters with any kind of logic/questions/responses you want?
When you configure a webhook mdata to receive all future responses, and to simply respond with whatever the webhook returns, you can now build whatever stateful response logic you want to into your webhook. To the left are two examples:
- A minimal relational organizing flow.
- A bare-bones reimplementation of a student loans calculator.
Here is the source code for these webhooks, easily deployed with AWS Lambda
Conclusion
If you can imagine it, and it will be useful for staff, volunteers, and voters, then Mobile Common’s API and functionality is flexible enough (barely : ) to make it happen!
I’m happy to help (free) if you have any question about how to make any of these things happen!
Many thanks to Amanda Robinson, Leah Alpert, Rachel Funk, Josh Matfess, Olivia Robinson, Nina Vyedin, Annie Wang, and many others for helping explore these ideas.