Skip to content

Repository files navigation

Pendo Agent Analytics Full Conversation for Salesforce Agentforce

This repository contains code and configuration for capturing full agent conversation data from Salesforce Agentforce and sending it to Pendo for Agent Analytics. It interacts with Pendo solely through Pendo's authenticated Conversation API.

This example code is not a Pendo product and is not part of any Pendo commercial agreement.

License

Copyright 2026 Pendo

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Support and Warranty

This code is provided "AS IS," without warranty of any kind, as described in the license above. It is example code rather than a Pendo product, so it is not covered by a warranty or SLA, and Pendo does not commit to maintaining or fixing it.

Pendo Support can assist with standard Pendo functionality, documented in the Pendo Help Center. Implementing and troubleshooting this code, however, involves your Salesforce environment, which falls outside the scope of what the support team can advise on. For hands-on implementation help, Professional Services is the appropriate path.

Deploying in Your Environment

This code runs within your own Salesforce environment, and deploying and operating it there is your responsibility. We recommend reviewing the code, validating it against your own policies and requirements, and testing it in a non-production sandbox before deploying to production.

Because Pendo has no visibility into or control over your Salesforce org, Pendo accepts no responsibility for its behavior once deployed, including any effect on your Salesforce configuration, data, users, performance, API or event limits, security, or compliance posture. Testing in a non-production environment first is the best way to identify anything specific to your setup.


Table of Contents


How It Works

Every time a user talks to a tracked Agentforce agent, both the user's message and the agent's reply are captured and forwarded to Pendo Agent Analytics.

For each conversation turn, two events are posted to the Pendo agentic API (POST https://data.pendo.io/data/agentic) — one for the user's message (prompt) and one for the agent's reply (agentResponse) — grouped by a conversation/session ID.

Authentication is a request header: the shipped Named Credential supplies only the base URL (https://data.pendo.io), and the Apex code adds the x-pendo-integration-key header using the API key you store in the Pendo_Config__c custom setting. The Named Credential itself performs no authentication.


Deploy with an AI Coding Agent

This repo ships agent skills so an AI coding assistant can run the deployment for you. Open the repo in your tool of choice and ask it to deploy — it reads the bundled guidance, automates the deployment and verification steps, and pauses to ask for your Pendo credentials and to walk you through the manual Salesforce steps.

  • Claude Code / Claude — the pendo-agent-analytics-agentforce skill (in .claude/skills/) is auto-discovered when you open the repo. Just ask: "Deploy Pendo Agent Analytics."
  • Cursor (2.4+) — the same skill (in .cursor/skills/) appears in the / menu as /pendo-agent-analytics-agentforce.
  • Codex and other AGENTS.md-aware agents — the repo's root AGENTS.md is read automatically; open the repo and ask it to deploy. Enable network access for the agent's shell — the Salesforce CLI needs it to authenticate and deploy, and some sandboxes (e.g. Codex) disable network by default.

You still need the prerequisites (Salesforce CLI access, org capabilities, and your Pendo API Key + Agent ID). The agent handles the automatable deployment and verification; it guides you through the parts that require the Salesforce or Pendo UI.


Choose Your Approach

This repo supports two independent approaches. Both send the same core data — the difference is how they trigger. Pick one.

Data Cloud Approach (Recommended) Custom Action Approach
Best for Orgs with Data Cloud enabled Orgs without Data Cloud
How it triggers A Data Cloud-triggered Flow fires when Agentforce writes interaction records to Data Cloud Two Apex actions added to each agent topic in Agent Builder; fire during the turn
Latency Batched via Data Cloud ingestion (typically ~30–40 minutes) Synchronous during the turn (near real-time)
Agent targeting Explicit — a Pendo_Target_Agent__mdt allowlist selects which agents are tracked Implicit — only agents you wire the actions into are tracked
Per-agent setup Deploy once; add one metadata record per agent Configure each agent in Agent Builder
Runs as The Automated Process system user The interacting user
Data captured Prompt, response, session, visitor plus agentType, agentTraceId, agentResponseDurationMs, agentSubagentsUsed Prompt, response, session, visitor

Recommended: the Data Cloud approach — it captures richer telemetry with no per-turn Agent Builder wiring. Use it when Data Cloud is available. If Data Cloud does not appear in your Salesforce Setup, use the Custom Action approach.


What Gets Sent to Pendo

For every conversation turn, two events are posted to Pendo — one for the user's message and one for the agent's reply.

Field Description Approach
conversationId Groups all turns of a session together Both
messageId Unique ID per event Both
visitorId Salesforce user email address Both
account_id Salesforce Org ID Both
content The actual message text Both
agentId Pendo Agent ID from the custom setting Both
modelUsed Defaults to "Agentforce" — customizable Both
agentType e.g. Employee Data Cloud only
agentTraceId Salesforce telemetry trace reference Data Cloud only
agentResponseDurationMs How long the agent took to respond Data Cloud only
agentSubagentsUsed Which topic or subagent handled the request Data Cloud only

Customizing the payload: modelUsed is set to "Agentforce" in code — you can change it to another value, and you can send additional properties to Pendo, by customizing the payload the integration builds in PendoConversationService.buildPayload.


Repository Structure

The repo is split into three directories inside salesforce-pendo-agent-analytics/main/. Deploy base first — both approaches depend on it.

salesforce-pendo-agent-analytics/
├── README.md
├── sfdx-project.json                       # sourceApiVersion 66.0
└── salesforce-pendo-agent-analytics/
    └── main/
        ├── base/                            # Required by BOTH approaches — deploy first
        │   ├── classes/
        │   │   ├── model/       ConversationMessage.cls (+ test)
        │   │   └── service/     PendoConversationService.cls (+ BaseTestDataFactory, tests)
        │   ├── namedCredentials/ Pendo_API.namedCredential-meta.xml
        │   └── objects/         Pendo_Config__c/          # Custom Setting: API_Key__c, Agent_Id__c
        │
        ├── agentforce-custom-action/        # Custom Action approach
        │   ├── classes/         AgentforceConversationAdapter, GenerateConversationId (+ tests)
        │   ├── genAiFunctions/  Generate_Conversation_ID/, Log_Conversation_Turn_to_Pendo/
        │   ├── bots/            Agentforce_Employee_Agent/         # EXAMPLE agent — reference only
        │   └── genAiPlannerBundles/                                # EXAMPLE topics — reference only
        │
        └── data-cloud/                      # Data Cloud approach
            ├── classes/         DataCloudConversationAdapter.cls (+ test)
            ├── objects/         Pendo_Target_Agent__mdt/           # Agent allowlist (custom metadata type)
            ├── flows/           Agentforce_to_Pendo_Conversation_Sync_New_V1.flow-meta.xml
            ├── flowDefinitions/ Agentforce_to_Pendo_Conversation_Sync_New_V1.flowDefinition-meta.xml
            ├── bots/            IT_Support_Test_Agent/, Session_Trace_Test_Agent/   # EXAMPLE agents — reference only
            └── genAiPlannerBundles/         Session_Trace_Test_Agent/               # EXAMPLE bundle — reference only

Example agents are reference only. The bots/ and genAiPlannerBundles/ folders contain example Agentforce agents included to illustrate how tracking is wired up. Do not deploy them for a real integration — the deployment steps below deploy only the code, objects, and (for Data Cloud) the Flow.


Prerequisites

The lists below state what must be true before you start; resolving anything org-specific (licensing, permissions, enablement, data model upgrades) is your responsibility. Get these in place, then follow the deployment steps.

Common (both approaches)

  • Ability to deploy metadata to the org — via the Salesforce CLI (sf v2+) or any deployment method you prefer.
  • Agentforce enabled, with at least one active agent.
  • Named Credentials available (standard in Enterprise editions and above).
  • Outbound callouts permitted to https://data.pendo.io. This is handled by the shipped Named Credential — you do not need a Remote Site Setting.
  • The deploying user can create metadata (e.g. Customize Application) and the running context is allowed to make outbound callouts.

From Pendo (both approaches)

  • The agent is configured in Pendo Agent Analytics and associated with a Pendo Application. This is a prerequisite, not an afterthought: it is the source of your Agent ID, and it determines which Application's Raw Events the conversation data lands under. Configure this in Pendo first.
  • Pendo API Key — the Track Event / agentic Shared Secret for the target subscription (Pendo → Settings → Integrations → Track Event Shared Secret).
  • Pendo Agent ID — from the Agent Analytics configuration for the agent above.
  • Confirm your Pendo region/endpoint. The Named Credential ships pointing at https://data.pendo.io (US). EU or other data residencies use a different host — update the Named Credential endpoint accordingly, and verify events in the same subscription the API key belongs to.

Data Cloud approach only

  • Data Cloud provisioned in the org.
  • Salesforce Standard Data Model ≥ v1.130. Session Tracing depends on it. Check Setup → Installed Packages → Salesforce Standard Data Model and upgrade if below 1.130.
  • Einstein Generative AI and Agentforce turned on.
  • Agentforce Session Tracing available to enable (you enable it during deployment, below). See Salesforce's Session Tracing setup guide.

Clone and Authenticate

Start by cloning this repository and authenticating to the Salesforce org you are deploying into, using your preferred method.

The deployment commands below use the Salesforce CLI and assume an org alias of pendo-target — substitute your own alias, or apply the same steps in the same order if you deploy by another method.


Data Cloud Approach — Deployment

Skip to Custom Action Approach if you are not using Data Cloud.

Order matters. The Flow triggers on a Data Cloud object (ssot__AiAgentInteractionMessage__dlm) that does not exist until Session Tracing is enabled. Deploy the code and objects first, enable Session Tracing, and deploy the Flow last. Deploying the whole data-cloud/ directory in one shot will fail (the Flow can't compile before the object exists) and would also pull in the reference-only example agents.

Step 1 — Deploy base + Data Cloud code and objects

Deploy base, the Data Cloud Apex classes, and the objects — not the Flow yet, and not the example bots//genAiPlannerBundles/:

sf project deploy start \
  --source-dir salesforce-pendo-agent-analytics/main/base \
  --source-dir salesforce-pendo-agent-analytics/main/data-cloud/classes \
  --source-dir salesforce-pendo-agent-analytics/main/data-cloud/objects \
  --target-org pendo-target

Then run the tests:

sf apex run test --target-org pendo-target --test-level RunLocalTests --wait 10 --result-format human

Checkpoint: the deploy succeeds with no errors and local tests pass.

Step 2 — Add your Pendo credentials

Create the org-default record of the Pendo_Config__c custom setting:

  1. Setup → Custom Settings → Pendo Config → Manage → New

  2. Enter:

    Field Value
    API_Key__c Your Pendo Track Event / agentic Shared Secret
    Agent_Id__c Your Pendo Agent ID
  3. Save.

Never commit API key values to the repository.

Step 3 — Verify the Named Credential

  1. Setup → Named Credentials → Pendo API
  2. Confirm the URL is https://data.pendo.io (change only for non-US Pendo regions).
  3. Confirm the Identity Type is Anonymous.

Why Anonymous: the Data Cloud Flow runs as the Automated Process system user, which has no per-user (Named User) principal. With Anonymous, the callout works in that context. Authentication to Pendo is the x-pendo-integration-key header set in Apex — the Named Credential provides only the URL, so no External Credential is required.

Step 4 — Enable Agentforce Session Tracing

This is what writes Agentforce conversations into Data Cloud and creates the object the Flow triggers on. Without it, the Flow never fires.

  1. Ensure Standard Data Model ≥ v1.130 (see Prerequisites).
  2. Setup → Einstein Generative AI → Einstein Audit, Analytics, and Monitoring Setup.
  3. Scroll to Agentforce Session Tracing and toggle it on.
  4. Wait 15–30 minutes for the Data Cloud objects to become available and to populate.

Checkpoint: after the wait, the object ssot__AiAgentInteractionMessage__dlm exists. If you use the CLI:

sf sobject describe --sobject ssot__AiAgentInteractionMessage__dlm --target-org pendo-target

Step 5 — Identify the agent's recorded API name

The allowlist must match the agent name as Data Cloud recorded it, which can differ from the API name shown in Setup. After at least one conversation with the agent has been traced, read the recorded value:

sf data query \
  --query "SELECT ssot__AiAgentApiName__c FROM ssot__AiAgentSessionParticipant__dlm" \
  --target-org pendo-target

Use the exact ssot__AiAgentApiName__c value in the next step. (No rows yet? The conversation hasn't finished ingesting — wait for the Data Cloud batch and try again.)

Step 6 — Configure which agents to track

The Pendo_Target_Agent__mdt custom metadata type is an allowlist. Only agents with an active record are tracked. At least one active record is required — with none, the integration raises a configuration error.

Via Setup:

  1. Setup → Custom Metadata Types → Pendo Target Agent → Manage Records → New
  2. Set Agent API Name to the value from Step 5.
  3. Check Is Active.
  4. Save.

Or deploy a metadata record — create salesforce-pendo-agent-analytics/main/data-cloud/customMetadata/Pendo_Target_Agent.<Name>.md-meta.xml and deploy it. (If your Salesforce CLI version rejects the custom-metadata deploy, create the record via the Setup UI above, or via the Apex Metadata API — Metadata.Operations.enqueueDeployment.)

Step 7 — Deploy the Flow

Now that the trigger object exists, deploy the Flow and its definition:

sf project deploy start \
  --source-dir salesforce-pendo-agent-analytics/main/data-cloud/flows \
  --source-dir salesforce-pendo-agent-analytics/main/data-cloud/flowDefinitions \
  --target-org pendo-target

The Flow deploys as Draft.

Step 8 — Activate the Flow

  1. Setup → Flows
  2. Open Agentforce to Pendo Conversation Sync.
  3. Click Activate.

The Flow will not capture anything until it is active. It begins firing on new conversations immediately after activation.

Step 9 — Verify

See Verifying It Works. Expect a batch delay of roughly 30–40 minutes for the first conversations to appear via the Data Cloud path.


Custom Action Approach — Deployment

Skip this section if you are using the Data Cloud approach.

Step 1 — Deploy base + Custom Action metadata

sf project deploy start \
  --source-dir salesforce-pendo-agent-analytics/main/base \
  --source-dir salesforce-pendo-agent-analytics/main/agentforce-custom-action \
  --target-org pendo-target

Then run the tests:

sf apex run test --target-org pendo-target --test-level RunLocalTests --wait 10 --result-format human

Step 2 — Add your Pendo credentials

Same as the Data Cloud approach:

  1. Setup → Custom Settings → Pendo Config → Manage → New
  2. Enter API_Key__c (Track Event Shared Secret) and Agent_Id__c (Pendo Agent ID).
  3. Save.

Step 3 — Verify the Named Credential

  1. Setup → Named Credentials → Pendo API
  2. Confirm the URL is https://data.pendo.io.

Step 4 — Wire the actions into your agent (Agent Builder)

Option A — Using the example agent included in this repo

The Agentforce_Employee_Agent deploys with everything already configured:

  • The Conversation ID context variable is included in the bot metadata
  • Both Generate Conversation ID and Log Conversation Turn to Pendo actions are wired into the GeneralFAQ topic
  • Simply activate the agent in Setup → Agentforce Agents after deployment

Option B — Adding Pendo tracking to your own existing agent

Follow these steps for each agent you want to track.

Create the Conversation ID context variable

  1. Go to Setup → Agentforce Agents → [your agent] → Open in Builder

  2. Click the Context tab on the left

  3. Under Custom Variables, click New Variable

  4. Set the following:

    Field Value
    Label Conversation ID
    API Name Conversation_ID
    Data Type Text
    Description Stores the unique conversation ID for the current session. Persists across all turns and is used as conversationId in Pendo.
  5. Click Save

Add the two actions to every topic on your agent

Important: You must add both actions to every topic (subagent) on your agent. If a topic handles a conversation turn without these actions, that turn will not be captured in Pendo.

For each topic on your agent:

  1. Open the topic in Agent Builder

  2. Add Generate Conversation ID action and map:

    Parameter Map to
    Current Conversation ID (input) Conversation_ID variable
    Conversation ID (output) Conversation_ID variable
  3. Add Log Conversation Turn to Pendo action and map:

    Parameter Map to
    User Prompt The user's original message
    Agent Response The agent's reply
    Session ID Conversation_ID variable

Confirm the action order within each topic

Turn starts
    ↓
Generate Conversation ID        ← always first, before reading user message
    ↓
Agent processes and responds
    ↓
Log Conversation Turn to Pendo  ← always last, after response is ready

Verifying It Works

Check Pendo Raw Events (primary)

  1. In Pendo, go to Subscription Settings → Applications.
  2. Select the Application associated with your configured Agent.
  3. Open the Raw Events tab.
  4. Filter to your visitor — the Salesforce user email of the person who chatted.
  5. Confirm both a prompt and an agentResponse event per turn.

Read receivedTime, not the conversation timestamp. Each event carries the time the conversation happened as a property — that is not when Pendo received it. Use the event's receivedTime (epoch milliseconds) to judge arrival. On the Data Cloud path the two can differ by the full ingestion delay.

Latency expectations

  • Custom Action approach: synchronous — events arrive in near real-time.
  • Data Cloud approach: batched via Data Cloud ingestion — typically ~30–40 minutes, delivered in batches rather than per turn. A delay here is normal and does not indicate a failure.

Check the debug log (optional)

Add a debug trace for the running user (Custom Action) or check the Automated Process user's logs (Data Cloud) and look for:

Success: both messages sent to Pendo. SessionId: ...

Optional / advanced — anonymous-Apex smoke test (Data Cloud)

The fastest possible signal, useful before waiting on the Data Cloud batch (and handy for automated deployments). It invokes the adapter directly against a real interaction row and asserts success. isSuccess = true means both callouts returned HTTP 200 — the service throws on any non-2xx response.

List<SObject> rows = Database.query(
  'SELECT ssot__AiAgentSessionId__c, ssot__ContentText__c, ssot__AiAgentInteractionId__c, ' +
  'ssot__AiAgentSessionParticipantId__c, ssot__Id__c, ssot__MessageSentTimestamp__c ' +
  'FROM ssot__AiAgentInteractionMessage__dlm ' +
  'WHERE ssot__AiAgentInteractionMessageType__c = \'Output\' LIMIT 1');
if (rows.isEmpty()) { System.debug('No Output rows yet — wait for ingestion.'); return; }
SObject r = rows[0];
DataCloudConversationAdapter.FlowInput fi = new DataCloudConversationAdapter.FlowInput();
fi.sessionId     = (String)   r.get('ssot__AiAgentSessionId__c');
fi.messageType   = 'Output';
fi.content       = (String)   r.get('ssot__ContentText__c');
fi.interactionId = (String)   r.get('ssot__AiAgentInteractionId__c');
fi.participantId = (String)   r.get('ssot__AiAgentSessionParticipantId__c');
fi.messageId     = (String)   r.get('ssot__Id__c');
fi.messageTimestamp = (Datetime) r.get('ssot__MessageSentTimestamp__c');
for (DataCloudConversationAdapter.FlowOutput o :
     DataCloudConversationAdapter.handleMessage(new List<DataCloudConversationAdapter.FlowInput>{ fi })) {
  System.debug('isSuccess=' + o.isSuccess + ' | ' + o.status);
}

Run it with sf apex run --file <script>.apex --target-org pendo-target and look for isSuccess=true. This sends real events to Pendo, so you can confirm them in Raw Events immediately afterward.


Troubleshooting

Symptom Likely cause Fix
Pendo_Config__c record not found Custom Setting org-default not created Add the org-default record in Setup → Custom Settings → Pendo Config
Pendo API Key is blank Record saved without a value Edit the Custom Setting and add your key
No active Pendo_Target_Agent__mdt records No active allowlist record (Data Cloud) Add or activate a record in Custom Metadata Types
Agent's turns aren't captured (Data Cloud) Allowlist value doesn't match Data Cloud's recorded name Set Agent_API_Name__c to the exact ssot__AiAgentApiName__c value (Step 5)
Flow deploy: "We can't find the object specified in the Start element" Deployed the Flow before Session Tracing created the object Enable Session Tracing, wait 15–30 min, then deploy the Flow (Steps 4 → 7)
Flow not firing Flow still in Draft Activate it in Setup → Flows
Flow active but no events Session Tracing not enabled, or too soon after enabling Enable Session Tracing and allow 15–30 min
"Events look ~40 min late" / timestamps confusing Normal Data Cloud batch latency; you're reading the conversation timestamp Judge arrival by the event's receivedTime, not the embedded conversation time
Error [Callout] Network or URL issue Confirm the Named Credential URL is https://data.pendo.io (or your region's host)
Error [RateLimit] Pendo returned 429 Reduce conversation volume or contact Pendo support
No data in Pendo Wrong Agent ID, or wrong subscription/region Confirm the Agent ID in the Custom Setting and that you're viewing the same Pendo subscription as the API key
Events not visible under a browser filter in Pendo Server-side callouts aren't browser events Remove browser filters — these events originate from Salesforce, not a browser
Conversations not grouped (Custom Action) Conversation_ID variable not mapped consistently Map both input and output of Generate Conversation ID to the same variable

Security

  • API keys live in the Pendo_Config__c custom setting — never in code or the repo.
  • The Pendo endpoint URL is in a Named Credential — Apex never builds raw URLs.
  • Data Cloud queries use WITH USER_MODE — Salesforce sharing rules are enforced.
  • Messages containing Salesforce-masked sensitive data are skipped.
  • No secrets of any kind are committed to this repository.

About

This code is provided "AS IS," without warranty of any kind, as described in the readme license. Send Agentforce full conversations to Pendo Agent Analytics vis the Pendo Conversation API.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages