Mastering Salesforce Prompt Builder: How to Securely Invoke Apex for Dynamic Data Grounding?

Mastering Salesforce Prompt Builder: How to Securely Invoke Apex for Dynamic Data Grounding?

Generative AI is transforming how we interact with CRM data, but an LLM is only as good as the context you provide it. Without specific, real-time data from your Salesforce org, even the most advanced AI models can hallucinate or provide generic answers.

This is where Grounding comes in.

By using the Salesforce Prompt Builder, you can dynamically inject CRM data into your prompts. While merge fields work great for simple fields, sometimes you need complex logic, related records, or calculated data. The solution? Invoking Apex directly within your Prompt Template.

In this guide, we will walk through how to create a service class to fetch Account data and pass it securely to an LLM.

Why Use Apex with Prompt Templates?

Integrating Apex into your Prompt Templates allows you to:

  1. Retrieve Related Data: Fetch records that aren’t directly linked to the source object (e.g., the last 5 closed tasks).
  2. Apply Business Logic: Format dates, calculate totals, or filter data based on complex user permissions before the AI ever sees it.
  3. Secure Data Access: Ensure that the data passed to the LLM respects Salesforce sharing rules and field-level security.

The Technical Implementation: Invocable Methods

To make Apex visible to the Prompt Builder, we use the standard @InvocableMethod annotation. If you have worked with Salesforce Flows, this architecture will look very familiar.

The architecture generally follows a simple pattern:

  1. Request Class: Defines what inputs the Prompt Builder sends to Apex.
  2. Response Class: Defines what string/data the Apex returns to the Prompt.
  3. The Method: The logic that queries and formats the data.

Code Walkthrough: Fetching Recent Account Activity

Below is a production-ready example of a service class designed to be called by a Prompt Template. This class takes an Account as input and returns a formatted summary of the 5 most recent tasks.

Note: The use of WITH USER_MODE in the SOQL query is critical here. It ensures that the AI only generates content based on data the current user is actually allowed to see.

Sample Apex:

/**
 * Service class responsible for generating account summaries.
 * Designed to be invoked via Salesforce Prompt Builder.
 */
public class AccountSummaryService {
    
    /**
     * Wrapper class for the input parameters from Prompt Builder
     */
    public class Request {
        @InvocableVariable(required=true)
        public Account Input_Account;
    }

    /**
     * Wrapper class for the output result back to Prompt Builder.
     */
    public class Response {
        @InvocableVariable
        public String Prompt;
    }

    /**
     * Invocable method to retrieve recent tasks for an Account.
     * * @param requests List of Request objects containing Account details.
     * @return List<Response> containing the formatted history string.
     */
    @InvocableMethod(
        label='Get Recent Activities' 
        description='Returns the last 5 tasks for the provided Accounts'
    )
    public static List<Response> getRecentActivity(List<Request> requests) {
        
        String strSummary;
        List<Response> responses = new List<Response>();
        
        // Ensure the request list is not empty before processing
        if (requests.size() > 0) {
            
            // Note: Currently retrieving only the first request (See Best Practices regarding Bulkification)
            Request req = requests.get(0);
            Response res = new Response();
            
            if (req.Input_Account != null) {
                // Query the 5 most recent tasks associated with the specific Account
                // Enforcing USER_MODE to respect user permissions
                List<Task> recentTasks = [
                    SELECT Subject, Status, ActivityDate
                    FROM Task 
                    WHERE WhatId = :req.Input_Account.Id 
                    WITH USER_MODE
                    ORDER BY CreatedDate DESC 
                    LIMIT 5
                ];
                
                // Initialize a list to hold formatted task strings
                List<String> taskInfoList = new List<String>();
                
                // Format each task into a readable string
                for (Task t : recentTasks) {
                    taskInfoList.add(
                        '- ' + t.Subject + ' (' + t.Status + ')'
                        + ' with Due Date: ' + t.ActivityDate
                    );
                }
                
                // Join the list into a single string with newlines
                strSummary = 'Recent Tasks: \n' + String.join(taskInfoList, '\n');
            
            } else {
                // Handle scenarios where Account data is missing
                strSummary = 'Account is not passed. Please pass the Account.';
            }
                
            // Assign result to response object and add to the return list
            res.Prompt = strSummary;
            responses.add(res);
        }
        
        return responses;
    }
}

Configuring in Prompt Builder

Once your code is deployed, integrating it is straightforward:

  1. Open Prompt Builder in Salesforce Setup.
  2. Create a new Prompt Template (e.g., “Account Summary”).
  3. In the prompt workspace, select Insert Resource.
  4. Choose Apex and select the Get Recent Activities action.
  5. Map the Input Account to the current record context.

Sample Prompt:

Please analyze the activity list provided below for this Account. Categorize and summarize the activities into two distinct sections:
1. Completed Activities: Summarize what has been done, highlighting key outcomes or decisions made.
2. Pending/Upcoming Activities: List outstanding tasks, next steps, and any associated due dates.
Keep the summaries concise and professional.
{!$Apex:AccountSummaryService.Prompt}

By doing this, you drastically reduce the “noise” the LLM receives and ensure the output is hyper-relevant to the specific customer interactions logged in your CRM.


Leave a Reply