Skip to main content

Customizing My Welcome Campaign with Local Weather

I recently re-joined the team at Customer.io to head a new Professional Services team after about a year away from the company. After spending that year leading the Marketing team at Simple Finance, I had a greater appreciation for the struggles that B2C companies face when trying to keep their lifecycle comms as relevant as possible. This is one of the biggest reasons I came back to Customer.io. I am passionate obstinate about how our product gives lifecycle marketers and product teams the canvas they need to create some incredible journeys for their customers…And after spending that year in another lifecycle tool, there’s just no contest that we’re doing it better than anyone else in the space.

One of the things that every new hire at Customer.io is asked to do is to create a five-email welcome series to introduce themselves to the rest of the company. The core principles that are required to get the welcome series out are:

  • Getting data into Customer.io
  • Creating a Segment
  • Setting up a Segment Triggered Campaign
  • Learning about Drafts
  • Creating and Managing our Drag-and-Drop Workflow Builder

It’s a lot of information for any new employee, especially non-marketers that have never sent out an email campaign before (i.e. that send-button stress is real.) However, this experience lets every employee, whether they’re in Finance or a new Salesperson, the experience of living our customers’ life in a very real way. Even as a returning employee and heavy user of the platform, I had a few things to learn about the platform after being gone for a year…To name a few things there’s Webhook Responses, the Visual Workflow Builder, a complete visual redesign, and Journey Metrics.

As I was planning out my re-welcome series, I wanted to use the opportunity to reacquaint myself with the platform and try out some of the new features. I was MOST excited about Webhook Responses, so I decided to focus the first message on giving my recipients a real-time weather report using OpenWeather. Here’s a high-level overview of how I approached the problem:

  • Every 15 minutes trigger an Event Triggered Campaign to update the weather. The scheduling of the update was a neat thing to figure out.
    • I originally approached the trigger using a stack of Google Cloud Scheduler/Google Cloud Pub Sub/ and Google Cloud Functions. While this worked well, I wanted something that was contained completely in Customer.io
    • After 30 seconds of brainstorming with our VP of engineering, he handed me the cleanest solution on a platter.  He suggested that I create an Event Triggered Campaign that did everything. 
    • The only complication of doing this with an event triggered campaign was that I needed a way to get all users into the flow for the first time. Said another way, every customer had to “perform” the update_weather event, or in this case, I had to do that for them. To accomplish this task, I used a basic Python script to fire the event for each customer with their id. 
  • Moving down the workflow, I used attribute updates to clear any existing weather attributes from each profile.
  • Then, a Webhook fired to get the recent weather from OpenWeather and store the response in each users’ profile
  • After the webhook responses were stored, the workflow paused for 15-minutes, and finally concluded with a webhook that POSTs to the customer.io track API, sending the same event that triggers the campaign. Essentially creating a loop. 

With the weather information stored on the users’ profiles I was able to include it in my first welcome email that looked something like this:

There are three specific bits of Liquid magic happening in the middle section there. 

  1. OpenWeather provides an icon id in their API responses and a resource endpoint for pulling the image into the HTML. The liquid looked like this:
{% for weather in customer.weather_current_weather %}
  {% if forloop.first == true %}
    <img alt src="http://openweathermap.org/img/wn/{{weather.icon}}@2x.png"/>
  {% else %}
  {% endif %}
{% endfor %}
  1. The second column seems relatively straightforward with the exception of unit conversion. A lot (if not most) of the Customer.io team is on the metric system, and they’re proud of it. OpenWeather looks for a units parameter on the API call, and if I was storing an address, including country, it would have been very easy to conditionally flip between imperial and metric with an “unless country == US” liquid statement. Since I was only storing ZIP Code, I handily grabbed the country name from the OpenWeather response to enable switching between metric and imperial using the aforementioned logic. Here’s what it looked like:
{% if customer.weather_current_sys.country == "US"%}   
  {{customer.weather_current_main.temp | round}}º F
{% else %} 
  {{customer.weather_current_main.temp | minus: 32 | times: 0.556 | round }}º C {% endif %} 

3. The last bit took the longest, and I definitely could have spent more time making the message feel even more personal. My goal was to address the current weather in real-human terms, then provide some anecdote around the temperature…Something in line with the theme of the email. Brace yourself, here’s the Liquid for that section:

{% for weather in customer.weather_current_weather %}
{% if forloop.first == true %}
{% if weather.main == 'Thunderstorm' %}Bang! Boom! Looks nasty with {{ weather.description }} in {{ customer.city }}. My old dog, Lola, used to hide in the bathroom during storms.
{% elsif weather.main == 'Drizzle' or weather.main == 'Rain' %}Grab your raincoat, there's {{ weather.description }} in {{ customer.city }}.
{% elsif weather.main == 'Snow' %}Snowshoeing, skiing, snowboarding! 
		{% if weather.id == 611 or weather.id == 612 or weather.id == 613 %}I hope that sleet turns into snow{% else %} I hope there's enough {{ weather.description }} to do something fun
		{% endif %} in {{ customer.city }}. (My wife and I just took up snowboarding this year. SHRED, BRO!
{% elsif weather.id == 701 or weather.id == 711 or weather.id == 721 or weather.id == 731 or weather.id == 741 or weather.id == 751 or weather.id == 761 or weather.id == 762 or weather.id == 771 or weather.id == 781 %}Might want to stay inside right now...There's some nasty {{ weather.description }} going on in {{ customer.city }}. Does this happen a lot there?
{% elsif weather.main == 'Clear' %}It looks like there's {{ weather.description }} in {{ customer.city }}. Sounds pretty good to me!
{% elsif weather.main == 'Clouds' %}It looks like there's just {{ weather.description }} right now in {{ customer.city }}. I hope blue skies are not far off!
{% endif %}
{% if customer.weather_current_main.feels_like < 60 %}I prefer slightly warmer temps than what you've got in {{ customer.city }} right now. 
	{% elsif customer.weather_current_main.feels_like < 40 %}I'd prefer it were A LOT warmer than what you're dealing with right now in {{ customer.city }}
	{% else %}And I'll tell you what...The temps sound pretty good in {{customer.city}} right now.
	{% endif %}
{% else %}
{% endif %}
{% endfor %}

One of the biggest learnings for me in building the Liquid for this email was how I should deal with the fact that OpenWeather could return more than one weather result per ZIP code. After some research I found a built-in forloop helper that used a first attribute to evaluate whether or not the result is the first in a loop or not. It’s basically a way to filter out everything but the first result.

If you’re looking for ways to add some relevance to your lifecycle messages, here are a number of public APIs for inspiration!