How I use Salesforce as a Simple Campaign Outreach Tool

The Salesforce Campaign object is a great way to bucket contacts for mass outreach. However, there are limited ways to actually getting contacts assigned to a campaign. In this project, I’ll discuss the Salesforce limitations and how I can get around them using the Salesforce API and Python.

The Campaign Object

The Campaign and Campaign Member objects in Salesforce are essentially a way for you to group contacts and send then send all of them an email through Salesforce. If you have Salesforce and use Gmail, it’s an easy way to do bulk outreach without having to purchase another tool.

There are two main ways to add contacts to a Campaign in Salesforce:

  1. Directly on the Contacts Page by visiting the related section and looking for the Campaign History
  2. From a Contacts view by checking the box and clicking the Add to Campaign button on the top right of the page

The first option works if you have a few contacts you are adding to a campaign. Option two is great if you have a lot of contacts to add. However, there are several limitations to option two:

  • You can only select up to 200 contacts from the view to add to a campaign
  • You can only filter your View with Accounts or Contacts fields. You won’t be able to filter from data on any other standard or custom object.

Another big limitation is that you can’t add contacts from an Accounts perspective. In other words, you can’t just add all the contacts from an account or a list of accounts. It’s been my experience that Sales teams in an organization are typically Contact focused, while Customer Success teams tend to be more Accounts focused.

For my specific task, I needed to work with thousands of contacts and use information from a custom object so I couldn’t rely on out of the box Salesforce functionality.

Goals

My goal with this project was to be able to pull Account, Contact, and other data from Salesforce, filter the data, bucket customers, and then ultimately put Contacts into a handful of Campaigns.

Overview

  1. Import packages and connect to the Salesforce API in Python
  2. Create a Parent Campaign (which will be used later on to link child campaigns
  3. Run custom queries to pull down the information needed from each object. For me, this is the Accounts, Contacts, and Custom Objects
  4. Dynamically create campaign names by concatenating DataFrame columns
  5. Return all the unique Campaign Names and create them in Salesforce
  6. Assign each contact to the campaign

Step 1: Importing Python Packages

To start, I’ll import the packages I need:

import pandas as pd
import json
from pandas.io.json import json_normalize
import os
from datetime import datetime

Next, I’ll sign in to the Simple Salesforce package. Note that I am using environmental variables so that I don’t have my credentials directly in my code.

from simple_salesforce import Salesforce
sf = Salesforce(username='your_email', password=os.environ.get('salesforce_password'), security_token=os.environ.get('salesforce_security_token'))

Next, I am going to create a Parent Campaign so that I can link my child campaigns to it and allow for an easy way to find them:

Step 2: Create a Parent Campaign

new_campaign = 'Test Campaign'

parent_campaign = sf.Campaign.create(
    {
        'Name': new_campaign, 
        'StartDate': datetime.now().strftime("%Y-%m-%d"),
        'OwnerId': 'my_id',
        }

        )

#return the campaign id
parent_id = list(parent_campaign.items())[0][1] 

You may get an error if you have any fields that are required to save them. I just have the bare minimum above.

Step 3: Get data from Salesforce

In this section, you’ll want to grab the data from each object you want to add to your model. For this project, I am pulling data from the Accounts, Contacts, and a Custom Object.

Below, you’ll see a simple way to run a query using the Simple Salesforce package as well as a way to convert the results back to a data frame. Check out my post Getting Started with the Salesforce API in Python for more information on how to retrieve the data from Salesforce.

On line 15, you’ll see that the data from the Account Owner is packed into one column and that code is used to unpack it and add it back into the DataFrame.

#Get account info
# Dont forget to add any exclusions here
accounts_query = sf.query_all('''
select 
ID, 
Name, 
Owner.Alias,
Owner.Id,
from Account 
'''
)

sf_accounts_df = pd.DataFrame(accounts_query['records']).drop(columns='attributes')

sf_owner = sf_accounts_df['Owner'].apply(pd.Series).rename(columns={'attributes':'attributes','Alias':'Alias','Id':'Owner Id'}).drop('attributes',axis=1)

sf_accounts_owner_df = pd.concat([sf_accounts_df.drop(['Owner'], axis=1), sf_owner], axis=1)

Step 4: Assign Buckets to each Account

There are a number of ways you might complete this step. The end goal is to have the campaign name for each contact. You may approach this using a function or just simply concatenating multiple fields.

For this part, I create a new column that is a combination of a string and an existing column:

df['campaign_name'] = "Monthly outreach for " + df['owner']

Step 5: Create Campaigns in Salesforce

In this step, I am creating a subset of the DataFrame only with the unique campaign names. I’m creating a DataFrame because it makes it easier to convert into JSON in order to upload to Salesforce.

unique_campaigns_df = df[~df.duplicated(subset=['campaign_name'], keep='first')]

Next, I can loop through the DataFrame, create the Campaigns, and then return the Salesforce IDs:

#First, I'll create the Campaigns in Saleforce with a for loop
for index, row in unique_campaigns_df.iterrows():
    sf.Campaign.create(
        {
            'Name': row['campaign_name'], 
            'Type': 'Monthly Outreach',
            'StartDate': datetime.datetime.now().strftime("%Y-%m-%d"),
            'OwnerId': row['Owner Id'],
            'Target_Sent_Number__c': 0.0,
            'Strategy__c': 'Other',
            'Sales_Rep__c': row['Alias'],
            'ParentId': parent_id
        })
    
#Then, I can query Salesforce to get the Campaign IDs

campaign_names = sf.query_all('''
Select Name, Id
from Campaign
where Name like '%'''+new_campaign+'''%'
'''
)

campaign_names_df = pd.DataFrame(campaign_names['records']).drop(columns='attributes').rename(columns={'Name':'Salesforce Campaign Name','Id':'Campaign Id'})
df_merged = pd.merge(df,campaign_names_df, how='inner', left_on='campaign_name', right_on='Salesforce Campaign Name')

Step 6: Upload Contacts to the Campaign Member Object

Finally, we can pull the campaign names from Salesforce to grab the Campaign ID and then merge it back into our main DataFrame. Now that we have all the data in one DataFrame, we can filter the columns just to include the ContactID, CampaignId, and the Status column. The Status column is a default column in Salesforce and I just mark it as sent.

#First Filter out all other columns
campaign_members_df = df_merged[['Contact Id', 'Campaign Id']]

#Then we can rename the column for the upload
campaign_members_df = campaign_members_df.rename(columns={'Contact Id':'ContactId','Campaign Id':'CampaignId'})

#Add the Status column
campaign_members_df['Status'] = 'Sent'

Now we are ready to upload:

#convert the dataframe to json
insert_json = campaign_members_df.to_json(orient='records',date_format='iso')
#convert string to file object
contacts_to_insert = json.loads(insert_json)

insert_results = sf.bulk.CampaignMember.insert(contacts_to_insert,batch_size=10000,use_serial=True)

Now when you log into Salesforce, you will see that the contacts have been added to the Campaigns!

Thanks for reading!


Posted

in

by

Tags: