This article is the first in a two-part series focused on Lead-to-Account (L2A) matching and lead routing in Salesforce. Here, we delve into the foundational concept of L2A matching—a critical process that determines whether a lead can be matched to an existing account.
If no account match is found, the lead is routed based on business rules such as geographic territory, lead source, or other criteria. Conversely, if a match is identified, the lead is routed to the account owner or a designated user specified in a custom lookup field.
A robust L2A matching system ensures efficient routing, avoids duplicate ownership conflicts, and maintains consistent customer interactions.
Why Lead-to-Account (L2A) Matching Matters
L2A matching is essential for streamlining operations, improving customer experience, and driving sales efficiency. Without it, duplicate records and disconnected customer interactions can cause confusion and redundancy. For example:
A customer downloads an eBook or attends an event, creating a new lead even though they’ve already engaged with a sales rep. Without L2A matching, this lead could be routed to a different rep, disrupting the customer experience.
Effective L2A matching routes leads to the appropriate account owner, ensuring:
- Consistent communication with customers.
- Efficient internal operations.
- Advanced automation, such as data enrichment or auto-conversion (though auto-conversion requires custom Apex code).
By reducing manual interventions, sales teams can focus on meaningful engagements that align with broader organizational strategies.
The Role of Normalization in L2A Matching
Normalization standardizes data for accurate comparisons. Inconsistent formats for company names, addresses, or phone numbers can lead to false positives or missed matches.
Key normalization steps include:
- Removing Special Characters: Stripping symbols like commas or ampersands.
- Standardizing Case: Converting text to uppercase or lowercase.
- Unifying Abbreviations: Converting variations like "Corp." and "Corporation."
- Trimming White Spaces: Eliminating extra spaces.
Example:
Field Name | Lead Value | Account Value | Normalized Value |
---|---|---|---|
Company Name | Acme, Inc. | ACME Corporation | acme |
Website | www.acme.com | acme.com | acme |
Address | 123 Elm St. | 123 Elm Street | 123elmstreet |
Both leads and accounts must follow the same normalization logic to ensure accurate matches.
Data Normalization Algorithm
Once data is normalized, key fields such as company name, domain, phone number, and address are compared. Storing normalized values on records improves efficiency by eliminating the need to reprocess data.
Key Considerations:
- Use consistent normalization logic for leads and accounts.
- Map fields correctly to ensure operations apply uniformly, regardless of the source object.
Example Logic:
Below is example code demonstrating normalization for fields like company name, address, phone, and domain.
public with sharing class NormalizeLeadToAccount {
@InvocableMethod(label='Normalize Lead and Account Data' description='Normalize Lead and Account Data for Matching')
public static List<NormalizedResult> normalizeData(List<NormalizeRequest> requests) {
List<NormalizedResult> results = new List<NormalizedResult>();
for (NormalizeRequest req : requests) {
NormalizedResult result = new NormalizedResult();
result.normalizedCompanyName = !String.isEmpty(req.companyName) ? normalizeCompanyName(req.companyName) : null;
result.normalizedAddress = !String.isEmpty(req.address) ? normalizeAddress(req.address) : null;
result.normalizedPhone = !String.isEmpty(req.phone) ? normalizePhone(req.phone) : null;
result.normalizedDomain = !String.isEmpty(req.domain) ? normalizeDomain(req.domain) : null;
results.add(result);
}
return results;
}
private static String normalizeCompanyName(String companyName) {
String normalized = companyName.toLowerCase().replaceAll('[^a-zA-Z0-9]', '');
normalized = normalized.replaceAll('\\b(inc|incorporated|corp|corporation|llc|limitedliabilitycompany|ltd|limited|co|company|plc|publiclimitedcompany|gmbh|gesellschaftmitbeschränkterhaftung|llp|limitedliabilitypartnership|lp|limitedpartnership|sa|sociedadanónima|ag|aktiengesellschaft|bv|beslotenvennootschap|nv|naamlozevennootschap|pvtltd|privatelimited|pvt|private|pteltd|privatelimitedcompanyinsingapore|sl|sociedadlimitada|kk|kabushikikaisha|oy|osakeyhtiö|as|aktieselskab|spa|societàperazioni|ab|aktiebolag|sarl|sociétéàresponsabilitélimitée|ulc|unlimitedliabilitycompany|ltdco|limitedcompany|cv|commanditairevennootschap|sc|sociedadcivil|scp|sociétéencommanditeparactions|jsc|jointstockcompany|sadecv|sociedadanónimadecapitalvariable|ooo|obshchestvosogranichennoyotvetstvennostyu|sarl|sociétéàresponsabilitélimitée)\\b', '');
return normalized;
}
private static String normalizeAddress(String address) {
address = address.toLowerCase();
Map<String, String> replacements = new Map<String, String>{
'aly' => 'alley', 'anx' => 'annex', 'arc' => 'arcade', 'ave' => 'avenue', 'bch' => 'beach',
'blvd' => 'boulevard', 'st' => 'street', 'rd' => 'road', 'ln' => 'lane', 'dr' => 'drive'
// Add other street suffixes as needed
};
for (String key : replacements.keySet()) {
address = address.replaceAll('\\b' + key + '\\b', replacements.get(key));
}
address = address.replaceAll('\\s+', ''); // Remove all whitespace
return address;
}
private static String normalizePhone(String phone) {
phone = phone.replaceAll('[^0-9]', '');
if (phone.startsWith('1') && phone.length() == 11) {
phone = phone.substring(1);
}
return phone;
}
private static String normalizeDomain(String domain) {
domain = domain.toLowerCase();
if (domain.contains('@')) {
domain = domain.split('@')[1];
}
domain = domain.replaceAll('^(https?://)?(www\\.)?', '');
domain = domain.split('\\.')[0]; // Remove everything after first "."
return domain;
}
public class NormalizeRequest {
@InvocableVariable(required=false)
public String companyName;
@InvocableVariable(required=false)
public String address;
@InvocableVariable(required=false)
public String phone;
@InvocableVariable(required=false)
public String domain;
}
public class NormalizedResult {
@InvocableVariable
public String normalizedCompanyName;
@InvocableVariable
public String normalizedAddress;
@InvocableVariable
public String normalizedPhone;
@InvocableVariable
public String normalizedDomain;
}
}
Implementing dedicated fields for normalized values simplifies matching and improves reliability.
L2A Match Processing
Account values should normalize continuously whenever one of the normalized fields is edited so that the Account data is already primed and ready for a Lead to match against it.
For Leads, the normalization and L2A comparison operations must happen in the same transaction one after the other. First, the new Lead's values must be normalized immediately upon creation (or edit), and then the Lead must be checked against all Accounts to see if there is a matching Account.
Only the normalization step above is best handled in Apex. The rest of the process can be built declaratively in Flow. Once the normalized values are passed from the invoked Apex back into the Lead Flow, those values can be used to query the Account table for any match using some conditional logic, such as:
1 OR (2 and (3 or 4)) OR (3 AND 4)
Where...
Website match is 1
Name match is 2
Address match is 3
Phone match is 4
Synchronous vs Asynchronous Processing
The choice between synchronous and asynchronous processing depends on lead volume and organizational needs.
Asynchronous Processing:
- Best for high-volume operations like bulk imports.
- Scalable and avoids system limits using methods like Queueable Apex or Platform Events.
- Ensures smooth user experiences without delays.
Synchronous Processing:
- Ideal for real-time lead creation.
- Ensures accurate ownership assignment during save transactions.
Example Asynchronous Implementation:
public class NormalizeAndMatchLeads implements Queueable {
public void execute(QueueableContext context) {
List<Lead> leads = [SELECT Id, Company FROM Lead WHERE ...];
// Normalize and match logic here
}
}
Lead Conversion and Customization Challenges
Even when a lead is matched to an account, Salesforce’s standard lead conversion process doesn’t use this lookup. To overcome this, you can:
- Map the Account ID: Store the matched account ID in a custom field on the lead.
- Validation Rules: Ensure the account ID matches during conversion.
- Error Messaging: Guide users on resolving mismatches to ensure Leads only convert into the same Account that they were matched against by the L2A matching algorithm.
This approach ensures leads are converted into the correct accounts, maintaining data integrity.
Looking Ahead
This article outlined the basics of L2A matching, including normalization, matching logic, and lead conversion challenges. In Part 2 of this series, we’ll explore building a lead routing model using Custom Metadata Types in Salesforce.
By implementing a thoughtful L2A matching strategy, organizations can enhance efficiency, improve customer satisfaction, and streamline their sales processes.
Comments