How to Automatically Match Salesforce Leads to Accounts

Sponge’s most popular project in 2020?  Writing custom code to automatically match Salesforce Leads to Accounts.

Why?  When Salesforce set up their data infrastructure, they created two different objects that relate to people (Leads and Contacts). And out-of-the-box there’s no relationship between the Lead and Account objects.  It is… annoying, and a huge headache for most B2B MOPs teams:

  • You can’t create one MQL report because MQLs probably span across Leads and Contacts
  • You get duplicates across Leads and Contacts and it’s hard to flag/merge them without buying third party tools
  • In short… twice the number of fields and twice the reports 🤦🏼‍♂️

<< Related:  How to Use Process Builder to Stamp UTM Parameters in Salesforce >>

Leads are bad for Sales and ABM?

Working off Salesforce Leads is also a burden on Sales reps:

  • They’re supposed to call anyone who engages within a marketing campaign, but they might have to follow up from two different queues.
  • If you’re in an ABM-centric org, it’s really annoying to work with Leads because they’re not connected to specific companies yet.
  • It’s almost impossible to see engagement across an Account (say during a cross-sell campaign).
  • A bandaid solution is asking reps to convert Leads before working them, but we’re adding manual steps to the process and slowing it down.

I humbly suggest you ditch Salesforce Leads entirely

More and more B2B companies are opting to just work Salesforce Contacts.  Instead of generating Leads and then converting them to Contacts once they’ve met some qualification criteria, they’re automatically converting Leads to Contacts (and bonus matching those Contacts to the right Account).

The rub is marketing automation platforms handle this differently.  In HubSpot, you can sync directly with Contacts, no conversion/automation needed.  But Marketo?  It only syncs to the Salesforce Lead object.  🤦🏼‍♂️

So, ready to have your Sales reps work off Contacts?  And you have Marketo?  You’re going to:

  1. Sync the person from Marketo to Salesforce as a Lead
  2. Immediately/systematically convert that person to a Contact
  3. Associate the Contact with the right Account
  4. And send it to Sales to work

Buying a tool vs. custom coding

There are some tools you can use to do this.  LeanData is one, and Salesforce has some packages, too.  But they’re expensive.

My approach is to be as flexible and modular as possible.  The code is broken into pieces, so it can accommodate the exact use case:

  • “We actually want to keep Leads, but see them on the Account level and assign them to the Account’s owners.” – Cool, I will match Leads to Accounts using a lookup relationship, and leave it at that.
  • “We don’t want BDRs reaching out to customers.” – Okay, we’ll automatically convert customers only.
  • “We don’t want BDRs reaching out to accounts with open opportunities.” – Okay, we’ll automatically convert just the open opps only.
  • “We only want to work Contacts.” Great, I will auto-convert them and associate them with the Account.

Whatever your logic (and Salesforce data) is, you can use it.

How to automatically match Salesforce Leads to Accounts

Most typically, I use the Email domain to match Leads to Accounts, but you can use any logic you want as long as you’ve got the data available. To extract the domain from the Lead’s email address, create a formula field on Leads. The simplest version of this is just:

RIGHT(Email,LEN(Email)-FIND("@",Email))

Bonus points for adding some additional logic to return null if the email address is a personal one. I then create a text field on Accounts, also called Domain, to match to existing Accounts.

Remember how I said I this has to be flexible and modular? Let’s break down the steps of auto-converting Leads to Contacts.

  1. Find the correct Account to match the Lead
  2. Once matched, determine whether or not we should automatically convert the Lead
  3. Convert the Lead into a Contact under the matched Account

Why break it down into distinct steps? So you can easily toggle off pieces of the logic without writing any code.

Step 1: Match Leads to the correct Account

I like to do this by creating a custom Account lookup field on Leads called Matched Account, which I populate with the new code. Once this is populated, I can do lots of interesting things with it like update Leads with information from the linked Account, assign Leads to the Account Owner, or just populate a related list on Accounts for Leads we’re pretty sure belong to that Account.

In this example, I’m using domain to match, so the code will look something like this:

public static List populateAccLookup(List leadsToMatch){
	Set domainSet = new Set();
	for(Lead l : leadsToMatch){
		if(l.Domain__c != null){
			domainSet.add(l.Domain__c);
		}
	}
	Map<String,Account> domainAccMap = new Map<String,Account>();
	for(Account a : [SELECT Id, Domain__c, Type FROM Account WHERE Domain__c IN :domainSet]){
		domainAccMap.put(a.Domain__c,a);
	}
	for(Lead l : leadsToMatch){
		if(l.Domain__c != null && domainAccMap.get(l.Domain__c) != null){
			l.Matched_Account__c = domainAccMap.get(l.Domain__c).Id;
		}
	}
	return leadsToMatch;
}

If you wanted to extend this and assign these Leads to the Account Owner, you could also add a line like this (be sure to update your query to return the OwnerId):

l.OwnerId = domainAccMap.get(l.Domain__c).OwnerId;

And because Apex triggers process before Lead Assignment Rules, you could add a condition to your assignment rules that if Matched Account is not blank, exempt from other assignment rules and leave it with the current owner (which I set in the code). You can pull in any other data from the Account that you want as well, now that you’ve established the direct relationship.

At this point, all I’ve done is link the Lead and the Account–it hasn’t been converted, and it’s still a Lead.

Step 2: To auto-convert or not to auto-convert?

The next step is deciding whether or not we want to convert this Lead automatically. You could choose to populate the lookup, and then leave the actual conversion up to the User. If so, nothing else to do here. But if you want to auto-convert in some or all cases, I create a checkbox on Leads called Convert Lead. I populate this using a workflow rule, and, if checked will signal our later code (coming in Step 3) to actually convert the Lead into a Contact.

In the example above, if you wanted to only auto-convert Customers, your rule might look like:

AND(
    NOT(ISBLANK(Matched_Account__c)),
    Matched_Account__r.Type = "Customer"
)

Your options are entirely flexible based on whatever data you have available on both the Lead and the Account. Whatever rules you decide on, your workflow action would simply be to check the Convert Lead checkbox. The appeal of this approach is that if you decide you want to change your rules at any point in the future, or decide you want to turn off auto-conversion, just edit this workflow; no need to edit the code.

Step 3: Auto-convert Leads into Contacts at matched Accounts

This is the most straightforward part, where I look for Leads with the Convert Lead box checked, and convert them into the Matched Account.

for(Lead l : new Leads){
    if(l.Matched_Account__c != null && l.Convert_Lead__c == TRUE && l.IsConverted == FALSE){
        leadsToConvert.add(l);
    }
}
convertLeads(leadsToConvert);

public static void convertLeads (List leadsToConvert){
        LeadStatus cLeadStatus= [SELECT Id, MasterLabel FROM LeadStatus WHERE IsConverted=true Limit 1];
        List leadConverts1 = new List();
        List leadConverts2 = new List();        
        for(Lead l : leadsToConvert){
            if(l.Matched_Account__c != null){
                Database.LeadConvert lc = new Database.LeadConvert();
                lc.setLeadId(l.Id);
                lc.setConvertedStatus(cLeadStatus.MasterLabel);
                lc.setDoNotCreateOpportunity(true);
                lc.setAccountId(l.Matched_Account__c);
                if(leadConverts1.size()<100){ leadConverts1.add(lc); } else{ leadConverts2.add(lc); } } } if(leadConverts1.size()>0){
            List lcr1 = Database.convertLead(leadConverts1,true);
        }
        if(leadConverts2.size()>0){
            List lcr2 = Database.convertLead(leadConverts2,true);
        }
        
    }

Some points to note here:

  • The Database.convertLead(List) method can only support lists of up to 100 records, but Salesforce transactions are typically chunked into batches of 200, so I need to create two separate lists to account for this.
  • If you have multiple converted Lead statuses, you may need to intelligently choose the correct one, but this code will only choose 1

What happens when you don’t know which Account?

What I haven’t covered here, but is an important decision to make is: What happens when we don’t know which Account to convert the Lead into?

For orgs that aren’t auto-converting anything, this is easy — nothing. But if you’re trying to move to Contacts-only model, the most common approaches are to either:

  • Create a new Account, and leave it up to Users to merge any potential duplicates
  • Create a dummy Unknown Account that will hold any unmatched records, and rely on Users to re-parent those Contacts into the correct Account

Both require training your Users to handle these cases, but it’s necessary if you want to kick Leads to the curb.

Know what you’re doing

If any of this code scares you, please contact a developer before attempting this yourself.

Also, please ensure you’re actually converting Leads to Contacts.  I recently audited some code that just updated or inserted Contacts with the Lead data and then deleted the corresponding Leads.  Which erased all of the associated tracking data in Marketo. 🤦🏼‍♂️  And created dupes in Marketo. 🤦🏼‍♂️

When you fire the code, don’t query the entire Account database — it’s inefficient, and if you have 50k+ rows, it will result in a Query Exception. You’ll also likely get a CPU timeout error because you’re looping through the entire thing every time. 🤦🏼‍♂️  I build a set of Lead domains and then only query for Accounts with matching values.  It’s much more efficient and saves precious processing time. (Looking at you System.LimitException: Apex CPU time limit exceeded)

Don’t forget dupes

All this automation may have you nervous about duplicates, and rightfully so.  If I fill out 2 forms using my gmail and my work address, I will indeed become 2 contacts.  Some nuances:

  • If it’s dupe on email, you could theoretically merge it into the existing Contact with some additional automation to make sure newly merged records get synced with Marketo. The only issue here is how Salesforce handles field conflicts, which can be a bit hard to handle programmatically.
  • An alternative would be to allow Marketo to create the dupe, and then block the rep from editing it until they merge the two. You’d still want to make sure that any merged records sync with Marketo, but it puts the onus on the User.

In short, make sure you talk through all the corner cases and decision criteria before implementing a match.

Image via Giphy.

 

Ready to automatically match Salesforce Leads to Accounts?  Get in touch ⬇️

Sponge | Marketing and Operations Analysts

Get a Free System Audit

Whether you inherited a new instance or just want a second opinion, we'll dive in and benchmark your tech stack.

Sponge | Marketing and Operations Analysts

Download Resource

Use this form to recieve your free resource in your inbox today!