Back to pre-built integrations
Gmail to Salesforce integration
In today's fast-paced business landscape, effective customer relationship management (CRM) has become a linchpin of success. Salesforce, a leading CRM platform, empowers organizations to streamline their sales, marketing, and customer support efforts, providing invaluable insights into customer interactions. However, to truly harness the full potential of Salesforce, seamless integration with essential communication tools like email clients is imperative.
The example below demonstrates how to integrate Gmail and Salesforce to create new leads in Salesforce for each new marketing email.
import ballerina/lang.runtime;
import ballerina/log;
import ballerina/mime;
import ballerinax/googleapis.gmail;
import ballerinax/openai.chat;
import ballerinax/salesforce as sf;
type Email record {|
string 'from;
string subject;
string body;
|};
type Name record {|
string firstName__c;
string lastName__c;
|};
type Lead record {|
*Name;
string email__c;
string phoneNumber__c;
string company__c;
string designation__c;
|};
configurable string gmailAccessToken = ?;
configurable string openAIKey = ?;
configurable string salesforceBaseUrl = ?;
configurable string salesforceAccessToken = ?;
const LABEL = "Lead";
final gmail:Client gmail = check new ({auth: {token: gmailAccessToken}});
final chat:Client openAiChat = check new ({auth: {token: openAIKey}});
final sf:Client salesforce = check new ({baseUrl: salesforceBaseUrl, auth: {token: salesforceAccessToken}});
public function main() returns error? {
while true {
Email[] emails = check getEmails(LABEL);
Lead[] leads = from Email email in emails
let Lead? lead = generateLead(email)
where lead is Lead
select lead;
addLeadsToSalesforce(leads);
runtime:sleep(600);
}
}
function getEmails(string label) returns Email[]|error {
string[] labelIdsToMatch = check getLabelIds(gmail, [label]);
if labelIdsToMatch.length() == 0 {
return error("Unable to find any labels to match.");
}
gmail:MailThread[] matchingMailThreads = check getMatchingMailThreads(gmail, labelIdsToMatch);
removeLabels(gmail, matchingMailThreads, labelIdsToMatch);
gmail:Message[] matchingEmails = getMatchingEmails(gmail, matchingMailThreads);
return from gmail:Message message in matchingEmails
let Email|error email = parseEmail(message)
where email is Email
select email;
}
function getLabelIds(gmail:Client gmail, string[] labelsToMatch) returns string[]|error {
gmail:LabelList labelList = check gmail->listLabels("me");
return from gmail:Label {name, id} in labelList.labels
where labelsToMatch.indexOf(name) != ()
select id;
}
function getMatchingMailThreads(gmail:Client gmail, string[] labelIdsToMatch) returns gmail:MailThread[]|error {
gmail:MsgSearchFilter searchFilter = {
includeSpamTrash: false,
labelIds: labelIdsToMatch
};
return from gmail:MailThread mailThread in check gmail->listThreads(filter = searchFilter)
select mailThread;
}
function removeLabels(gmail:Client gmail, gmail:MailThread[] mailThreads, string[] labelIds) {
foreach gmail:MailThread mailThread in mailThreads {
gmail:MailThread|error removeLabelResponse = gmail->modifyThread(mailThread.id, [], labelIds);
if removeLabelResponse is error {
log:printError("An error occured in removing the labels from the thread.",
removeLabelResponse, removeLabelResponse.stackTrace(), threadId = mailThread.id, labelIds = labelIds);
}
}
}
function getMatchingEmails(gmail:Client gmail, gmail:MailThread[] mailThreads) returns gmail:Message[] {
gmail:Message[] messages = [];
foreach gmail:MailThread mailThread in mailThreads {
gmail:MailThread|error response = gmail->readThread(mailThread.id);
if response is error {
log:printError("An error occured while reading the email.",
response, response.stackTrace(), threadId = mailThread.id);
continue;
}
if !(response.messages is gmail:Message[]) || (<gmail:Message[]>response.messages).length() < 1 {
log:printError("Unable to find any messages in the thread.", threadId = mailThread.id);
continue;
}
messages.push((<gmail:Message[]>response.messages)[0]);
}
return messages;
}
function parseEmail(gmail:Message message) returns Email|error {
do {
gmail:MessageBodyPart bodyPart = check message.emailBodyInText.ensureType(gmail:MessageBodyPart);
string bodyPartText = check bodyPart.data.ensureType(string);
string body = check mime:base64Decode(bodyPartText).ensureType(string);
return {
'from: check message.headerFrom.ensureType(string),
subject: check message.headerSubject.ensureType(string),
body: body
};
} on fail error e {
log:printError("An error occured while parsing the email.", e, e.stackTrace(), message = message);
return e;
}
}
function generateLead(Email email) returns Lead? {
chat:CreateChatCompletionRequest request = {
model: "gpt-3.5-turbo",
messages: [
{
role: "user",
content: string `
Extract the following details in JSON from the email.
{
firstName__c: string, // Mandatory
lastName__c: string, // Mandatory
email__c: string // Mandatory
phoneNumber__c: string, // With country code. Use N/A if unable to find
company__c: string, // Mandatory
designation__c: string // Not mandatory. Use N/A if unable to find
}
Here is the email:
{
from: ${email.'from},
subject: ${email.subject},
body: ${email.body}
}
`
}
]
};
do {
chat:CreateChatCompletionResponse response = check openAiChat->/chat/completions.post(request);
if response.choices.length() < 1 {
check error("Unable to find any choices in the response.");
}
string content = check response.choices[0].message?.content.ensureType(string);
return check content.fromJsonStringWithType(Lead);
} on fail error e {
log:printError("An error occured while generating the lead.", e, e.stackTrace(), email = email);
return;
}
}
function addLeadsToSalesforce(Lead[] leads) {
from Lead lead in leads
do {
sf:CreationResponse|error createResponse = salesforce->create("EmailLead__c", lead);
if createResponse is error {
log:printError("An error occured while creating a Lead object on salesforce.",
createResponse, createResponse.stackTrace(), lead = lead);
} else {
log:printInfo("Lead successfully created.", lead = lead);
}
};
}