# GPT Action Library: Salesforce + Gong ## Introduction This page provides an instruction & guide for developers building middleware to connect a GPT Action to a specific application. Before you proceed, make sure to first familiarize yourself with the following information: - [Introduction to GPT Actions](https://platform.openai.com/docs/actions) - [Introduction to GPT Actions Library](https://platform.openai.com/docs/actions/actions-library) - [Example of Building a GPT Action from Scratch](https://platform.openai.com/docs/actions/getting-started) This particular GPT Action provides an overview of how to build a GPT that retrieves information from Salesforce and Gong. This will include creating multiple custom actions which are documented in existing cookbooks. We will highlight these cookbooks in the next section. ### Value + Example Business Use Cases **Value**: Users can now leverage ChatGPT's capabilities to: - Connect to Salesforce - Search for customer accounts - Retrieve Gong transcripts from previous calls **Example Use Cases**: A sales rep is preparing for an upcoming customer meeting. Using this integration, they can quickly retrieve relevant account details from Salesforce, access recent Gong call transcripts, and receive AI-generated summaries and insights structured around proven sales methodologies like MEDPICC or SPICED. This empowers the rep with a clear, actionable understanding of the customer's current state and next steps — all in minutes ## Application Information In this example, we are connecting to Salesforce and Gong (via a middleware). We are going to refer to existing cookbooks for basic setup and authentication instructions for Salesforce and creating a middleware. ### Salesforce GPT Action Refer to our cookbook on setting up a GPT Action for Salesforce. The two settings to pay attention to in that cookbook are: - [Application Information](https://cookbook.openai.com/examples/chatgpt/gpt_actions_library/gpt_action_salesforce#application-information) - this covers the necessary concepts to be familiar with in Salesforce - [Authentication Instructions](https://cookbook.openai.com/examples/chatgpt/gpt_actions_library/gpt_action_salesforce#authentication-instructions) - this covers creating a Connected App in Salesforce and configuring OAuth (on both Salesforce and ChatGPT) ### Middleware GPT Action Refer to any one of our cookbooks on creating a middleware: - [GPT Actions library (Middleware) - AWS](https://cookbook.openai.com/examples/chatgpt/gpt_actions_library/gpt_middleware_aws_function) - [GPT Actions library (Middleware) - Azure Functions](https://cookbook.openai.com/examples/chatgpt/gpt_actions_library/gpt_middleware_azure_function) - [GPT Actions library (Middleware) - Google Cloud Function](https://cookbook.openai.com/examples/chatgpt/gpt_actions_library/gpt_middleware_google_cloud_function) ### Application Prerequisites In addition to the prerequisites in the cookbooks above, please ensure that you have access to a Gong API key ## Application Setup ### Deploying a serverless function This serverless function will accept an array of `callIds`, fetch the transcripts from Gong and clean up the response that it sends to ChatGPT. Here is an example of what it looks like on Azure Functions (Javascript) ```javascript const { app } = require('@azure/functions'); const axios = require('axios'); // Replace with your Gong API token const GONG_API_BASE_URL = "https://api.gong.io/v2"; const GONG_API_KEY = process.env.GONG_API_KEY; app.http('callTranscripts', { methods: ['POST'], authLevel: 'function', handler: async (request, context) => { try { const body = await request.json(); const callIds = body.callIds; if (!Array.isArray(callIds) || callIds.length === 0) { return { status: 400, body: "Please provide call IDs in the 'callIds' array." }; } // Fetch call transcripts const transcriptPayload = { filter: { callIds } }; const transcriptResponse = await axios.post(`${GONG_API_BASE_URL}/calls/transcript`, transcriptPayload, { headers: { 'Authorization': `Basic ${GONG_API_KEY}`, 'Content-Type': 'application/json' } }); const transcriptData = transcriptResponse.data; // Fetch extensive call details const extensivePayload = { filter: { callIds }, contentSelector: { exposedFields: { parties: true } } }; const extensiveResponse = await axios.post(`${GONG_API_BASE_URL}/calls/extensive`, extensivePayload, { headers: { 'Authorization': `Basic ${GONG_API_KEY}`, 'Content-Type': 'application/json' } }); const extensiveData = extensiveResponse.data; // Create a map of call IDs to metadata and speaker details const callMetaMap = {}; extensiveData.calls.forEach(call => { callMetaMap[call.metaData.id] = { title: call.metaData.title, started: call.metaData.started, duration: call.metaData.duration, url: call.metaData.url, speakers: {} }; call.parties.forEach(party => { callMetaMap[call.metaData.id].speakers[party.speakerId] = party.name; }); }); // Transform transcript data into content and include metadata transcriptData.callTranscripts.forEach(call => { const meta = callMetaMap[call.callId]; if (!meta) { throw new Error(`Metadata for callId ${call.callId} not found.`); } let content = ''; call.transcript.forEach(segment => { const speakerName = meta.speakers[segment.speakerId] || "Unknown Speaker"; // Combine all sentences for the speaker into a paragraph const sentences = segment.sentences.map(sentence => sentence.text).join(' '); content += `${speakerName}: ${sentences}\n\n`; // Add a newline between speaker turns }); // Add metadata and content to the call object call.title = meta.title; call.started = meta.started; call.duration = meta.duration; call.url = meta.url; call.content = content; delete call.transcript; }); // Return the modified transcript data return { status: 200, headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(transcriptData) }; } catch (error) { context.log('[ERROR]', "Error processing request:", error); return { status: error.response?.status || 500, body: { message: "An error occurred while fetching or processing call data.", details: error.response?.data || error.message } }; } } }); ``` Here are the dependencies that you would include in your `package.json` file ```javascript "dependencies": { "@azure/functions": "^4.0.0", "axios": "^1.7.7" } ``` ## ChatGPT Steps ### Custom GPT Instructions Once you've created a Custom GPT, copy the text below in the Instructions panel. Have questions? Check out [Getting Started Example](https://platform.openai.com/docs/actions/getting-started) to see how this step works in more detail. ``` # Trigger User enters the name of an account that they want to prepare for # Steps 1. Retrieve Account Names - Make a call to the `executeSOSLSearch` custom action searching for a Salesforce Account with that name (SOSL). Retrieve up to 5 accounts. This is what the query should look like - `FIND {Acme} IN ALL FIELDS RETURNING Account(Id, Name) LIMIT 5` 2. Show the accounts in this format - `Account Name - salesforceID`. Ask the user to confirm which account they are interested in. 3. Get Gong Call IDs for the account - For the confirmed account, make a call to `executeSOQLQuery` to get all the Gong Call IDs. It should look like this - `SELECT XXX, YYY, ZZZ FROM Gong__Gong_Call__c WHERE Gong__Primary_Account__c = '' ORDER BY Gong__Call_Start__c DESC LIMIT 2 ` 4. Pass in the callIds to `getTranscriptsByCallIds ` # Trigger User says "Summarize call" # Steps Use both the transcripts and provide the following output ## Account Name Print out the account name ## Details of calls >Please list the calls for which you retrieved the transcripts along with their dates and attendees in this table format: >>Headers: , <Date>, <Attendees>, <Gong URL> ## Recommended Meeting Focus Areas: >Analyze the transcripts to identify themes, challenges, and opportunities. Based on this, generate a list of recommended focus areas for the next meeting. These should be actionable and specific to the client’s needs. Explain **why** each item should be a meeting focus. For each of the following insights, specify **which call and date** you sourced the insight from: ### Metrics Quantifiable outcomes the customer is trying to achieve. These could be cost reduction, increased revenue, user growth, efficiency gains, etc. Look for KPIs or success measures mentioned. ### Economic Buyer Identify if the true economic decision-maker was mentioned or involved. This includes titles, names, or hints at budget ownership or final authority. ### Decision Criteria What are the key factors the customer will use to evaluate solutions? These could include price, performance, support, integrations, ease of use, etc. ### Decision Process Describe how the customer plans to make the buying decision: stages, stakeholders involved, approval processes, timelines. ### Paper Process Any mention of legal, procurement, compliance, or contract-related steps and timelines should be captured here. ### Identify Pain Highlight the core business challenges the customer is facing, ideally in their own words. Understand what’s driving urgency. ### Champion Is there someone internally who is championing our solution? Mention names, roles, or behaviors that indicate advocacy (e.g., “I’m pushing this internally”). ### (Optional) Competition Mention any competing vendors, internal builds, or alternative solutions discussed. ``` In the above example, replace the query in (3) to retrieves the Gong Call IDs from your custom Salesforce object. You will now create 2 separate actions - one to connect to Salesforce and the other to connect to the middleware that calls the Gong APIs ### OpenAPI Schema for Salesforce custom action Once you've created a Custom GPT, copy the text below in the Actions panel. Have questions? Check out [Getting Started Example](https://platform.openai.com/docs/actions/getting-started) to see how this step works in more detail. Below is an example of what connecting to Salesforce might look like. You'll need to insert your URL in this section. ```javascript openapi: 3.1.0 info: title: Salesforce API version: 1.0.0 description: API for accessing Salesforce sObjects and executing queries. servers: - url: https://<subdomain>.my.salesforce.com/services/data/v59.0 description: Salesforce API server paths: /query: get: summary: Execute a SOQL Query description: Executes a given SOQL query and returns the results. operationId: executeSOQLQuery parameters: - name: q in: query description: The SOQL query string to be executed. required: true schema: type: string responses: '200': description: Query executed successfully. content: application/json: schema: $ref: '#/components/schemas/QueryResult' /search: get: summary: Execute a SOSL Search description: Executes a SOSL search based on the given query and returns matching records. operationId: executeSOSLSearch parameters: - name: q in: query description: The SOSL search string (e.g., 'FIND {Acme}'). required: true schema: type: string responses: '200': description: Search executed successfully. content: application/json: schema: $ref: '#/components/schemas/SearchResult' components: schemas: QueryResult: type: object description: Result of a SOQL query. properties: totalSize: type: integer description: The total number of records matching the query. done: type: boolean description: Indicates if the query result includes all records. records: type: array description: The list of records returned by the query. items: $ref: '#/components/schemas/SObject' SearchResult: type: object description: Result of a SOSL search. properties: searchRecords: type: array description: The list of records matching the search query. items: $ref: '#/components/schemas/SObject' SObject: type: object description: A Salesforce sObject, which represents a database table record. properties: attributes: type: object description: Metadata about the sObject, such as type and URL. properties: type: type: string description: The sObject type. url: type: string description: The URL of the record. Id: type: string description: The unique identifier for the sObject. additionalProperties: true ``` ### Authentication instructions for Salesforce custom actions Please follow the steps shown in [GPT Actions library - Salesforce](https://cookbook.openai.com/examples/chatgpt/gpt_actions_library/gpt_action_salesforce#in-chatgpt) ### OpenAPI Schema for the middleware that connects to Gong In this example, we are setting this up for an Azure Function that calls the Gong APIs. Replace `url` with your own Middleware URL ``` openapi: 3.1.0 info: title: Call Transcripts API description: API to retrieve call transcripts and associated metadata by specific call IDs. version: 1.0.1 servers: - url: https://<subdomain>.azurewebsites.net/api description: Production server paths: /callTranscripts: post: operationId: getTranscriptsByCallIds x-openai-isConsequential: false summary: Retrieve call transcripts by call IDs description: Fetches specific call transcripts based on the provided call IDs in the request body. requestBody: required: true content: application/json: schema: type: object properties: callIds: type: array description: List of call IDs for which transcripts need to be fetched. items: type: string required: - callIds responses: '200': description: A successful response containing the requested call transcripts and metadata. content: application/json: schema: type: object properties: requestId: type: string description: Unique request identifier. records: type: object description: Metadata about the pagination. properties: totalRecords: type: integer description: Total number of records available. currentPageSize: type: integer description: Number of records in the current page. currentPageNumber: type: integer description: The current page number. callTranscripts: type: array description: List of call transcripts. items: type: object properties: callId: type: string description: Unique identifier for the call. title: type: string description: Title of the call or meeting. started: type: string format: date-time description: Timestamp when the call started. duration: type: integer description: Duration of the call in seconds. url: type: string format: uri description: URL to access the call recording or details. content: type: string description: Transcript content of the call. '400': description: Invalid request. Possibly due to missing or invalid `callIds` parameter. '401': description: Unauthorized access due to invalid or missing API key. '500': description: Internal server error. ``` *Are there integrations that you’d like us to prioritize? Are there errors in our integrations? File a PR or issue in our github, and we’ll take a look.*