In today’s fast-paced sales environment, making accurate predictions about opportunities is essential. What if you could improve your forecast accuracy based on the insights from call comments and email conversations? That’s exactly what I set out to do by integrating salesforce opportunity prediction using Gemini AI into Salesforce, providing a data-driven way to gauge the likelihood of closing opportunities
The Challenge: Improving Opportunity Predictions
Sales teams engage with potential clients in many ways—calls, emails, meetings, and more. Yet, turning these interactions into concrete insights for opportunity management often involves manual input or reliance on subjective judgment. I wanted to create a more automated, intelligent system that interprets these interactions and provides a clear probability of an opportunity being closed-won. This is where the salesforce opportunity prediction using Gemini AI comes into play.
The Solution: Salesforce Opportunity Prediction
My solution leverages Gemini AI to analyse call comments and email conversations and return a probability score for an opportunity’s success. This solution is designed to make real-time predictions using a streamlined process that connects Salesforce tasks with Gemini AI through platform events and Lightning Web Components (LWC).
Here’s a breakdown of the process :
1. Triggering the Platform Event :
- Whenever a task (like logging a call or sending an email) is created or updated in Salesforce, a platform event is fired via a Task trigger.
- This event signals the LWC to capture the task data and initiate the API call to Gemini AI.
TaskTrigger :
trigger TaskTrigger on Task (after insert, after update) {
if (Trigger.isAfter && (Trigger.isInsert || Trigger.isUpdate)) {
// Set to store unique Opportunity IDs
Set<Id> uniqueOpportunityIds = new Set<Id>();
// Loop through new Task records to collect Opportunity IDs
for (Task record : Trigger.New) {
if (record.WhatId != null && record.WhatId.getSObjectType() == Opportunity.SObjectType) {
uniqueOpportunityIds.add(record.WhatId);
}
}
for (Id oppId : uniqueOpportunityIds) {
Test_Platform_Event__e eventInstance = new Test_Platform_Event__e();
eventInstance.Display__c = oppId; // Customize the Display field as needed
EventBus.publish(eventInstance);
}
System.debug('Published events for unique Opportunity IDs: ' + uniqueOpportunityIds);
}
}
2. Making the API Call to Gemini AI:
- The LWC then sends the task’s details—call comments and email conversations—to Gemini AI for analysis.
- Gemini AI processes this data and returns a probability score, estimating the likelihood of the opportunity closing successfully.
- We could ground the prompt with more data’s as per the individual requirements.
LWC – JS :
import { LightningElement, api } from 'lwc';
import getChatGptResponse from '@salesforce/apex/AISampleClass.makeGeminiCallout';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
import { subscribe, unsubscribe, onError, setDebugFlag } from 'lightning/empApi';
export default class AiIntegration extends LightningElement {
@api recordId; // Case record Id from the record page
loading = false;
response;
subscription = {};
displayValue = '';
channelName = '/event/Test_Platform_Event__e';
connectedCallback() {
this.handleClick('initial call');
const messageCallback = (response) => {
console.log('New Platform Event message received: ', JSON.stringify(response));
if (response && response.data && response.data.payload) {
const eventOpportunityId = response.data.payload.Display__c;
console.log('Received Opportunity ID: ' + eventOpportunityId);
// Check if the received Opportunity ID matches the recordId
if (eventOpportunityId === this.recordId) {
console.log('Opportunity ID matches the recordId');
// Call handleClick if the Opportunity ID matches the recordId
this.handleClick();
} else {
console.log('Opportunity ID does not match the recordId');
}
}
};
// Subscribe to the platform event channel
subscribe(this.channelName, -1, messageCallback).then((response) => {
console.log('Successfully subscribed to: ', response.channel);
this.subscription = response;
});
console.log('subscription--', this.subscription);
}
disconnectedCallback() {
// Unsubscribe when the component is destroyed
this.handleUnsubscribe();
}
handleUnsubscribe() {
unsubscribe(this.subscription, (response) => {
console.log('Unsubscribed from channel:', response.channel);
});
}
handleClick(dataFromCallback) {
this.loading = true;
console.log('oppId= '+this.recordId);
getChatGptResponse({ oppId: this.recordId, call: dataFromCallback })
.then(result => {
console.log(result);
this.response = result;
if (parseFloat(result) > 75) {
this.response += '🎉';
}
this.loading = false;
})
.catch(error => {
this.loading = false;
console.log('error= '+error);
this.showToast('Error', error.body.message, 'error');
});
}
showToast(title, message, variant) {
const event = new ShowToastEvent({
title,
message,
variant,
});
this.dispatchEvent(event);
}
}
Apex class :
public class OpenAISampleClass {
private static final String GEMINI_API_URL = 'https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash-latest:generateContent?key=<YOUR_KEY>';
public static String getPromptFromOpp(String oppId) {
List<Task> callTasks = [
SELECT Id, Description, TaskSubtype
FROM Task
WHERE WhatId = :oppId
ORDER BY CreatedDate ASC
];
List<String> callComments = new List<String>();
Integer count = 1;
for (Task task : callTasks) {
if (String.isNotBlank(task.Description)) {
String bodyContent = '';
if (task.Description.contains('Body:') && task.Description.contains('___')) {
bodyContent = task.Description.substringBetween('Body:', '___').trim();
} else if (task.TaskSubtype == 'Call') {
bodyContent = task.Description;
}
if (String.isNotBlank(bodyContent)) {
callComments.add(count + '. ' + bodyContent);
count++;
}
}
}
return String.join(callComments, ' ');
}
@AuraEnabled
public static String makeGeminiCallout(String oppId, String call) {
Opportunity opp = [SELECT Id, Probability__c FROM Opportunity WHERE Id = :oppId LIMIT 1];
// Check if there's already a probability value and it's an initial call
if (opp.Probability__c != null && call == 'Initial Call') {
return opp.Probability__c.toString();
}
// Generate the prompt from related tasks
String prompt = getPromptFromOpp(oppId);
if (String.isBlank(prompt)) {
return 'No relevant task data found.';
}
// Modify the prompt with the final note
prompt += '. Note: Pay close attention to the final statement, which indicates the customer\'s intent to make a purchase.';
// Make the Gemini API call
String geminiResponse = callGeminiAPI(prompt);
// If response is received, update the Opportunity field
if (String.isNotBlank(geminiResponse)) {
opp.Probability__c = Decimal.valueOf(geminiResponse.replaceAll('[^0-9]', '')); update opp; }
return geminiResponse;
}
private static String callGeminiAPI(String prompt) {
String responseText = '';
HttpRequest req = new HttpRequest();
req.setEndpoint(GEMINI_API_URL);
req.setMethod('POST');
req.setHeader('Content-Type', 'application/json');
String requestBody = '{"contents":[{"parts":[{"text":"Provided is the conversation on a Salesforce opportunity. Based on the conversation, provide the probability percentage of the opportunity getting closed won. Only provide the probability percentage. No other details to be provided. Priority to be given to the last conversation. Conversation: ' + String.escapeSingleQuotes(prompt) + '"}]}]}';
req.setBody(requestBody);
try {
Http http = new Http();
HttpResponse res = http.send(req);
if (res.getStatusCode() == 200) {
Map<String, Object> parsedResponse = (Map<String, Object>) JSON.deserializeUntyped(res.getBody());
List<Object> candidates = (List<Object>) parsedResponse.get('candidates');
if (!candidates.isEmpty()) {
Map<String, Object> firstCandidate = (Map<String, Object>) candidates[0];
Map<String, Object> content = (Map<String, Object>) firstCandidate.get('content');
List<Object> parts = (List<Object>) content.get('parts');
if (!parts.isEmpty()) {
Map<String, Object> firstPart = (Map<String, Object>) parts[0];
responseText = (String) firstPart.get('text');
}
}
} else {
System.debug('Error: ' + res.getStatusCode() + ' - ' + res.getStatus());
}
} catch (Exception e) {
System.debug('HTTP Request failed: ' + e.getMessage());
}
return responseText;
}
}
3. Displaying the Probability in LWC :
- The LWC captures the response from Gemini AI and displays the probability score directly on the Opportunity record, giving sales reps a real-time insight into their chances of winning the deal.
LWC – HTML :
<template>
<lightning-card class=”slds-p-horizontal_medium slds-p-vertical_medium” title=”Opportunity Insights” icon-name=”standard:opportunity”>
<div class=”slds-m-around_medium”>
<template if:true={loading}>
<div class=”slds-m-top_medium slds-text-align_center”>
<lightning-spinner alternative-text=”Loading” size=”medium”></lightning-spinner>
</div>
</template>
<template if:true={response}>
<div class=”slds-m-top_medium slds-text-align_center”>
<div class=”slds-box”>
<div class=”slds-text-heading_medium slds-m-bottom_small”>
Probability of Winning:
</div>
<div class=”slds-text-heading_large”>
{response}
</div>
</div>
</div>
</template>
</div>
</lightning-card>
</template>
Why Platform Events?
By using platform events, I ensured that the process remains efficient and scalable. The task trigger firing the event keeps the LWC updated in real-time. The separation of the task trigger and the API call to Gemini AI (handled by the LWC) prevents any performance bottlenecks in Salesforce while allowing for asynchronous processing. This makes the salesforce opportunity prediction using Gemini AI more efficient
The Benefits of This Approach
- AI-Powered Predictions : Sales reps receive instant feedback on their opportunities based on AI’s analysis of call and email content.
- Real-Time Updates : As soon as a task is created or updated, the LWC captures the platform event and makes the call to Gemini, ensuring that the predictions are fresh and timely.
- Enhanced Efficiency : This automation reduces the need for manual analysis, allowing sales reps to focus more on high-priority opportunities.
Future: Salesforce opportunity prediction
This integration of Salesforce opportunity prediction using Gemini AI marks the beginning of a more data-driven approach to opportunity management in Salesforce. As more data is analyzed and more interactions are processed, Gemini AI’s predictions will continue to improve, further assisting sales teams in closing deals. Future enhancements could include incorporating other forms of communication data or even predictive suggestions for the next best action. One could leverage AI even further by developing custom language models. Imagine having a model trained exclusively on your internal data, fine-tuned for your company’s unique processes, terminology and customer interactions. This could revolutionize how employees interact with Salesforce data and perform complex tasks. The possibilities are endless!