---
schema_version: "1"
id: a00ba65356a2cf0e0d6ed558379b99b29ed6b64c954ac1553e5d6b89943b6833
title: Add Apex Merge Fields to a Sales Email Prompt Template
collection: admin
version: current
locale: en-us
portal_url: https://docs.salesforce.com/admin/current/en-us/ai/264-0-0/prompt_builder_add_apex_sales_email.md
canonical_url: https://help.salesforce.com/s/articleView?id=ai.prompt_builder_add_apex_sales_email.htm&release=264.0.0&type=5
source_site: help.salesforce.com
content_type: documentation
content_hash: f1c00910b0462b864ed0fff18706e170bff4509cb4852689dcb5ed986c7c05c3
description: Create an example sales email prompt template that uses Apex.
products:
  - Agentforce
  - Cross Cloud
release: "264"
---
# Add Apex Merge Fields to a Sales Email Prompt Template

Create an example sales email prompt template that uses Apex.

### Required Editions

<table class="slds-table slds-table_bordered slds-max-medium-table_stacked slds-m-bottom_small edition"><colgroup><col></colgroup><tbody><tr class=""><td class="slds-cell-wrap" style="vertical-align:top;">Available in: Lightning Experience</td></tr><tr class=""><td class="slds-cell-wrap" style="vertical-align:top;">Available in: <strong>Enterprise</strong>, <strong>Performance</strong>, and <strong>Unlimited</strong> Editions with the Einstein for Platform, or Einstein or Agentforce for Sales or Service add-on, or Agentforce Foundations</td></tr></tbody></table>

<table class="slds-table slds-table_bordered slds-max-medium-table_stacked slds-m-bottom_small permission">
        <colgroup>
          <col style="width:50%">
          <col style="width:50%">
        </colgroup>
        <thead class="slds-text-title_caps ">
          <tr class="">
            <th scope="colgroup" class="slds-cell-wrap" colspan="2" style="text-align:center;vertical-align:top;" id="d136578e63">User Permissions Needed</th>
          </tr>
        </thead>
        <tbody>
          <tr class="">
            <td data-label="User Permissions Needed" class="slds-cell-wrap" style="vertical-align:top;" headers="d136578e63 ">To create and manage prompt templates in Prompt Builder:</td>

            <td class="slds-cell-wrap" style="vertical-align:top;" headers="d136578e63 ">
              <p>Prompt Template Manager permission set</p>
              <ul>
                <li>Manage Prompt Templates</li>

                <li>Execute Prompt Templates</li>

              </ul>
              <p>OR</p>
              <p style="margin-bottom:0;">Customize Application permission set</p>
            </td>

          </tr>
        </tbody>
      </table>

Say you’re a Salesforce admin, and your sales team requests an easier way to send emails to contacts about nearby Marketing events.

You create an email prompt template that launches an Apex class to get Marketing events in the same city and state as your contact. The Apex class gets a list of events and their locations for the LLM.

Here’s the Apex class that we access as a resource in a prompt template.

```java
public class ContactEventsPrompt {
    @InvocableMethod(label='Contact Events'
      description='Find Events for a Contact'
      CapabilityType='PromptTemplateType://einstein_gpt__salesEmail')
    public static List<Response> getContactsPrompt(List<Request> requests) {
        // validate the expected number of requests as an input
        if (requests.size() != 1)
            throw new ListException('The requests list must contain one entry only');
        Contact c = requests[0].Recipient;
        // Protect against SOQL Injection
        string searchCity = '%' + c.MailingCity + '%';
        string searchState = '%' + c.MailingState + '%';
        List<Event> events =
          [SELECT Id, Subject, Description, Location, StartDateTime
           FROM Event
           WHERE IsArchived = false AND Location LIKE :searchCity
            AND Location LIKE :searchState WITH USER_MODE]; 
        string responseData = '';

        if(events.isEmpty()) {
            responseData = 'No upcoming Marketing events near you.';
        } else {
            for(Event e : events) {
                responseData += 'Event details: ';
                responseData += e.Subject;
                responseData += ', ';
                responseData += e.Description;
                responseData += ', ';
                responseData += e.Location;
                responseData += '/nStart Time: ';
                responseData += e.StartDateTime.format();
                responseData += '.';
            }
        }

        List<Response> responses = new List<Response>();
        Response res = new Response();
        res.Prompt = responseData;
        responses.add(res);
        return responses;
      }

      public class Request {
          @InvocableVariable(required=true)
          public User Sender;
          @InvocableVariable(required=true)
          public Contact Recipient;
      }

      public class Response {
          @InvocableVariable
          public String Prompt;
      }
}
```

The requirements for the Apex class are explained in [Grounding with Apex Merge Fields](https://help.salesforce.com/s/articleView?id=ai.prompt_builder_ground_apex.htm&release=264.0.0&type=5 "You can use an Apex merge field in a prompt template to return data from a SOQL query or an external API. Apex is also effective if you want to generate well-formatted JSON or do programmatic data filtering.").

Test coverage is important so let's add a [test class](https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_testing.htm). The test class simulates data sent from Prompt Builder. Two test cases are provided: one for when events are found, and one for when there are no events.

```java
@IsTest
public class ContactEventsPrompt_Test {
  @IsTest
  public static void noEventsForContactResponse() {
    List<ContactEventsPrompt.Request> requestsInput
     = new List<ContactEventsPrompt.Request>();
    ContactEventsPrompt.Request request
     = new ContactEventsPrompt.Request();
    request.Recipient = new Contact();
    requestsInput.add(request);
    List<ContactEventsPrompt.Response> responses =
      ContactEventsPrompt.getContactsPrompt(requestsInput);
    Assert.areEqual(1, responses.size());
    ContactEventsPrompt.Response response = responses[0];
    Assert.areEqual('No upcoming Marketing events near you.', 
      response.Prompt);
  }
  

  @IsTest
  public static void findMatchingEventsForContact() {
    Contact c = new Contact(LastName='TestContact',
      MailingCity='San Francisco', MailingState='CA');
    insert c;
    Event e = new Event(Subject='Test Event', Description='Big Event', 
      Location='415 Mission St, San Francisco, CA 94105, USA',   
      StartDateTime=DateTime.newInstance(2023,09,28), 
      DurationInMinutes=5 );
    insert e;
    List<ContactEventsPrompt.Request> requestsInput
      = new List<ContactEventsPrompt.Request>();
    ContactEventsPrompt.Request request
      = new ContactEventsPrompt.Request();
    request.Recipient = c;
    requestsInput.add(request);
    

    Test.startTest();
    List<ContactEventsPrompt.Response> responses = 
      ContactEventsPrompt.getContactsPrompt(requestsInput);
    Test.stopTest();
    Assert.areEqual(1, responses.size());
    ContactEventsPrompt.Response response = responses[0];
    Assert.areEqual(formatEventString(e), response.Prompt);
  }

   
  private static String formatEventString(Event event) {
    String formattedEventString = 'Event details: ';
    formattedEventString += event.Subject;
    formattedEventString += ', ';
    formattedEventString += event.Description;
    formattedEventString += ', ';
    formattedEventString += event.Location;
    formattedEventString += '/nStart Time: ';
    formattedEventString += event.StartDateTime.format();
    formattedEventString += '.';
    return formattedEventString;
  }
}
```

Now that the Apex class is ready, let’s call it from Prompt Builder.

In Prompt Builder, create a new prompt template.

1. In the Prompt Template Type dropdown, select **Sales Email**.
2. For the Prompt Template Name, enter Apex: Nearby Events Marketing Email.
3. For the Recipient Type, select **Contact**.
4. Leave the Description field blank.
5. Click **Create**, and you’re redirected to the Prompt Template Workspace.
6. In the [Example Prompt Template Library](https://help.salesforce.com/s/articleView?id=ai.prompt_builder_example_library.htm&release=264.0.0&type=5 "This library of example prompt templates gives you a starting point for creating your own custom prompt templates."), find the sales email prompt template “Draft an email for a contact about regional marketing events.” Copy and paste this template into the Prompt Template Workspace.
7. Before you can run this template in Prompt Builder, in the Prompt section, click **Insert Resource**. The search field appears the first time you click this option.
8. Use the search bar to replace the placeholders with merge fields.
    1. In the Prompt Template Workspace, delete the `<Get_Events_Near_Contact__Prompt__Flow>` placeholder.
    2. In the Prompt section, click the **Resource** search bar, and select **Apex**.
    3. Select **Contact Events** to add the `ContactEventsPrompt` Apex class to your template. Contact Events is the label attribute that you set in the `@InvocableMethod` annotation in the `ContactEventsPrompt` Apex class.
        
        The Apex class is inserted into the prompt template as`Apex:ContactEventsPrompt`.
    4. Delete the `<sender.Name__Merge_Field>` placeholder.
    5. Click the **Resource** search bar, and select **Sender** | **Full Name** .
    6. Delete the `<recipient.Name__Merge_Field>` placeholder.
    7. Click the **Resource** search bar, and select **Recipient** | **Full Name**.
    8. Delete the `<recipient.Company__Merge_Field>` placeholder.
    9. Click the **Resource** search bar, and select **Current Organization** | **Name**.