<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet title="XSL_formatting" type="text/xsl" href="https://developer.salesforce.com/blogs/wp-content/themes/dfctheme/includes/feed_styles.xsl" ?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd"
xmlns:podcast="https://podcastindex.org/namespace/1.0"
xmlns:rawvoice="https://blubrry.com/developer/rawvoice-rss/"
xmlns:media="http://search.yahoo.com/mrss/"
	xmlns:dscblog="https://developer.salesforce.com/blog/dscblog/"
>


<channel>
	<title>Salesforce Developers Blog</title>
	<atom:link href="https://developer.salesforce.com/blogs/feed" rel="self" type="application/rss+xml" />
	<link>https://developer.salesforce.com/blogs</link>
	<description>Elevating developer skills and connecting with the Salesforce Developers community</description>
	<lastBuildDate>Thu, 02 Jul 2026 16:04:16 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	
	<atom:link rel="hub" href="https://pubsubhubbub.appspot.com/" />
	<itunes:author>Salesforce Developers Blog</itunes:author>
	<itunes:explicit>false</itunes:explicit>
	<itunes:image href="https://developer.salesforce.com/blogs/wp-content/plugins/powerpress/itunes_default.jpg" />
	<itunes:owner>
		<itunes:name>Salesforce Developers Blog</itunes:name>
	</itunes:owner>
	<podcast:medium>podcast</podcast:medium>
	<image>
		<title>Salesforce Developers Blog</title>
		<url>https://developer.salesforce.com/blogs/wp-content/plugins/powerpress/rss_default.jpg</url>
		<link>https://developer.salesforce.com/blogs</link>
	</image>
	<podcast:podping usesPodping="true" />
<site xmlns="com-wordpress:feed-additions:1">244780846</site>	<item>
		<title>Build Native Mobile Apps Within Minutes Using Agent Skills</title>
		<link>https://developer.salesforce.com/blogs/2026/07/build-native-mobile-apps-within-minutes-using-agent-skills</link>
		<comments>https://developer.salesforce.com/blogs/2026/07/build-native-mobile-apps-within-minutes-using-agent-skills#respond</comments>
		<pubDate>Thu, 02 Jul 2026 15:00:35 +0000</pubDate>
		<dc:creator><![CDATA[Shalini Jain]]></dc:creator>
				<category><![CDATA[Agentforce]]></category>
		<category><![CDATA[Announcements]]></category>
		<category><![CDATA[App Development]]></category>
		<category><![CDATA[Developer Tooling]]></category>
		<category><![CDATA[Mobile]]></category>
		<category><![CDATA[Agent Skills]]></category>
		<category><![CDATA[Android]]></category>
		<category><![CDATA[app development]]></category>
		<category><![CDATA[iOS]]></category>
		<category><![CDATA[Mobile Skills]]></category>
		<category><![CDATA[Offline Sync]]></category>
		<category><![CDATA[salesforce mobile sdk]]></category>
		<category><![CDATA[smartstore]]></category>

		<guid isPermaLink="false">https://developer.salesforce.com/blogs/?p=206640</guid>
		<description><![CDATA[<p>Accelerate mobile development with the new generally available Mobile Skills, using simple prompts to generate native projects with authentication, offline support, and Mobile SDK built in.</p>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/07/build-native-mobile-apps-within-minutes-using-agent-skills">Build Native Mobile Apps Within Minutes Using Agent Skills</a> appeared first on <a href="https://developer.salesforce.com/blogs">Salesforce Developers Blog</a>.</p>
]]></description>
				<content:encoded><![CDATA[<p><span style="font-weight: 400">If you&#8217;ve ever built a Salesforce mobile app, you know the drill. You start with an idea, then spend the first two weeks wiring up authentication, setting up sync targets, and debugging offline behavior, long before you write a single line of the business logic your customer actually asked for.</span></p>
<p><span style="font-weight: 400">What if you could skip all of that and just </span><i><span style="font-weight: 400">describe</span></i><span style="font-weight: 400"> what you want? In this post, we&#8217;ll walk through how the new </span><a href="https://github.com/forcedotcom/sf-skills/tree/main/skills/mobile-apps-create"><span style="font-weight: 400">Mobile Skills</span></a><span style="font-weight: 400"> can accelerate your mobile app development, from initial project setup all the way through building real features and functionality.</span></p>
<h2>Getting past the mobile learning curve</h2>
<p><span style="font-weight: 400">Most Salesforce Developers are comfortable with business logic. What trips them up is everything around it, including OAuth handshakes, SDK version mismatches, Xcode build settings, and Gradle configurations. Native mobile development involves significant infrastructure overhead that has nothing to do with the app&#8217;s actual purpose, and it can take days just to get a project into a state where real work can begin.</span></p>
<p><span style="font-weight: 400">Mobile Skills take that entire bootstrapping phase off your plate. They generate a clean, modern project structure with authentication and SDK integration already wired up, so your first task is building a feature, not fighting a toolchain.</span></p>
<h2><b>Built for developers who live in IDEs, not GUIs</b></h2>
<p><span style="font-weight: 400">Mobile Skills output standard source files organized the way a professional iOS or Android project should be. You get a real Xcode workspace or Android Studio project that you can read, refactor, and push to Git. The code is yours from the moment it&#8217;s generated.</span></p>
<h2><b>Skills: How agents go from generic to useful</b></h2>
<p><span style="font-weight: 400">An Agent Skill is a packaged, reusable capability that an AI agent can invoke. Think of it as a well-documented function your AI assistant knows how to call, except instead of returning a string, it can scaffold a project, generate code, configure SDKs, or run commands on your behalf.</span></p>
<p><span style="font-weight: 400">Each skill ships with:</span></p>
<ul>
<li style="font-weight: 400"><b>A clear contract</b><span style="font-weight: 400"> — what it does, what inputs it expects, what it produces</span></li>
<li style="font-weight: 400"><b>Domain knowledge</b><span style="font-weight: 400"> — the patterns, APIs, and conventions for the task</span></li>
<li style="font-weight: 400"><b>Executable instructions</b><span style="font-weight: 400"> — so the agent can actually do the work, not just talk about it</span></li>
</ul>
<p><span style="font-weight: 400">Skills are how you turn a general-purpose AI into a domain expert. A generic LLM might know that the Salesforce Mobile SDK exists. A skill </span><i><span style="font-weight: 400">knows</span></i><span style="font-weight: 400"> the right project template, the right dependencies, and exactly which boilerplate to generate for your org&#8217;s auth flow.</span></p>
<h2><b>Introducing Mobile Skills</b></h2>
<p><span style="font-weight: 400">Mobile Skills give your AI agent everything it needs to build production-quality native mobile apps using Salesforce Mobile SDK, the same SDK that powers Salesforce mobile apps.</span></p>
<p><span style="font-weight: 400">Here&#8217;s what the Mobile Skills are capable of today</span><span style="font-weight: 400">:</span></p>
<ul>
<li>
<h3><b>Create a Mobile SDK app from scratch</b></h3>
</li>
</ul>
<p><span style="font-weight: 400">Describe the app you want, for example </span><i><span style="font-weight: 400">&#8220;Build me a native iOS app for field reps with offline access to Accounts and Contacts&#8221;</span></i><span style="font-weight: 400">, and </span><span style="font-weight: 400">Mobile Skills</span><span style="font-weight: 400"> scaffold a fresh Mobile SDK project, configured for your org, ready to run.</span></p>
<ul>
<li>
<h3><b>Add Mobile SDK to an existing app</b></h3>
</li>
</ul>
<p><span style="font-weight: 400">Already have an iOS or Android app?</span><span style="font-weight: 400"> Mobile Skills</span><span style="font-weight: 400"> can integrate Mobile SDK into your existing codebase without forcing you to start over. They handle dependencies, configuration, and the integration patterns that often trip people up.</span></p>
<ul>
<li>
<h3><b>SmartStore sync</b></h3>
</li>
</ul>
<p><span style="font-weight: 400">Configure encrypted, on-device storage with a single prompt. </span><span style="font-weight: 400">Mobile Skills</span><span style="font-weight: 400"> know the soup definitions, indexing strategies, and query patterns that work, so there’s no more digging through docs to figure out what IndexSpec to use.</span></p>
<ul>
<li>
<h3><b>Mobile sync</b></h3>
</li>
</ul>
<p><span style="font-weight: 400">Set up bidirectional sync between your app and your org with the right sync targets, sync down/up policies, and conflict resolution, and do it all declaratively, from a prompt.</span></p>
<ul>
<li>
<h3><b>Offline-first behavior</b></h3>
</li>
</ul>
<p><span style="font-weight: 400">Tell your agent &#8220;this app needs to work offline&#8221; and </span><span style="font-weight: 400">Mobile Skills</span><span style="font-weight: 400"> wire up the right combination of SmartStore + MobileSync, sets up appropriate cache policies, and handles the edge cases (for example, network transitions, failed syncs, and dirty records) that can take weeks to get right by hand.</span></p>
<ul>
<li>
<h3><b>Authentication and login</b></h3>
</li>
</ul>
<p><span style="font-weight: 400">Basic auth, OAuth flows, and login screens are all generated correctly the first time, following the secure patterns Salesforce recommends.</span></p>
<ul>
<li>
<h3><b>Biometric authentication</b></h3>
</li>
</ul>
<p><span style="font-weight: 400">Add Face ID, Touch ID, or Android biometrics to your app with a single instruction. </span><span style="font-weight: 400"> Mobile Skills</span><span style="font-weight: 400"> handles the platform differences, the fallback flows, and the security best practices.</span></p>
<ul>
<li>
<h3><b>Agentforce chat panel integration</b></h3>
</li>
</ul>
<p><span style="font-weight: 400">Drop a fully-wired Agentforce chat experience into your mobile app. Your end users get an intelligent, contextual agent inside </span><i><span style="font-weight: 400">their</span></i><span style="font-weight: 400"> app, configured to talk to </span><i><span style="font-weight: 400">your</span></i><span style="font-weight: 400"> org, and you don&#8217;t have to read a single integration guide.</span></p>
<h2><b>Getting started with Mobile Skills</b></h2>
<p><span style="font-weight: 400">Mobile Skills are part of </span><a href="https://github.com/forcedotcom/sf-skills"><span style="font-weight: 400">forcedotcom/sf-skills</span></a><span style="font-weight: 400">, the open-source collection of Salesforce Agent Skills for platform development. The repository is public: you can browse it, fork it, contribute to it, and pull skills into whatever agentic tool you&#8217;re already using. Think of it as a growing catalog of things AI agents should know how to do on Salesforce.</span></p>
<h3><b>Works wherever you work</b></h3>
<p><span style="font-weight: 400">Skills in this repository follow the open Agent Skills specification, which means they aren&#8217;t locked to a single tool. Mobile Skills work across surfaces, whether that&#8217;s Agentforce Vibes (available out of the box), Claude Code, Cursor, OpenCode, Codex, or any other agent that supports the spec. To install in a code agent, just run:<br />
<code>npx skills add https://github.com/forcedotcom/sf-skills --skill mobile-apps-create</code><br />
</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206644" >
			    <img fetchpriority="high" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260626111650/image1_da5284-e1782497854718.png?w=1000" class="postimages" width="1000" height="526" alt="Screenshot showing skill installation in Claude CLI" />
			  </span>
			</p>
<h3><b>How it works under the hood</b></h3>
<p><code>/mobile-apps-create</code><span> is the primary entry point — </span><span style="font-weight: 400">a lightweight orchestrator that lives in sf-skills. When you describe what you want to build (for example, &#8220;I need a Salesforce mobile app with offline sync&#8221;), the orchestrator determines which SDK family and platform you&#8217;re targeting, then routes your request to the appropriate individual skill that handles the actual work.</span></p>
<h2><b>A real example</b></h2>
<p><span style="font-weight: 400">It starts with a single sentence. In Agentforce Vibes, describe the app that you want to build. In this example, I’m building an iOS app to manage a store inventory and sync the data back to Salesforce. </span></p>
<p><span style="font-weight: 400">Agentforce Vibes picks up the intent, activates Mobile Skills under the hood — handling Mobile SDK authentication, SmartStore for offline data, and MobileSync for bidirectional Salesforce sync — and starts generating the full app.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206646" >
			    <img decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260626111656/image3_435160-e1782497957522.png?w=1000" class="postimages" width="1000" height="647" alt="Screenshot displaying Agentforce Vibes chat panel with the prompt to create a mobile app and Mobile-apps-create skill being invoked by the agent" />
			  </span>
			</p>
<p><span style="font-weight: 400">Within moments, the app is built and running in the iOS simulator. The first thing you see is the standard Salesforce OAuth login screen, secure, familiar, and fully functional. Mobile SDK handles the entire authentication flow, including token management, refresh, and session persistence. No custom auth code required.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206648" >
			    <img decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260626111702/image5_7dc8a1-e1782498037153.png?w=1000" class="postimages" width="1000" height="647" alt="Screenshot displaying the login page of the app after it launched in a simulator" />
			  </span>
			</p>
<p><span style="font-weight: 400">After login, the app lands on a home screen with a clean product inventory dashboard. Counts, categories, and visual summaries are all pulled live from the Salesforce org. This isn&#8217;t a mockup; it&#8217;s real data flowing through MobileSync&#8217;s soup-to-cloud sync pipeline, displayed in a native SwiftUI interface.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206647" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260626111659/image4_82883c-e1782498074311.png?w=1000" class="postimages" width="1000" height="647" alt="Screenshot showing the home page of the app in the simulator" />
			  </span>
			</p>
<p><span style="font-weight: 400">Tapping into the Products tab reveals the full inventory list, with each item editable. You can add a new product, update a quantity, or change a description; every modification syncs back to Salesforce automatically. Even if the device goes offline, SmartStore caches changes locally and MobileSync pushes them upstream when connectivity returns.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206650" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260626111711/image7_716480-e1782498178666.png?w=1000" class="postimages" width="1000" height="647" alt="Screenshot showing the Products tab of the app in the simulator" />
			  </span>
			</p>
<p><span style="font-weight: 400">But why stop at inventory management? Back in the Agentforce Vibes prompt, I went ahead and asked the agent to embed an Agentforce chat panel. Just like that, the app now includes a native Agentforce conversational interface right inside the app. The Agentforce SDK handles the agent connection, conversation threading, and action orchestration, all wired up from a single prompt.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206645" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260626111654/image2_5d2329-e1782498218517.png?w=1000" class="postimages" width="1000" height="647" alt="Screenshot showing the Agentforce added to the app" />
			  </span>
			</p>
<p><span style="font-weight: 400">Store Managers can use the Agentforce chat to ask questions like &#8220;Which products are low in stock?&#8221; or ask it to refill stock without ever leaving the app. It&#8217;s not just an app anymore; it&#8217;s an AI-powered mobile experience, built in minutes.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206649" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260626111705/image6_201d89-e1782498252472.png?w=1000" class="postimages" width="1000" height="647" alt="Screenshot of the Agentforce chat panel during an ongoing conversation to order stock replenishment" />
			  </span>
			</p>
<h2><b>What&#8217;s Next</b></h2>
<p><span style="font-weight: 400">The Mobile Skills you see today are the foundation. We&#8217;re actively expanding the library to cover more SDK capabilities, more frameworks, and richer end-to-end app generation flows. If you&#8217;re building mobile apps on Salesforce, this is the moment to plug AI into your workflow, not as a typing assistant, but as a companion engineer who already knows the platform.</span></p>
<p><b>Your feedback matters! Try Mobile Skills, share your </b><a href="https://forms.gle/5cyRGd9tt5JCSk3x7"><b>feedback</b></a><b>, and tell us what to build next.</b></p>
<h2><b>Resources:</b></h2>
<ul>
<li style="font-weight: 400"><a href="https://github.com/forcedotcom/sf-skills"><span style="font-weight: 400">Explore sf-skills on GitHub</span></a></li>
<li style="font-weight: 400"><a href="https://developer.salesforce.com/workshops/agentforce-workshop/agents/1-get-started"><span style="font-weight: 400">Get Started with Agentforce</span></a></li>
<li style="font-weight: 400"><a href="https://developer.salesforce.com/docs/platform/mobile-sdk/overview"><span style="font-weight: 400">Mobile SDK Developer Guide</span></a></li>
<li style="font-weight: 400"><a href="https://developer.salesforce.com/docs/platform/mobile-sdk/guide/install-github.html"><span style="font-weight: 400">Mobile SDK GitHub repository</span></a></li>
</ul>
<h2><b>About the author</b></h2>
<p><span style="font-weight: 400"><strong>Shalini Jain</strong> is a Director of Product Management at Salesforce, where her primary focus lies in shaping the Mobile Platform Experience for developers through AI-driven innovation. Her current initiatives are heavily invested in enhancing developer productivity by bringing agentic AI capabilities directly into the mobile development workflow, as evidenced by the tools highlighted throughout this blog post. Furthermore, she is spearheading new features in Mobile SDK to further streamline and optimize how developers build, test, and ship mobile apps, with the ultimate goal of delivering a truly seamless and intuitive experience for all Salesforce mobile developers.</span></p>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/07/build-native-mobile-apps-within-minutes-using-agent-skills">Build Native Mobile Apps Within Minutes Using Agent Skills</a> appeared first on <a href="https://developer.salesforce.com/blogs">Salesforce Developers Blog</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://developer.salesforce.com/blogs/2026/07/build-native-mobile-apps-within-minutes-using-agent-skills/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
	<post-id xmlns="com-wordpress:feed-additions:1">206640</post-id><media:thumbnail url="https://d259t2jj6zp7qm.cloudfront.net/images/20260626143403/Generic-B-4-e1782509658601.png?w=1000" />
<media:content url="https://d259t2jj6zp7qm.cloudfront.net/images/20260626143403/Generic-B-4-e1782509658601.png?w=1000" medium="image" />
	</item>
		<item>
		<title>LWC State Managers: Share Reactive State Across Components</title>
		<link>https://developer.salesforce.com/blogs/2026/07/lwc-state-managers-share-reactive-state-across-components</link>
		<comments>https://developer.salesforce.com/blogs/2026/07/lwc-state-managers-share-reactive-state-across-components#respond</comments>
		<pubDate>Wed, 01 Jul 2026 15:00:22 +0000</pubDate>
		<dc:creator><![CDATA[Ben Sklar]]></dc:creator>
				<category><![CDATA[App Development]]></category>
		<category><![CDATA[Architecture]]></category>
		<category><![CDATA[Lightning Web Components]]></category>
		<category><![CDATA[Salesforce Releases]]></category>
		<category><![CDATA[@lwc/state]]></category>
		<category><![CDATA[Context API]]></category>
		<category><![CDATA[lightning data service]]></category>
		<category><![CDATA[LWC State Managers]]></category>
		<category><![CDATA[Reactive Programming]]></category>
		<category><![CDATA[State Management]]></category>
		<category><![CDATA[Summer '26 Release]]></category>

		<guid isPermaLink="false">https://developer.salesforce.com/blogs/?p=206634</guid>
		<description><![CDATA[<p>Learn how to use the new generally available @lwc/state module to share reactive data across component trees without prop drilling.</p>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/07/lwc-state-managers-share-reactive-state-across-components">LWC State Managers: Share Reactive State Across Components</a> appeared first on <a href="https://developer.salesforce.com/blogs">Salesforce Developers Blog</a>.</p>
]]></description>
				<content:encoded><![CDATA[<p><span>State managers are now generally available in Lightning Web Components (LWC)! The Summer &#8217;26 release ships the </span><a href="https://developer.salesforce.com/docs/platform/lwc/guide/state-management.html"><u>@lwc/state</u></a><span> module, providing developers with a first-class, reactive pattern for organizing and sharing application data across component trees without prop drilling, custom events, or pub/sub workarounds, plus a library of ready-made state managers from the </span><code>lightning</code><span> namespace built on the same Lightning Data Service and UI API foundations you already use today.</span></p>
<h2><span>Defining state in LWC with </span><code>defineState</code></h2>
<p><span>A state manager is a dedicated JavaScript module that encapsulates a related set of data (the &#8220;state&#8221;) and the actions that operate on it. You create one with the </span><code>defineState</code><span> function from </span><code>@lwc/state</code><span>, passing a setup function. The setup function&#8217;s first argument is an object of building blocks: </span><code>atom</code><span>, </span><code>computed</code><span>, and </span><code>setAtom</code><span>. </span><code>defineState</code><span> returns a factory; any arguments you pass when you call it flow through to your setup function after that object, so you can parameterize each instance:</span></p>
<ul>
<li><span>An </span><code><span>atom</span></code><span> is a reactive “atomic” value, which is essentially a wrapper around a piece of data that LWC can use to make components and other state managers reactive to changes in that data.</span></li>
<li><span>A </span><code><span>computed</span></code><span> is a lazily-evaluated derivation that recalculates only when its atom dependencies change.</span></li>
<li><code><span>setAtom</span></code><span> is used whenever you need to modify state.</span></li>
</ul>
<pre language="javascript">import { defineState } from '@lwc/state';

export const createCounter = defineState(({ atom, computed, setAtom }, initialCount = 0) =&gt; {
    const count = atom(initialCount);
    const doubled = computed([count], (countValue) =&gt; countValue * 2);


    const increment = () =&gt; setAtom(count, count.value + 1);


    return { count, doubled, increment };
});
</pre>
<p><span>Any component that references </span><code>count</code><span> or </span><code>doubled</code><span> in a reactive context is automatically re-rendered after </span><code>increment</code><span> runs. Computed values recompute lazily and only when their inputs change, and multiple </span><code>setAtom</code><span> calls in the same tick are batched into a single notification cycle, so you get reactivity without the re-render churn.</span></p>
<h2><span>Sharing state across a component tree with </span><code>fromContext</code></h2>
<p><span>State managers go beyond a single component. You can create an instance at the top of a component tree and consume it in a descendant component below using </span><code>fromContext</code><span>, with no prop drilling, no event handling, and no pub/sub plumbing:</span></p>
<p><span style="font-weight: 400">Example code for the top-level component that instantiates the state manager:</span></p>
<pre language="javascript">import createShopStateManager from 'x/shopState';

export default class App extends LightningElement {
    shopState = createShopStateManager();
}
</pre>
<p><span style="font-weight: 400">Example of a descendant component that reads the same instance via LWC context:</span></p>
<pre language="javascript">import { LightningElement } from 'lwc';
import { fromContext } from '@lwc/state';
import createShopStateManager from 'x/shopState';

export default class Cart extends LightningElement {
    shopState = fromContext(createShopStateManager);

    get cartTotal() {
        return `$${this.shopState.value.cartTotal.toFixed(2)}`;
    }
}
</pre>
<h2><span>Built-in state managers for Salesforce data</span></h2>
<p><span>Salesforce ships a </span><a href="https://developer.salesforce.com/docs/platform/lwc/guide/reference-state-managers.html"><u>collection of state managers</u></a><span> in the </span><code>lightning</code><span> namespace that expose Salesforce data and metadata through the Lightning Data Service. Each one presents a uniform shape:</span></p>
<ul>
<li><code><span>status</span></code><span> (</span><code><span>”unconfigured”</span></code><span> | </span><code><span>”loading”</span></code><span> | </span><code><span>”loaded”</span></code><span> | </span><code><span>”error”</span></code><span>)</span></li>
<li><code><span>data</span></code><code></code></li>
<li><code><span>error</span></code></li>
<li><code></code><span>setter functions for config</span><span>so using and composing them is straightforward.</span></li>
</ul>
<p><span>Available state managers expose record, objectInfo, layout, and related list data. Because state managers can be nested and wired together with </span><code>computed</code><span>, you can build sophisticated, fully reactive data waterfalls, for example: fetch a record to discover its record type, then fetch the matching layout, then fetch the full record using the layout&#8217;s fields.</span></p>
<p><span>Example of a component that consumes a lightning/stateManagerRecord:</span></p>
<pre language="javascript">import { LightningElement, api } from 'lwc';
import { getFieldValue } from 'lightning/uiRecordApi';
import createRecordStateManager from 'lightning/stateManagerRecord';
import ACCOUNT_NAME_FIELD from '@salesforce/schema/Account.Name';
import ACCOUNT_INDUSTRY_FIELD from '@salesforce/schema/Account.Industry';

export default class AccountCard extends LightningElement {
    _recordId;

    // The built-in state manager instance. Config takes the schema field refs;
    // recordId is supplied later via the @api setter.
    state = createRecordStateManager({
        fields: [ACCOUNT_NAME_FIELD, ACCOUNT_INDUSTRY_FIELD]
    });

    @api
    set recordId(id) {
        this._recordId = id;
        // Updaters live on the frozen outer shape, so go through .value.
        this.state.value.setRecordId(id);
    }
    get recordId() {
        return this._recordId;
    }

    get displayLabel() {
        const { status, data } = this.state.value;
        if (status !== 'loaded') return 'Loading…';
        const name = getFieldValue(data, ACCOUNT_NAME_FIELD) ?? 'Unknown';
        const industry = getFieldValue(data, ACCOUNT_INDUSTRY_FIELD) ?? '';
        return industry ? `${name} — ${industry}` : name;
    }
}
</pre>
<h2><b>See state managers in action</b></h2>
<p><span style="font-weight: 400">State managers bring a modern, reactive data-flow pattern to LWC that scales from a single component to a full application. Because state managers are plain JavaScript modules with no DOM dependency, you can unit-test them with Jest without mounting a component. You can reuse the same state module across multiple component trees. And because consumers read values from the state manager&#8217;s public shape rather than reaching into its internals, you can refactor how state is stored without updating every consumer. Additionally, with the built-in UI API state managers, you get that same shape for Salesforce data out of the box. Note: state managers are available in Lightning Experience, but not yet supported in Experience Cloud.</span></p>
<p><span style="font-weight: 400">To help you get started, we&#8217;ve published two complete examples in the </span><a href="https://github.com/forcedotcom/state-management/tree/main/examples"><span style="font-weight: 400">forcedotcom/state-management GitHub repository</span></a><span style="font-weight: 400">:</span></p>
<ul>
<li style="font-weight: 400"><b>simple-store</b><span style="font-weight: 400">: A standalone shopping-cart application (</span><a href="https://youtu.be/u-N1kEYB7ts?si=DJlbEz3DeyhqptAU&amp;t=371"><span style="font-weight: 400">demoed at TDX in March 2026</span></a><span style="font-weight: 400">) that shows how multiple components can read and mutate the same state manager entirely through context, with no properties or events. You can run it live on </span><a href="https://stackblitz.com/github/forcedotcom/state-management/tree/main/examples/simple-store"><span style="font-weight: 400">StackBlitz</span></a><span style="font-weight: 400">.</span></li>
<li style="font-weight: 400"><b>platform-state-managers</b><span style="font-weight: 400">: A Salesforce DX project that builds a layout-driven record detail panel using the record and layout state managers. The source is heavily commented, and is the best place to learn the basics.</span></li>
</ul>
<h2><b>More resources</b></h2>
<p><span style="font-weight: 400">Ready to start managing your state? Here are additional resources you need to dive in:</span></p>
<ul>
<li style="font-weight: 400"><span style="font-weight: 400">Developer Documentation: </span><a href="https://developer.salesforce.com/docs/platform/lwc/guide/state-management.html"><span style="font-weight: 400">Manage State Across LWC Components</span></a></li>
<li style="font-weight: 400"><span style="font-weight: 400">GitHub repository (</span><a href="https://github.com/forcedotcom/state-management"><span style="font-weight: 400">forcedotcom/state-management</span></a><span style="font-weight: 400">): Find the platform-state-managers and simple-store examples.</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Live playground (</span><a href="https://stackblitz.com/github/forcedotcom/state-management/tree/main/examples/simple-store"><span style="font-weight: 400">Simple Store on StackBlitz</span></a><span style="font-weight: 400">): Explore a full state-manager app in your browser.</span></li>
</ul>
<h2><b>About the Authors</b></h2>
<p><b>Mike Burr</b><span style="font-weight: 400"> is a Principal Member of Technical Staff at Salesforce from the Lightning Data Service team. When he’s not building client data libraries, he&#8217;s busy tweaking his home automation and listening to audiobooks.</span></p>
<p><b>Ben Sklar</b><span style="font-weight: 400"> is a Director of Product Management at Salesforce responsible for UI API, GraphQL API, Lightning Data Service, and AI Developer Kit. Ben is a major fan of GraphQL, but when not using GraphQL, you can find him playing ultimate frisbee or skiing during the winter. Follow him on </span><a href="https://www.linkedin.com/in/benjamin-sklar/"><span style="font-weight: 400">LinkedIn</span></a><span style="font-weight: 400">.</span></p>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/07/lwc-state-managers-share-reactive-state-across-components">LWC State Managers: Share Reactive State Across Components</a> appeared first on <a href="https://developer.salesforce.com/blogs">Salesforce Developers Blog</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://developer.salesforce.com/blogs/2026/07/lwc-state-managers-share-reactive-state-across-components/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
	<post-id xmlns="com-wordpress:feed-additions:1">206634</post-id><media:thumbnail url="https://d259t2jj6zp7qm.cloudfront.net/images/20260626093251/Generic-A-5-e1782491584474.png?w=1000" />
<media:content url="https://d259t2jj6zp7qm.cloudfront.net/images/20260626093251/Generic-A-5-e1782491584474.png?w=1000" medium="image" />
	</item>
		<item>
		<title>How to Secure Salesforce Hosted MCP Servers</title>
		<link>https://developer.salesforce.com/blogs/2026/06/how-to-secure-salesforce-hosted-mcp-servers</link>
		<comments>https://developer.salesforce.com/blogs/2026/06/how-to-secure-salesforce-hosted-mcp-servers#respond</comments>
		<pubDate>Tue, 30 Jun 2026 15:00:38 +0000</pubDate>
		<dc:creator><![CDATA[Philippe Ozil]]></dc:creator>
				<category><![CDATA[App Development]]></category>

		<guid isPermaLink="false">https://developer.salesforce.com/blogs/?p=206595</guid>
		<description><![CDATA[<p>Learn how to secure Salesforce Hosted MCP Servers from authentication, authorization, permission controls, and logging.</p>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/06/how-to-secure-salesforce-hosted-mcp-servers">How to Secure Salesforce Hosted MCP Servers</a> appeared first on <a href="https://developer.salesforce.com/blogs">Salesforce Developers Blog</a>.</p>
]]></description>
				<content:encoded><![CDATA[<p><a href="https://developer.salesforce.com/docs/platform/hosted-mcp-servers/guide/hosted-mcp-servers-overview.html"><span style="font-weight: 400">Salesforce Hosted MCP (Model Context Protocol) Servers</span></a><span style="font-weight: 400"> are one of the key components of Headless 360, letting you extend agents with </span><a href="https://developer.salesforce.com/docs/platform/hosted-mcp-servers/guide/servers-reference.html"><span style="font-weight: 400">standard</span></a><span style="font-weight: 400"> and </span><a href="https://developer.salesforce.com/docs/platform/hosted-mcp-servers/guide/custom-servers.html"><span style="font-weight: 400">custom</span></a><span style="font-weight: 400"> capabilities. As you expose tools, prompts, and resources to users, securing access to these servers is critical to protect your data and operations.</span></p>
<p><span style="font-weight: 400">Whether you&#8217;re a developer building MCP servers or an admin managing them, understanding the security model helps you control who can access what, when, and how. In this post, we&#8217;ll cover authentication, authorization, permission controls, and logging best practices for Salesforce Hosted MCP Servers.</span></p>
<h2><span style="font-weight: 400">Understanding the Salesforce Hosted MCP Server security model</span></h2>
<p><span style="font-weight: 400">The Salesforce MCP security model has three layers:</span></p>
<ol>
<li style="font-weight: 400"><b>Authentication:</b><span style="font-weight: 400"> Verifies who&#8217;s making the request</span></li>
<li style="font-weight: 400"><b>Authorization:</b><span style="font-weight: 400"> Determines what they&#8217;re allowed to do</span></li>
<li style="font-weight: 400"><b>Permission controls:</b><span style="font-weight: 400"> Enforce granular access at the object and field level when working with the primitives (tools, prompts and resources) exposed by the server</span></li>
</ol>
<p><span style="font-weight: 400">And, in addition to these security layers, </span><b>logging</b><span style="font-weight: 400"> tracks all activity for audit and compliance.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206605" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260624113313/image2_b02f7e-e1782326005117.png?w=1000" class="postimages" width="1000" height="770" alt="Diagram illustrating the three security layers and logging" />
			  </span>
			</p>
<p><span style="font-weight: 400">The first two layers are directly linked to the MCP standard and the third (permission controls) is specific to the Salesforce Platform. Each security layer builds on the previous one. You can&#8217;t authorize someone until you know who they are. You can&#8217;t enforce permissions without authorization rules.</span></p>
<p><span style="font-weight: 400">Understanding how these layers and logging work together helps you build a defense-in-depth strategy. Let&#8217;s look at each layer in detail.</span></p>
<h3><span style="font-weight: 400">Authentication: Verifying identity</span></h3>
<p><span style="font-weight: 400">Authentication is the first line of defense. It answers the question: &#8220;Who are you?&#8221;</span></p>
<p><span style="font-weight: 400">Authentication and authorization are technically defined as optional for MCP implementations as per the </span><a href="https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization"><span style="font-weight: 400">MCP specification</span></a><span style="font-weight: 400">, but we will assume for the rest of this post that it&#8217;s mandatory in an enterprise context given that without proper security, unauthorized users could access sensitive data or trigger unwanted operations.</span></p>
<p><span style="font-weight: 400">MCP specifies </span><a href="https://datatracker.ietf.org/doc/html/rfc6749"><span style="font-weight: 400">OAuth 2.0</span></a><span style="font-weight: 400"> as its authentication and authorization mechanism. It supports only a subset of OAuth 2.0 — not every authorization flow or grant type is available. Popular clients like Claude and ChatGPT support fewer flows than Salesforce does.</span></p>
<p><span style="font-weight: 400">While MCP is fairly recent, OAuth 2.0 has been around for more than a decade and the Salesforce Platform has a number of mechanisms to integrate with OAuth, whether it acts as a client or a server. This means that, as a Salesforce Developer, you do not have to implement OAuth security. Instead, you control it declaratively through an </span><a href="https://help.salesforce.com/s/articleView?id=xcloud.external_client_apps.htm&amp;type=5"><span style="font-weight: 400">external client app</span></a><span style="font-weight: 400"> (ECA).</span></p>
<p><span style="font-weight: 400">We strongly recommend creating a dedicated ECA per MCP client (one for Claude, one for ChatGPT, one for Cursor, etc.) rather than sharing a single ECA across multiple clients. This helps with controlling access and auditing the client&#8217;s activity.</span></p>
<p><span style="font-weight: 400">When it comes to Salesforce Hosted MCP Servers, we only support the </span><a href="https://help.salesforce.com/s/articleView?id=xcloud.remoteaccess_oauth_web_server_flow.htm&amp;type=5"><span style="font-weight: 400">authorization code flow</span></a><span style="font-weight: 400"> through the ECA. This means that users have to authenticate with Salesforce when connecting to the MCP server. Their individual Salesforce user account is tied to the MCP session.</span></p>
<p><b>Notes:</b></p>
<ul>
<li style="font-weight: 400"><span style="font-weight: 400">There&#8217;s no option to specify a service account with a principal user, such as an integration user, to be used across all sessions. This is an anti-pattern that customers should avoid.</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">At this time, there&#8217;s no plan to allow machine-to-machine flows. The human remains in the loop to connect to the org and grant access to the MCP tools.</span></li>
</ul>
<p><span style="font-weight: 400">By default, any user from your org can connect to your ECA and access the MCP servers. However, you can specify authorization rules to control the access.</span></p>
<h3><span style="font-weight: 400">Authorization: Controlling access</span></h3>
<p><span style="font-weight: 400">Authorization tells you what the user can do. Once a user is authenticated, Salesforce evaluates their permissions to determine which resource they can access.</span></p>
<p><span style="font-weight: 400">There are a number of mechanisms that you can put in place to control authorization.</span></p>
<h4><span style="font-weight: 400">Authorize access with OAuth scopes</span></h4>
<p><span style="font-weight: 400">The first level of authorization to access specific resources is handled by </span><a href="https://help.salesforce.com/s/articleView?id=xcloud.remoteaccess_oauth_tokens_scopes.htm&amp;type=5"><span style="font-weight: 400">OAuth scopes</span></a><span style="font-weight: 400"> when configuring an ECA.</span></p>
<p><span style="font-weight: 400">The new &#8220;Access Salesforce hosted MCP servers (mcp_api)&#8221; scope grants access to Salesforce Hosted MCP Servers. We&#8217;ve released this scope to avoid exposing the &#8220;Manage user data via APIs (api)&#8221; scope that grants full access to the Platform APIs (REST, Tooling, Metadata, etc.).</span></p>
<p><span style="font-weight: 400">As a general rule, it&#8217;s preferable to only expose a limited set of &#8220;safe&#8221; operations to agents via MCP rather than to allow full API access.</span></p>
<h4><span style="font-weight: 400">Restrict access to pre-authorized users</span></h4>
<p><span style="font-weight: 400">With the default ECA configuration, all users from your org can authenticate and access the MCP servers. You can restrict access to specific pre-authorized users by configuring an </span><a href="https://help.salesforce.com/s/articleView?id=xcloud.preauth_user_app_access_through_eca.htm&amp;type=5"><span style="font-weight: 400">app policy</span></a><span style="font-weight: 400">. This lets you select users with specific profiles or permission sets.</span></p>
<p><span style="font-weight: 400">For example, this configuration only allow users with the &#8220;MCP Client User&#8221; permission set to connect to the ECA:</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206606" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260624113407/image6_3e1252-e1782326059603.png?w=830" class="postimages" width="830" height="1000" alt="Screenshot of the ECA configuration showing how to only allow users with the &quot;MCP Client User&quot; permission set" />
			  </span>
			</p>
<h4><span style="font-weight: 400">Enforce IP restrictions</span></h4>
<p><span style="font-weight: 400">In addition to restricting the use of the ECA to specific users, you can apply IP restrictions by only specifying IP ranges to connect. These restrictions are enabled in the default ECA configuration under </span><b>App Authorization</b><span style="font-weight: 400">.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206607" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260624113454/image4_d55bd8.png?w=971" class="postimages" width="971" height="352" alt="Screenshot of the ECA configuration showing how to apply IP restrictions" />
			  </span>
			</p>
<p><span style="font-weight: 400">You can specify trusted IP ranges for all users in the </span><b>Network Access</b><span style="font-weight: 400"> setup menu or for specific user profiles.</span></p>
<h4><span style="font-weight: 400">Shorten refresh token lifetime</span></h4>
<p><span style="font-weight: 400">Once a user logs in with the ECA, they acquire an access token that is passed to all subsequent MCP operations. The token must be refreshed after its lifetime expires.</span></p>
<p><span style="font-weight: 400">The default token lasts for a year, but you can control the duration of the token lifetime and reduce it in production. You can do this to limit the risk of token theft and reuse for malicious intents. To control the token lifetime, go in the ECA configuration and look for </span><b>Refresh Token Policy</b><span style="font-weight: 400"> under App Authorization. </span><a href="https://developer.salesforce.com/docs/platform/hosted-mcp-servers/guide/create-external-client-app.html"><span style="font-weight: 400">Our documentation</span></a><span style="font-weight: 400"> includes recommendations on how to improve security in this area.</span></p>
<h4><span style="font-weight: 400">Revoke access</span></h4>
<p><span style="font-weight: 400">Should you need to revoke tokens for your ECA before they expire, go to Setup, search for </span><b>OAuth Usage</b><span style="font-weight: 400">, select your ECA, and revoke individual tokens or run a bulk revoke operation.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206608" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260624113541/image5_601ace-e1782326154340.png?w=1000" class="postimages" width="1000" height="285" alt="Screenshot showing how to revoke your ECA&apos;s tokens." />
			  </span>
			</p>
<h4><span style="font-weight: 400">Activate MCP servers</span></h4>
<p><span style="font-weight: 400">By default, all MCP servers are inactive. Only </span><a href="https://help.salesforce.com/s/articleView?id=platform.api_catalog_activate_salesforce_mcp_servers.htm&amp;type=5"><span style="font-weight: 400">activate</span></a><span style="font-weight: 400"> the ones that you intend to expose to agents.</span></p>
<p><span style="font-weight: 400">For example, here we&#8217;ve only activated a custom server and two standard servers:</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206609" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260624113627/image7-e1782326199645.png?w=1000" class="postimages" width="1000" height="445" alt="Screenshot showing three active MCP servers." />
			  </span>
			</p>
<h4><span style="font-weight: 400">Annotate MCP tools</span></h4>
<p><span style="font-weight: 400">While not strictly a security measure, it&#8217;s a best practice to </span><a href="https://help.salesforce.com/s/articleView?id=platform.api_catalog_create_custom_salesforce_mcp_servers.htm&amp;type=5"><span style="font-weight: 400">annotate MCP tools</span></a><span style="font-weight: 400"> that you expose to provide hints about their behavior. Doing so lets agents know how they should use the tools. For example, should the agent ask the user for confirmation when running potentially destructive operations, such as deleting a record, or whether a tool can safely be run multiple times.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206610" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260624113723/image3_c4b552.png?w=831" class="postimages" width="831" height="722" alt="Screenshot of MCP tool annotation configuration" />
			  </span>
			</p>
<p><b>Note:</b><span style="font-weight: 400"> Support for </span><a href="https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations"><span style="font-weight: 400">MCP tool annotations</span></a><span style="font-weight: 400"> is optional on the client side and these may not be enforced by all agents.</span></p>
<h3><span style="font-weight: 400">Permission controls: Granular access management</span></h3>
<p><span style="font-weight: 400">You cannot restrict access to a specific MCP server through the ECA configuration, but you control access to the tools that compose those servers.</span></p>
<p><span style="font-weight: 400">MCP tools run with the same permissions as the user who authenticated with the ECA. This means that our </span><a href="https://help.salesforce.com/s/articleView?id=platform.security_data_access.htm&amp;type=5"><span style="font-weight: 400">core security model</span></a><span style="font-weight: 400"> applies at all levels:</span></p>
<ul>
<li style="font-weight: 400"><span style="font-weight: 400">Object-level access (CRUD permissions)</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Field-level security (FLS) restrictions</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Record sharing rules</span></li>
</ul>
<p><span style="font-weight: 400">It’s also possible to </span><a href="https://developer.salesforce.com/docs/platform/hosted-mcp-servers/guide/custom-servers.html"><span style="font-weight: 400">implement custom tools</span></a><span style="font-weight: 400"> with Agentforce, Apex, or Flow to </span><a href="https://developer.salesforce.com/docs/platform/hosted-mcp-servers/guide/security-best-practices.html"><span style="font-weight: 400">address unique security requirements</span></a><span style="font-weight: 400">. If you choose this path, always follow </span><a href="https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_classes_perms_enforcing.htm"><span style="font-weight: 400">security best practices</span></a><span style="font-weight: 400">.</span></p>
<p><span style="font-weight: 400">Be sure to work with permission sets to protect sensitive operations and follow the principle of least privilege. Grant only the permissions that users need to do their jobs. Review permissions regularly and remove access that&#8217;s no longer required.</span></p>
<p>Also, test permissions before deploying to production. For example, run Apex tests and Flow tests on your MCP tools with the <code>runAs</code> <a href="https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_testing_tools_runas.htm"><u>method</u></a> to simulate the behavior of users with different access levels.</p>
<h3><span style="font-weight: 400">Logging: Tracking access</span></h3>
<p><span style="font-weight: 400">Logging creates an audit trail of who accessed what and when. This is essential for security monitoring, compliance, and troubleshooting.</span></p>
<p><span style="font-weight: 400">All actions that are performed by the MCP tools are attributed to the user that connected to the ECA in audit trails. Salesforce automatically logs MCP server activity using Event Monitoring.</span></p>
<p>To access the logs, navigate to <b>Setup</b> and search for <b>Event Log File Browser</b>. Filter Event Type on &#8220;API Total Usage&#8221;. You can identify MCP traffic by filtering the event log CSV files for rows where the <code>API_CLIENT_CATEGORY</code> column matches <code>SALESFORCE_HOSTED_MCP</code>. You&#8217;ll see the users that called the MCP tools and the affected objects (entities) among other details.</p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206611" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260624113811/image1_7551df-e1782326303548.png?w=1000" class="postimages" width="1000" height="234" alt="Screenshot of an event log filtered to show MCP tool calls" />
			  </span>
			</p>
<p>Review logs regularly. Look for errors thanks to <code>STATUS_CODE</code> or unexpected access patterns with <code>USER_NAME</code> or <code>CLIENT_IP</code>. These can indicate security issues or misconfigured permissions.</p>
<h2><span style="font-weight: 400">Conclusion</span></h2>
<p><span style="font-weight: 400">In this post, we’ve reviewed how to secure Salesforce Hosted MCP Servers, and we&#8217;ve covered all of the components that form this layered security architecture, including:</span></p>
<ul>
<li style="font-weight: 400"><span style="font-weight: 400">Authentication verifies identity</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Authorization controls what tools users can access</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Permission controls enforce granular data access</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Logging creates an audit trail for compliance and security monitoring</span></li>
</ul>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206612" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260624113847/image8-e1782326340691.png?w=1000" class="postimages" width="1000" height="490" alt="Diagram that summarizes the security architecture for MCP servers and tools" />
			  </span>
			</p>
<p><span style="font-weight: 400">It&#8217;s now up to you to implement these security best practices and protect your data while shipping powerful MCP tools that agents can use.</span></p>
<h2><span style="font-weight: 400">Resources</span></h2>
<ul>
<li style="font-weight: 400"><span style="font-weight: 400">Documentation: </span><a href="https://developer.salesforce.com/docs/platform/hosted-mcp-servers"><span style="font-weight: 400">Salesforce Hosted MCP Servers</span></a></li>
<li style="font-weight: 400"><span style="font-weight: 400">Documentation: </span><a href="https://help.salesforce.com/s/articleView?id=platform.api_catalog_manage_mcp_servers.htm"><span style="font-weight: 400">Manage MCP Servers in API Catalog</span></a></li>
<li style="font-weight: 400"><span style="font-weight: 400">Documentation: </span><a href="https://help.salesforce.com/s/articleView?id=xcloud.external_client_apps.htm&amp;type=5"><span style="font-weight: 400">External Client Apps</span></a></li>
<li style="font-weight: 400"><span style="font-weight: 400">Documentation: </span><a href="https://help.salesforce.com/s/articleView?id=sf.perm_sets_overview.htm"><span style="font-weight: 400">Permission Sets</span></a></li>
<li style="font-weight: 400"><span style="font-weight: 400">GitHub: </span><a href="https://github.com/forcedotcom/mcp-hosted/wiki"><span style="font-weight: 400">Community Wiki</span></a></li>
</ul>
<h2><span style="font-weight: 400">About the author</span></h2>
<p><b>Philippe Ozil</b><span style="font-weight: 400"> is a Principal Developer Advocate at Salesforce, where he focuses on the Salesforce Platform. He writes technical content and speaks frequently at conferences. He is a full-stack developer and enjoys working with APIs, DevOps, robotics, and VR projects. Follow him on </span><a href="https://x.com/PhilippeOzil"><span style="font-weight: 400">X</span></a><span style="font-weight: 400">, </span><a href="https://www.linkedin.com/in/philippeozil/"><span style="font-weight: 400">LinkedIn</span></a><span style="font-weight: 400">, and </span><a href="https://bsky.app/profile/pozil.bsky.social"><span style="font-weight: 400">Bluesky</span></a><span style="font-weight: 400">, and check out his </span><a href="https://github.com/pozil"><span style="font-weight: 400">GitHub projects</span></a><span style="font-weight: 400">.</span></p>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/06/how-to-secure-salesforce-hosted-mcp-servers">How to Secure Salesforce Hosted MCP Servers</a> appeared first on <a href="https://developer.salesforce.com/blogs">Salesforce Developers Blog</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://developer.salesforce.com/blogs/2026/06/how-to-secure-salesforce-hosted-mcp-servers/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
	<post-id xmlns="com-wordpress:feed-additions:1">206595</post-id><media:thumbnail url="https://d259t2jj6zp7qm.cloudfront.net/images/20260624110231/SingleHeadshot-6-e1782324168910.png?w=1000" />
<media:content url="https://d259t2jj6zp7qm.cloudfront.net/images/20260624110231/SingleHeadshot-6-e1782324168910.png?w=1000" medium="image" />
	</item>
		<item>
		<title>Build Continuous Planning Systems in Agentforce Financial Services</title>
		<link>https://developer.salesforce.com/blogs/2026/06/build-continuous-planning-systems-in-agentforce-financial-services</link>
		<comments>https://developer.salesforce.com/blogs/2026/06/build-continuous-planning-systems-in-agentforce-financial-services#respond</comments>
		<pubDate>Thu, 25 Jun 2026 15:00:31 +0000</pubDate>
		<dc:creator><![CDATA[Vignesh Damodharan]]></dc:creator>
				<category><![CDATA[Agentforce]]></category>
		<category><![CDATA[Apex]]></category>
		<category><![CDATA[App Development]]></category>
		<category><![CDATA[Architecture]]></category>
		<category><![CDATA[Automation]]></category>
		<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[Account Planning]]></category>
		<category><![CDATA[Business Relationship Plan]]></category>
		<category><![CDATA[data model]]></category>
		<category><![CDATA[Document Generation]]></category>
		<category><![CDATA[financial services]]></category>
		<category><![CDATA[salesforce]]></category>

		<guid isPermaLink="false">https://developer.salesforce.com/blogs/?p=206583</guid>
		<description><![CDATA[<p>Replace static annual planning cycles with continuous account tracking using the Business Relationship Plan data model, admin configurations, and custom Apex logic.</p>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/06/build-continuous-planning-systems-in-agentforce-financial-services">Build Continuous Planning Systems in Agentforce Financial Services</a> appeared first on <a href="https://developer.salesforce.com/blogs">Salesforce Developers Blog</a>.</p>
]]></description>
				<content:encoded><![CDATA[<p><span style="font-weight: 400">Static, siloed data makes it challenging for financial services organizations and their client relationship managers to accurately, and efficiently develop their annual account plans. </span><a href="https://help.salesforce.com/s/articleView?id=ind.fsc_admin_business_relationship_plan.htm&amp;type=5"><span style="font-weight: 400">Business Relationship Plan (BRP) in Agentforce Financial Services</span></a><span style="font-weight: 400"> provides a robust data model for continuous account planning, replacing static annual planning cycles. With BRP, organizations can track goals in real time and make faster decisions throughout the year.</span></p>
<p><span style="font-weight: 400">Whether adapting to regulatory shifts or launching new products, account teams can use BRP to implement fresh metrics in minutes, surfacing real-time, actionable insights for every client relationship manager. In addition, BRP supports organizational agility by shifting logic ownership from engineering tickets to a simpler admin configuration. Clear extension points enable developers to customize BRP further using Flow, Apex, or Lightning web components.</span></p>
<p><span style="font-weight: 400">In this blog post, we&#8217;ll walk through the BRP architecture and the core data model. We’ll then demonstrate how admins can configure BRP , and how business users can set up and use the system. Finally, we’ll explore how to extend BRP using Apex business logic and document generation.</span></p>
<h2><span style="font-weight: 400">What Business Relationship Plan is (and why it matters)</span></h2>
<p><span style="font-weight: 400">Business Relationship Plan is the layer in </span><a href="https://help.salesforce.com/s/articleView?id=ind.fsc_agents_overview.htm&amp;type=5"><span style="font-weight: 400">Agentforce Financial Services</span></a><span style="font-weight: 400"> that sits on top of the </span><a href="https://developer.salesforce.com/docs/atlas.en-us.object_reference.meta/object_reference/sforce_api_objects_accountplan.htm"><span style="font-weight: 400">AccountPlan</span></a><span style="font-weight: 400"> object in Salesforce. It gives organizations a structure for plan records, objectives, measures, and tasks, and it allows them to extend this model with Flow, Apex, and UI components.</span></p>
<p><span style="font-weight: 400">At a high level, BRP connects:</span></p>
<ul>
<li style="font-weight: 400"><b>Plan</b><span style="font-weight: 400">: Account-level strategy record</span></li>
<li style="font-weight: 400"><b>Objectives</b><span style="font-weight: 400">: Prioritized outcomes with owners</span></li>
<li style="font-weight: 400"><b>Measures</b><span style="font-weight: 400">: Key performance indicators (KPIs) and targets</span></li>
<li style="font-weight: 400"><b>Action Plan</b><span style="font-weight: 400">: Action items tied to objectives</span></li>
<li style="font-weight: 400"><b>Visibility</b><span style="font-weight: 400">: Progress tracking across accounts</span></li>
</ul>
<p><span style="font-weight: 400">When we enable </span><a href="https://help.salesforce.com/s/articleView?id=ind.fsc_eac_setup_einstein_activity_capture_for_financial_services_cloud.htm&amp;type=5"><span style="font-weight: 400">Einstein</span></a><span style="font-weight: 400"> features, we can also add interaction summaries for better context before meetings.</span></p>
<h2><span style="font-weight: 400">Account Plans vs. BRP: What to use when</span></h2>
<p><span style="font-weight: 400">While </span><b>Account Plans</b><span style="font-weight: 400"> serve standard, linear management needs, </span><b>Business Relationship Plan</b><span style="font-weight: 400"> delivers a highly extensible architecture specifically engineered for sophisticated, multi-outcome strategies and deep platform integration.</span></p>
<table>
<tbody>
<tr>
<td><b>Topic</b></td>
<td><b>Account Plans</b></td>
<td><b>Business Relationship Plans</b></td>
</tr>
<tr>
<td><span style="font-weight: 400">Standard objects</span></td>
<td><span style="font-weight: 400">Yes</span></td>
<td><span style="font-weight: 400">Yes</span></td>
</tr>
<tr>
<td><span style="font-weight: 400">Strategic Tracker component</span></td>
<td><span style="font-weight: 400">Yes</span></td>
<td><span style="font-weight: 400">Yes</span></td>
</tr>
<tr>
<td><span style="font-weight: 400">Flexcard Selector component for 360 Information</span></td>
<td><span style="font-weight: 400">No</span></td>
<td><span style="font-weight: 400">Yes</span></td>
</tr>
<tr>
<td><span style="font-weight: 400">Objectives component with measures</span></td>
<td><span style="font-weight: 400">Yes (1:1)</span></td>
<td><span style="font-weight: 400">Yes (1:N)</span></td>
</tr>
<tr>
<td><span style="font-weight: 400">Einstein summary</span></td>
<td><span style="font-weight: 400">No</span></td>
<td><span style="font-weight: 400">Yes</span></td>
</tr>
<tr>
<td><span style="font-weight: 400">Action Plans integration</span></td>
<td><span style="font-weight: 400">Limited </span></td>
<td><span style="font-weight: 400">Yes</span></td>
</tr>
<tr>
<td><span style="font-weight: 400">Omniscript support</span></td>
<td><span style="font-weight: 400">No</span></td>
<td><span style="font-weight: 400">Yes</span></td>
</tr>
<tr>
<td><span style="font-weight: 400">OOTB prompt templates</span></td>
<td><span style="font-weight: 400">No</span></td>
<td><span style="font-weight: 400">Yes</span></td>
</tr>
</tbody>
</table>
<h2><span style="font-weight: 400">Business Relationship Plan architecture at a glance</span></h2>
<p><span style="font-weight: 400">The architecture of Business Relationship Plan spans four layers: </span><b>Data</b><span style="font-weight: 400"> (Plan, Objective, Measure records), </span><b>Experience</b><span style="font-weight: 400"> (Lightning pages and Flexcards), </span><b>Orchestration</b><span style="font-weight: 400"> (Flow, Apex, Omniscript), and </span><b>Insight</b><span style="font-weight: 400"> (Einstein summaries and analytics). </span></p>
<ol>
<li style="font-weight: 400"><span style="font-weight: 400">  </span><b>Data layer:</b><span style="font-weight: 400"> Plan, Objective, Measure, and Task records with standard and custom fields</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">  </span><b>Experience layer: </b><span style="font-weight: 400">Lightning pages, Flexcards, and Strategic Tracker components</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">  </span><b>Orchestration layer:</b><span style="font-weight: 400"> Flow, Omniscript, and Apex automation</span></li>
<li style="font-weight: 400"><span style="font-weight: 400"> </span><b> Insight layer: </b><span style="font-weight: 400">Einstein summaries and dashboard analytics</span></li>
</ol>
<p><span style="font-weight: 400">The diagram below shows how data flows between BRP’s four layers and their components: the Data layer with sObjects, the Experience layer with Lightning components, the Orchestration layer with Flow and Apex, and the Insight layer with Einstein and dashboards.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206585" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260624100953/image1_75eec1-e1782321006742.png?w=1000" class="postimages" width="1000" height="847" alt="Architecture diagram showing BRP’s four layers" />
			  </span>
			</p>
<h2><span style="font-weight: 400">BRP data model: Core objects and relationships</span></h2>
<p><span style="font-weight: 400">Business Relationship Plan offers a technical structure for Account Plan records, objectives, measures, and action plans, connecting client account goals directly to Financial Deal, Opportunities, Cases, and Contacts within the Salesforce data model.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206586" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260624101047/image6-e1782321060644.png?w=883" class="postimages" width="883" height="1000" alt="Diagram showing the components of the BRP architecture" />
			  </span>
			</p>
<h3><span style="font-weight: 400">Core objects</span></h3>
<p><span style="font-weight: 400">While each of the Account Plan entities contains numerous fields, we’ll focus here on the fundamental fields that are essential across all entities to help you understand the core data structure.</span></p>
<p><b><code>AccountPlan</code></b>: The root planning object.</p>
<ul>
<li>Standard fields: <code>Name, AccountId, Status, StartDate, EndDate</code></li>
<li>Key relationships:<code> Account</code> (lookup),<code> Owner</code> (lookup)</li>
</ul>
<p><b><code>AccountPlanObjectiveCategory</code></b>: Categories for classifying objectives.</p>
<ul>
<li>Standard fields: <code>Name, Description, StartDate, EndDate, IsActive</code></li>
</ul>
<p><b><code>AccountPlanObjective</code></b>: Strategic goals within a plan.</p>
<ul>
<li>Standard fields: <code>Name, AccountPlanId, Priority, Status, Owner, StartDate, EndDate, Description</code></li>
<li>Key relationships:<code> AccountPlan</code> (master-detail), <code>Owner</code> (lookup), <code>AccountPlanObjCategory</code> (lookup)</li>
</ul>
<p><b><code>AccountPlanObjectiveMeasure</code></b>: Quantifiable KPIs tracking objective progress.</p>
<ul>
<li>Standard fields: <code>Name, AccountPlanObjectiveId, ValueType, TargetValue, CurrentValue</code></li>
<li>Key relationships:<code> AccountPlanObjective</code> (master-detail), <code>AccountPlanObjMeasCalcDef</code> (lookup)</li>
</ul>
<p><b><code>AccountPlanObjectiveMeasureRela</code></b>: Links measures to related records.</p>
<ul>
<li>Standard fields:<code> Name, AccountPlanObjectiveMeasureId, ReferenceRecordId</code></li>
<li>Key relationships: <code>AccountPlanObjectiveMeasure</code> (master-detail), <code>ReferenceRecord</code> (multi-lookup to Opportunity, Financial Deal, Case, Contact, Campaign, etc.)</li>
</ul>
<p><b><code>ActionPlan</code></b>: Structured task-based execution plans.</p>
<ul>
<li>Standard fields: <code>Name, ActionPlanType, StartDate, EndDate</code></li>
<li>Key relationships: Target (multi-lookup)</li>
</ul>
<p><b><code>ActionPlanItem</code></b>: Individual tasks within an action plan.</p>
<ul>
<li>Standard fields: <code>Name, ActionPlanId, IsRequired, DisplayOrder, DependencyStatus</code></li>
<li>Key relationships: <code>ActionPlan</code> (master-detail), <code>ActionPlanItem</code> (multi-lookup)</li>
</ul>
<p><b><code>Task</code></b>: Action items tied to objectives.</p>
<ul>
<li>Standard fields: <code>Subject, Status, Priority, Due Date, Assigned To</code></li>
<li>Key relationships: What (lookup to Objective)</li>
</ul>
<h3><span style="font-weight: 400">Sample SOQL query</span></h3>
<p><span style="font-weight: 400">The query below retrieves active plans with nested objectives and measures, which is useful for building custom dashboards.</span></p>
<pre language="sql">SELECT Id, Name, Account.Name, Status, StartDate, EndDate,                                                                                                                                                                                                                                                                                                                                                                                
      (SELECT Id, Name, Priority, Status, Owner.Name,
          (SELECT Id, Name, TargetValue, CurrentValue                                                                                                                                                                                                                                                                                                                                                         
           FROM AccountPlanObjectiveMeasures)                                                                                                                                                                                                                                                                                                                                                                                               
       FROM AccountPlanObjectives                                                                                                                                                                                                                                                                                                                                                                                                           
       ORDER BY Priority, CreatedDate)                                                                                                                                                                                                                                                                                                                                                                                                      
  FROM AccountPlan                                                                                                                                                                                                                                                                                                                                                                                                                          
  WHERE Status = 'Active'                                                                                                                                                                                                                                                                                                                                                                                                                   
    AND Account.Industry = 'Banking'                                                                                                                                                                                                                                                                                                                                                                                                        
  ORDER BY StartDate DESC                                                                                                                                                                                                                                                                                                                                                                                                                   
  LIMIT 100
</pre>
<h2><span style="font-weight: 400">Calculation definitions in Business Relationship Plan</span></h2>
<p><span style="font-weight: 400">A </span><a href="https://help.salesforce.com/s/articleView?id=sales.account_plans_objective_measures.htm&amp;type=5"><b>Calculation Definition</b></a><span style="font-weight: 400"> is a core configuration record in BRP that defines the logic for platform-led aggregations. It establishes a technical framework with three fundamental pillars:</span></p>
<ul>
<li style="font-weight: 400"><b>Target object</b><span style="font-weight: 400">: Identify the specific record type for data aggregation</span></li>
<li style="font-weight: 400"><b>Target field</b><span style="font-weight: 400">: Specify the numeric or currency value to be rolled up</span></li>
<li style="font-weight: 400"><b>Rollup type</b><span style="font-weight: 400">: Define the mathematical operation (Sum, Count, Min, or Max)</span></li>
</ul>
<p><span style="font-weight: 400">Furthermore, users can implement conditional logic to filter the underlying data set, ensuring that only qualifying records contribute to the final metric. Once this setup is complete, client relationship managers can leverage these definitions across multiple Account Plan measures without writing a single line of code.</span></p>
<h3><span style="font-weight: 400">Summary of roles</span></h3>
<table>
<tbody>
<tr>
<td><b>Process Step</b></td>
<td><b>Owner</b></td>
<td><b>Key Outcome</b></td>
</tr>
<tr>
<td><span style="font-weight: 400">Define logic</span></td>
<td><span style="font-weight: 400">Admin</span></td>
<td><span style="font-weight: 400">Reusable Calculation Definition</span></td>
</tr>
<tr>
<td><span style="font-weight: 400">Activation</span></td>
<td><span style="font-weight: 400">Admin</span></td>
<td><span style="font-weight: 400">Availability in Measure UI</span></td>
</tr>
<tr>
<td><span style="font-weight: 400">Plan setup</span></td>
<td><span style="font-weight: 400">User</span></td>
<td><span style="font-weight: 400">Strategic measure creation</span></td>
</tr>
<tr>
<td><span style="font-weight: 400">Data mapping</span></td>
<td><span style="font-weight: 400">User</span></td>
<td><span style="font-weight: 400">Associated related records</span></td>
</tr>
<tr>
<td><span style="font-weight: 400">Orchestration</span></td>
<td><span style="font-weight: 400">Platform</span></td>
<td><span style="font-weight: 400">Automated nightly rollups</span></td>
</tr>
</tbody>
</table>
<p><span style="font-weight: 400">This collaborative framework ensures that while admins govern the formula, the platform manages the data, and users focus on results. That is the power of </span><b>Calculation Definitions</b><span style="font-weight: 400">.</span></p>
<p><span style="font-weight: 400">Let’s now take a look at the steps an admin would take to set up calculation definitions in Agentforce Financial Services. Once set up, we’ll then look at how a business user would use the system.</span></p>
<h2><span style="font-weight: 400">The admin experience: Configuring the engine</span></h2>
<p><span style="font-weight: 400">The initial setup phase creates a reusable asset for the entire organization. Once published, these definitions become standard options for all team members during measure creation. </span></p>
<p><span style="font-weight: 400">As an admin, you would follow these steps to configure BRP.</span></p>
<h3><span style="font-weight: 400">Step 1: Access configuration</span></h3>
<p><span style="font-weight: 400">Navigate to </span><b>Setup</b><span style="font-weight: 400"> -&gt; </span><b>Sales Account Plans </b><span style="font-weight: 400">and you will see the calculation definition section. You will see both out-of-the-box (OOTB) logic and custom configurations tailored to your specific business requirements.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206587" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260624101243/image2_37aa44-e1782321182568.png?w=1000" class="postimages" width="1000" height="497" alt="Screenshot of the Configure Plans for Financial Services start screen" />
			  </span>
			</p>
<h3><span style="font-weight: 400">Step 2: Define the data parameters</span></h3>
<p><span style="font-weight: 400">Click on </span><b>New Calculation Definition</b><span style="font-weight: 400"> and populate the following essential fields:</span></p>
<ul>
<li style="font-weight: 400"><b>Identity</b><span style="font-weight: 400">: Assign a Name and unique API Name for the definition</span></li>
<li style="font-weight: 400"><b>Context</b><span style="font-weight: 400">: Provide a clear description, so end users understand what the metric represents</span></li>
<li style="font-weight: 400"><b>Source</b><span style="font-weight: 400">: Select the Target Object (e.g., Financial Deal) and the corresponding Target Field</span></li>
<li style="font-weight: 400"><b>Logic</b><span style="font-weight: 400">: Choose the Rollup Type; the platform automatically infers the data type from your field selection</span></li>
</ul>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206588" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260624101354/image5_c02a90.png?w=949" class="postimages" width="949" height="918" alt="Screenshot of the New Calculation Definition dialog box showing rollup criteria" />
			  </span>
			</p>
<h3><span style="font-weight: 400">Step 3: Implement conditional filters</span></h3>
<p><span style="font-weight: 400">Admins can apply up to five </span><b>conditions</b><span style="font-weight: 400"> to refine the calculation scope. For example, you might exclude records where the status is &#8220;Inactive&#8221; or filter by specific transaction thresholds to ensure metric accuracy.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206589" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260624101439/image4.png?w=913" class="postimages" width="913" height="898" alt="Screenshot of the New Calculation Definition dialog box showing conditional filters" />
			  </span>
			</p>
<h3><span style="font-weight: 400">Step 4: Lifecycle Management</span></h3>
<p><span style="font-weight: 400">New definitions begin in </span><b>Draft</b><span style="font-weight: 400"> mode for review. Transitioning to </span><b>Active</b><span style="font-weight: 400"> status locks the core configuration to prevent unintended changes to live metrics, ensuring consistency across quarterly planning cycles.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206590" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260624101533/image3_9c1ecb-e1782321344245.png?w=1000" class="postimages" width="1000" height="370" alt="Screenshot showing button to activate the calculation definition" />
			  </span>
			</p>
<h2><span style="font-weight: 400">The business user experience: Operationalizing strategy</span></h2>
<p><span style="font-weight: 400">Once activated, these technical definitions are handed off to client relationship managers to drive daily business outcomes.</span></p>
<p><span style="font-weight: 400">Business users would follow these steps:</span></p>
<h3><span style="font-weight: 400">Step 1: Establish the plan hierarchy</span></h3>
<p><span style="font-weight: 400">Users begin by creating an </span><b>Account Plan</b><span style="font-weight: 400"> and defining high-level </span><b>Objectives</b><span style="font-weight: 400">, such as increasing portfolio retention or growing fee income.</span></p>
<h3><span style="font-weight: 400">Step 2: Link measures to logic</span></h3>
<p><span style="font-weight: 400">For each Objective, the user adds a </span><b>measure</b><span style="font-weight: 400">. They define the name and target value, then select the appropriate </span><b>calculation definition</b><span style="font-weight: 400"> from the dropdown to assign the aggregation logic.</span></p>
<h3><span style="font-weight: 400">Step 3: Associate records </span></h3>
<p>The user manually associates relevant records (e.g., Financial Deals) using the <b><code>AccountPlanObjectiveMeasureRela</code></b><b> </b>records. This flexible mapping allows single records to contribute to multiple plans without limit.</p>
<h3><span style="font-weight: 400">Step 4: Automate orchestration</span></h3>
<p><span style="font-weight: 400">The platform executes a nightly scheduled job that processes these associations, applies admin-defined conditions, and updates the </span><b>Current Value</b><span style="font-weight: 400"> field automatically, ensuring that fresh data is available every morning.</span></p>
<h2><span style="font-weight: 400">Architecting for performance: The daily cadence</span></h2>
<p><span style="font-weight: 400">Agentforce Financial Services utilizes a batch approach for these calculations to maintain optimal system performance. This design ensures that today&#8217;s updates to deals or record associations are consistently reflected in the next day&#8217;s planning metrics without creating unnecessary overhead.</span></p>
<h3><span style="font-weight: 400">Workflow example: Tracking Q3 deal revenue</span></h3>
<p><span style="font-weight: 400">Consider a scenario where a financial advisor tracks transaction revenue for a high-net-worth client.</span></p>
<p><b>Phase 1: Admin configuration</b></p>
<ol>
<li style="font-weight: 400"><span style="font-weight: 400">Admin creates an &#8220;Active Deal Transaction Value&#8221; definition targeting the </span><b>Financial Deal</b><span style="font-weight: 400"> object.</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Logic is set to </span><b>Sum</b><span style="font-weight: 400"> the transaction field with a filter for active status.</span></li>
</ol>
<p><b>Phase 2: Execution</b></p>
<ol>
<li style="font-weight: 400"><b>Monday</b><span style="font-weight: 400">: The advisor sets up the Account Plan and links eight deals to a new measure.</span></li>
<li style="font-weight: 400"><b>Tuesday</b><span style="font-weight: 400">: The system reflects a $1.45M current value automatically.</span></li>
<li style="font-weight: 400"><b>Quarter-End</b><span style="font-weight: 400">: As new deals close and are associated, the platform tracks the revenue growth until the $2M goal is surpassed.</span></li>
</ol>
<h2><span style="font-weight: 400">Extend Business Relationship Plan with Apex</span></h2>
<p><span style="font-weight: 400">For complex business logic, developers can use Apex to extend BRP functionality. This allows you to implement sophisticated patterns beyond Flow capabilities, such as a nightly scheduled job that computes weighted health scores (on a 0–100 scale) for objectives by analyzing progress from baseline to target. Such automation can programmatically refresh status labels like On Track, At Risk, or Off Track, while utilizing batchable Apex to maintain performance for large-scale deployments exceeding 2,000 records.</span></p>
<h3><span style="font-weight: 400">Prerequisites</span></h3>
<p><span style="font-weight: 400">Before implementing Apex extensions for BRP, ensure that your org meets these requirements:</span></p>
<ul>
<li><b>Salesforce Edition:</b> Enterprise Edition or higher</li>
<li><b>Financial Services Cloud license:</b> Active FSC license</li>
<li><b>Agentforce Financial Services:</b> Enabled in org settings</li>
<li><b>Permission sets:</b> <code>AccountPlan</code> object permissions (Read, Create, Edit) assigned to users</li>
<li><b>Omnistudio runtime</b>: Enabled for Flexcards and Omniscript functionality</li>
<li><b>Custom field access:</b> Ability to create custom fields on <code>AccountPlanObjective</code> and <code>AccountPlanObjectiveMeasure</code> objects</li>
</ul>
<p><span style="font-weight: 400">The following example shows how to customize BRP to track objective health scores.</span></p>
<h3><span style="font-weight: 400">Example: Calculate objective health score</span></h3>
<p><span style="font-weight: 400">To prepare your organization for the Apex calculation, create these custom fields:</span></p>
<ul>
<li><b>On </b><b><code>AccountPlanObjectiveMeasure</code></b><b>:</b>
<ul>
<li><code>BaselineValue__c</code> (Text, 255): Stores the starting value for progress calculation</li>
<li><code>Weight__c</code> (Number, 5,2): Defines the relative importance (weight) of the measure in the objective&#8217;s health score</li>
</ul>
</li>
<li><b>On </b><b><code>AccountPlanObjective</code></b><b>:</b>
<ul>
<li><code>HealthScore__c</code> (Number, 5,2): Stores the calculated weighted health score (0–100)</li>
<li><b>Status picklist update:</b> To use the<code> determineHealthStatus</code> method, add the values <b>On Track</b>, <b>At Risk</b>, and <b>Off Track</b> to the <code>AccountPlanObjective.Status</code> picklist</li>
</ul>
</li>
<li><b><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/26a0.png" alt="⚠" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Crucial Data Note:</b> Ensure that the <code>ValueType</code> field (Number, Currency, or Percent) is set on <code>AccountPlanObjectiveMeasure</code> records before <code>TargetValue</code> and <code>CurrentValue</code> can be persisted.</li>
</ul>
<pre language="java">/**
 * Computes a weighted health score per Account Plan Objective from its measures,
 * then sets HealthScore__c and a text Status from score bands.
 */
public class BRPObjectiveHealthCalculator {

    /**
     * Loads objectives (and related measures), aggregates progress by weight,
     * and updates objectives that have at least one valid weighted measure.
     *
     * @param setOfObjectiveIds Ids of AccountPlanObjective records to recalculate; no-op if null/empty.
     */
    public static void calculateHealthScores(Set setOfObjectiveIds) {
        if (setOfObjectiveIds == null || setOfObjectiveIds.isEmpty()) {
            return;
        }

        // Child subquery: FROM clause must be the child relationship API name + __r (verify in object manager).
        List objectives = [
            SELECT Id, Name, HealthScore__c, Status,
                   (SELECT Id,
                           BaselineValue__c,
                           TargetValue,
                           CurrentValue,
                           Weight__c
                    FROM AccountPlanObjectiveMeasures)
            FROM AccountPlanObjective
            WHERE Id IN :setOfObjectiveIds
        ];

        // Only objectives we actually change are DML’d to avoid unnecessary updates/triggers.
        List objectivesToUpdate = new List();

        for (AccountPlanObjective obj : objectives) {
            // Running totals for weighted average of per-measure progress (0–100 each).
            Decimal weightedScore = 0;
            Decimal totalWeight = 0;

            for (AccountPlanObjectiveMeasure measure : obj.AccountPlanObjectiveMeasures) {
                if (isValidMeasure(measure)) {
                    Decimal progress = calculateProgress(measure);
                    // Null weight defaults to 1 so the measure still counts equally if unspecified.
                    Decimal weight = measure.Weight__c != null ? measure.Weight__c : 1;

                    weightedScore += progress * weight;
                    totalWeight += weight;
                }
            }

            // Skip update when no valid measures contributed weight (avoids divide-by-zero and blank scores).
            if (totalWeight &gt; 0) {
                // Weighted average progress, expressed on a 0–100 scale for storage/display.
                obj.HealthScore__c = (weightedScore / totalWeight);
                obj.Status = determineHealthStatus(obj.HealthScore__c);
                objectivesToUpdate.add(obj);
            }
        }

        if (!objectivesToUpdate.isEmpty()) {
            update objectivesToUpdate;
        }
    }

    /**
     * A measure is included only when baseline, target, and current are all present,
     * so progress and comparisons are well-defined.
     */
    private static Boolean isValidMeasure(AccountPlanObjectiveMeasure measure) {
        return measure.BaselineValue__c != null
            &amp;&amp; measure.TargetValue != null
            &amp;&amp; measure.CurrentValue != null;
    }

    /**
     * Linear progress from baseline toward target, as a percentage (0–100).
     * Values are coerced with Decimal.valueOf in case the fields are numeric types other than Decimal.
     */
    private static Decimal calculateProgress(AccountPlanObjectiveMeasure measure) {
        Decimal baseline = Decimal.valueOf(measure.BaselineValue__c);
        Decimal target = Decimal.valueOf(measure.TargetValue);
        Decimal current = Decimal.valueOf(measure.CurrentValue);

        // Degenerate case: no movement defined; treat as fully achieved to avoid division by zero.
        if (target == baseline) {
            return 100;
        }

        // Raw % along the line from baseline to target; can exceed 0–100 before clamping.
        Decimal progress = ((current - baseline) / (target - baseline)) * 100;
        // Clamp so a single outlier measure cannot push the aggregate beyond 0–100 contribution.
        return Math.min(Math.max(progress, 0), 100);
    }

    /**
     * Maps stored score to a simple status label for reporting/UI.
     */
    private static String determineHealthStatus(Decimal score) {
        if (score &gt;= 80) {
            return 'On Track';
        }
        if (score &gt;= 60) {
            return 'At Risk';
        }
        return 'Off Track';
    }
}
</pre>
<p><b>Usage</b><span style="font-weight: 400">: Call this class from a scheduled Apex job to recalculate health scores nightly.</span></p>
<pre language="java">public class BRPHealthScoreScheduler implements Schedulable {
    public void execute(SchedulableContext ctx) {
       Set objectiveIds = new Set();
      
       for (AccountPlanObjective obj : [
           SELECT Id
           FROM AccountPlanObjective
           WHERE Status = 'In Progress'
           AND AccountPlan.Status = 'Active'
       ]) {
           objectiveIds.add(obj.Id);
       }
      
       if (!objectiveIds.isEmpty()) {
           BRPObjectiveHealthCalculator.calculateHealthScores(objectiveIds);
       }
    }
}
</pre>
<p>Schedule with: <code>System.schedule('BRP Health Score Calculator', '0 0 2 * * ?', new BRPHealthScoreScheduler());</code><span>.</span></p>
<p>Note: <span>For orgs with more than 2,000 active objectives, refactor this into a </span><code>Database.Batchable</code><span> class with a batch size of 200. The </span><code>calculateHealthScores</code><span> method already accepts a </span><code>Set&lt;Id&gt;</code><span>, so the </span><code>batchable execute()</code><span> method can pass each batch directly.</span></p>
<pre language="java">global class ObjectiveHealthScoreBatch implements Database.Batchable {

    global Database.QueryLocator start(Database.BatchableContext bc) {
        return Database.getQueryLocator(
            'SELECT Id FROM AccountPlanObjective'
        );
    }

    global void execute(Database.BatchableContext bc, List scope) {
        Set objectiveIds = new Map&lt;Id, SObject&gt;(scope).keySet();
        calculateHealthScores(objectiveIds); // already accepts Set
    }

    global void finish(Database.BatchableContext bc) {
        // optional: send notification, log results
    }
}
</pre>
<h2><span style="font-weight: 400">Extend Business Relationship Plan with Document Generation</span></h2>
<p><a href="https://help.salesforce.com/s/articleView?id=ind.sf_docgen_overview_one.htm&amp;type=5"><span style="font-weight: 400">Salesforce Document Generation</span></a><span style="font-weight: 400"> enables organizations to automatically create Word documents and PowerPoint presentations from Account Plan data. By placing the </span><a href="https://help.salesforce.com/s/articleView?id=ind.fsc_admin_tear_sheets_parent.htm&amp;type=5"><span style="font-weight: 400">Summary Documents</span></a><span style="font-weight: 400"> component on the Account Plan record page, developers can allow users to generate documents on-demand from pre-built templates with merge fields and conditional logic. Generated documents pull live data (objectives, measures, activities, related records) and are stored in Salesforce, where they can be:</span></p>
<ul>
<li><b>Exported</b><span style="font-weight: 400">: Download as native .docx/.pptx formats or convert to PDF </span></li>
<li><span style="font-weight: 400"><b>Shared</b>: Email as attachments or share via public/private links</span></li>
<li><span style="font-weight: 400"><b>Printed</b>: Download and print for client meetings or internal reviews</span></li>
<li><span style="font-weight: 400"><b>Archived</b>: Store for compliance and audit trails<br />
</span></li>
</ul>
<p><span style="font-weight: 400">This simple extension adds a lot of value and can be used for executive summaries, quarterly business reviews, and stakeholder presentations.</span></p>
<h2><span style="font-weight: 400">Best practices for developers</span></h2>
<ul>
<li style="font-weight: 400"><b>Review existing OOTB capabilities first: </b><span style="font-weight: 400">Before building custom components, check available features like Flexcard Selector, Objectives Component, Action Plans, and Summary Documents for complete out-of-the-box functionality to avoid duplicate development</span></li>
<li style="font-weight: 400"><b>Keep objective definitions specific and time-bound</b><span style="font-weight: 400">: Use validation rules to enforce date ranges and clear naming conventions</span></li>
<li style="font-weight: 400"><b>Limit KPIs to meaningful indicators</b><span style="font-weight: 400">: Avoid measure proliferation; focus on three to five key measures per objective</span></li>
<li style="font-weight: 400"><b>Build automation for repetitive tasks</b><span style="font-weight: 400">: Use Flow for measure reminders and status updates</span></li>
</ul>
<h2><span style="font-weight: 400">Conclusion</span></h2>
<p><span style="font-weight: 400">When essential account data is static and siloed, annual account planning becomes more challenging and can easily fail.Business Relationship Plan gives financial services organizations a technical and operational model that they can run every week. With the right data model, UI, and automation, account teams can track goals in real time, act faster, and be more productive.</span></p>
<p><span style="font-weight: 400">For developers, BRP provides a solid foundation with clear extension points. Whether you&#8217;re building Flow automation, Apex business logic, or custom LWC components, the platform supports continuous planning workflows that replace static annual planning cycles.</span></p>
<h2><span style="font-weight: 400">Resources</span></h2>
<ul>
<li style="font-weight: 400"><a href="https://help.salesforce.com/s/articleView?id=ind.fsc_admin_business_relationship_plan.htm&amp;type=5"><span style="font-weight: 400">Business Relationship Plan Overview</span></a></li>
<li style="font-weight: 400"><a href="https://help.salesforce.com/s/articleView?id=ind.fsc_admin_business_relationship_plan_enable.htm&amp;type=5"><span style="font-weight: 400">Enable Business Relationship Plan</span></a></li>
<li style="font-weight: 400"><a href="https://help.salesforce.com/s/articleView?id=ind.fsc_admin_business_relationship_plan_setup.htm&amp;language=en_US&amp;type=5"><span style="font-weight: 400">Business Relationship Plan Setup Guide</span></a></li>
<li style="font-weight: 400"><a href="https://help.salesforce.com/s/articleView?id=ind.fsc_admin_business_relationship_plan_add_flexcard_component.htm&amp;type=5"><span style="font-weight: 400">Add FlexCard Component</span></a></li>
<li style="font-weight: 400"><a href="https://help.salesforce.com/s/articleView?id=ind.fsc_admin_business_relationship_plan_configure_flexipage2.htm&amp;language=en_US&amp;type=5"><span style="font-weight: 400">Configure the Pre-Built Account Plan Lightning Page </span></a></li>
<li style="font-weight: 400"><a href="https://help.salesforce.com/s/articleView?id=ind.fsc_admin_business_relationship_plan_configure_fields.htm&amp;language=en_US&amp;type=5"><span style="font-weight: 400">Configure Fields Specific to BRP</span></a></li>
<li style="font-weight: 400"><a href="https://help.salesforce.com/s/articleView?id=ind.fsc_admin_business_relationship_plan_manage_access.htm&amp;language=en_US&amp;type=5"><span style="font-weight: 400">Manage User Access for BRP </span></a></li>
<li style="font-weight: 400"><a href="https://help.salesforce.com/s/articleView?id=sales.account_plans.htm&amp;type=5"><span style="font-weight: 400">Sales Account Plans Overview</span></a></li>
<li style="font-weight: 400"><a href="https://help.salesforce.com/s/articleView?id=sales.account_plans_setup.htm&amp;language=en_US&amp;type=5"><span style="font-weight: 400">Account Plans Setup</span></a></li>
<li style="font-weight: 400"><a href="https://help.salesforce.com/s/articleView?id=sales.account_plans_strategic_tracker.htm&amp;language=en_US&amp;type=5"><span style="font-weight: 400">Strategic Tracker with Sales Action Plans </span></a></li>
<li style="font-weight: 400"><a href="https://help.salesforce.com/s/articleView?id=ind.fsc_admin_business_relationship_plan_einstein_summaries.htm&amp;language=en_US&amp;type=5"><span style="font-weight: 400">Einstein Summaries for BRP</span></a></li>
<li style="font-weight: 400"><a href="https://github.com/trailheadapps"><span style="font-weight: 400">Salesforce Developers sample apps on GitHub</span></a></li>
</ul>
<h2><span style="font-weight: 400">About the author</span></h2>
<p><b>Vignesh Damodharan</b><span style="font-weight: 400"> is a Lead Member of Technical Staff with Agentforce Financial Services, where he leads Salesforce engineering teams building enterprise products on the Salesforce Platform. Follow him on </span><a href="https://www.linkedin.com/in/vigneshdamodharan/"><span style="font-weight: 400">LinkedIn</span></a><span style="font-weight: 400">.</span></p>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/06/build-continuous-planning-systems-in-agentforce-financial-services">Build Continuous Planning Systems in Agentforce Financial Services</a> appeared first on <a href="https://developer.salesforce.com/blogs">Salesforce Developers Blog</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://developer.salesforce.com/blogs/2026/06/build-continuous-planning-systems-in-agentforce-financial-services/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
	<post-id xmlns="com-wordpress:feed-additions:1">206583</post-id><media:thumbnail url="https://d259t2jj6zp7qm.cloudfront.net/images/20260624102452/Generic-C-4-e1782321903642.png?w=1000" />
<media:content url="https://d259t2jj6zp7qm.cloudfront.net/images/20260624102452/Generic-C-4-e1782321903642.png?w=1000" medium="image" />
	</item>
		<item>
		<title>Master the Agentic Development Lifecycle for Agentforce</title>
		<link>https://developer.salesforce.com/blogs/2026/06/master-the-agentic-development-lifecycle-for-agentforce</link>
		<comments>https://developer.salesforce.com/blogs/2026/06/master-the-agentic-development-lifecycle-for-agentforce#respond</comments>
		<pubDate>Wed, 24 Jun 2026 15:00:04 +0000</pubDate>
		<dc:creator><![CDATA[Mohith Shrivastava]]></dc:creator>
				<category><![CDATA[Agentforce]]></category>
		<category><![CDATA[Agentforce Vibes]]></category>
		<category><![CDATA[App Development]]></category>
		<category><![CDATA[Headless 360]]></category>
		<category><![CDATA[Agent Skills]]></category>

		<guid isPermaLink="false">https://developer.salesforce.com/blogs/?p=206559</guid>
		<description><![CDATA[<p>Learn how to design, build, test, and deploy Agentforce agents using plain language, Agent Skills, and a design-first development lifecycle.</p>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/06/master-the-agentic-development-lifecycle-for-agentforce">Master the Agentic Development Lifecycle for Agentforce</a> appeared first on <a href="https://developer.salesforce.com/blogs">Salesforce Developers Blog</a>.</p>
]]></description>
				<content:encoded><![CDATA[<p><span style="font-weight: 400">Building with </span><a href="https://developer.salesforce.com/docs/ai/agentforce/guide"><span style="font-weight: 400">Agentforce</span></a><span style="font-weight: 400"> rarely involves a single task. An AI agent can span a data model, actions (flows or Apex), agent definition, and permissions. Each piece has its own setup screens, so you have to spend time clicking to wire it all together. With </span><a href="https://developer.salesforce.com/blogs/2026/05/headless-360-what-it-means-for-developers"><b>Salesforce Headless 360</b></a><span style="font-weight: 400">, the platform now exposes every one of those capabilities as an application programming interface (API), a Model Context Protocol (MCP) tool, or a command-line interface (CLI) command. Because the whole platform can be accessed by coding agents, they can take your intent and handle much of that work — which opens a new, agentic way to build.</span></p>
<p><span style="font-weight: 400">Many Salesforce Developers want a repeatable, design-first lifecycle for agent development. The Headless 360 approach pairs a coding agent like Claude Code, Codex, or </span><a href="https://developer.salesforce.com/docs/platform/einstein-for-devs/guide/einstein-overview.html"><span style="font-weight: 400">Agentforce Vibes</span></a><span style="font-weight: 400"> with </span><a href="https://agentskills.io/home"><span style="font-weight: 400">Agent Skills</span></a><span style="font-weight: 400">, so you can design, build, deploy, test, and debug from a single plain-language conversation. You stay the designer; the coding agent does the development. </span></p>
<p><span style="font-weight: 400">In this post, we&#8217;ll look at how to set up Salesforce Agent Skills, scaffold a project and connect an org, design before you build, generate your metadata, validate and test, and debug with traces. Everything we’ll discuss here works today through the open-source </span><a href="https://github.com/forcedotcom/sf-skills"><span style="font-weight: 400">Salesforce Skills Library</span></a><span style="font-weight: 400"> and the </span><a href="https://developer.salesforce.com/tools/salesforcecli"><span style="font-weight: 400">Salesforce CLI</span></a><span style="font-weight: 400">. </span></p>
<h2><strong>Teach your coding agent Salesforce with Agent Skills</strong></h2>
<p><span style="font-weight: 400">Agent Skills are how your coding assistant learns Salesforce. A skill is a small bundle of instructions and commands that teaches the assistant a specific task, such as building an Apex class or a Lightning web component (LWC), building a flow, or developing Agentforce agents. Without skills, the assistant guesses. With them, it runs the right steps and necessary CLI commands or MCP tools and follows conventions.</span></p>
<p><span style="font-weight: 400">You should install these skills either globally or within your project directory for any coding agents besides Agentforce Vibes, as the latter includes them by default. The Salesforce Skills Library ships dozens of skills covering Apex, LWC, Agentforce, Data 360, and metadata deployment. </span></p>
<p>Three skills drive the agent lifecycle specifically:</p>
<ul>
<li><code>developing-agentforce</code>: Design, build, deploy, debug (see <a href="https://github.com/forcedotcom/sf-skills/tree/main/skills/developing-agentforce"><u>docs</u></a>)</li>
<li><code>testing-agentforce</code>: Test specs and batch runs (see <a href="https://github.com/forcedotcom/sf-skills/tree/main/skills/testing-agentforce"><u>docs</u></a>)</li>
<li><code>observing-agentforce</code>: Inspect production traces from Data 360 (see <a href="https://github.com/forcedotcom/sf-skills/tree/main/skills/observing-agentforce"><u>docs</u></a>)</li>
</ul>
<p>Once installed, in most coding agents you should be able to type <code>‘/’</code> in your assistant to browse them. You&#8217;ll see a skill load by name as the assistant works, for example, <code>developing-agentforce</code> activates the moment you ask for an agent scaffold.</p>
<p><span style="font-weight: 400">You&#8217;ll also need a few common tools on your machine first: </span><a href="https://nodejs.org"><span style="font-weight: 400">Node.js</span></a><span style="font-weight: 400"> and the </span><a href="https://developer.salesforce.com/tools/salesforcecli"><span style="font-weight: 400">Salesforce CLI</span></a><span style="font-weight: 400">. And here&#8217;s the best part — if something is missing, use your assistant to get help. And if the assistant has permissions, it can also install these for you.</span></p>
<p><span style="font-weight: 400">To install Salesforce Agent Skills, run the below command from your terminal or ask your assistant to run the below command.</span></p>
<pre language="sh">npx skills add forcedotcom/sf-skills
</pre>
<p><span style="font-weight: 400">After executing the command, you will navigate through a few quick configuration choices. First, you’ll pick the specific skills from the library to equip your assistant. Then, designate your preferred coding agent and decide if these instructions should be available across all your projects or just for the current local directory.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206576" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260623124718/image2-e1782244050713.png?w=1000" class="postimages" width="1000" height="618" alt="A terminal showing the Agent Skills install command asking to select list of installed skills" />
			  </span>
			</p>
<h2><strong>Scaffold the project and connect your org</strong></h2>
<p><span style="font-weight: 400">Every Salesforce project starts with a scaffold — a folder holding all your metadata as files. This is the same project structure you already pull into an integrated development environment (IDE) like VS Code. Your assistant works against those local files, then deploys them to an org.</span></p>
<p><span style="font-weight: 400">Here&#8217;s the shift that matters. You no longer memorize CLI flags or look up command syntax. You describe what you want in plain language, and the coding agent runs the right Salesforce CLI command for you. The CLI does the actual work — the agent just knows which command to call and translates your words into it.</span></p>
<p><span style="font-weight: 400">For example, scaffolding a project used to mean typing the exact command and its flags:</span></p>
<pre language="sh">sf project generate --name agent-script-demo
</pre>
<p><span style="font-weight: 400">Now, you simply describe the goal in plain language, and the agent runs that command for you:</span></p>
<blockquote><p><i><span style="font-weight: 400">&#8220;Create a Salesforce project scaffold named agent-script-demo&#8221;</span></i></p></blockquote>
<p><span style="font-weight: 400">Keep each project in its own folder, so the agent only touches what it should. From there, the same pattern repeats for every step. For example, when you ask it to set up your org, the agent checks the current default and sets your scratch org or sandbox as the default for this project using the commands below.</span></p>
<pre language="sh">sf config get target-org --json
sf config set target-org agent-build --json
</pre>
<p><span style="font-weight: 400">You can also ask the coding agent to open your default org, and the coding agent will use the command below to open it.</span></p>
<pre language="sh">sf org open
</pre>
<p><span style="font-weight: 400">A best practice is to build in scratch orgs or sandboxes and never give your agent access to the production environment. Also, never paste secrets like passwords into a prompt. A handy workflow is to run a second terminal </span><i><span style="font-weight: 400">without</span></i><span style="font-weight: 400"> the assistant. That way you can copy, edit, and run sensitive commands yourself. </span></p>
<h2><strong>Design before you build</strong></h2>
<p><span style="font-weight: 400">This is the most important habit in the whole lifecycle. If you let an agent build on its own, it may produce something you have to throw away. So, slow down and design first.</span></p>
<p><span style="font-weight: 400">Two things make this easy. First, switch your assistant into </span><b>plan mode</b><span style="font-weight: 400"> (in Claude Code, press Shift+Tab to cycle to &#8220;plan mode&#8221;). In plan mode, the assistant proposes a plan instead of changing files. Second, ask the assistant to interview you before it builds. A prompt like this works well:</span></p>
<blockquote><p><i><span style="font-weight: 400">&#8220;Before you build anything, interview me to design this agent. Ask one question </span></i><i><span style="font-weight: 400">at a time and recommend a sensible default for each. Treat the design as a </span></i><i><span style="font-weight: 400">decision tree: when my answer opens new choices, follow that branch and keep </span></i><i><span style="font-weight: 400">asking until it&#8217;s fully resolved, then move to the next branch. Cover the data </span></i><i><span style="font-weight: 400">model, actions, permissions, and agent structure. Don&#8217;t stop or start building </span></i><i><span style="font-weight: 400">until every decision needed to build the agent is made.&#8221;</span></i></p></blockquote>
<p><span style="font-weight: 400">That single instruction turns the assistant into a design partner. It walks down every branch of the decision tree — following each answer to the sub-decisions it raises — and only stops once nothing is left to decide. Nothing gets built on a guess. For our example, an employee To-Do Manager agent, it walked through real architectural choices:</span></p>
<ul>
<li style="font-weight: 400"><span style="font-weight: 400">Where should to-dos live: in a custom object, or the standard Task object?</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Which fields and status values does the agent need?</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Should the agent run as the logged-in user?</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Use one sub-agent with three flow actions, or a hub-and-spoke model with a sub-agent per task?</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Should the backing logic be Salesforce Flows or Apex?</span></li>
</ul>
<p><span style="font-weight: 400">Note that the above is an example prompt, and based on your organization’s best practices, you can </span><a href="https://developer.salesforce.com/blogs/2026/05/build-a-salesforce-agent-skill-with-claude-code"><span style="font-weight: 400">build an agent skill of your own</span></a><span style="font-weight: 400"> to help you design this, so you do not have to repeat this prompt.</span></p>
<p><span style="font-weight: 400">The Claude Code coding assistant interviews the developer one question at a time, recommending a default for each design decision.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206577" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260623124808/image3-e1782244100641.png?w=1000" class="postimages" width="1000" height="608" alt="The Claude Code assistant interviewing the developer" />
			  </span>
			</p>
<p><span style="font-weight: 400">Visualizing your agent as a graph is helpful. Each sub-agent acts as a node representing a specific domain: a single, focused job with its own dedicated instructions and actions. A router node at the top orchestrates these sub-agents based on the conversation. Designing the agent means defining these nodes and providing clear instructions and tools to ensure that the agent achieves the user&#8217;s goals.</span></p>
<p><span style="font-weight: 400">So, the rule of thumb is about domains, not difficulty. Keep a single sub-agent when the whole job lives in one domain, for example, frequently asked questions (FAQ) or a status lookup. Reach for multiple sub-agents in a hub-and-spoke shape when the work spans distinct domains that each deserve their own instructions, actions, or security gate. Most agents land at one to five domain sub-agents. You make these calls, not your coding agent. That&#8217;s the point — you end up more confident because you designed the graph.</span></p>
<p><span style="font-weight: 400">The AI-generated info graphic below conveys these ideas.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206578" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260623124842/image1-e1782244136116.png?w=1000" class="postimages" width="1000" height="546" alt="Visualizing agents on Agentforce as a graph" />
			  </span>
			</p>
<h2><b>Generate, deploy, and let the agent fix and retry</b></h2>
<p><span style="font-weight: 400">Once the plan is approved, the assistant builds everything: the custom object and fields, the flows, and the agent definition file written in Agent Script (the declarative format that specifies your agent&#8217;s sub-agents, routing logic, instructions, and action bindings). The output is an authoring bundle: the deployable package containing the agent&#8217;s complete definition as local metadata files. You can watch it happen live in VS Code as files appear.</span></p>
<p><span style="font-weight: 400">Be sure to scope the work explicitly in your plan. A common choice is &#8220;build, deploy, and validate&#8221; before you publish. You can also add reminders that the assistant might otherwise skip, for instance, &#8220;make sure the new object has the right permissions; create a permission set.&#8221; Field-level security (FLS) and create-read-update-delete (CRUD) permissions matter, so call them out.</span></p>
<p><span style="font-weight: 400">The standout behavior is an automated </span><b>fix-and-retry loop</b><span style="font-weight: 400">. When a deploy fails, the assistant reads the compiler or deploy error, traces it to the offending metadata, applies a fix, and redeploys, repeating that cycle until the deploy succeeds. It&#8217;s the same edit-compile-debug loop you&#8217;d run by hand, just driven by the agent. If you want to go faster, you can fan out the work, so parallel agents build several flows at once instead of one after another. When you go for parallel work, make sure you understand dependencies and group them, so they can execute without stepping on each other.</span></p>
<p>You drive this in plain language: <i>“Build a single sub-agent To-Do Manager using Salesforce Flows, build, deploy, and validate it, and create a permission set for the new object”</i>. Behind that prompt, the <code>developing-agentforce</code> skill runs a precise sequence. It first generates the authoring bundle (the agent scaffold), then validates that Agent Script compiles with a local check, deploys the backing flows or Apex the actions reference, and finally deploys the authoring bundle itself. Knowing the sequence helps you follow along and step in when needed.</p>
<pre language="sh">sf agent generate authoring-bundle --json --no-spec --name "To-Do Manager" --api-name To_Do_Manager
sf agent validate authoring-bundle --json --api-name To_Do_Manager
sf project deploy start --json --metadata Flow
sf project deploy start --json --metadata AiAuthoringBundle:To_Do_Manager
</pre>
<p>Notice two best practices baked into the skill. Every command leads with <code>--json</code> to make the output machine-readable, and every deploy names its metadata explicitly. A bare <code>sf project deploy start</code> ships everything that changed, and scoping each deploy keeps agent metadata from going out by accident.</p>
<h2><strong>Validate, then test in two modes</strong></h2>
<p><span style="font-weight: 400">When the build finishes, the assistant validates the agent&#8217;s behavior paths automatically, checking far more paths than you&#8217;d test by hand. Validation is required before the agent can be published, so it&#8217;s built into the flow.</span></p>
<p>Still, don&#8217;t skip your own testing. Automated validation is a safety net, not a substitute for judgment. This is where the <code>testing-agentforce</code> skill comes in. It gives you two modes: quick smoke tests while you iterate, and a saved batch suite for regression testing and continuous integration. Use the first to move fast, and the second to keep the agent honest as it grows.</p>
<h3><strong>Quick smoke tests with preview sessions</strong></h3>
<p>The quick mode is a live preview session. You start a session, send a real utterance, and end the session when you&#8217;re done — each preview sends a trace you can read afterward. Note that <code>--authoring-bundle</code> must appear on all three subcommands.</p>
<pre language="sh">sf agent preview start --json --authoring-bundle To_Do_Manager

sf agent preview send --json --authoring-bundle To_Do_Manager --session-id SESSION_ID --utterance "Create a to-do to write a blog"\

sf agent preview end --json --authoring-bundle To_Do_Manager --session-id SESSION_ID
</pre>
<h3><b>Batch regression testing with test specs</b></h3>
<p><span style="font-weight: 400">For repeatable testing, you describe the cases you care about and the skill writes a test spec. The spec is a YAML file that lists each utterance alongside what should happen. Three assertions matter per case:</span></p>
<ul>
<li><code>expectedTopic</code>: The sub-agent that the conversation should route to</li>
<li><code>expectedActions</code>: The actions that should fire</li>
<li><code>expectedOutcome</code>: A plain-language description of the right result</li>
</ul>
<p><span style="font-weight: 400">That last one is graded by a large language model (LLM) acting as a judge, so it reads the response the way a person would. It&#8217;s the most reliable assertion, so include it on every case.</span></p>
<pre language="YAML">name: To_Do_Manager_Tests
subjectType: AGENT
subjectName: To_Do_Manager
testCases:
  - utterance: "Create a to-do to write a blog"
    expectedTopic: To_Do_Management
    expectedActions:
      - Create_To_Do
    expectedOutcome: "A new to-do titled 'write a blog' is created and confirmed to the user."
</pre>
<p><span style="font-weight: 400">You then create the test definition from that spec and run it as a batch, waiting for the results.</span></p>
<pre language="sh">sf agent test create --json --spec specs/To_Do_Manager-testSpec.yaml --api-name To_Do_Manager_Tests --force-overwrite

sf agent test run --json --api-name To_Do_Manager_Tests --wait 10
</pre>
<p><span style="font-weight: 400">When a case fails, the skill diagnoses it straight from the trace — a sub agent that didn&#8217;t match, an action that never fired, or an ungrounded response. It then applies a targeted fix and retries for a few iterations before asking for your help. You can also run the live preview in the new </span><b>Agent Builder</b><span style="font-weight: 400"> for an admin-friendly view, or use the </span><b>Agentforce DX</b><span style="font-weight: 400"> panel in VS Code to start a test session.</span></p>
<p><span style="font-weight: 400">Note: We are enhancing the automated testing experience to support multi-turn conversation, injection of state variables, and verify action validations. Keep an eye on the </span><a href="https://help.salesforce.com/s/articleView?id=release-notes.rn_einstein_platform.htm&amp;release=262&amp;type=5"><span style="font-weight: 400">release notes</span></a><span style="font-weight: 400">.</span></p>
<h2><strong>Publish and activate your agent</strong></h2>
<p><span style="font-weight: 400">When it works, publish and activate the agent. Publishing turns the deployed bundle into a runnable agent version as </span><i><span style="font-weight: 400">Inactive</span></i><span style="font-weight: 400">; activating turns it on so users and tests can reach it. You can publish now and activate later if you prefer. Below are commands that the agent runs for publish and activation.</span></p>
<pre language="sh">sf agent publish authoring-bundle --json --api-name To_Do_Manager
sf agent activate --json --api-name To_Do_Manager
</pre>
<p><span style="font-weight: 400">And if you forgot a piece of metadata, just ask the agent to build it for you. The whole point is that you describe the gap and the assistant fills it. Maintain a repeatable history by committing your Agent Script and associated metadata to a source control system like </span><a href="https://git-scm.com/"><span style="font-weight: 400">Git</span></a><span style="font-weight: 400">. This practice ensures you can revert to a known functional version whenever necessary, keeping your development environment stable and secure.</span></p>
<h2><strong>Debug with traces, not guesswork</strong></h2>
<p><span style="font-weight: 400">The agent trace is the primary debugging signal for Agentforce agents. It’s a JSON file generated after each conversation turn that shows which sub-agent handled the request, what the LLM received, and which actions fired. Agents rarely come out perfect on the first try, so you&#8217;ll keep tweaking them. You&#8217;ll work with traces in two places, and they look slightly different in each.</span></p>
<h3><strong>Local traces during development</strong></h3>
<p>While you build, the traces are local. After each <code>preview send</code>, the runtime writes one JSON file per conversation turn. Each trace shows the full execution path: which sub-agent handled the turn, what variables were set, what the LLM saw, and which actions it called. You&#8217;ll find these files in your project.</p>
<pre language="sh">.sfdx/agents/To_Do_Manager/sessions//traces/.json
</pre>
<p><span style="font-weight: 400">These local traces are instant and complete, but they only cover your own preview sessions. The fix loop is simple: open the trace, paste it back into your assistant, and explain what you expected versus what happened. In the Agent DX panel, you can grab the same trace with one click. The trace is detailed enough that the assistant usually pinpoints and fixes the issue — you can even ask it to &#8220;self-test using the trace and fix it.&#8221; This resolves the large majority of problems before the agent ever ships.</span></p>
<h3>Production traces with the <code>observing-agentforce</code> skill</h3>
<p>Once real users are on the agent, you need a different lens. That&#8217;s the job of the <code>observing-agentforce</code> skill. Instead of one local file, it queries the <a href="https://help.salesforce.com/s/articleView?id=ai.generative_ai_session_trace_data_model.htm&amp;type=5"><u>Session Trace Data Model (STDM) </u></a>— the production session data that Agentforce stores in Data 360 — so you can see how the agent behaves across real conversations at volume.</p>
<p><span style="font-weight: 400">The skill follows a three-step loop: </span><b>observe, reproduce, improve</b><span style="font-weight: 400">. It queries production sessions to surface the failures that matter — sub-agent misroutes, action errors, low adherence, slow actions, and abandoned sessions. It then reproduces a suspect case in a local preview to confirm the root cause, and only then edits the agent to fix it. You ask in plain language, </span><i><span style="font-weight: 400">&#8220;find the worst-performing sessions from the last day and tell me why&#8221;</span></i><span style="font-weight: 400">, and the skill runs the queries and reads the results for you.</span></p>
<p><span style="font-weight: 400">For either kind of trace, a visualizer helps when the routing itself is hard to follow. The agent is a graph: a router hands each conversation to a sub-agent, which builds variables and prompts, sends them to the LLM, and calls tools until it has an answer. A tool like </span><a href="https://developer.salesforce.com/blogs/2026/05/agentlens-debug-agentforce-with-interactive-visualizations"><span style="font-weight: 400">AgentLens</span></a><span style="font-weight: 400"> lets you walk that graph step by step, seeing each sub-agent handoff, the tools sent to the LLM, and the response that ends the flow.</span></p>
<p><span style="font-weight: 400">Note: for agent observability at scale for production agents, it is recommended to set up dashboards and metrics using </span><a href="https://help.salesforce.com/s/articleView?id=005226932&amp;type=1"><span style="font-weight: 400">Agent Observability </span></a></p>
<h2><b>Extend the lifecycle with custom agent skills</b></h2>
<p><span style="font-weight: 400">Adopt this lifecycle and your day-to-day changes. You stop clicking through Salesforce setup for routine work and start describing outcomes instead. When something isn&#8217;t right, the fix almost always lives in one of four places: your prompt, your context, the tools you built, or the skills you installed.</span></p>
<p><span style="font-weight: 400">That last one is the real unlock. When a skill produces metadata you don&#8217;t like, improve the skill and contribute it back. Your fixes compound for everyone, and the assistant gets better at Salesforce over time. Pairing that with a design-first interview keeps you in control — you slow down just enough to learn and to own the design.</span></p>
<h2><b>The agentic development lifecycle: Recap</b></h2>
<p><span style="font-weight: 400">The agentic development lifecycle gives you a faster, more reliable way to build Agentforce agents. Install Agent Skills from the Salesforce Skills Library so your assistant knows Salesforce, scaffold and connect your project, then design before you build with plan mode and a design-first interview. Let the assistant generate the metadata and fix-and-retry failed deployments, validate and test the result, and debug with traces when something&#8217;s off. You move quicker and stay the designer the entire time.</span></p>
<p><span style="font-weight: 400">Want to go deeper or share what you&#8217;ve built? Join the conversation in the </span><a href="https://trailhead.salesforce.com/trailblazer-community/groups/0F93A000000DJbJSAW?tab=discussion&amp;sort=LAST_MODIFIED_DATE_DESC"><span style="font-weight: 400">Salesforce Developers Trailblazer Community</span></a><span style="font-weight: 400">.</span></p>
<h2><b>Resources</b></h2>
<ul>
<li style="font-weight: 400"><a href="https://developer.salesforce.com/developer-centers/agentforce"><span style="font-weight: 400">Agentforce Developer Center</span></a></li>
<li style="font-weight: 400"><a href="https://github.com/forcedotcom/sf-skills"><span style="font-weight: 400">Salesforce Skills Library on GitHub (forcedotcom/sf-skills)</span></a></li>
<li style="font-weight: 400"><a href="https://marketplace.visualstudio.com/items?itemName=salesforce.salesforcedx-vscode-agents"><span style="font-weight: 400">Agentforce DX Visual Studio Code extension</span></a></li>
</ul>
<h2><b>About the author</b></h2>
<p><b>Mohith Shrivastava</b><span style="font-weight: 400"> is a Principal Developer Advocate at Salesforce with 15 years of experience building enterprise-scale products on the Agentforce 360 Platform. Mohith is currently among the lead contributors on Salesforce Stack Exchange, a developer forum where Salesforce Developers can ask questions and share knowledge. You can follow him on </span><a href="https://www.linkedin.com/in/mohith-shrivastava-9a36464a/"><span style="font-weight: 400">LinkedIn</span></a><span style="font-weight: 400">.</span></p>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/06/master-the-agentic-development-lifecycle-for-agentforce">Master the Agentic Development Lifecycle for Agentforce</a> appeared first on <a href="https://developer.salesforce.com/blogs">Salesforce Developers Blog</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://developer.salesforce.com/blogs/2026/06/master-the-agentic-development-lifecycle-for-agentforce/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
	<post-id xmlns="com-wordpress:feed-additions:1">206559</post-id><media:thumbnail url="https://d259t2jj6zp7qm.cloudfront.net/images/20260618092236/SingleHeadshot-5-e1781799775222.png?w=1000" />
<media:content url="https://d259t2jj6zp7qm.cloudfront.net/images/20260618092236/SingleHeadshot-5-e1781799775222.png?w=1000" medium="image" />
	</item>
		<item>
		<title>Analyze and Consolidate Apex Triggers via Agentforce Vibes</title>
		<link>https://developer.salesforce.com/blogs/2026/06/analyze-and-consolidate-apex-triggers-via-agentforce-vibes</link>
		<comments>https://developer.salesforce.com/blogs/2026/06/analyze-and-consolidate-apex-triggers-via-agentforce-vibes#respond</comments>
		<pubDate>Tue, 23 Jun 2026 15:00:56 +0000</pubDate>
		<dc:creator><![CDATA[Lakshmi Anusha Myneni]]></dc:creator>
				<category><![CDATA[Agentforce]]></category>
		<category><![CDATA[Agentforce Vibes]]></category>
		<category><![CDATA[Apex]]></category>
		<category><![CDATA[Developer Tooling]]></category>
		<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[Apex Best Practices]]></category>
		<category><![CDATA[Apex triggers]]></category>
		<category><![CDATA[Code Refactoring]]></category>
		<category><![CDATA[Developer Tools]]></category>
		<category><![CDATA[Salesforce CLI]]></category>
		<category><![CDATA[salesforce dx]]></category>
		<category><![CDATA[technical debt]]></category>

		<guid isPermaLink="false">https://developer.salesforce.com/blogs/?p=206556</guid>
		<description><![CDATA[<p>Learn how to use Agentforce Vibes to automatically audit, risk-scan, and safely consolidate multiple Apex triggers into a single, clean, and maintainable framework.</p>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/06/analyze-and-consolidate-apex-triggers-via-agentforce-vibes">Analyze and Consolidate Apex Triggers via Agentforce Vibes</a> appeared first on <a href="https://developer.salesforce.com/blogs">Salesforce Developers Blog</a>.</p>
]]></description>
				<content:encoded><![CDATA[<p><span style="font-weight: 400">Every org has a story, and it usually begins the same way: one Apex trigger here, another there, each solving a problem in the moment. But over time, orgs quietly accumulate multiple Apex triggers on the same object, written by different developers with no unified strategy. Before long, you&#8217;re staring down unpredictable execution orders, governor limit landmines, logic conflicts, and recursion nightmares. Sound familiar?</span></p>
<p><span style="font-weight: 400">Of course, managed package triggers are a black box. We don&#8217;t own them, and we can&#8217;t touch them. But our custom triggers? That&#8217;s our house, and we get to set the rules. The answer isn&#8217;t more triggers. It&#8217;s one, done right.</span></p>
<p><span style="font-weight: 400">That&#8217;s where </span><a href="https://www.salesforce.com/agentforce/developers/vibe-coding/"><span style="font-weight: 400">Agentforce Vibes</span></a><span style="font-weight: 400"> becomes your best pair programmer. It reads through your triggers, tells you exactly what each one is doing, spots the landmines, and helps you consolidate everything into a clean, scalable pattern iejggcbhdbklunjvfhejciflcuchnvrn the way it should have been built from the start. What would typically take days of manual code review and architectural planning gets done in a fraction of the time, so your team can focus on what actually matters building great solutions.</span></p>
<p><span style="font-weight: 400">In my org, I had multiple triggers firing on the Opportunity object, each one written at a different point in time, by different developers, for different reasons. In this post, I’ll walk you through the approach I used to bring all of that chaos under one roof, into a single, clean trigger while intentionally leaving any managed package triggers untouched.</span></p>
<p><span style="font-weight: 400">The workflows, skills, prompts, and steps you&#8217;ll see throughout this post are based on my own org, and your org will certainly look different. Your trigger count, logic complexity, and dependency map are uniquely yours. That&#8217;s completely normal, and honestly, that&#8217;s the point.</span></p>
<h2><span style="font-weight: 400">From concept to practice: Skills and workflows</span></h2>
<p><span style="font-weight: 400">What makes this approach powerful is that you can turn it into a reusable</span><a href="https://developer.salesforce.com/docs/platform/einstein-for-devs/guide/skills.html"> <span style="font-weight: 400">skill</span></a><span style="font-weight: 400"> or</span><a href="https://developer.salesforce.com/docs/platform/einstein-for-devs/guide/devagent-workflows.html"> <span style="font-weight: 400">workflow</span></a><span style="font-weight: 400"> — a framework that you can run on any object, share across your team, or plug into other agents. The principles are universal; the execution is yours to shape.</span></p>
<h3><span style="font-weight: 400">Skills: Your on-demand code analyst</span></h3>
<p><span style="font-weight: 400">Think of a skill as a specialist you can call on instantly. Point it at your triggers and it surfaces what actually matters: recursion traps, SOQL inside loops, conflicting field assignments, or gaps in test coverage. Analysis that used to take hours of careful reading? It now takes minutes.</span></p>
<h3><span style="font-weight: 400">Workflows: Safe, repeatable refactoring at scale</span></h3>
<p><span style="font-weight: 400">A workflow takes that analysis and turns it into a structured, sequenced process: audit first, then refactor what needs fixing, then generate the consolidated trigger, handler, and tests in the right order. No steps skipped, no code merged before it&#8217;s ready. Just a clean, predictable path from messy to maintainable.</span></p>
<h2><span style="font-weight: 400">From chaos to a single, clean trigger</span></h2>
<p><span style="font-weight: 400">Below is the a generic workflow that I implemented to take multiple Apex triggers on a single object and consolidate them into one clean, maintainable handler.</span></p>
<table>
<tbody>
<tr>
<td><b>Step</b></td>
<td><b>Description</b></td>
</tr>
<tr>
<td><span style="font-weight: 400">1. Workflow start</span></td>
<td>You run the <code>/trigger-consolidation</code> command to start the trigger consolidation workflow.</td>
</tr>
<tr>
<td><span style="font-weight: 400">2. Metadata retrieval</span></td>
<td><span style="font-weight: 400">The trigger consolidation workflow uses the Salesforce CLI to query the org’s metadata and pull all trigger and class sources.</span></td>
</tr>
<tr>
<td><span style="font-weight: 400">3. Trigger analysis</span></td>
<td><span style="font-weight: 400">The workflow runs the 15-dimension risk assessment on your code thanks to the two trigger analysis skills.</span></td>
</tr>
<tr>
<td><span style="font-weight: 400">4. Plan creation</span></td>
<td>The workflow writes the audit plan to <code>docs/trigger-audit-plan.md</code>.</td>
</tr>
<tr>
<td><span style="font-weight: 400">5. Plan review</span></td>
<td><span style="font-weight: 400">You read, edit, adjust, and approve the plan.</span></td>
</tr>
<tr>
<td><span style="font-weight: 400">6. Plan execution</span></td>
<td><span style="font-weight: 400">The workflow reads your plan, and generates files that implement the trigger consolidation.</span></td>
</tr>
<tr>
<td><span style="font-weight: 400">7. Manual review</span></td>
<td>You review generated files and resolve the <code>// CONFLICT</code> and <code>// ASYNC REQUIRED</code> comments.</td>
</tr>
</tbody>
</table>
<p><span style="font-weight: 400">This workflow works on any standard or custom object as it contains no hardcoded object names. You can run it multiple times on different objects. You’ll get the same process and the same quality of analysis without the need for reconfiguration.</span></p>
<p><span style="font-weight: 400">Let’s take a look at the workflow steps in more detail.</span></p>
<h3><span style="font-weight: 400">Step 0: Project setup</span></h3>
<p><span style="font-weight: 400">Before you get started, there are a few things you&#8217;ll need in place.</span></p>
<p><span style="font-weight: 400">This walkthrough assumes that you&#8217;re working in VS Code with the</span><a href="https://marketplace.visualstudio.com/items?itemName=salesforce.salesforcedx-vscode"> <b>Salesforce Extension Pack</b></a><span style="font-weight: 400"> installed, your org is authenticated via the</span><a href="https://developer.salesforce.com/tools/salesforcecli"> <b>Salesforce CLI</b></a><span style="font-weight: 400">, and the</span><a href="https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_mcp_server.htm"> <b>Salesforce DX MCP Server</b></a><span style="font-weight: 400"> is enabled in Agentforce Vibes. If you&#8217;re already set up, feel free to skip ahead. Otherwise, each link above will get you there quickly.</span></p>
<p><span style="font-weight: 400">I&#8217;ve published a base version of the skills and workflows for trigger consolidation on</span><a href="https://github.com/anushamyneni1/apexTriggerConsolidation"> <b>GitHub</b></a><span style="font-weight: 400">. Think of it as your ready-to-run starting point: fork it, adapt it to your org&#8217;s objects and conventions, and make it your own.</span></p>
<p>Now, let&#8217;s install the skills and workflows. Head over to this<a href="https://github.com/anushamyneni1/apexTriggerConsolidation#setup"> <b><u>README</u></b></a> for setup instructions, it walks you through copying the <code>.a4drules/</code> directory into your Salesforce DX project root.</p>
<p>This copies three files into your project: <code>apex-trigger-risk-scan.md</code>,<code> </code><code>pex-trigger-consolidation-analysis.md</code>, and <code>trigger-consolidation.md</code>.</p>
<pre>your-sfdx-project/
├─ .a4drules/
│   ├─ skills/
│   │   └─ apex-trigger-risk-scan.md                  ← single trigger analysis skill
│   │      └─ apex-trigger-consolidation-analysis.md  ← cross trigger analysis skill
│   └─ workflows/
│       └─ trigger-consolidation.md    ← trigger consolidation workflow
</pre>
<p><span style="font-weight: 400">That&#8217;s the entire install — no restart or configuration required. Agentforce Vibes picks up the new skills and workflow immediately.</span></p>
<h3><span style="font-weight: 400">Step 1: Workflow start</span></h3>
<ol>
<li style="font-weight: 400"><span style="font-weight: 400">Click the </span><b>Agentforce Vibes</b><span style="font-weight: 400"> icon in the VS Code Activity Bar to open the chat panel.</span></li>
<li style="font-weight: 400"><span style="font-weight: 400"><span style="font-weight: 400">Type the following command to start the trigger consolidation workflow.</span></span>
<pre>/trigger-consolidation
</pre>
</li>
<li style="font-weight: 400"><span style="font-weight: 400"><span style="font-weight: 400">The workflow kicks off by asking which object to work on.</span></span>
<pre>Which Salesforce object do you want to consolidate triggers for?
(e.g. Opportunity, Case, Account, Invoice__c)
</pre>
<p><span style="font-weight: 400"></span></li>
<li>Type an object name like <code>Opportunity</code> and press Enter.</li>
</ol>
<h3><span style="font-weight: 400">Step 2: Metadata retrieval</span></h3>
<p><span style="font-weight: 400">The workflow uses the Salesforce CLI to query your org’s metadata to retrieve Apex triggers and the related helper classes.</span></p>
<p><span style="font-weight: 400">You can watch it work in the chat panel with an output similar to this:</span></p>
<pre>Running: sf data query --query "SELECT Name, ... FROM ApexTrigger
WHERE TableEnumOrId = 'Opportunity' AND Status = 'Active'"

## Summary of what was retrieved:
**Triggers (9 total, 5 readable):**
1. **OpportunityBefore** (x2) - Body hidden (managed package / org-installed, unreadable)
2. **OpportunityAfter** - Body hidden (unreadable)
3. **OpportunityTrigger** - Body hidden (unreadable)
4. **OppDeleteTrigger** - before delete — readable
5. **OppCreateTrigger** - before insert, after insert — readable
6. **OpportunityNotifyTrigger** - after insert, after update — readable
7. **OppStageTrigger** - before update — readable
8. **OpportunityUpdateTrigger** - before update, after update — readable
The dependent classes are all accessible: OpportunityUtils, OpportunityHelper, and NotificationService. No test classes exist for this trigger suite. Let me load the apex-trigger-analysis skill to conduct the risk assessment.
</pre>
<h3><span style="font-weight: 400">Step 3: Trigger analysis</span></h3>
<p><span style="font-weight: 400">With the source in hand, the workflow loads the two trigger analysis skills and runs every trigger through all risk dimensions.</span></p>
<h4><span>Per-trigger scan: </span><code>apex-trigger-risk-scan</code><span> skill (seven dimensions)</span></h4>
<table>
<tbody>
<tr>
<td><b>Dimension</b></td>
<td><b>What It Catches</b></td>
</tr>
<tr>
<td><b>Execution order risk</b></td>
<td><span style="font-weight: 400">Flags assumptions that the trigger makes about running before or after another specific trigger, e.g., comments or field reads that depend on values another trigger should set first.</span></td>
</tr>
<tr>
<td><b>Recursion traps</b></td>
<td><span style="font-weight: 400">Flags absence of a static Boolean recursion guard when DML is present — CRITICAL if no guard and DML exists, and HIGH if no guard only.</span></td>
</tr>
<tr>
<td><b>Governor limit exposure</b></td>
<td><span style="font-weight: 400">Scans for SOQL inside loops (CRITICAL), DML inside loops (CRITICAL), synchronous HTTP callouts in trigger context (HIGH), and aggregate queries without LIMIT (MEDIUM).</span></td>
</tr>
<tr>
<td><b>Before/after context boundary</b></td>
<td><span style="font-weight: 400">Maps which contexts that the trigger fires in and flags misplaced logic, e.g., field assignments in after context (silently lost) or DML on triggering record in before context (causes recursion).</span></td>
</tr>
<tr>
<td><b>Bypass mechanism</b></td>
<td><span style="font-weight: 400">Looks for kill-switch patterns, such as custom setting/metadata checks, permission-based skips, or static Boolean flags set externally, and notes absence as well as presence.</span></td>
</tr>
<tr>
<td><b>Static variable inventory</b></td>
<td><span style="font-weight: 400">Lists every static variable in handler/helper classes and flags generic names (isRunning, processed) that collide with the same name in another trigger&#8217;s class after consolidation.</span></td>
</tr>
</tbody>
</table>
<h4><span>Cross-trigger analysis: </span><code>apex-trigger-consolidation-analysis</code><span> skills (eight dimensions)</span></h4>
<table>
<tbody>
<tr>
<td><b>Dimension</b></td>
<td><b>What It Checks</b></td>
</tr>
<tr>
<td><b>Logic overlap</b></td>
<td><span style="font-weight: 400">Compares all triggers and flags the same field assigned in multiple triggers, identical validation logic, and the same helper method called from multiple triggers.</span></td>
</tr>
<tr>
<td><b>Conflicting logic</b></td>
<td><span style="font-weight: 400">Flags contradictions across triggers on the same event, such as opposing field values, one trigger nulling a field another reads, mutually exclusive conditionals, or read-after-write dependencies.</span></td>
</tr>
<tr>
<td><b>Helper class coupling</b></td>
<td><span style="font-weight: 400">Maps helper/utility classes to dependent triggers, i.e., any class used by more than two triggers is a consolidation risk since modifying it can break all callers.</span></td>
</tr>
<tr>
<td><b>Test coverage gaps</b></td>
<td><span style="font-weight: 400">Determines per trigger whether or not a test class exists, coverage is below 85%, or there’s an absence of a bulk test with 200+ records.</span></td>
</tr>
<tr>
<td><b>Static variable collision</b></td>
<td><span style="font-weight: 400">Cross-references static variable inventories from all per-trigger scans and identifies name collisions, such as two classes with the same static variable name, corrupting recursion guards.</span></td>
</tr>
<tr>
<td><b>Bypass consolidation strategy</b></td>
<td><span style="font-weight: 400">Assesses whether bypass mechanisms can be unified: compatible bypasses merge into one; inconsistent bypasses require per-method flags; or triggers with no bypass that inherit a unified bypass unintentionally are flagged.</span></td>
</tr>
<tr>
<td><b>Automation re-entry risk</b></td>
<td>Identifies flows, process builders, or workflow field updates that can commit DML re-firing Apex triggers mid-transaction, and flags <code>@future/Queueable/Platform Event</code> calls that could loop back.</td>
</tr>
<tr>
<td><b>Cumulative governor limit budget</b></td>
<td><span style="font-weight: 400">Sums SOQL, DML, and heap consumption across all triggers to surface compound risk invisible per-trigger, and flags when the combined estimate approaches Salesforce limits even if no single trigger is problematic alone.</span></td>
</tr>
</tbody>
</table>
<p><span style="font-weight: 400">You’ll see findings stream into the panel in real time.</span></p>
<pre>Score Meaning
1–3      Low — safe to consolidate with minimal prep
4–6      Medium — refactor required before merge
7–9      High — urgent action recommended
10       Critical — production incident likely without immediate action
Overall risk score: 7/10
</pre>
<h3><span style="font-weight: 400">Step 4: Plan creation</span></h3>
<p>Here&#8217;s the part that matters: <b>the workflow does not generate a single line of code yet.</b><br />
Instead, it writes a detailed plan file to <code>docs/trigger-audit-plan.md</code> and pauses.</p>
<pre>Audit complete. I've written the full plan to docs/trigger-audit-plan.md.
Overall risk score: 7/10
Please review the findings and edit anything before I generate
the consolidated scaffold.
When you're ready, reply YES to proceed. Reply NO to stop here.
</pre>
<h3><span style="font-weight: 400">Step 5: Plan review</span></h3>
<p><span style="font-weight: 400">Open the plan file in VS Code. It contains:</span></p>
<ul>
<li style="font-weight: 400"><b>Trigger inventory</b><span style="font-weight: 400">: Every trigger, its events, line count, risk score, and top finding</span></li>
<li style="font-weight: 400"><b>Risk register</b><span style="font-weight: 400">: Every finding with severity, plain-English description, and recommendation</span></li>
<li style="font-weight: 400"><b>Dependency graph</b><span style="font-weight: 400">: Which triggers share which helper classes, as a plain-text diagram</span></li>
<li style="font-weight: 400"><b>Logic merge map</b><span style="font-weight: 400">: Exactly which logic from which trigger goes into each consolidated context, with conflicts flagged explicitly</span></li>
<li style="font-weight: 400"><b>Best practices compliance:</b><span style="font-weight: 400"> Where current triggers violate Apex standards and what the consolidated code will fix</span></li>
</ul>
<p><b>This file is yours to edit.</b><span style="font-weight: 400"> You can change the instructions, add team notes, remove a trigger from the plan, etc. When you’re ready to proceed, the workflow reads your edited version before it writes any code. What you see in the plan is what gets built.</span></p>
<p><span style="font-weight: 400">When the plan looks right, go back to the Agentforce Vibes chat panel and type:</span></p>
<pre>YES
</pre>
<h3><span style="font-weight: 400">Step 6: Plan execution</span></h3>
<p><span style="font-weight: 400">The workflow reads your (possibly edited) plan and generates consolidated files directly into your Salesforce DX project.</span></p>
<p><span style="font-weight: 400">For instance, this is what you could see when consolidating an opportunity trigger:</span></p>
<pre>✓ force-app/main/default/triggers/OpportunityTrigger.trigger        (28 lines)
✓ force-app/main/default/classes/OpportunityTriggerHandler.cls      (74 lines)
✓ force-app/main/default/classes/OpportunityTriggerHelper.cls       (32 lines)
✓ force-app/main/default/classes/OpportunityTriggerTest.cls         (58 lines)

Consolidation complete. Search for // CONFLICT and // ASYNC REQUIRED
comments — these are the only places that need manual review.
</pre>
<p><span style="font-weight: 400">In this example, these four files are generated:</span></p>
<ul>
<li><b><code>OpportunityTrigger.trigger</code></b>: This is a single trigger covering only the events that existed across the originals. No business logic in the body, everything delegates to the handler.</li>
<li><b><code>OpportunityTriggerHandler.cls</code></b>: One method per trigger context has a static Boolean recursion guard at the top, and every method is annotated with its consolidated source triggers. Logic is merged in the correct sequence per the Logic Merge Map you reviewed.</li>
<li><b><code>OpportunityTriggerHelper.cls</code></b>: This is the actual business logic, fully ported from the original triggers. All SOQL is bulkified outside loops, and duplicate logic from multiple triggers appears exactly once.</li>
<li><b><code>OpportunityTriggerTest.cls</code></b>: Test methods for single insert, bulk insert (200 records), update, bulk update, and delete are each traceable back to the source trigger.</li>
</ul>
<h3><span style="font-weight: 400">Step 7: Manual review</span></h3>
<p><span style="font-weight: 400">Once the workflow terminates, the last manual task is to review the comments that were left during the execution. There are two types of comments that you should address:</span></p>
<ul>
<li><code>// CONFLICT</code>: Where two triggers set the same field to different values. You decide which wins</li>
<li><code>// ASYNC REQUIRED</code>: Where a sync callout needs to move to <code>@future</code> or <code>Queueable</code> before deploying</li>
</ul>
<p><span style="font-weight: 400">Finally, the trigger jungle is a solved problem. Once a trigger is optimized, you can leverage prompts to deactivate the old trigger and, once tested and validated, promote the new implementation to a higher environment.</span></p>
<h2><span style="font-weight: 400">Considerations</span></h2>
<ul>
<li><b><span>Before you adapt this framework, make it yours.</span></b><span> The workflow and skill are starting points, not scripts. Your org&#8217;s complexity, naming conventions, and business logic are unique to everything generated here, so edit the audit plan before you commit to it.</span></li>
<li><b><span>Spend real time in plan mode before generating any code.</span></b><span> Open </span><code><span>docs/trigger-audit-plan.md</span></code><span>, work through the phases, flag the conflicts you already know about, and align the merge order with your team&#8217;s plan. The more thinking you do upfront, the cleaner the output.</span></li>
<li><b><span>Treat generated code as a recommendation, not a final answer.</span></b><span> Always review and validate before you deploy. These are smart suggestions, not certified production-ready commits.</span></li>
<li><b><span>Fix High severity issues before you consolidate anything.</span></b><span> If the audit surfaces recursion risks, SOQL or DML in loops, or field conflicts, stop and resolve those first. Merging broken logic into a single trigger doesn&#8217;t fix it and just makes it harder to debug later.</span></li>
<li><b><span>Version control everything.</span></b><span> This includes triggers, handler classes, service classes, audit files, test classes — all of it. If it gets generated or modified, it belongs in source control.</span></li>
<li><b><span>Always test in a sandbox first and never run directly against production.</span></b><span> The workflow connects to whichever org you&#8217;re authenticated to, so before running </span><code><span>/trigger-consolidation</span></code><span>, verify that </span><code><span>sf org list</span></code><span> shows a sandbox or scratch org as your default. Consolidating triggers in production without testing is how outages happen.</span></li>
<li><b><span>Run one object at a time, but think about the whole org.</span></b><span> This isn&#8217;t a one-and-done fix. Once Opportunity is clean, move to Case, then Account, then your custom objects. The phased approach is intentional: each consolidation builds confidence for the next.</span></li>
</ul>
<h2><span style="font-weight: 400">Conclusion</span></h2>
<p><span style="font-weight: 400">Trigger sprawl is common and costly, and with the right approach, it&#8217;s completely solvable.</span></p>
<p><b>Ready to clean up your org?</b><span style="font-weight: 400"> Start with your simplest triggers, follow the steps in this guide, build confidence, then tackle the high-traffic objects. Run the Agentforce Vibes analysis skill and let the audit canvas show you exactly what you&#8217;re dealing with. One trigger, one handler, zero surprises.</span></p>
<h2><span style="font-weight: 400">Resources</span></h2>
<p><b>Agentforce Vibes</b></p>
<ul>
<li style="font-weight: 400"><span style="font-weight: 400">Developer Guide: </span><a href="https://developer.salesforce.com/docs/platform/einstein-for-devs/overview"><span style="font-weight: 400">Agentforce Vibes Overview</span></a></li>
<li style="font-weight: 400"><span style="font-weight: 400">Developer Guide: </span><a href="https://developer.salesforce.com/docs/platform/einstein-for-devs/guide/devagent-getstarted.html"><span style="font-weight: 400">Agentforce Vibes Quick Start</span></a></li>
<li style="font-weight: 400"><span style="font-weight: 400">Install:</span> <a href="https://marketplace.visualstudio.com/items?itemName=salesforce.salesforcedx-vscode"><span style="font-weight: 400">Salesforce Extension Pack for VS Code</span></a></li>
<li style="font-weight: 400"><span style="font-weight: 400">Documentation:</span> <a href="https://developer.salesforce.com/docs/platform/einstein-for-devs/guide/devagent-mcp.html"><span style="font-weight: 400">Agentforce Vibes MCP Server </span></a></li>
<li style="font-weight: 400"><span style="font-weight: 400">Developer Guide: </span><a href="https://developer.salesforce.com/docs/platform/einstein-for-devs/guide/skills.html"><span style="font-weight: 400">Skills in Agentforce Vibes</span></a></li>
<li style="font-weight: 400"><span style="font-weight: 400">Developer Guide: </span><a href="https://developer.salesforce.com/docs/platform/einstein-for-devs/guide/devagent-workflows.html"><span style="font-weight: 400">Workflows in Agentforce Vibes</span></a></li>
</ul>
<p><b>Salesforce CLI</b></p>
<ul>
<li style="font-weight: 400"><span style="font-weight: 400">Setup Guide: </span><a href="https://developer.salesforce.com/docs/atlas.en-us.sfdx_setup.meta/sfdx_setup/sfdx_setup_intro.htm"><span style="font-weight: 400">Salesforce CLI </span></a></li>
<li style="font-weight: 400"><span style="font-weight: 400">Command Reference:</span> <a href="https://developer.salesforce.com/docs/atlas.en-us.sfdx_cli_reference.meta/sfdx_cli_reference/cli_reference_project_retrieve_start.htm"><span style="font-weight: 400">SF Project Retrieve</span></a></li>
<li style="font-weight: 400"><span style="font-weight: 400">Command Reference: </span><a href="https://developer.salesforce.com/docs/atlas.en-us.sfdx_cli_reference.meta/sfdx_cli_reference/cli_reference_data_query.htm"><span style="font-weight: 400">SF Data Query </span></a></li>
</ul>
<p><b>Apex Trigger Best Practices</b></p>
<ul>
<li style="font-weight: 400"><span style="font-weight: 400">Developer Guide: </span><a href="https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_triggers.htm"><span style="font-weight: 400">Apex Triggers </span></a></li>
<li style="font-weight: 400"><span style="font-weight: 400">Developer Guide:</span> <a href="https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_best_practices.htm"><span style="font-weight: 400">Apex Best Practices</span></a></li>
<li style="font-weight: 400"><span style="font-weight: 400">Trailhead:</span> <a href="https://trailhead.salesforce.com/content/learn/modules/apex_triggers/apex_triggers_intro"><span style="font-weight: 400">Apex Trigger Badge</span></a></li>
</ul>
<p><b>Metadata &amp; Deployment</b></p>
<ul>
<li style="font-weight: 400"><span style="font-weight: 400">Developer Guide: </span><a href="https://developer.salesforce.com/docs/atlas.en-us.api_meta.meta/api_meta/meta_intro.htm"><span style="font-weight: 400">Metadata API </span></a></li>
<li style="font-weight: 400"><span style="font-weight: 400">Reference:</span> <a href="https://developer.salesforce.com/docs/atlas.en-us.api_meta.meta/api_meta/manifest_samples.htm"><span style="font-weight: 400">Package.xml Manifest</span></a></li>
<li style="font-weight: 400"><span style="font-weight: 400">Developer Guide:</span> <a href="https://help.salesforce.com/s/articleView?id=platform.custommetadatatypes_overview.htm&amp;type=5"><span style="font-weight: 400">Custom Metadata Types </span></a></li>
</ul>
<h2><span style="font-weight: 400">About the author</span></h2>
<p><b>Lakshmi Anusha Myneni</b><span style="font-weight: 400"> is a Principal Technical Architect at Salesforce with 15 years of experience in the financial services industry. She partners closely with customers during the most critical stages of their journey, translating complex business challenges into compelling, technically sound Salesforce solutions. </span></p>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/06/analyze-and-consolidate-apex-triggers-via-agentforce-vibes">Analyze and Consolidate Apex Triggers via Agentforce Vibes</a> appeared first on <a href="https://developer.salesforce.com/blogs">Salesforce Developers Blog</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://developer.salesforce.com/blogs/2026/06/analyze-and-consolidate-apex-triggers-via-agentforce-vibes/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
	<post-id xmlns="com-wordpress:feed-additions:1">206556</post-id><media:thumbnail url="https://d259t2jj6zp7qm.cloudfront.net/images/20260622144837/Generic-B-2-e1782164932989.png?w=1000" />
<media:content url="https://d259t2jj6zp7qm.cloudfront.net/images/20260622144837/Generic-B-2-e1782164932989.png?w=1000" medium="image" />
	</item>
		<item>
		<title>The Big Objects Playbook: Payload Capture and Replay</title>
		<link>https://developer.salesforce.com/blogs/2026/06/big-objects-playbook-payload-capture-and-replay</link>
		<comments>https://developer.salesforce.com/blogs/2026/06/big-objects-playbook-payload-capture-and-replay#respond</comments>
		<pubDate>Thu, 18 Jun 2026 15:00:28 +0000</pubDate>
		<dc:creator><![CDATA[Laxman Vattam]]></dc:creator>
				<category><![CDATA[Apex]]></category>
		<category><![CDATA[APIs and Integrations]]></category>
		<category><![CDATA[Architecture]]></category>
		<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[Big Objects]]></category>
		<category><![CDATA[Composite Primary Index]]></category>
		<category><![CDATA[Data Cloud]]></category>
		<category><![CDATA[Database.insertImmediate]]></category>
		<category><![CDATA[Payload Capture]]></category>
		<category><![CDATA[Platform Events]]></category>
		<category><![CDATA[scheduled apex]]></category>

		<guid isPermaLink="false">https://developer.salesforce.com/blogs/?p=206431</guid>
		<description><![CDATA[<p>Salesforce Big Objects explained: when to use them, how to design composite indexes, and how to build a payload capture and replay system for high-volume integrations.</p>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/06/big-objects-playbook-payload-capture-and-replay">The Big Objects Playbook: Payload Capture and Replay</a> appeared first on <a href="https://developer.salesforce.com/blogs">Salesforce Developers Blog</a>.</p>
]]></description>
				<content:encoded><![CDATA[<p><span style="font-weight: 400">You’ve likely hit this performance wall before or know someone who has. Standard object storage starts creeping toward its limits. Queries slow down. Compliance asks for seven years of audit history. An Internet of Things (IoT) integration wants to push ten thousand events a minute. The platform wasn’t designed to take that load on transactional objects, and you start wondering if you’re supposed to move everything off-platform just to keep up.</span></p>
<p><span style="font-weight: 400">Well, you don’t have to. </span><a href="https://developer.salesforce.com/docs/atlas.en-us.bigobjects.meta/bigobjects/"><span style="font-weight: 400">Big Objects</span></a><span style="font-weight: 400"> were built for exactly this scenario. In this post, we’ll look at what makes them tick and explore how to avoid the design traps that bite most developers. </span></p>
<h2><span style="font-weight: 400">What are big objects?</span></h2>
<p>A big object is a specialized Salesforce object designed to store hundreds of millions, or even billions of records, without degrading query performance. Unlike custom objects, they don’t count against your org’s <a href="https://help.salesforce.com/s/articleView?id=xcloud.overview_storage.htm&amp;type=5"><u>standard storage limits</u></a>. They end in <code>__b</code> instead of <code>__c</code>, and that one character signals a fundamentally different contract with the Salesforce Platform.</p>
<p><span style="font-weight: 400">With the growing conversation around Data 360, some developers assume that the Big Objects feature is on its way out. That is not the case. Big Objects continues to be the right tool for high-volume, append-heavy, write-once-query-later workloads, and it is still the only native tool that can handle that level of scale.</span></p>
<p>There are two types of big objects:</p>
<ul>
<li><b>Standard Big Objects:</b> These are predefined by Salesforce and cannot be customized. An example is <code>FieldHistoryArchive</code> used by Field Audit Trail.</li>
<li><b>Custom Big Objects:</b> These are defined by developers through the Setup UI or the <a href="https://developer.salesforce.com/docs/atlas.en-us.api_meta.meta/api_meta/metadata.htm"><u>Metadata API</u></a>. That’s where most of the interesting work happens.</li>
</ul>
<p><span style="font-weight: 400">Adopting big objects means accepting strict trade-offs. For example, you lose platform automation (no triggers or flows) and out-of-the-box UI (no standard reports, list views, or sharing rules), and frontends require custom Lightning web components (LWC). Big objects are not transactional database tables, instead they are a durable, queryable ledger. Once you adopt that framing, they become an incredibly powerful tool.</span></p>
<p><span style="font-weight: 400">Big Objects can be used to implement advanced architectural patterns. Let’s take a look at a pattern that captures and logs your org&#8217;s platform events using a big object, for improved traceability and error handling.</span></p>
<h2><span style="font-weight: 400">Architectural pattern with big objects: Payload capture and replay</span></h2>
<p><span style="font-weight: 400">Modern Salesforce orgs act as integration hubs. Platform events stream in from Enterprise Resource Planning (ERP) systems. Outbound callouts push data to marketing platforms, and webhooks arrive from payment gateways. Platform events are retained only briefly — up to 72 hours for high-volume platform events. When things fail, payloads can disappear silently as the platform event bus moves on. The data is effectively gone.</span></p>
<p><span style="font-weight: 400">In this pattern, you write a copy of every platform event that your org publishes to a big object </span><i><span style="font-weight: 400">before</span></i><span style="font-weight: 400"> any business logic runs, so the big object becomes a durable ledger of every event that has crossed the platform event bus. A platform event Apex trigger subscribes to the event (or a platform event–triggered flow) and persists it to the big object. There&#8217;s no extra publishing step, you simply log the event that you already received. After an event is captured and processed, if anything fails, the error details are written to a lightweight custom object. This improves error visibility and lets failures be replayed automatically.</span></p>
<p>The flowchart below shows integration traffic captured to the <code>IntegrationPayloadLog__b</code> big object before processing, failures logged to the <code>IntegrationError__c</code> custom object, and a daily batch job replaying failed payloads. The two objects are linked by <code>EventUuid__c</code>.</p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206553" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260617120126/Flowchart-showing-integration-traffic-e1781722900206.png?w=1000" class="postimages" width="1000" height="768" alt="Flowchart showing integration traffic." />
			  </span>
			</p>
<p><span style="font-weight: 400">Let’s take a look at how to build this flow in six steps: define each of the two objects, capture every payload, log failures, automate retries, and add visibility.</span></p>
<h3><span style="font-weight: 400">Step 1: Define the Integration Payload Log big object</span></h3>
<p>A robust payload log requires a well-structured big object schema. For instance, <b>a big object</b> like <span>IntegrationPayloadLog__b</span><span> can capture essential data. Key fields include:</span></p>
<ul>
<li><b><code><span>IntegrationName__c</span></code><span>:</span></b><span> </span><span>A label for the integration or channel (for example, ERP_Inbound) and part of the composite index</span></li>
<li><b><code><span>TransactionId__c</span></code><span>:</span></b><span> </span><span>The source system&#8217;s correlation or transaction ID, when one is available</span></li>
<li><b><code><span>EventUuid__c</span></code><span>:</span></b><span> </span><span>The platform event&#8217;s globally unique ID, linking the record back to the original event and leading the composite index</span></li>
<li><b><code><span>Timestamp__c</span></code><span>:</span></b><span> </span><span>When the payload was captured</span></li>
<li><b><code><span>Direction__c</span></code><span>:</span></b><span> </span><span>Whether the payload was Inbound or Outbound</span></li>
<li><b><code><span>PayloadBody__c</span></code><span>:</span></b><span> </span><span>The raw request or response body, stored as a Long Text Area</span></li>
</ul>
<p>Because this pattern logs platform event traffic, we store the platform event&#8217;s <code>EventUuid__c</code>. <code>EventUuid__c</code> is a system-generated, globally unique identifier that Salesforce assigns to each platform event upon publishing. Storing it on your big object gives you an unbreakable link between the durable payload record and the original event on the platform event bus.</p>
<p><span style="font-weight: 400">Something key in this architectural pattern is to design your </span><a href="https://help.salesforce.com/s/articleView?id=platform.custom_bo_index.htm&amp;language=en_US&amp;type=5"><span style="font-weight: 400">composite primary index</span></a><span style="font-weight: 400"> right. The composite primary index is the only entry point into your big object. Queries must filter on indexed fields as full table scans don’t work at this scale. The mistake that most developers make is designing the index around what the data looks like. Instead, design it around the question you’ll ask most often. </span></p>
<p>For instance, if you want to build a system in which the core question is, &#8220;Give me the exact payloads for these specific failed events,&#8221; then the composite index should become <code>EventUuid__c</code><code> → </code><code>IntegrationName__c</code>. Set the most highly selective identifier first (the UUID), followed by the integration name. This order can map directly to the <code>WHERE</code> clause in SOQL queries to retrieve specific records.</p>
<p><span style="font-weight: 400">Designing the composite primary index is the most important design decision that you’ll make, and unlike almost everything else on the platform, you can’t change it after deployment.</span></p>
<h3><span style="font-weight: 400">Step 2: Define the Integration Error custom object</span></h3>
<p>Next, you define a lightweight custom object — for example, <code>IntegrationError__c</code> — specifically for capturing platform event processing failures. To ensure that your support team has exactly what they need to troubleshoot and retry the payload, we recommend creating the following fields on this custom object:</p>
<ul>
<li><b><code>IntegrationName__c</code></b><b>:</b> Name of the integration</li>
<li><b><code>EventUuid__c</code></b><b>:</b> The shared ID linking directly to the <code>EventUuid__c</code> on the big object record</li>
<li><b><code>Exception_Type__c</code></b><b>:</b> The specific class of error (e.g., <code>CalloutException</code>, <code>DmlException</code>)</li>
<li><b><code>Error_Message__c</code></b><b>:</b> The detailed system error message</li>
<li><b><code>Stack_Trace__c</code></b><b>:</b> The developer stack trace for deep debugging</li>
<li><b><code>Failed_Record_IDs__c</code></b><b>:</b> A text field capturing the specific Salesforce record IDs that failed during processing</li>
<li><b><code>Status__c</code></b><b>:</b> A picklist (e.g., Open, Retrying, Resolved) to manage the recovery workflow</li>
<li><b><code>RetryCount__c</code></b><b>:</b> Number of times the error is reprocessed</li>
</ul>
<p>At first glance, creating two separate objects might seem redundant. Why not just track errors in the big object? The answer comes down to the fundamental differences between big objects and standard custom objects. Think of <code>IntegrationPayloadLog__b</code> as your <b>Universal Ledger</b> (the filing cabinet that permanently stores everything) and <code>IntegrationError__c</code> as your <b>Action Queue</b> (the daily to-do list for your support team).</p>
<p><span style="font-weight: 400">Here is the breakdown of when to use what:</span></p>
<ul>
<li><b><span>IntegrationPayloadLog__b</span><span> (The Ledger):</span></b><span> </span>
<ul>
<li><b>What it stores:</b> <i>Every</i> payload, regardless of success or failure. It holds the heavy, bulky JSON/XML data.</li>
<li><b>Volume:</b> Millions to billions of records.</li>
<li><b>Platform features:</b> No standard UI, no list views, no declarative automation (flows/triggers). It is strictly for massive-scale storage and code-based retrieval.</li>
</ul>
</li>
<li><b><span>IntegrationError__c</span><span> (The Action Queue):</span></b><span> </span>
<ul>
<li><b>What it stores:</b> <i>Only</i> the failures. It is extremely lightweight and does not store the heavy payload, it only stores error metadata and a pointer (<code>EventUuid__c</code>) back to the big object.</li>
<li><b>Volume:</b> Hundreds to thousands of records (only your active, unresolved errors).</li>
<li><b>Platform features:</b> Full Salesforce Platform power. You get standard list views, reportable error trends, and flow-driven escalations.</li>
</ul>
</li>
</ul>
<p><span style="font-weight: 400">By splitting the architecture, the custom object gives you everything that big objects lack (visibility and automation). When reprocessing is needed, your retry logic queries the custom object for open errors, uses the UUID to retrieve the massive payload from the big object, and replays it. This separation keeps your human-facing error management inside the platform’s declarative tooling while preserving Big Objects as your high-volume storage engine. </span></p>
<h3><span style="font-weight: 400">Step 3: Capture the payload (the code)</span></h3>
<p><span style="font-weight: 400">Here&#8217;s the capture step for an inbound platform event. The trigger writes each event to the big object before any downstream logic runs.</span></p>
<pre language="apex">trigger OrderEventCaptureTrigger on Order_Event__e (after insert) {

    List logsToInsert = new List();

    // 1. Map each incoming platform event to a big object record
    for (Order_Event__e event : Trigger.new) {
        logsToInsert.add(new IntegrationPayloadLog__b(
            EventUuid__c       = event.EventUuid,    // Index field 1
            IntegrationName__c = 'ERP_Inbound',      // Index field 2
            Direction__c       = 'Inbound',
            Timestamp__c       = System.now(),
            PayloadBody__c     = event.Payload__c    // The raw JSON/XML message
        ));
    }

    // 2. Persist to the big object BEFORE any business logic runs.
    //    insertImmediate commits immediately, so the payload is safely
    //    stored even if processing later fails.
    if (!logsToInsert.isEmpty()) {
        Database.insertImmediate(logsToInsert);
    }

    // 3. Now run the business logic. The processor handles each event in a
    //    try/catch and writes any failures to IntegrationError__c,
    //    linked back to the stored payload by EventUuid__c.
    OrderEventProcessor.process(Trigger.new);
}

</pre>
<p>The trigger does two things in order: it captures every event to the big object, then hands the same events to <code>OrderEventProcessor.process()</code> (shown in the next step) to run the business logic. Because <code>Database.insertImmediate()</code> commits the big object record immediately — outside the normal Apex transaction, so it can&#8217;t be rolled back — the payload survives even if processing throws later. That&#8217;s the whole point of capturing <i>before</i> the logic runs.</p>
<p>When managing records across both standard custom objects and big objects, it is important to understand their different DML requirements. While standard objects rely on typical DML operations like <code>update</code>, Big Objects does not support the <code>update</code> statement at all. Instead, they require the specialized <code>Database.insertImmediate()</code> method. To modify an existing record in a big object ledger, you simply insert a new record with the exact same Composite Primary Key. The system treats this as an upsert, overwriting the non-indexed fields with your new values without raising a duplicate error.</p>
<h3><span style="font-weight: 400">Step 4: Log the errors</span></h3>
<p>When processing of a platform event fails, you write the error details to <code>IntegrationError__c</code>. The shared <span>EventUuid__c</span><span> lets your team trace the error to the exact platform event, and you can then pull the exact payload from the big object for reprocessing.</span><br />
<span>How you detect a failure depends on where the processing runs:</span></p>
<ul>
<li><b>Apex:</b> Wrap the processing logic in <code>try/catch</code> and create the <code>IntegrationError__c</code> record in the catch block</li>
<li><b>Flow:</b> Add a Fault path to any element that can fail, and create the error record there</li>
</ul>
<p>In every case, store the same <code>EventUuid__c</code> so the error points back to the exact payload in the ledger. Here&#8217;s the Apex version, where a shared <code>IntegrationReplayService.handle()</code> method holds the actual processing logic:</p>
<pre language="apex">public class OrderEventProcessor {

    // Runs AFTER the payload has been captured to the big object
    public static void process(List events) {
        List errors = new List();

        for (Order_Event__e event : events) {
            try {
                // Business logic that may fail: parsing, DML, callouts, etc.
                IntegrationReplayService.handle(event.Payload__c);
            } catch (Exception ex) {
                // Record the failure, linked to the payload by EventUuid__c
                errors.add(new IntegrationError__c(
                    IntegrationName__c = 'ERP_Inbound',
                    EventUuid__c       = event.EventUuid,
                    Exception_Type__c  = ex.getTypeName(),
                    Error_Message__c   = ex.getMessage(),
                    Stack_Trace__c     = ex.getStackTraceString(),
                    Status__c          = 'Open',
                    RetryCount__c      = 0
                ));
            }
        }

        if (!errors.isEmpty()) {
            insert errors;   // Standard DML on the custom object
        }
    }
}
</pre>
<h3><span style="font-weight: 400">Step 5: Automate error recovery and retries</span><span style="font-weight: 400"><br />
</span></h3>
<p>To maintain data integrity, resilient systems rely on automated mechanisms to periodically query for failures and attempt reprocessing. Now that our failures are neatly tracked in <span>IntegrationError__c</span><span>, we can use Batch Apex to drive our automated retry logic.</span></p>
<p><span style="font-weight: 400">Driving the retry process from your custom error object, rather than querying the big object directly, is highly scalable. Standard custom objects support clean Batch Apex chunking and governor limit management. Your batch job simply queries the Action Queue for open errors and only queries the massive big object ledger when a payload actually needs to be reprocessed.</span></p>
<p>Schedule <code>PayloadRetryBatch</code> to run once a day — say, at 2:00 a.m — using a scheduled Apex class. Each run picks up every error still marked <code>Open</code> with fewer than three attempts, pulls the matching payload from the ledger by <code>EventUuid__c</code>, and replays it. If a payload succeeds, its error is marked <code>Resolved</code>. If it fails again, <code>RetryCount__c</code> is incremented and the record stays <code>Open</code>, so the <i>next day&#8217;s</i> run retries it. After three failed attempts it&#8217;s marked <code>PermanentFailure</code> and skipped, so a persistently broken payload can&#8217;t loop forever.</p>
<p><span style="font-weight: 400">Here is what the Batch Apex pattern looks like: </span></p>
<pre language="apex">public class PayloadRetryBatch implements Database.Batchable {

    public Database.QueryLocator start(Database.BatchableContext BC) {
        // 1. Drive the logic from the standard custom object (The Action Queue)
        return Database.getQueryLocator([
            SELECT Id, EventUuid__c, RetryCount__c
            FROM IntegrationError__c
            WHERE Status__c = 'Open'
            AND RetryCount__c &lt; 3
        ]);
    }

    public void execute(Database.BatchableContext BC, List scope) {
        List errorsToUpdate = new List();

        // 2. Gather the single key field (EventUuid__c) to retrieve the heavy payloads
        Set eventUuids = new Set();

        for (IntegrationError__c err : scope) {
            eventUuids.add(err.EventUuid__c);
        }

        // Retrieve the payloads from the Ledger using the EventUuid__c index
        List payloads = [
            SELECT EventUuid__c, PayloadBody__c
            FROM IntegrationPayloadLog__b
            WHERE EventUuid__c IN :eventUuids
        ];

        // Map payloads by EventUuid__c for easy access
        Map&lt;String, IntegrationPayloadLog__b&gt; payloadMap = new Map&lt;String, IntegrationPayloadLog__b&gt;();
        for (IntegrationPayloadLog__b p : payloads) {
            payloadMap.put(p.EventUuid__c, p);
        }

        // 3. Attempt to reprocess each failed payload
        for (IntegrationError__c err : scope) {
            IntegrationPayloadLog__b originalPayload = payloadMap.get(err.EventUuid__c);
            if (originalPayload == null) {
                continue; // Payload not found; leave the error Open for investigation
            }

            try {
                // Re-run the SAME logic the original handler uses, with the stored payload
                IntegrationReplayService.handle(originalPayload.PayloadBody__c);
                err.Status__c = 'Resolved';
            } catch (Exception ex) {
                // Failed again — increment and let the next daily run pick it up
                err.RetryCount__c += 1;
                err.Error_Message__c = ex.getMessage();
                if (err.RetryCount__c &gt;= 3) {
                    err.Status__c = 'PermanentFailure';
                }
            }
            errorsToUpdate.add(err);
        }


        // 4. Commit changes to both systems
        if (!errorsToUpdate.isEmpty()) {
            update errorsToUpdate;     // Standard DML for the custom object
        }
    }

    public void finish(Database.BatchableContext BC) {
        // Optional: Send notifications for any records that reached PermanentFailure
    }
}
</pre>
<p>Because big object queries rely entirely on your composite primary index to handle massive data volumes, your <code>WHERE</code> clauses are strictly governed. Standard SOQL only permits direct matches or range filters (<code>=</code>, <code>&lt;</code>, <code>&gt;</code>, <code>&lt;=</code>, <code>&gt;=</code>, and <code>IN</code>). By structuring the batch job to collect a consolidated list of identifiers into sets, we can fully leverage the <code>IN</code> operator to query our <code>EventUuid__c</code> index field efficiently.</p>
<p>It&#8217;s worth understanding how this rule scales when your index has two or more fields. You don&#8217;t have to filter on every index field in every query; you can use any leading subset of your index fields, as long as you respect the order in which they were defined. In this pattern, the composite index is <code>EventUuid__c → IntegrationName__c</code>. That means two valid query shapes are supported:</p>
<pre language="apex">// Valid — filters on the first index field only
WHERE EventUuid__c IN :eventUuids

// Also valid — filters on both index fields, in order
WHERE EventUuid__c IN :eventUuids AND IntegrationName__c = 'ERP_Inbound'

// Invalid — skips EventUuid__c and jumps straight to IntegrationName__c
WHERE IntegrationName__c = 'ERP_Inbound'

</pre>
<p>The retry batch uses the first shape — filtering on <code>EventUuid__c</code> alone — because the UUID is precise enough to locate the exact records needed. If you ever needed to narrow a query further (for example, to isolate a specific integration channel), you could add <code>IntegrationName__c</code> as a second filter and the query would remain valid. What you can never do is filter on a later index field while omitting an earlier one — the platform enforces strict left-to-right ordering, and any violation returns an error at runtime.</p>
<h3><span style="font-weight: 400">Step 6: Ensuring system observability</span></h3>
<p>For daily monitoring, use standard list views on your <code>IntegrationError__c</code> object. Build an LWC to add deep payload inspection and one-click replay capabilities. The LWC should list open failures from <code>IntegrationError__c</code> and fetch the full payload from the big object by <code>EventUuid__c</code> on demand, with a Retry Now button. To track operational metrics like failure rates, build reports and dashboards on <code>IntegrationError__c</code>. Finally, fire a proactive notification when a record reaches <code>PermanentFailure</code>, so your team doesn&#8217;t discover dropped payloads by accident.</p>
<h2><span style="font-weight: 400">The same pattern, across every domain</span></h2>
<p><span style="font-weight: 400">After you’ve built this once, you’ll start seeing the same shape everywhere. Use a big object wherever you need to write at high volume, hold data durably, and query on a predictable access pattern.</span></p>
<ul>
<li style="font-weight: 400"><b>Telecom / call detail records (CDRs): </b><span style="font-weight: 400">Carriers use big objects for call detail records, where billions of immutable, subscriber-keyed events need to be queryable for billing and retained for regulatory compliance</span></li>
<li style="font-weight: 400"><b>Financial services / transaction history: </b><span style="font-weight: 400">Write-once records, queried by account and date range, are retained for years without nightly batch jobs copying data into standard objects</span></li>
<li style="font-weight: 400"><b>IoT / device telemetry: </b><span style="font-weight: 400">High-frequency sensor readings, indexed by device ID and timestamp, are fed into CRM Analytics to surface health trends to service teams</span></li>
<li style="font-weight: 400"><b>SaaS metered billing: </b><span style="font-weight: 400">Every API call and feature activation written to a durable ledger is rolled up on a schedule, with full history available if a charge is ever disputed</span></li>
<li style="font-weight: 400"><b>Marketing engagement history: </b><span style="font-weight: 400">Email sends, opens, and clicks are stored at scale and queryable by campaign or subscriber, without bloating transactional objects, and are ready to feed into Data 360 for attribution modeling</span></li>
</ul>
<p><span style="font-weight: 400">In all of these cases, the index question is the same: what filter will you apply most often? Start there, and the schema design follows naturally.</span></p>
<h2><span style="font-weight: 400">Big object limits and constraints</span></h2>
<p><span style="font-weight: 400">Note, in addition to the behaviour we already described, Big Objects have the following limits to take into account: </span></p>
<ul>
<li style="font-weight: 400"><span style="font-weight: 400">Each org supports up to 100 big objects</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Field types are limited to Text, Number, DateTime, Lookup, Email, Phone, URL and Long Text Area — no picklists, formulas, or checkboxes</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Fields cannot be deleted after creation </span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Triggers, flows, and Process Builder don’t fire on big object records </span></li>
</ul>
<p><span style="font-weight: 400">These aren’t blockers, but they make upfront planning non-negotiable.</span></p>
<h2><span style="font-weight: 400">Conclusion</span></h2>
<p><span style="font-weight: 400">Big Objects isn’t a legacy feature waiting to be replaced. It’s a precision tool for a specific class of problem, and that problem keeps getting bigger. The payload capture system above is a starting point, but the same principles apply anywhere you need platform-native, durable, high-scale data storage.</span></p>
<p><span style="font-weight: 400">Salesforce is also working on deeper integration between Big Objects and Data 360, which will make high-volume, on-platform data an even more powerful foundation for real-time analytics and AI-driven workflows. Keep an eye on the </span><a href="https://www.salesforce.com/blog/category/product-roadmaps/"><span style="font-weight: 400">Salesforce Product Roadmap</span></a><span style="font-weight: 400"> for upcoming Big Object and Data 360 integration capabilities.</span></p>
<p><span style="font-weight: 400">Have questions or want to share how you’re using big objects? Join the conversation in the </span></p>
<p><a href="https://trailhead.salesforce.com/trailblazer-community/groups/0F93A000000DJbJSAW?tab=discussion&amp;sort=LAST_MODIFIED_DATE_DESC"><span style="font-weight: 400">Salesforce Developer Community</span></a><span style="font-weight: 400"> or on </span><a href="https://salesforce.stackexchange.com/"><span style="font-weight: 400">Salesforce Stack Exchange</span></a><span style="font-weight: 400">.</span></p>
<h2><span style="font-weight: 400">Resources</span></h2>
<ul>
<li style="font-weight: 400"><span style="font-weight: 400">Implementation Guide: </span><a href="https://developer.salesforce.com/docs/atlas.en-us.bigobjects.meta/bigobjects/"><span style="font-weight: 400">Big Objects</span></a></li>
<li style="font-weight: 400"><span style="font-weight: 400">Apex Reference: </span><a href="https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex_methods_system_database.htm"><span style="font-weight: 400">Database Class</span></a></li>
<li style="font-weight: 400"><span style="font-weight: 400">Trailhead: </span><a href="https://trailhead.salesforce.com/content/learn/modules/large-data-volumes"><span style="font-weight: 400">Large Data Volumes</span></a></li>
<li style="font-weight: 400"><span style="font-weight: 400">Developer Guide: </span><a href="https://developer.salesforce.com/docs/atlas.en-us.bi_dev_guide_rest.meta/bi_dev_guide_rest/"><span style="font-weight: 400">CRM Analytics</span></a></li>
<li style="font-weight: 400"><span style="font-weight: 400">Developer Guide: </span><a href="https://developer.salesforce.com/docs/atlas.en-us.platform_events.meta/platform_events/platform_events_intro.htm"><span style="font-weight: 400">Platform Events</span></a></li>
</ul>
<h2><span style="font-weight: 400">About the author</span></h2>
<p><b>Laxman Vattam</b><span style="font-weight: 400"> is a Senior Technical Architect at Salesforce focused on AI enablement, digital transformation, and large-scale platform modernization. He has spent over a decade designing scalable architectures for regulated industries. You can follow Laxman on </span><a href="https://www.linkedin.com/in/laxman-vattam-296a8014/"><span style="font-weight: 400">LinkedIn</span></a><span style="font-weight: 400">.</span></p>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/06/big-objects-playbook-payload-capture-and-replay">The Big Objects Playbook: Payload Capture and Replay</a> appeared first on <a href="https://developer.salesforce.com/blogs">Salesforce Developers Blog</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://developer.salesforce.com/blogs/2026/06/big-objects-playbook-payload-capture-and-replay/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
	<post-id xmlns="com-wordpress:feed-additions:1">206431</post-id><media:thumbnail url="https://d259t2jj6zp7qm.cloudfront.net/images/20260617114627/Generic-A-4-e1781722002836.png?w=1000" />
<media:content url="https://d259t2jj6zp7qm.cloudfront.net/images/20260617114627/Generic-A-4-e1781722002836.png?w=1000" medium="image" />
	</item>
		<item>
		<title>Build a Decision-Scoring Skill for Any Agent</title>
		<link>https://developer.salesforce.com/blogs/2026/06/build-a-decision-scoring-skill-for-any-agent</link>
		<comments>https://developer.salesforce.com/blogs/2026/06/build-a-decision-scoring-skill-for-any-agent#respond</comments>
		<pubDate>Tue, 16 Jun 2026 15:00:00 +0000</pubDate>
		<dc:creator><![CDATA[Dave Norris]]></dc:creator>
				<category><![CDATA[Agentforce]]></category>
		<category><![CDATA[Agentforce Vibes]]></category>
		<category><![CDATA[Architecture]]></category>
		<category><![CDATA[Developer Tooling]]></category>
		<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[Agent Skills]]></category>
		<category><![CDATA[Architectural Decision Record]]></category>
		<category><![CDATA[Claude Code]]></category>
		<category><![CDATA[Code Guardrails]]></category>
		<category><![CDATA[Decision Record]]></category>
		<category><![CDATA[developer tooling]]></category>
		<category><![CDATA[Model context protocol]]></category>
		<category><![CDATA[Well-Architected Framework]]></category>

		<guid isPermaLink="false">https://developer.salesforce.com/blogs/?p=206534</guid>
		<description><![CDATA[<p>Learn how to build agent skills for a structured decision pipeline that covers discovery, risk scoring and a decision record you can give to stakeholders.</p>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/06/build-a-decision-scoring-skill-for-any-agent">Build a Decision-Scoring Skill for Any Agent</a> appeared first on <a href="https://developer.salesforce.com/blogs">Salesforce Developers Blog</a>.</p>
]]></description>
				<content:encoded><![CDATA[<p><span style="font-weight: 400">Technical decisions on a project rarely get documented at the point when they are made. By the time someone asks &#8220;why did we choose X?&#8221;, the reasoning lives in a Slack thread, a meeting memory, or the head of someone who may not be in the team anymore.  </span></p>
<p><span>Instead of letting critical project context vanish, you can leverage AI to enforce architectural rigour. By implementing a decision-scoring skill, project teams can transform their coding agents into guardrails for engineering design. In this post, you’ll learn how to build the </span><code>/decide</code><span> pipeline, a structured framework that moves you from broad discovery to verifiable decision record.</span></p>
<h2><span style="font-weight: 400">A skill to guide technical decisions</span></h2>
<p><a href="https://github.com/deejay-hub/salesforce-ada-agent-skills"><u>Salesforce Ada Agent Skills</u></a><span> is an open source skill repository for agent skills beyond writing code. </span><code>/decide</code><span> is a structured pipeline that demonstrates how to use AI to drive technical decisions. Type </span><code>‘/decide managed package vs. unlocked package vs. unpackaged metadata for distributing utilities across 3 orgs’</code><span> and your coding agent runs you through discovery, options grounded in official documentation, a risk matrix you challenge based on real-world experience, and a scored recommendation written to disk as a decision record. </span></p>
<p><span style="font-weight: 400">The output is a file: versioned, reviewable, and available to the next person who asks.</span></p>
<p><span style="font-weight: 400">The underlying pattern of discovery, debating assessment criteria, and scoring works for any technical decision for </span><b>a</b><span style="font-weight: 400">dmins, </span><b>d</b><span style="font-weight: 400">evelopers and </span><b>a</b><span style="font-weight: 400">rchitects — hence the </span><b><i>ada</i></b><span style="font-weight: 400"> acronym. I’ll be using Claude Code in the examples below, but it has been designed to be used in any agent you choose with scaffolding in GitHub for Gemini and Codex CLIs.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206538" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260615100514/The-decide-skill-in-Claude-Code-e1781543129720.png?w=1000" class="postimages" width="1000" height="637" alt="The decide skill in Claude Code" />
			  </span>
			</p>
<h2><span style="font-weight: 400">How the pipeline works</span></h2>
<p><span style="font-weight: 400">Each step of the pipeline further refines the scope and context of a decision. Each step&#8217;s output constrains the next step&#8217;s input, narrowing from broad research to a single verifiable number. </span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206539" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260615100544/The-decision-pipeline-e1781543158547.png?w=1000" class="postimages" width="1000" height="283" alt="The decision pipeline" />
			  </span>
			</p>
<table>
<tbody>
<tr>
<td><b>Step</b></td>
<td><b>Description</b></td>
</tr>
<tr>
<td><b>1. Discovery</b></td>
<td><span>Gather what makes this decision unique, things like data volumes, timelines, team skills, and constraints. The agent asks structured questions and skips anything already answered in the slash command argument.</span></td>
</tr>
<tr>
<td><b>2. Solution options</b></td>
<td><span>Research official documentation in a single broad search, then present it as a table. A person confirms, adds, removes, or modifies before proceeding.</span></td>
</tr>
<tr>
<td colspan="2"><b>Check and review</b></td>
</tr>
<tr>
<td><b>3. Assessment criteria</b></td>
<td><span>Define what &#8220;better&#8221; means for this specific decision, generated from the research results, confirmed by you. If a criterion is missing, the final score may be lacking context to that dimension.</span></td>
</tr>
<tr>
<td colspan="2"><b>Check and review</b></td>
</tr>
<tr>
<td><b>4. Risk assessment</b></td>
<td><span>Rate each option against each criterion as Low, Medium, or High risk with cited rationale. This is the data that drives the decision — a matrix that a person challenges before scoring happens.</span></td>
</tr>
<tr>
<td colspan="2"><b>Check and review</b></td>
</tr>
<tr>
<td><b>5. Decision scoring</b></td>
<td><span>Create a weighted sum of the risk ratings across well-architected pillars. The highest score wins. No narrative override.</span></td>
</tr>
<tr>
<td colspan="2"><b>Create a persistent document (optional)</b></td>
</tr>
</tbody>
</table>
<h2><span style="font-weight: 400">Key design patterns</span></h2>
<p><span style="font-weight: 400">The pipeline above is what the skill does. The patterns below are how it’s implemented and the design choices to make a skill like this reliable, adaptable and worth trusting with a technical decision. Together, they give you output that you can verify, share and build on.</span></p>
<h3><span style="font-weight: 400">The folder structure</span></h3>
<p><span style="font-weight: 400">The skill is a directory of markdown files.</span></p>
<pre language="text">salesforce-ada-agent-skills/
  ├── CLAUDE.md                            	# Global rules
  ├── .mcp.json                            	# MCP server config
  ├── .claude/
  │   └── skills/
  │       ├── learn/
  │       │   └── SKILL.md                 # Learning workflow
  │       └── decide/
  │           └── SKILL.md                 	# Pipeline steps, constraints
  ├── knowledge/
  │   ├── formats/                         	# Shared output templates
  │   │   ├── options-format.md            	# Template for solution options table
  │   │   ├── criteria-format.md           	# Template for assessment criteria
  │   │   ├── risk-matrix-format.md
  │   │   └── decision-format-*.md         	# Templates for decision record output
  │   ├── scoring/                         	# Decision score methodology
  │   └── modifiers/                       	# Composable lenses
  └── output/
      └── decisions/                       	# Generated decision records
</pre>
<p><b><code>CLAUDE.md</code></b> at the repo root defines the scoring pillar weights, MCP source-selection rules, and hard constraints that apply across the decision skill. The skill file (<code>SKILL.md</code>) handles pipeline sequencing; <code>CLAUDE.md</code> handles the rules that govern every step.</p>
<p><b><code>SKILL.md</code></b><span> loads when you type </span><code>/decide</code><span> and controls the pipeline sequence. </span><b><span>Format files</span></b><span> are templates that the agent copies rather than interprets, producing consistent output regardless of conversation length. The </span><b><span>scoring methodology</span></b><span> loads only at the final step. This is about 350 lines of rubric detail that would compete for attention during discovery if inlined earlier.</span></p>
<p><span style="font-weight: 400">Splitting files this way means you can iterate on a format without touching pipeline logic, swap scoring dimensions without changing steps, and keep the context window lean at each stage.</span></p>
<h3><span style="font-weight: 400">You are the expert</span></h3>
<p><span style="font-weight: 400">At every transition the agent pauses and asks whether you&#8217;re happy to proceed. You can reshape the analysis at any gate: add an option, remove a criterion, or challenge a rating. The pipeline prevents a flawed assumption in step one from propagating unchallenged to the recommendation. It makes the most of your real-world experiences and helps produce a higher quality output.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206540" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260615100634/Discovery-questions-to-elicit-details-about-the-decision-e1781543208226.png?w=1000" class="postimages" width="1000" height="392" alt="Discovery questions to elicit details about the decision" />
			  </span>
			</p>
<h3><span style="font-weight: 400">Research early and broadly, then verify claims</span></h3>
<p><span style="font-weight: 400">The initial research in the options step uses a broad query, so competing approaches surface together rather than being cherry-picked individually. Each proposed option is validated against documentation before being presented, and every factual claim in the risk matrix is verified with a targeted search before a rating is assigned. This layered approach balances breadth (discover all options fairly) with depth (no ungrounded claim reaches the final score).</span></p>
<p><span style="font-weight: 400">The skill works without any MCP (Model Context Protocol) server that can search documentation.  It falls back to web search or the agent&#8217;s training knowledge and clearly states which grounding method it&#8217;s using. But documentation quality drives decision quality, so I built </span><a href="https://github.com/deejay-hub/salesforce-ada-content-mcp-server"><span style="font-weight: 400">salesforce-content</span></a><span style="font-weight: 400">, a custom MCP server that provides keyword and vector search across recent official documentation from the admin, developer, and architect ecosystems. Any documentation on MCP will work (the repo ships with a Context7 configuration as a zero-setup alternative), but salesforce-content is preferred because it indexes trusted Salesforce sources directly rather than relying on web search, which often misses key information when the source is hard to parse.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206541" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260615100710/Options-delivered-based-on-trusted-sources-e1781543249950.png?w=1000" class="postimages" width="1000" height="849" alt="Options delivered based on trusted sources" />
			  </span>
			</p>
<h3><span style="font-weight: 400">Separate format files for every output step</span></h3>
<p><span style="font-weight: 400">Each step has its own template file specifying the exact table structure. The agent follows a concrete template more reliably than prose instructions. Splitting them from the pipeline logic means you can iterate on a format without touching the steps.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206542" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260615100755/Criteria-for-assessment-delivered-as-a-table-e1781543290779.png?w=1000" class="postimages" width="1000" height="690" alt="Criteria for assessment delivered as a table" />
			  </span>
			</p>
<h3><span style="font-weight: 400">A mechanical score the agent can self-verify</span></h3>
<p><span style="font-weight: 400">Risk ratings map to fixed confidence values (Low risk = 85, Medium risk = 55, High risk = 25), weighted across pillars (Trust 20%, Reliability 20%, Operational Excellence 20%, Resource Optimization 15%, Cost Optimization 15%, Fairness 10%), and summed up into a single decision score per option. The invariant: the highest score must be the recommendation. The agent verifies this by comparing two numbers and prompt instructions to make sure there is no judgement or reasoning applied. Fixed values remove ambiguity. Pinning each level to a single value makes the score fully deterministic from the risk matrix.</span></p>
<p><span style="font-weight: 400">I am using optimized bands based on the direction being taken by the </span><a href="https://www.youtube.com/watch?v=Gt9tMPl_Kf8&amp;list=PLn15mOuXqGJWg9YJNnAqO-Rv-dN3MngoT"><span style="font-weight: 400">Well-Architected framework</span></a><span style="font-weight: 400">, but these can be changed based on your preference.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206543" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260615100835/An-example-confidence-score-from-a-decision-based-on-user-guidance-e1781543330817.png?w=1000" class="postimages" width="1000" height="532" alt="An example confidence score from a decision based on user guidance" />
			  </span>
			</p>
<h3><span style="font-weight: 400">An output that can be shared</span></h3>
<p><span style="font-weight: 400">Key decisions should be documented. New team members should understand the reasons behind a technical choice and allowing the output to be persistently stored was a key design choice. For this skill, I chose an </span><a href="https://github.com/adr"><span style="font-weight: 400">Architectural Decision Record (ADR)</span></a><span style="font-weight: 400"> in markdown using the option for a minimal or verbose output.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206544" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260615100911/A-decision-summary-and-output-markdown.png?w=1999" class="postimages" width="1999" height="619" alt="A decision summary and output markdown" />
			  </span>
			</p>
<h2><span style="font-weight: 400">Build your own decision-scoring skill</span></h2>
<p><span style="font-weight: 400">The skill is a transferable pattern. It can be adapted to your preferred steps, scoring mechanism, and agent.</span></p>
<table>
<tbody>
<tr>
<td><b>Step</b></td>
<td><b>What to consider</b></td>
</tr>
<tr>
<td><b>1. Define pipeline steps</b></td>
<td><span style="font-weight: 400">What context do you need before listing options? What dimensions matter for scoring?</span></td>
</tr>
<tr>
<td><b>2. Choose dimensions and weights</b></td>
<td><span style="font-weight: 400">What does your organization optimize for? Assign percentages that sum up to 100.</span></td>
</tr>
<tr>
<td><b>3. Write format files</b></td>
<td><span style="font-weight: 400">For each step that produces output, create a template showing the exact structure. Include a confirmation prompt.</span></td>
</tr>
<tr>
<td><b>4. Write your scoring mechanism</b></td>
<td><span style="font-weight: 400">Define risk-to-score bands. State the invariant: highest score wins.</span></td>
</tr>
<tr>
<td><b>5. Ground in external data</b></td>
<td><span style="font-weight: 400">Connect a documentation MCP server, or let the agent fall back to web search. Any MCP that returns docs works; the skill describes what to search for, not which tool to call. The quality of decisions depends on the quality of the sources.</span></td>
</tr>
<tr>
<td><b>6. Repeat rules that matter</b></td>
<td><span style="font-weight: 400">Your most important constraint should appear in at least two files. Agents lose track over long conversations.</span></td>
</tr>
</tbody>
</table>
<h2><span style="font-weight: 400">Get started</span></h2>
<p><span>Clone the </span><a href="https://github.com/deejay-hub/salesforce-ada-agent-skills"><u>repo</u></a><span>, follow the instructions from the readme, and type </span><code>/decide</code><span> followed by your question. </span></p>
<p><span style="font-weight: 400">I built this because feedback loops matter. You can see the reasoning decomposed into a matrix of specific, citable claims, rather than a wall of persuasive prose. With a skill like this, you make better decisions, catch assumptions faster, and leave a record of the decision that the next person can actually use.</span></p>
<p><b>Resources</b></p>
<ul>
<li style="font-weight: 400"><span style="font-weight: 400">Docs: </span><a href="https://code.claude.com/docs/en/skills.md"><span style="font-weight: 400">Anthropic skills documentation</span></a></li>
<li style="font-weight: 400"><span style="font-weight: 400">Docs: </span><a href="https://code.claude.com/docs/en/best-practices.md"><span style="font-weight: 400">Anthropic skills best practices</span></a></li>
<li style="font-weight: 400"><span style="font-weight: 400">Docs: </span><a href="https://architect.salesforce.com/docs/architect/well-architected/guide/overview"><span style="font-weight: 400">Salesforce Well-Architected</span></a></li>
<li style="font-weight: 400"><span style="font-weight: 400">Video: </span><a href="https://www.youtube.com/watch?v=Gt9tMPl_Kf8"><span style="font-weight: 400">The Next Chapter of the Well-Architected Framework</span></a></li>
</ul>
<h2><b>About the author</b></h2>
<p><b>Dave Norris</b><span style="font-weight: 400"> is a Developer Advocate at Salesforce. He’s passionate about making technical subjects broadly accessible to a diverse audience. Dave has been with Salesforce for over a decade, has over 40 Salesforce and MuleSoft certifications, and became a Salesforce Certified Technical Architect in 2013.</span></p>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/06/build-a-decision-scoring-skill-for-any-agent">Build a Decision-Scoring Skill for Any Agent</a> appeared first on <a href="https://developer.salesforce.com/blogs">Salesforce Developers Blog</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://developer.salesforce.com/blogs/2026/06/build-a-decision-scoring-skill-for-any-agent/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
	<post-id xmlns="com-wordpress:feed-additions:1">206534</post-id><media:thumbnail url="https://d259t2jj6zp7qm.cloudfront.net/images/20260615102947/SingleHeadshot-3-1-e1781544863838.png?w=1000" />
<media:content url="https://d259t2jj6zp7qm.cloudfront.net/images/20260615102947/SingleHeadshot-3-1-e1781544863838.png?w=1000" medium="image" />
	</item>
		<item>
		<title>Understanding the Data 360 Web SDK: From Website Event to Data Model</title>
		<link>https://developer.salesforce.com/blogs/2026/06/understanding-the-data-360-web-sdk</link>
		<comments>https://developer.salesforce.com/blogs/2026/06/understanding-the-data-360-web-sdk#respond</comments>
		<pubDate>Thu, 11 Jun 2026 19:12:42 +0000</pubDate>
		<dc:creator><![CDATA[Chris Charalambous]]></dc:creator>
				<category><![CDATA[APIs and Integrations]]></category>
		<category><![CDATA[Architecture]]></category>
		<category><![CDATA[Data 360]]></category>
		<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[Behavioral Events]]></category>
		<category><![CDATA[Data Cloud]]></category>
		<category><![CDATA[Data Model Objects (DMO)]]></category>
		<category><![CDATA[Identity resolution]]></category>
		<category><![CDATA[Salesforce Interactions SDK]]></category>
		<category><![CDATA[sendEvent]]></category>
		<category><![CDATA[Sitemap]]></category>

		<guid isPermaLink="false">https://developer.salesforce.com/blogs/?p=206515</guid>
		<description><![CDATA[<p>Learn how your sitemap code becomes actionable data through transformation, validation, routing, and mapping in this guide to the full event pipeline.</p>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/06/understanding-the-data-360-web-sdk">Understanding the Data 360 Web SDK: From Website Event to Data Model</a> appeared first on <a href="https://developer.salesforce.com/blogs">Salesforce Developers Blog</a>.</p>
]]></description>
				<content:encoded><![CDATA[<p>If you&#8217;ve explored the <a href="https://developer.salesforce.com/docs/data/salesforce-interactions-sdk/guide/c360a-api-salesforce-interactions-web-sdk.html"><u>Salesforce Interactions SDK</u></a>, you likely understand how to configure a website connector, initialize the SDK, and trigger <code>sendEvent()</code>. However, implementing this in a production environment often leads to a common scenario: events are being dispatched, but where is the data actually landing — and why is it not appearing as expected?</p>
<p><span style="font-weight: 400">This post is designed to bridge the gap between basic setup and a production-ready deployment. We will trace the end-to-end journey of an event — from your sitemap code, through the SDK&#8217;s internal transformation layer, and into Data 360&#8217;s schema, data streams, and data model objects (DMOs). By the end, you will understand not just </span><i><span style="font-weight: 400">how to dispatch</span></i><span style="font-weight: 400"> events, but </span><i><span style="font-weight: 400">how they’re structured, partitioned, validated, and mapped</span></i><span style="font-weight: 400"> as they evolve into actionable data.</span></p>
<h2><span style="font-weight: 400">How the Data 360 module transforms your events</span></h2>
<p>When you invoke <code>SalesforceInteractions.sendEvent()</code> (see <a href="https://developer.salesforce.com/docs/data/salesforce-interactions-sdk/guide/c360a-api-event-structure.html#example"><u>docs</u></a>), your data undergoes a significant evolution before reaching Data 360. Between sending an event and the final destination sits the Data 360 module of the Salesforce Interactions SDK. This is a transformation layer within the SDK that converts your hierarchical interaction events into the flattened schema required by Data 360.</p>
<p><b>Key insight: </b><span style="font-weight: 400">A single event dispatched from your sitemap can actually trigger </span><i><span style="font-weight: 400">multiple</span></i><span style="font-weight: 400"> Data 360 events under the hood.</span></p>
<p>To illustrate this, consider a standard <code>sendEvent()</code> call designed to capture a specific button interaction along with the user&#8217;s identity.</p>
<pre language="javascript">SalesforceInteractions.sendEvent({
    interaction: {
        eventType: 'userEngagement',
        name: 'button_click'
    },
    user: {
        attributes: {
            eventType: 'contactPointEmail',
            email: 'joe.smith@domain.com'
        }
    }
})
</pre>
<p><span style="font-weight: 400">We designed the Data 360 module to split this into two separate events before sending them to Data 360.</span></p>
<pre language="json">// Event 1: Profile data
{
    "eventType": "contactPointEmail",
    "email": "joe.smith@domain.com",
    "category": "Profile",
    // ... auto-populated fields
}

// Event 2: Engagement data
{
    "eventType": "userEngagement",
    "interactionName": "button_click",
    "category": "Engagement",
    // ... auto-populated fields
}
</pre>
<p>The <code>eventType</code> defined within the <code>interaction</code> block determines which engagement schema — and ultimately which DMO — captures behavioral data. Conversely, the <code>eventType</code> specified under <code>user.attributes</code> dictates the destination for identity data within the profile schema. This mechanism allows a single user interaction to simultaneously refresh a customer&#8217;s engagement timeline <i>and</i> update their contact details, routing data to distinct segments of your data model.</p>
<h2><span style="font-weight: 400">The end-to-end data flow</span></h2>
<p><span style="font-weight: 400">Understanding the full pipeline makes debugging significantly easier. Here&#8217;s how data flows from your website to your data model:</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206519" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260610124702/How-data-flows-from-the-sitemap-on-the-website-to-Data-360s-data-model-e1781120845323.png?w=1000" class="postimages" width="1000" height="280" alt="How data flows from the sitemap on the website to Data 360’s data model" />
			  </span>
			</p>
<p><span style="font-weight: 400">Every stage of the pipeline presents unique failure modes. If your data isn&#8217;t appearing as expected, consider these common troubleshooting areas:</span></p>
<ul>
<li><b><span>Schema validation: </span></b><span>Verify that your event payload contains all of the schema fields flagged as </span><code><span>required</span></code><span>, otherwise Data 360 will reject the event. Any field in your event that is not defined in the schema will be ignored within the target schema. Pay close attention to data types and ensure that you are using the </span><i><span>exact</span></i><span> field names.</span></li>
<li><b><span>Data stream coverage: </span></b><span>Confirm that your data stream is configured to include the specific event type you&#8217;re dispatching. Remember, if you introduce new schema objects after deployment, you must manually update the stream to incorporate them.</span></li>
<li><b><span>DMO mapping: </span></b><span>Ensure that the </span><code>eventType</code> field from your behavioral data stream is correctly mapped to the Engagement Type field on your target DMO. For a generic event type data point, the <code>eventType</code> field in the Browse section on the left should be used instead of the <code>eventType</code> field in the “All Event Data” when mapping to the Website Engagement DMO. We recommend leveraging Data Explorer to view the various <code>eventType</code> fields in the Behavioural Events DLO to make an informed decision on which DMO they should map to.</li>
</ul>
<p><span style="font-weight: 400">Setting the right level of </span><a href="https://developer.salesforce.com/docs/data/salesforce-interactions-sdk/guide/c360a-api-debugging.html"><span style="font-weight: 400">debugging</span></a><span style="font-weight: 400"> is important to not only give you full visibility over failures, but to also help troubleshoot the end-to-end data flow. You can set a numerical logging level while also specifying your own custom messages from your site’s code.</span></p>
<pre language="javascript">// Example: Enable debug messages
SalesforceInteractions.setLoggingLevel("debug");

// Same as above, using the numeric value
SalesforceInteractions.setLoggingLevel(4);

//Output your own custom messages
SalesforceInteractions.log.debug("here")
</pre>
<h2><span style="font-weight: 400">Standard vs. custom interaction names: Choose carefully</span></h2>
<p>The Interactions SDK utilizes a suite of standard interaction names, such as <code>View Catalog Object</code>, <code>Add To Cart</code>, and <code>Purchase</code>. These are far more than mere labels; they serve as triggers for pre-configured transformation logic residing within the Data 360 module.</p>
<p>When employing a standard interaction name, the resulting <code>eventType</code> in the Data 360 event is automatically assigned and cannot be modified. For instance, any event containing <code>name: View Catalog Object</code> will invariably generate <code>eventType: catalog</code> during the Data 360 translation process, effectively overriding any custom <code>eventType</code> you might have specified within the interaction block.</p>
<pre language="json">// Both of these produce eventType: "catalog" in the Data 360 event
{
    interaction: {
        name: "View Catalog Object",
        catalogObject: {
            type: "Product",
            id: "product-xyz"
        }
    }
}

{
    interaction: {
        name: "View Catalog Object",
        catalogObject: {
            type: "Article",
            id: "article-abc"
        }
    }
}
</pre>
<p><span style="font-weight: 400">This distinction is critical because all events sharing a common </span><span style="font-weight: 400">eventType</span><span style="font-weight: 400"> are constrained to a single DMO mapping. If your website users are engaging with diverse entities, such as insurance policies versus knowledge articles, relying on the standard </span><span style="font-weight: 400">View Catalog Object</span><span style="font-weight: 400"> interaction for both will force them into the same DMO. This overlap complicates the creation of object-specific engagement models and dilutes your data granularity.</span></p>
<p><span style="font-weight: 400">To circumvent this, you should consider custom interaction names and event types.</span></p>
<pre language="json">// Product engagement → maps to Product Browse Engagement DMO
{
    interaction: {
        name: "View",
        eventType: "productEngagement",
        catalogObject: {
            type: "Product",
            id: "product-xyz"
        }
    }
}

// Article engagement → maps to Article Engagement DMO
{
    interaction: {
        name: "Download",
        eventType: "articleEngagement",
        catalogObject: {
            type: "Article",
            id: "article-abc"
        }
    }
}
</pre>
<p><span style="font-weight: 400">If your implementation aligns with standard patterns, then leverage the standard schema, for example, catalog interactions, cart activity, or identity updates. This approach simplifies your architecture by utilizing out-of-the-box DMO mappings via Data Kit packages. You should only implement </span><i><span style="font-weight: 400">custom event types</span></i><span style="font-weight: 400"> when your specific business logic diverges from these supported interaction models.</span></p>
<p><b>Key insight:</b> We have the SDK automatically assign a <code>pageView</code> flag to every dispatched event. A page view event (<code>pageView: 1</code>) is triggered when a <code>pageConfig</code> matches during initial page load, driven by the <code>isMatch</code> logic within your sitemap&#8217;s <code>pageTypes</code> array. Conversely, an interaction-only event (<code>pageView: 0</code> or absent) is dispatched via <code>sendEvent()</code> through an <code>interaction</code> object, typically following a user action such as a click or form submission. Mastering this distinction is critical to ensuring the integrity of your engagement analytics.</p>
<h3><span style="font-weight: 400">How nested event payloads translate to flat Data 360 rows</span></h3>
<p><span style="font-weight: 400">Data 360 events are flat, but the SDK payloads you send are nested. When the Data 360 module ingests an event, it walks the nested structures and flattens them into a single row of columns on the target DMO. Understanding these flattening rules is critical to getting your schema and data stream to line up.</span></p>
<p><span style="font-weight: 400">Looking at a standard interaction first:</span></p>
<pre language="json">// Product engagement → maps to Product Browse Engagement DMO
{
    interaction: {
        name: "View Catalog Object",
        catalogObject: {
            type: "Product",
            id: "product-xyz",
            attributes: {
                name: "Product name",
                description: "Product description",
                inventory: 5,
                price: 123.45
            }
        }
    }
}
</pre>
<p><span style="font-weight: 400">For this sample of a standard event payload sent by the SDK, the Data 360 event that lands in your Behavioural Events data stream DLO will look like this:</span></p>
<table>
<tbody>
<tr>
<td><b>Behavioural Events DLO Field Name</b></td>
<td><b>Value</b></td>
</tr>
<tr>
<td><span style="font-weight: 400">eventType</span></td>
<td><span style="font-weight: 400">catalog</span></td>
</tr>
<tr>
<td><span style="font-weight: 400">catalog.interactionName</span></td>
<td><span style="font-weight: 400">View Catalog Object</span></td>
</tr>
<tr>
<td><span style="font-weight: 400">catalog.type</span></td>
<td><span style="font-weight: 400">Product</span></td>
</tr>
<tr>
<td><span style="font-weight: 400">catalog.id</span></td>
<td><span style="font-weight: 400">product-xyz</span></td>
</tr>
<tr>
<td><span style="font-weight: 400">catalog.attributeName</span></td>
<td><span style="font-weight: 400">Product name</span></td>
</tr>
<tr>
<td><span style="font-weight: 400">catalog.attributeDescription</span></td>
<td><span style="font-weight: 400">Product description</span></td>
</tr>
<tr>
<td><span style="font-weight: 400">catalog.attributeInventory</span></td>
<td><span style="font-weight: 400">5</span></td>
</tr>
<tr>
<td><span style="font-weight: 400">catalog.attributePrice</span></td>
<td><span style="font-weight: 400">123.45</span></td>
</tr>
</tbody>
</table>
<p><span style="font-weight: 400">When using custom properties at the interaction level, these are added directly to the payload. For custom objects nested under interaction, these are flattened using lowerCamelCase.</span></p>
<p><span style="font-weight: 400">For example:</span></p>
<pre language="json">// Custom interaction
{
    interaction: {
        name: "Special Form Click",
        customProperty: "value"
    }
}
</pre>
<p><span style="font-weight: 400">They will land in the Behavioural Events data stream DLO like this:</span></p>
<table>
<tbody>
<tr>
<td><b>Behavioural Events DLO Field Name</b></td>
<td><b>Value</b></td>
</tr>
<tr>
<td><span style="font-weight: 400">interactionName</span></td>
<td><span style="font-weight: 400">Special Form Click</span></td>
</tr>
<tr>
<td><span style="font-weight: 400">customProperty</span></td>
<td><span style="font-weight: 400">value</span></td>
</tr>
</tbody>
</table>
<p><span style="font-weight: 400">For custom objects nested under interaction, these are flattened using lowerCamelCase. For example:</span></p>
<pre language="json">// Custom interaction
{
    interaction: {
        name: "Special Form Click",
        customProperties: {
            anotherCustomProperties: {
                customField: "value"
            }
        }
    }
}
</pre>
<p><span style="font-weight: 400">These will land in the Behavioural Events data stream DLO like this:</span></p>
<table>
<tbody>
<tr>
<td><b>Behavioural Events DLO Field Name</b></td>
<td><b>Value</b></td>
</tr>
<tr>
<td><span style="font-weight: 400">interactionName</span></td>
<td><span style="font-weight: 400">Special Form Click</span></td>
</tr>
<tr>
<td><span style="font-weight: 400">customPropertiesAnotherCustomPropertiesCustomField</span></td>
<td><span style="font-weight: 400">value</span></td>
</tr>
</tbody>
</table>
<h2><span style="font-weight: 400">Web schema: What must match, and what&#8217;s flexible</span></h2>
<p><span style="font-weight: 400">Your web schema is a JSON document uploaded to Data 360 that defines the allowed event structure. The Interactions SDK will silently drop any event that doesn&#8217;t conform to it. Understanding what&#8217;s strictly enforced versus what&#8217;s flexible saves hours of debugging.</span></p>
<p><span style="font-weight: 400">The following elements are </span><i><span style="font-weight: 400">strictly bound</span></i><span style="font-weight: 400"> to your schema keys:</span></p>
<ul>
<li><code><span>eventType</span></code><span>, </span><code><span>category</span></code><span>, and </span><code><span>interactionName</span></code></li>
<li><span>Field names utilized within </span><code><span>interaction</span></code><span>, </span><code><span>catalogObjects</span></code><span>, and </span><code><span>user.identitie</span></code></li>
<li><span style="font-weight: 400">Note that these identifiers must provide an </span><i><span style="font-weight: 400">exact match</span></i><span style="font-weight: 400"> to those defined in your uploaded schema</span></li>
</ul>
<p><span style="font-weight: 400">Conversely, these elements remain </span><i><span style="font-weight: 400">flexible</span></i><span style="font-weight: 400"> or free-form:</span></p>
<ul>
<li><code><span>pageConfig.name</span></code><span> (the page type name employed during sitemap matching)</span></li>
<li><span>Values populated within </span><code><span>interaction.attribute</span></code><span> fields</span></li>
<li><span>Custom payload strings, page name identifiers, and any derived values calculated via JavaScript</span></li>
</ul>
<p><b>Key insight:</b><span style="font-weight: 400"> Consider schema keys as the column names in a database. While the column headers must align with your schema perfectly, the specific values you use to populate them remain highly flexible.</span></p>
<p><b>Critical naming conventions</b></p>
<ul>
<li><span>Adhere to strict </span><i><span>lowerCamelCase</span></i><span> for all field names (e.g., use </span><code><span>pageUrl</span></code><span> rather than </span><code><span>page_url</span></code><span> or </span><code><span>PageUrl</span></code><span>)</span></li>
<li><span>Underscores in column or field names are </span><i><span>unsupported</span></i><span> and will cause data ingestion to fail</span></li>
<li><span>Avoid consecutive uppercase characters (e.g., prefer </span><code>urlPath</code> over <code>URLPath</code>)</li>
</ul>
<h2><span style="font-weight: 400">Three essential data streams</span></h2>
<p><span style="font-weight: 400">Upon deploying data streams via your website connector, the resulting architecture is partitioned into three distinct categories, each serving a specialized role in your data ecosystem.</span></p>
<h3><span style="font-weight: 400">1. Identity stream</span></h3>
<p>Automatically generated for the <code>identity</code> event type, this stream captures the <code>deviceId </code>— a unique identifier assigned by the SDK to every anonymous visitor.</p>
<p>This <code>deviceId</code> persists within the same browser typically via Local Storage or a first-party cookie. On every subsequent visit from the same browser, the same <code>deviceId</code> is issued. It does not, however, persist across browsers or different domains. For example, if a user visits the website in Chrome and then Safari, or on a different device, they get a different <code>deviceId</code>.</p>
<p>To ensure proper ingestion, map this to the Individual DMO using the key relationship: <code>deviceId</code> → <code>Individual Id</code>. This mapping is how Data 360 instantiates a new Individual record for every unique website visitor. The table below shows how these fields should be mapped from the identity data stream.</p>
<table>
<tbody>
<tr>
<td><b>DLO Field (from Interactions SDK)</b></td>
<td><b>Maps to DMO</b></td>
<td><b>DMO Field</b></td>
</tr>
<tr>
<td><span style="font-weight: 400">deviceId</span></td>
<td><span style="font-weight: 400">Individual</span></td>
<td><span style="font-weight: 400">Individual Id</span></td>
</tr>
<tr>
<td><span style="font-weight: 400">deviceId</span></td>
<td></td>
<td><span style="font-weight: 400">Party</span></td>
</tr>
<tr>
<td><span style="font-weight: 400">firstName</span></td>
<td></td>
<td><span style="font-weight: 400">First Name</span></td>
</tr>
<tr>
<td><span style="font-weight: 400">lastName</span></td>
<td></td>
<td><span style="font-weight: 400">Last Name</span></td>
</tr>
<tr>
<td><span style="font-weight: 400">isAnonymous</span></td>
<td></td>
<td><span style="font-weight: 400">isAnonymous</span></td>
</tr>
<tr>
<td><span style="font-weight: 400">dateTime</span></td>
<td></td>
<td><span style="font-weight: 400">Created Date</span></td>
</tr>
</tbody>
</table>
<h3><span style="font-weight: 400">2. Contact point / party identification streams</span></h3>
<p>These streams manage profile-level events, including <code>contactPointEmail</code>, <code>contactPointPhone</code>, and <code>partyIdentification</code>. They function as the critical nexus for identity resolution, bridging the gap between an anonymous <code>deviceId</code> and a verified customer identity. For instance, when a user authenticates and you dispatch a <code>partyIdentification</code> event containing their CRM ID, this data populates the Party Identification DMO, enabling Identity Resolution rulesets to unify disparate records.</p>
<p><span style="font-weight: 400">Each stream maps to a different Profile-category DMO, and each has a specific field mapping that you need to get right before Identity Resolution rulesets will fire. The table below shows generic mappings. </span></p>
<table>
<tbody>
<tr>
<td><b>Data Stream</b></td>
<td><b>DLO Field (from Web SDK)</b></td>
<td><b>Maps to DMO</b></td>
<td><b>DMO Field</b></td>
</tr>
<tr>
<td><span style="font-weight: 400">contactPointEmail</span></td>
<td><span style="font-weight: 400">deviceId</span></td>
<td><span style="font-weight: 400">Contact Point Email</span></td>
<td><span style="font-weight: 400">Party</span></td>
</tr>
<tr>
<td></td>
<td><span style="font-weight: 400">deviceId</span></td>
<td></td>
<td><span style="font-weight: 400">Contact Point Email Id</span></td>
</tr>
<tr>
<td></td>
<td><span style="font-weight: 400">email</span></td>
<td></td>
<td><span style="font-weight: 400">Email Address</span></td>
</tr>
<tr>
<td></td>
<td><span style="font-weight: 400">dateTime</span></td>
<td></td>
<td><span style="font-weight: 400">Created Date</span></td>
</tr>
<tr>
<td><span style="font-weight: 400">contactPointPhone</span></td>
<td><span style="font-weight: 400">deviceId</span></td>
<td><span style="font-weight: 400">Contact Point Phone</span></td>
<td><span style="font-weight: 400">Party</span></td>
</tr>
<tr>
<td></td>
<td><span style="font-weight: 400">deviceId</span></td>
<td></td>
<td><span style="font-weight: 400">Device</span></td>
</tr>
<tr>
<td></td>
<td><span style="font-weight: 400">deviceId</span></td>
<td></td>
<td><span style="font-weight: 400">Contact Point Id</span></td>
</tr>
<tr>
<td></td>
<td><span style="font-weight: 400">phoneNumber</span></td>
<td></td>
<td><span style="font-weight: 400">Phone Number</span></td>
</tr>
<tr>
<td></td>
<td><span style="font-weight: 400">phoneNumber</span></td>
<td></td>
<td><span style="font-weight: 400">Formatted E164 Phone Number</span></td>
</tr>
<tr>
<td></td>
<td><span style="font-weight: 400">dateTime</span></td>
<td></td>
<td><span style="font-weight: 400">Created Date</span></td>
</tr>
<tr>
<td><span style="font-weight: 400">partyIdentification</span></td>
<td><span style="font-weight: 400">deviceId</span></td>
<td><span style="font-weight: 400">Party Identification</span></td>
<td><span style="font-weight: 400">Party</span></td>
</tr>
<tr>
<td></td>
<td><span style="font-weight: 400">deviceId</span></td>
<td></td>
<td><span style="font-weight: 400">Party Identification Id</span></td>
</tr>
<tr>
<td></td>
<td><span style="font-weight: 400">IDName </span><i><span style="font-weight: 400">(a constant, like “Web ID”)</span></i></td>
<td></td>
<td><span style="font-weight: 400">Identification Name</span></td>
</tr>
<tr>
<td></td>
<td><span style="font-weight: 400">IDType</span></td>
<td></td>
<td><span style="font-weight: 400">Party Identification Type</span></td>
</tr>
<tr>
<td></td>
<td><span style="font-weight: 400">userId</span><i><span style="font-weight: 400"> (your known customer unique identifier like a CRM/loyalty ID)</span></i></td>
<td></td>
<td><span style="font-weight: 400">Identification Number</span></td>
</tr>
<tr>
<td></td>
<td><span style="font-weight: 400">dateTime</span></td>
<td></td>
<td><span style="font-weight: 400">Created Date</span></td>
</tr>
</tbody>
</table>
<p>The <code>deviceId</code> → <code>Party</code> mapping on the Contact Point DMOs is there because the SDK&#8217;s <code>deviceId</code> is what unifies the Contact Point record with the Individual DMO record (which itself has <code>deviceId</code> →<code> Individual Id</code>). They share the <code>deviceId</code> value as the join key. Party Identification DMO keeps <code>deviceId</code> as the anchor. Note the convention: <code>deviceId</code> goes into the <code>Party</code> field, and the known customer identifier (Lead ID, Contact ID, loyalty number) goes into Identification Number. The literal string &#8220;Web ID&#8221; (or similar) goes into<code> Identification Name</code>, so IR rulesets can target this identification type by name.</p>
<p><b>Why</b> <b>all</b> <b>three</b> <b>matter</b> <b>for</b> <b>Identity</b> <b>Resolution</b></p>
<p><span style="font-weight: 400">Each of these streams feeds a different identity rule:</span></p>
<ul>
<li><span>The </span><b><span>email</span></b><span> and </span><b><span>phone</span></b><span> streams give IR fuzzy-match anchors</span></li>
<li><span>The </span><b><span>partyIdentification</span></b><span> stream gives IR an </span><i><span>exact-match</span></i><span> anchor — when a user logs in or submits an authenticated form and you fire a partyIdentification event with their CRM ID for example, this is the deterministic bridge that says: &#8220;this anonymous </span><code>deviceId</code> is definitely this Lead/Contact.&#8221;</li>
</ul>
<h3><span style="font-weight: 400">3. Behavioral events stream</span></h3>
<p><span>This consolidated stream acts as a universal receiver for all engagement event types, including page views, product interactions, cart activity, and successful purchases. Because this stream is shared across multiple event types, the </span><code>eventType</code> field becomes the essential discriminator. It is the primary attribute used to route specific interaction models to their respective destination DMOs during the mapping phase.</p>
<h4><span style="font-weight: 400">Consent within the behavioral events stream</span></h4>
<p><span style="font-weight: 400">Before any meaningful tracking happens, the SDK needs a recorded consent decision for the visitor. Consent events are captured as a standard engagement event type and land in the Behavioral Events stream alongside every other engagement event. </span></p>
<p><span style="font-weight: 400">Within that stream, consent data appears as three prefixed fields:</span></p>
<ul>
<li><code><span>consentLog.provider</span></code><span>: The system that captured the consent decision (e.g. &#8220;Salesforce Interactions&#8221; when captured by the SDK directly)</span></li>
<li><code><span>consentLog.purpose</span></code><span>: What the visitor has consented to (e.g., tracking, personalization, targeting)</span></li>
<li><code>consentLog.status</code>: The decision itself (opt-in, opt-out)</li>
</ul>
<p><span style="font-weight: 400">If your website connector is deployed and you&#8217;re seeing no data at all in Data Explorer, check consent first. Without a recorded opt-in for the relevant purpose, the SDK will suppress downstream event tracking — which looks identical to a broken sitemap or schema. Always validate that consent is firing before you start debugging anything else.</span></p>
<p><b>Key configuration when deploying all data streams</b></p>
<ul>
<li><b>Refresh mode: </b><span style="font-weight: 400">You must consistently utilize </span><i><span style="font-weight: 400">Partial</span></i><span style="font-weight: 400"> for web data streams. Incremental mode is unsuitable here as it clears or overwrites values not explicitly present in the event payload, which invariably leads to significant data loss.</span></li>
<li><b>Data space: </b><span style="font-weight: 400">Designate the specific data space intended to receive the ingestion. Note that these streams can be extended to additional data spaces post-deployment without incurring redundant ingestion costs.</span></li>
</ul>
<h2><span style="font-weight: 400">Common field mappings</span></h2>
<p><span style="font-weight: 400">Standard field mappings often serve as a significant bottleneck during implementation. To help you avoid common pitfalls, here is a quick reference guide to ensure that your data correctly reaches its intended destination:</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206520" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260610124829/Screenshot-of-a-table-listing-each-website-schema-field-and-which-DMO-field-it-maps-to-e1781120921100.png?w=1000" class="postimages" width="1000" height="717" alt="Screenshot of a table listing each website schema field, and which DMO field it maps to" />
			  </span>
			</p>
<h2><span style="font-weight: 400">From Interactions SDK event to DLO</span></h2>
<p><span style="font-weight: 400">Grasping the end-to-end flow of your website events is fundamental for both debugging and schema design. This section provides a comprehensive reference for the lifecycle of each standard event type: from sending an event, through the Interactions SDK schema validation layer, and into the data stream that ultimately routes data to its target data lake object (DLO).</span></p>
<p><b>Architectural mapping: Schema objects to data streams</b></p>
<ul>
<li><span>Each </span><i><span>Profile</span></i><span> schema definition instantiates its own dedicated individual data stream, adhering to CamelCase naming conventions (e.g., </span><code><span>contactPointEmail</span></code><span> or </span><code><span>contactPointPhone</span></code><span>)</span></li>
<li><span>All </span><i><span>Engagement</span></i><span> schema definitions are consolidated into a universal Behavioral Events data stream, with fields utilizing an object-prefixed notation (e.g., </span><code>catalog.attributeColor</code>)</li>
</ul>
<p><span style="font-weight: 400">The following screenshot shows the architectural mapping for the Catalog interaction.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206521" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260610125025/The-architectural-mapping-for-the-Catalog-interaction-e1781121036970.png?w=1000" class="postimages" width="1000" height="625" alt="The architectural mapping for the Catalog interaction" />
			  </span>
			</p>
<p><span style="font-weight: 400">This screenshot shows the architectural mapping for the Order interaction.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206522" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260610125059/The-architectural-mapping-for-the-Order-interaction-e1781121070545.png?w=1000" class="postimages" width="1000" height="569" alt="The architectural mapping for the Order interaction" />
			  </span>
			</p>
<p><span style="font-weight: 400">This screenshot shows the architectural mapping for the Cart interaction.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206523" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260610125124/The-architectural-mapping-for-the-Cart-interaction-e1781121095830.png?w=1000" class="postimages" width="1000" height="532" alt="The architectural mapping for the Cart interaction" />
			  </span>
			</p>
<p><span style="font-weight: 400">This screenshot shows the architectural mapping for the Website Engagement interaction.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206524" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260610125151/The-architectural-mapping-for-the-Website-Engagement-interaction-e1781121130292.png?w=1000" class="postimages" width="1000" height="311" alt="The architectural mapping for the Website Engagement interaction" />
			  </span>
			</p>
<p><span style="font-weight: 400">And finally, this screenshot shows the architectural mapping for the Identity &amp; Contact Point interactions.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206525" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260610125249/The-architectural-mapping-for-the-Identity-Contact-Point-interactions-e1781121182901.png?w=1000" class="postimages" width="1000" height="707" alt="The architectural mapping for the Identity &amp; Contact Point interactions" />
			  </span>
			</p>
<p><b>Key insight: </b><span style="font-weight: 400">There are significant architectural advantages to leveraging standard, out-of-the-box DMOs for your mapping strategy. For instance, when capturing purchase transactions, line items, and product data, utilizing the Sales Order, Sales Order Product, and Goods Product DMOs ensures that Salesforce Personalization can natively recognize and process your data.</span></p>
<p>The Interactions SDK automatically injects several auto-populated fields into every event payload, including <code>category</code>, <code>dateTime</code>, <code>deviceId</code>, <code>eventId</code>, <code>eventType</code>, and <code>sessionId</code>. While these must be present in every schema object, they are handled entirely by the SDK — no manual configuration within your sitemap is required. The <code>eventType</code> field serves as the critical nexus; it binds your sitemap event to a specific schema definition and acts as the primary discriminator for routing data from the Behavioral Events stream to the appropriate destination DMO.</p>
<h2><span style="font-weight: 400">Deployment models: Sitemap-driven vs. Tag Manager-driven</span></h2>
<p><span style="font-weight: 400">Determining your event dispatch strategy hinges on a fundamental architectural choice: will you manage event capture natively within the sitemap, or leverage your Tag Management System? While both models are fully supported, they operate through distinct mechanisms and should not be implemented concurrently.</span></p>
<h3><span style="font-weight: 400">Option 1: Sitemap-driven architecture</span></h3>
<p>In this model, the sitemap serves as the central orchestrator for all tracking logic. You define <code>interaction</code> objects within your <code>pageTypes</code> array to capture page-load events, while utilizing <code>global.listeners</code> or explicit <code>sendEvent()</code> calls for behavioral interactions like clicks and form submissions. Because the sitemap is bundled into the CDN (Content Delivery Network) script generated by your website connector, your Tag Management System (TMS) simply injects a single <code>&lt;script&gt;</code> tag — eliminating the need for custom JavaScript within the TMS itself.</p>
<p><span style="font-weight: 400">This approach is ideal for maintaining a single source of truth for tracking logic. Furthermore, it is a strict requirement if your implementation includes Salesforce Personalization, as personalization experiences rely on the full sitemap context — including page types and content zones — to render correctly.</span></p>
<h3><span style="font-weight: 400">Option 2: Tag Manager-driven architecture</span></h3>
<p>Conversely, the TMS-led model utilizes a lean sitemap containing only <code>init()</code> and <code>initSitemap()</code>, shifting the responsibility for event dispatching to your TMS tags. Your TMS injects the CDN script and triggers page-level events via Page View triggers and behavioral data via Click or Form triggers. This provides greater flexibility for analytics teams who prefer working within a TMS and allows you to avoid managing JavaScript sitemaps directly within Data 360.</p>
<h4><span style="font-weight: 400">Dispatching events from a TMS</span></h4>
<p>When dispatching events from a TMS, you must explicitly set <code>"pageView": 1</code> in the payload for page-load events, or omit it for interaction-only events. To ensure a successful implementation, adhere to these critical technical requirements:</p>
<ul>
<li><b><span>Initialization sequence: </span></b><span>The </span><code><span>SalesforceInteractions.sendEvent()</span></code><span> method is only accessible after </span><code><span>init()</span></code><span> has resolved and </span><code><span>initSitemap()</span></code><span> has been invoked. Ensure that your </span><code><span>sendEvent</span></code><span> calls reside within the </span><code><span>.then()</span></code><span> callback of </span><code><span>init()</span></code><span>.</span></li>
<li><b><span>Race condition prevention: </span></b><span>When utilizing a TMS, always wrap your </span><code>sendEvent</code> calls in a safety check, such as <code>if (SalesforceInteractions)</code>, to prevent execution errors if the SDK has not finished loading.</li>
</ul>
<pre language="javascript">// Standard initialization pattern for TMS-based sendEvent calls
SalesforceInteractions.init({ ... }).then(() =&gt; {
    SalesforceInteractions.initSitemap(sitemapConfig);
    // Additional sendEvent calls from TMS are safe here
});
</pre>
<p><span style="font-weight: 400">The TMS-led approach may suit your organization if you manage a large number of brands or complex digital properties. Rather than maintaining different JavaScript sitemaps within Data 360 for each unique site, you can have just one lean sitemap for each site, with interactions managed via your Tag Management System. This can reduce the overhead of custom JavaScript maintenance within the Data 360 platform. </span></p>
<p><span style="font-weight: 400">It is best practice to leverage even a lean sitemap rather than trying to bypass this and do everything manually in a TMS. The CDN script that Data Cloud generates per tenant (see </span><a href="http://cdn.c360a.salesforce.com/beacon/c360a/abc123/scripts/c360a.min.js"><span style="font-weight: 400">example</span></a><span style="font-weight: 400">) already bundles the base SDK, the Data 360 module, and the sitemap, which can perform the initialization code above. This allows the sitemap to manage background operations like device ID persistence and consent orchestration.</span></p>
<p><span style="font-weight: 400">In practice, this architecture allows your TMS to orchestrate event capture just as it does for any other vendor. If you have an existing &#8220;form completed&#8221; trigger that currently dispatches data to your analytics and marketing pixels, you simply append a new logic block for Data 360.</span></p>
<pre language="javascript">// Example: TMS fires this snippet when a form-completion event triggers
SalesforceInteractions.sendEvent({
    interaction: {
        name: "Completed Form",
        attributes: {
            type: "Home Loan",
            id: "HomeLoanProduct-1",
            income: dataLayer.loanForm.income,
            loanAmount: dataLayer.loanForm.loanAmount
        }
    },
    user: {
        attributes: {
            id: "1234"
        }
    }
})
</pre>
<h3><span style="font-weight: 400">The personalization caveat: State vs. events</span></h3>
<p><span style="font-weight: 400">A critical distinction often overlooked by implementation teams is that the sitemap performs a dual role: it dispatches events while simultaneously establishing the global </span><i><span style="font-weight: 400">page state</span></i><span style="font-weight: 400">.</span></p>
<p>When you define <code>pageTypes</code> within a sitemap, you are doing more than specifying event triggers. You are initializing the contextual state of the page, identifying the current page type (e.g., a &#8220;Product Detail Page&#8221;), the specific anchor item (e.g., the product SKU), and the content zones eligible for personalization. This state is the foundational telemetry that Salesforce Personalization utilizes to execute logic such as:=</p>
<ul>
<li style="font-weight: 400"><b>&#8220;Inject a product recommendations carousel on all PDP pages&#8221;</b><span style="font-weight: 400">:  This requires explicit knowledge of the current page type</span></li>
<li style="font-weight: 400"><b style="color: #4a4a4a">&#8220;Surface recommendations related to the item currently being viewed&#8221;:</b><span style="font-weight: 400"> This requires an active anchor item ID</span></li>
</ul>
<p>Invoking <code>sendEvent()</code> in isolation, whether via a TMS or native application code, successfully transmits data to the Data 360 backend, but fails to set this local page state. While your events will reach Data 360, refresh customer profiles, and align with your DMO mappings, the personalization engine will lack the necessary context to identify the user&#8217;s current location or focal item. This architectural gap makes it significantly more complex to deploy context-aware, real-time personalized experiences.</p>
<h3><span style="font-weight: 400">The deciding question: </span></h3>
<p>If your Interactions SDK implementation is focused exclusively on data collection (triggering behavioral events), a TMS or application-led strategy is perfectly sufficient. However, if you intend to leverage Salesforce Personalization (either now or in a future phase) to render on-page experiences, the sitemap is required to maintain page state. This means your sitemap must define <code>pageTypes</code>, content zones, and anchor items, even if you supplement the architecture with additional <code>sendEvent()</code> calls from your TMS.</p>
<p><span style="font-weight: 400">Even in a pure TMS-driven data collection scenario, remember that the SDK still manages several background operations: consent orchestration, device ID persistence, and initial identity resolution. While these do not require explicit sitemap configuration, they are dependent on a properly initialized base SDK.</span></p>
<p>Lastly, never implement both approaches for the same set of interactions. If a sitemap triggers a <code>sendEvent</code> on page load and your TMS dispatches a parallel event for the same action, your data streams will suffer from duplication. Select a single architectural source of truth: either (a) centralize all tracking within the sitemap, or (b) utilize a lean sitemap and manage all event dispatching via your TMS.</p>
<h3><span style="font-weight: 400">Performance considerations</span></h3>
<p><span style="font-weight: 400">A frequent point of inquiry regarding the Interactions SDK is its potential impact on page performance. The SDK payload is approximately 100KB (minified), and its impact on page load is typically measured in mere milliseconds. By utilizing a Tag Manager to handle site-specific logic rather than loading it directly from Salesforce, the total script footprint remains highly optimized. We recommend running Lighthouse audits in Google Chrome on Interactions SDK deployments to demonstrate the negligible impact on overall performance scores.</span></p>
<h2><span style="font-weight: 400">Website connector strategy</span></h2>
<p>For most standard deployments, we recommend maintaining a 1:1 relationship between your website connectors and your domains. While the SDK technically permits a single connector to service multiple sites — typically by implementing conditional sitemap branching based on <code>window.location </code>— this architecture introduces unnecessary complexity and operational risk into your pipeline.</p>
<p><span style="font-weight: 400">When managing multiple brands or digital properties, your architectural strategy should hinge on your data governance requirements. If your goal is to enable cross-brand analytics and unified personalization, you should route these connectors into a single data space. Conversely, if your priority is strict data isolation and simplified governance, separate data spaces are the better path. Remember, you can deploy multiple connectors to feed a single data space, allowing you to maintain discrete sitemaps and schemas while still achieving a unified 360-degree view of your customer.</span></p>
<h2><span style="font-weight: 400">Building your schema iteratively</span></h2>
<p><span style="font-weight: 400">Rather than attempting to define your entire schema upfront, we recommend adopting a progressive implementation strategy to minimize operational risk. Deconstruct your web connector schema into discrete files, one for each specific event type. We recommend starting with a foundational behavioral event type, such as standard website engagement for tracking page views. Incorporating this event in your schema will mean that when you deploy the data stream and finalize the DMO mapping, you can verify that events are successfully ingested by validating the payload within Data Explorer. </span></p>
<p>We also strongly recommend an iterative approach: append the next event type and repeat the validation process. This approach allows you to validate each segment of the pipeline before introducing further architectural complexity. Furthermore, it leverages Data 360&#8217;s automated mapping engine: schema fields utilizing <code>masterLabel</code> values that provide an exact match to DMO field labels will be auto-mapped, significantly reducing manual configuration effort.</p>
<h2><span style="font-weight: 400">Conclusion</span></h2>
<p>Our Interactions SDK appears deceptively simple on the surface — <code>init()</code>, <code>sendEvent()</code>, and you&#8217;re done. However, the internal transformation layer, schema validation, data stream routing, and DMO mapping that occur behind the scenes are the critical junctures where implementations either succeed or fail. Mastering this full pipeline — and being deliberate about your event types, schema keys, and stream configuration — is the difference between simply &#8220;events are firing&#8221; and ensuring that &#8220;data is powering real-time personalization.&#8221;</p>
<p><span style="font-weight: 400">Some personalization examples include an abandoned cart: trigger an email or SMS journey in Marketing Cloud when an AddToCart event isn&#8217;t followed by a Purchase within a defined window. Or on-site article recommendations: use the anchor item from View Catalog Object events to render a &#8220;related articles&#8221; carousel via Salesforce Personalization.</span></p>
<p><span style="font-weight: 400">Wrapping up, we recommend that you construct your sitemap first, then test it thoroughly while monitoring the console logs. Once you validate the events within Data Explorer, build your schema to mirror the actual data payloads you observe. By adopting this progressive implementation strategy, the architectural pieces will fall into place.</span></p>
<h2><span style="font-weight: 400">Resources</span></h2>
<ul>
<li style="font-weight: 400"><span style="font-weight: 400">Documentation: </span><a href="https://developer.salesforce.com/docs/data/salesforce-interactions-sdk/guide/c360a-api-salesforce-interactions-web-sdk.html"><span style="font-weight: 400">Salesforce Data 360 Web SDK Developer Documentation</span></a></li>
<li style="font-weight: 400"><span style="font-weight: 400">Integration guide: </span><a href="https://developer.salesforce.com/docs/data/data-cloud-int/guide/c360-a-mobile-web-sdk-schema-quick-guide.html"><span style="font-weight: 400">Salesforce Data 360 Web and Mobile SDK</span></a></li>
<li style="font-weight: 400"><span style="font-weight: 400">Documentation: </span><a href="https://developer.salesforce.com/docs/data/salesforce-interactions-sdk/guide/c360a-api-translating-sdk-events-to-web-connector-schemas.html"><span style="font-weight: 400">Translation of SDK Events to Web Connector Schemas</span></a></li>
<li style="font-weight: 400"><span style="font-weight: 400">Blog post: </span><a href="https://developer.salesforce.com/blogs/2024/04/using-data-cloud-web-sdk-to-capture-engagement-on-your-website"><span style="font-weight: 400">Using Data Cloud Web SDK to Capture Engagement on Your Website</span></a></li>
</ul>
<h2><span style="font-weight: 400">About the author</span></h2>
<p><b>Chris Charalambous</b><span style="font-weight: 400"> is a Distinguished AI &amp; Data Architect for the Salesforce Data 360 &amp; Agentforce Solutions team, helping customers design scalable solutions and better understand how Data 360 and Agentforce can impact their business. His background includes software engineering, data engineering, mobile messaging, marketing automation, and data platforms. Follow Chris on </span><a href="https://www.linkedin.com/in/charalambouschris"><span style="font-weight: 400">LinkedIn</span></a><span style="font-weight: 400">.</span></p>
<p><b>Acknowledgements: </b><span style="font-weight: 400">A special thank you to Sergey Agadzhanov, Matija Vrzan, </span><a href="https://www.linkedin.com/in/lockryan/"><span style="font-weight: 400">Ryan Lock</span></a><span style="font-weight: 400">, and </span><a href="https://www.linkedin.com/in/felix-agung-4ba07b74/"><span style="font-weight: 400">Felix Agung</span></a><span style="font-weight: 400"> for their invaluable contributions and technical review of this article.</span></p>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/06/understanding-the-data-360-web-sdk">Understanding the Data 360 Web SDK: From Website Event to Data Model</a> appeared first on <a href="https://developer.salesforce.com/blogs">Salesforce Developers Blog</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://developer.salesforce.com/blogs/2026/06/understanding-the-data-360-web-sdk/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
	<post-id xmlns="com-wordpress:feed-additions:1">206515</post-id><media:thumbnail url="https://d259t2jj6zp7qm.cloudfront.net/images/20260610110804/Generic-D-3-e1781114897893.png?w=1000" />
<media:content url="https://d259t2jj6zp7qm.cloudfront.net/images/20260610110804/Generic-D-3-e1781114897893.png?w=1000" medium="image" />
	</item>
		<item>
		<title>Build Production-Ready Apps in Claude Code with Salesforce Skills</title>
		<link>https://developer.salesforce.com/blogs/2026/06/build-production-ready-apps-in-claude-code-with-salesforce-skills</link>
		<comments>https://developer.salesforce.com/blogs/2026/06/build-production-ready-apps-in-claude-code-with-salesforce-skills#respond</comments>
		<pubDate>Tue, 09 Jun 2026 15:07:12 +0000</pubDate>
		<dc:creator><![CDATA[Akshata Sawant]]></dc:creator>
				<category><![CDATA[Apex]]></category>
		<category><![CDATA[App Development]]></category>
		<category><![CDATA[Architecture]]></category>
		<category><![CDATA[Developer Tooling]]></category>
		<category><![CDATA[Lightning Web Components]]></category>
		<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[Claude Code]]></category>
		<category><![CDATA[Salesforce CLI]]></category>
		<category><![CDATA[Salesforce Code Analyzer]]></category>
		<category><![CDATA[Salesforce Skills]]></category>
		<category><![CDATA[Software Development]]></category>
		<category><![CDATA[Test Classes]]></category>

		<guid isPermaLink="false">https://developer.salesforce.com/blogs/?p=206498</guid>
		<description><![CDATA[<p>Build production-ready apps in Claude Code using Salesforce Skills to generate bulkified code, Apex controller logic, and test classes.</p>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/06/build-production-ready-apps-in-claude-code-with-salesforce-skills">Build Production-Ready Apps in Claude Code with Salesforce Skills</a> appeared first on <a href="https://developer.salesforce.com/blogs">Salesforce Developers Blog</a>.</p>
]]></description>
				<content:encoded><![CDATA[<p><a href="https://github.com/forcedotcom/sf-skills"><span style="font-weight: 400">Salesforce Skills</span></a><span style="font-weight: 400"> in Claude Code generate production-grade Apex, Lightning web components (LWCs), and test classes from natural language prompts. The code automatically passes </span><a href="https://developer.salesforce.com/docs/platform/salesforce-code-analyzer/overview"><span style="font-weight: 400">Salesforce Code Analyzer</span></a><span style="font-weight: 400"> and achieves 75%+ test coverage. This means you can describe what you need in plain English, and Claude generates deployment-ready code that follows Salesforce best practices.</span></p>
<p><span style="font-weight: 400">In this post, we&#8217;ll walk through building a complete account creation form with LWC, Apex controller, and test classes. Then we&#8217;ll explain how the skill system works under the hood, and explore advanced patterns like batch jobs, service layers, and debugging workflows.</span></p>
<h2><span style="font-weight: 400">Understanding Salesforce Skills</span></h2>
<p><span style="font-weight: 400">Salesforce Skills are specialized AI capabilities that understand Salesforce architecture, governor limits, security patterns, and deployment requirements. Think of them as expert developers embedded directly into your development workflow.</span></p>
<p>When you ask Claude Code to &#8220;Create an account creation form,&#8221; it doesn&#8217;t just generate generic code. Behind the scenes, the <code>generating-apex</code> skill discovers your project conventions and generates production-grade code with proper error handling and governor-safe queries. It enforces guardrails like preventing SOQL in loops and validates bulkification patterns. The skill then creates test classes with 75%+ coverage using <code>TestDataFactory</code> patterns. Before marking the task complete, it runs Salesforce Code Analyzer and executes your test suites automatically.</p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206500" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260608144207/Claude-Code-terminal-showing-the-generating-apex-skill-being-activated--e1780954943727.png?w=1000" class="postimages" width="1000" height="526" alt="Claude Code terminal showing the generating-apex skill being activated" />
			  </span>
			</p>
<h2><span style="font-weight: 400">How skills work under the hood</span></h2>
<p>When you make a request to Claude Code, the system follows a four-stage workflow. First, Claude analyzes your prompt and matches it to the appropriate skills. Keywords like &#8220;Lightning Web Component&#8221; activate <code>generating-lwc-components</code>, while &#8220;Apex class&#8221; triggers <code>generating-apex</code>, and &#8220;test class&#8221; activates <code>generating-apex-test</code>.</p>
<p>Once matched, each skill executes its workflow. The <code>generating-apex</code> skill discovers your project conventions, including naming patterns and existing classes. It chooses the correct pattern, i.e., Service, Controller, Batch, or another architecture, and reviews templates from its assets directory. The skill then generates your class with built-in guardrails that enforce sharing keywords and prevent SOQL in loops. Next, it calls the <code>generating-apex-test</code> skill to create comprehensive test coverage.</p>
<p>Throughout this process, Skills enforce guardrails automatically. They reject code with queries inside loops, force the sharing keyword on all classes, and won&#8217;t complete without generating test classes. For Lightning web components, they validate pattern formatting on both client and server sides. Every skill generates the required metadata files like <code>.cls-meta.xml</code> automatically.</p>
<p>Finally, before reporting success, Skills validate the generated code. They run <code>sf code-analyzer </code>to catch security violations, execute <code>sf apex run test</code> to verify functionality, and ensure 75%+ coverage. This means you get production-ready code, not just boilerplate.</p>
<h2><span style="font-weight: 400">Tutorial: Building an account creation form</span></h2>
<p><span style="font-weight: 400">Let&#8217;s build a real-world feature: a Lightning web component form that creates Account records with full validation, error handling, and test coverage.</span></p>
<h3><span style="font-weight: 400">Overview</span></h3>
<p><span>We&#8217;ll create a complete solution with three components: a Lightning web component with Account Name and Phone fields, an Apex Controller with an </span><code><span>@AuraEnabled</span></code><span> method and error handling, and a test class with 75%+ coverage and bulk testing for 251+ records. The form will include inline validation, toast notifications, and will be ready for deployment to App Builder and Home pages.</span></p>
<h3><span style="font-weight: 400">Prerequisites</span></h3>
<p><span>Before we dive in, make sure you have </span><a href="https://claude.ai/code"><u>Claude Code</u></a> installed (free for individual developers), Node.js for installing skills, and the <a href="https://developer.salesforce.com/tools/salesforcecli"><u>Salesforce CLI</u></a> authenticated to your org. You&#8217;ll need a Developer Edition org, sandbox, or scratch org. Run <code>sf org login web</code> to authenticate if you haven&#8217;t done so already.</p>
<h3><span style="font-weight: 400">Installing Salesforce Skills</span></h3>
<p><span style="font-weight: 400">Setting up Salesforce Skills takes just one command. Navigate to your Salesforce project directory and run:</span></p>
<pre language="text">npx skills add forcedotcom/sf-skills
</pre>
<p><span style="font-weight: 400">This pulls the official Salesforce Skills library from GitHub.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206501" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260608144246/Interactive-skill-selection-interface-showing-various-Salesforce-skills.png?w=968" class="postimages" width="968" height="994" alt="Interactive skill selection interface showing various Salesforce skills" />
			  </span>
			</p>
<p><span style="font-weight: 400">Use the </span><b>spacebar</b><span style="font-weight: 400"> to select the skills you want. For this tutorial, select:</span></p>
<ul>
<li><code>generating-apex</code>: For Apex class generation</li>
<li><code>generating-apex-test</code>: For Apex test class generation</li>
<li><code>generating-lwc-components</code>: For Lightning web component generation</li>
<li><code>debugging-apex-logs</code>: To debug your Apex classes</li>
</ul>
<p><span style="font-weight: 400">Press </span><b>Enter</b><span style="font-weight: 400"> to install. </span></p>
<p><span style="font-weight: 400">You can select the installation scope: </span></p>
<ul>
<li style="font-weight: 400"><b>Project:</b><span style="font-weight: 400"> Install in current directory (committed with you projects) </span></li>
<li style="font-weight: 400"><b style="color: #4a4a4a">Global:</b><span style="font-weight: 400">  Install in home directory (available for all your projects)</span></li>
</ul>
<p>The skills are now available in your Salesforce Project under <code>your-salesforce-project/skills-lock.json</code>.<code> </code></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206502" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260608144334/Screenshot-of-a-Salesforce-Project-folder-showing-installed-skills.png?w=678" class="postimages" width="678" height="405" alt="Screenshot of a Salesforce Project folder showing installed skills" />
			  </span>
			</p>
<p><span style="font-weight: 400">You can also take a look at all the skills in the </span><span style="font-weight: 400">skills-lock.json</span><span style="font-weight: 400"> file to view your installed skills.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206503" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260608144404/Screenshot-of-the-skills-lock.json-file-showing-installed-skills-e1780955067628.png?w=1000" class="postimages" width="1000" height="855" alt="Screenshot of the skills-lock.json file showing installed skills" />
			  </span>
			</p>
<p><span style="font-weight: 400">Currently, the forcedotcom/sf-skills library includes 60 + skills — and they’re growing. View the full list at the </span><a href="https://github.com/forcedotcom/sf-skills"><span style="font-weight: 400">official sf-skills repository</span></a><span style="font-weight: 400">.</span></p>
<p>To install all available skills, add the <code>--all</code> flag</p>
<pre language="text">npx skills add forcedotcom/sf-skills --all
</pre>
<p><span style="font-weight: 400">Note: </span><span style="font-weight: 400">Salesforce skills are NOT packaged as a npm package. We also do not own the </span><a href="https://www.npmjs.com/package/skills"><span style="font-weight: 400">&#8220;skills&#8221; npm package</span></a><span style="font-weight: 400">. We use it as a convenient way to install our skills from our GitHub repo.</span></p>
<h3><span style="font-weight: 400">Step 1: Start Claude Code</span></h3>
<p><span style="font-weight: 400">Launch Claude Code in your project directory.</span></p>
<pre language="text">claude
</pre>
<p><span style="font-weight: 400">You&#8217;ll see the Claude Code interactive prompt. This is where the magic happens.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206504" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260608144445/Claude-Code-command-line-interface-showing-the-welcome-message-and-prompt-ready-for-input-e1780955128999.png?w=1000" class="postimages" width="1000" height="246" alt="Claude Code command-line interface showing the welcome message and prompt ready for input" />
			  </span>
			</p>
<h3><span style="font-weight: 400">Step 2: Make your request</span></h3>
<p><span style="font-weight: 400">Type or paste the following prompt.</span></p>
<pre language="text">Create a complete Salesforce solution with the following components:

1. Lightning Web Component (LWC)
Build a form component named accountCreationForm with two input fields:
- Account Name (required)
- Phone (required, 10-15 digits)
- Include a Save button disabled until both fields are valid
- Display inline validation error messages
- Show success toast on save, error toast on failure
- Handle loading state with spinner

2. Apex Controller
- Create AccountCreationController class
- Method: @AuraEnabled(cacheable=false) saveAccount(String accountName, String phone)
- Use standard Phone field (Account has no standard Email field)
- Proper error handling with AuraHandledException
- Test class with ≥85% coverage

3. App Builder Integration
- Configure *.js-meta.xml for App Page and Home Page
- Provide deployment instructions

Constraints:
- Use imperative Apex (not @wire) since this is DML
- Follow Salesforce LWC best practices
- Standard objects and fields only
</pre>
<p><span style="font-weight: 400">Here’s what the complete prompt for creating an Account Creation form, including all the requirements listed, looks like in the terminal. </span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206511" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260609002034/image5-e1780989648382.png?w=1000" class="postimages" width="1000" height="526" alt="Terminal showing the complete prompt for creating an Account Creation form" />
			  </span>
			</p>
<h3><span style="font-weight: 400">Step 3: Watch Skills in action</span></h3>
<p>After you submit the prompt, Claude Code automatically activates the right skills: <code>generating-lwc-components</code> for the UI, <code>generating-apex</code> for the controller, and <code>generating-apex-test</code> for tests.</p>
<p>It discovers your project patterns, including existing naming conventions and architectural layers. Then it generates all required files:</p>
<ul>
<li><code>accountCreationForm.html: </code>Lightning Web Component template</li>
<li><code>accountCreationForm.js: </code>Component logic with imperative Apex calls</li>
<li><code>accountCreationForm.js-meta.xml</code>: Metadata for App/Home page exposure</li>
<li><code>AccountCreationController.cls:</code> Apex controller with error handling</li>
<li><code>AccountCreationController.cls-meta.xml:</code><code> </code>Apex metadata file</li>
<li><code>AccountCreationControllerTest.cls:</code> Test class with test methods</li>
<li><code>AccountCreationControllerTest.cls-meta.xml:</code> Test metadata file</li>
</ul>
<h3><span style="font-weight: 400">Step 4: Review the generated code and test classes</span></h3>
<p><span style="font-weight: 400">Review the generated code and the test classes. You’ll notice that they are production-ready with the help of Salesforce Skills, and we’ve achieved 100% test coverage for the Apex Class.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206505" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260608145328/Salesforce-CLI-output-showing-test-execution-results.png?w=624" class="postimages" width="624" height="934" alt="Salesforce CLI output showing test execution results" />
			  </span>
			</p>
<h3><span style="font-weight: 400">Step 5: Deploy and test your solution</span></h3>
<p><span style="font-weight: 400">Since we’re using Claude, it can help us deploy and test our solution, or you can use Salesforce CLI commands. </span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206506" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260608145356/Salesforce-CLI-showing-successful-deployment-of-Lightning-web-component-Apex-controller-and-test-class-files.png?w=614" class="postimages" width="614" height="975" alt="Salesforce CLI showing successful deployment of Lightning web component, Apex controller, and test class files" />
			  </span>
			</p>
<h3><span style="font-weight: 400">Step 6: Add component to your org’s Home page</span></h3>
<p><span style="font-weight: 400">Navigate to </span><b>Setup → Lightning App Builder</b><span style="font-weight: 400"> and click </span><b>New → Select Home Page</b><span style="font-weight: 400">. Choose a template like &#8220;Two Regions.&#8221; From the Custom components section, drag </span><b>Account Creation Form</b><span style="font-weight: 400"> onto the page. Click </span><b>Save → Activation → Assign</b><span style="font-weight: 400"> as Org Default.</span></p>
<p>The screenshot below shows the Lightning App Builder interface with the <code>accountCreationForm</code> component visible in the custom components panel and being placed in a Home Page layout.</p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206507" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260608145422/Lightning-App-Builder-interface-with-the-accountCreationForm-component-visible-e1780955674602.png?w=1000" class="postimages" width="1000" height="511" alt="Lightning App Builder interface with the accountCreationForm component visible" />
			  </span>
			</p>
<h3><span style="font-weight: 400">Step 7: Test in Salesforce UI</span></h3>
<p><span style="font-weight: 400">Navigate to your Salesforce Home page and test the component. Leave Account Name empty and an error message appears. Enter an invalid phone number with nine digits and an error message appears. Enter valid data and the Save button enables. </span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206509" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260609001730/Salesforce-Home-page-displaying-the-Account-Creation-Form-componentAdvanced-use-cases-e1780989466621.png?w=1000" class="postimages" width="1000" height="228" alt="Salesforce Home page displaying the Account Creation Form componentAdvanced use cases" />
			  </span>
			</p>
<p>This tutorial covered a single LWC plus Apex controller pattern, but Skills handle much more complex scenarios, including batch jobs with <code>Database.Stateful</code>, Service-Selector-Domain layered architectures, screen flows with decision routing, and debug log analysis.</p>
<p><span style="font-weight: 400">Here are five patterns that you can build with Salesforce Skills:</span></p>
<h3><span style="font-weight: 400">1. Generate a batch job for data processing</span></h3>
<p><span style="font-weight: 400">Ask Claude to &#8220;Create a batch Apex class to archive Cases older than two years named CaseArchivalBatch. Query Cases where ClosedDate &lt; LAST_N_YEARS:2, update Status__c to &#8216;Archived&#8217;, and include proper error handling and logging. Generate test class with 75%+ coverage.&#8221;</span></p>
<p>Claude activates <code>generating-apex</code> and generates <code>CaseArchivalBatch.cls</code> with <code>Database.Stateful</code> for error tracking, proper start/execute/finish methods, governor-safe queries with no SOQL in loops, and <code>CaseArchivalBatchTest.cls</code> with bulk testing for 251+ records.</p>
<h3><span style="font-weight: 400">2. Build a service layer class</span></h3>
<p><span style="font-weight: 400">Request &#8220;Create an AccountService class with a method to update Account billing addresses in bulk using the Service-Selector-Domain pattern. The method signature should be updateBillingAddresses(Map&lt;Id, Address&gt; addressByAccountId). Include proper error handling and generate test class with 90%+ coverage.&#8221;</span></p>
<p>Claude generates <code>AccountService.cls</code> with the with sharing keyword and bulkified DML, delegates queries to <code>AccountSelector.cls</code>, returns <code>List&lt;Database.SaveResult&gt;</code> for partial-success handling, and creates a test class using the <code>TestDataFactory</code> pattern.</p>
<h3><span style="font-weight: 400">3. Create a screen flow</span></h3>
<p><span style="font-weight: 400">Prompt &#8220;Create a screen flow for lead qualification named Lead_Qualification. Add screens for Company Name, Industry, and Annual Revenue. Include a decision element that routes to appropriate queues based on revenue. Assign to Enterprise Queue if revenue &gt; $1M, else assign to SMB Queue.&#8221;</span></p>
<p>Claude activates <code>generating-flow</code> and generates Flow metadata following Salesforce best practices.</p>
<h3><span style="font-weight: 400">4. Debug Apex logs</span></h3>
<p><span style="font-weight: 400">Tell Claude &#8220;Analyze the Apex logs in debug.log to find why the batch job is hitting governor limits.&#8221; </span></p>
<p>Claude uses the <code>debugging-apex-logs</code> skill to parse log files for SOQL and DML statements, identify SOQL-in-loop violations, report CPU time and heap size consumption, and suggest optimizations.</p>
<h2><span style="font-weight: 400">Best practices for working with Skills</span></h2>
<p><span style="font-weight: 400">The more context you provide in your prompts, the better the output. Instead of &#8220;Create an Apex class,&#8221; try &#8220;Create an Apex controller for my AccountForm LWC that saves Account records with Name and Phone validation.&#8221; Include field API names, object relationships, and any specific business logic requirements.</span></p>
<p><span style="font-weight: 400">Claude Code is conversational, so if the first pass isn&#8217;t quite right, just ask for changes. You can say &#8220;Add bulk error handling to that controller&#8221; or &#8220;Change the test class to use TestDataFactory patterns.&#8221; Skills maintain context across the conversation and update the code accordingly.</span></p>
<p><span style="font-weight: 400">While Skills generate production-grade code, always review the output before deploying to production. Verify that field API names match your org schema, and check that custom objects and fields exist in your target org. Review security settings including sharing rules and field-level security. Test in sandbox first before production deployment, and run Salesforce Code Analyzer manually as an extra safety check.</span></p>
<p>Even if you&#8217;re an experienced developer, Skills can teach you something new. Try using them once to discover best practices that you might have missed, see how test classes should be structured with <code>Given/When/Then</code> patterns, and learn which Code Analyzer flags are most important for security and performance. Skills are not just automation — they&#8217;re also a teaching tool.</p>
<h2><span style="font-weight: 400">Expanding your Skills toolkit</span></h2>
<p><span style="font-weight: 400">Now that you&#8217;ve seen Skills in action, explore the </span><a href="https://github.com/forcedotcom/sf-skills"><span style="font-weight: 400">full Skills library</span></a><span style="font-weight: 400"> to see all 60+ available skills. You can build OmniStudio solutions with Flexcards and Integration Procedures, connect Data Cloud data sources, create B2B commerce stores, and generate screen flows with orchestrations.</span></p>
<p>Try advanced patterns like <code>Trigger Frameworks</code> using the Trigger Actions Framework (TAF), <code>Queueable with Finalizers</code> for async operations with cleanup logic, and <code>Custom REST Resources</code> to build versioned APIs with proper error handling.</p>
<p><span style="font-weight: 400">Skills are open source, which means you can fork the repository and customize patterns, add your team&#8217;s naming conventions to templates, and create organization-specific skills. See the </span><a href="https://github.com/forcedotcom/sf-skills/blob/main/CONTRIBUTING.md"><span style="font-weight: 400">contribution guide</span></a><span style="font-weight: 400"> to get started.</span></p>
<p><span style="font-weight: 400">Finally, integrate Skills with your existing workflow. They work seamlessly with the </span><a href="https://marketplace.visualstudio.com/items?itemName=Anthropic.claude-code"><span style="font-weight: 400">Claude Code VS Code extension</span></a><span style="font-weight: 400">, CI/CD pipelines for generating code in automated workflows, and code review processes as a first-pass reviewer before PR submission.</span></p>
<h2><span style="font-weight: 400">Conclusion</span></h2>
<p><span style="font-weight: 400">Salesforce Skills in Claude Code transform AI from a code generator into a development expert. Instead of managing boilerplate and governor limits manually, you describe your goals and Claude implements them using production-grade patterns.</span></p>
<p><span style="font-weight: 400">From generating Apex and LWCs to debugging logs, Skills accelerate your workflow while reinforcing best practices. This allows you to focus on solving business problems rather than repetitive implementation tasks.</span></p>
<p>Ready to get started? <a href="https://claude.ai/code"><u>Download Claude Code for free</u></a> and run <code>npx skills</code> to add <code>forcedotcom/sf-skills</code> in your Salesforce Project. Within minutes, you&#8217;ll be generating production-ready code from natural language prompts.</p>
<p><span style="font-weight: 400">Have questions or want to share what you&#8217;ve built with Skills? Join the conversation in the </span><a href="https://developer.salesforce.com/forums"><span style="font-weight: 400">Salesforce Developer Community</span></a><span style="font-weight: 400"> or connect with us on </span><a href="https://www.linkedin.com/showcase/salesforce-developers/"><span style="font-weight: 400">LinkedIn</span></a><span style="font-weight: 400"> and </span><a href="https://twitter.com/SalesforceDevs"><span style="font-weight: 400">Twitter/X</span></a><span style="font-weight: 400">. We&#8217;d love to hear about your experience.</span></p>
<h2><span style="font-weight: 400">Resources</span></h2>
<ul>
<li style="font-weight: 400"><a href="http://github.com/forcedotcom/sf-skills"><span style="font-weight: 400">Salesforce Skills Library</span></a><b> </b></li>
<li style="font-weight: 400"><a href="http://claude.ai/code"><span style="font-weight: 400">Claude Code Documentation</span></a><b> </b></li>
<li style="font-weight: 400"><a href="http://docs.anthropic.com/claude-code"><span style="font-weight: 400">Claude Code Getting Started Guide</span></a><b> </b></li>
<li style="font-weight: 400"><a href="http://agentskills.io"><span style="font-weight: 400">Agent Skills Specification</span></a><b> </b></li>
<li style="font-weight: 400"><a href="http://developer.salesforce.com/tools/salesforcecli"><span style="font-weight: 400">Salesforce CLI Reference</span></a><b> </b></li>
<li style="font-weight: 400"><a href="http://trailhead.salesforce.com/content/learn/modules/lightning-web-components-basics"><span style="font-weight: 400">Trailhead: </span><span style="font-weight: 400">LWC Basics</span></a><b> </b></li>
<li style="font-weight: 400"><a href="http://trailhead.salesforce.com/content/learn/modules/apex_testing"><span style="font-weight: 400">Trailhead: Apex Testing</span></a></li>
</ul>
<h2><span style="font-weight: 400">About the author</span></h2>
<p><b>Akshata Sawant</b><span style="font-weight: 400"> is a Senior Developer Advocate at Salesforce and co-author of a book titled “MuleSoft for Salesforce Developers,” published by Packt Publication. For a more in-depth look at Akshata’s accomplishments, visit her </span><a href="https://www.linkedin.com/in/akshatasawant02/"><span style="font-weight: 400">LinkedIn</span></a><span style="font-weight: 400"> profile. </span></p>
<p>&nbsp;</p>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/06/build-production-ready-apps-in-claude-code-with-salesforce-skills">Build Production-Ready Apps in Claude Code with Salesforce Skills</a> appeared first on <a href="https://developer.salesforce.com/blogs">Salesforce Developers Blog</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://developer.salesforce.com/blogs/2026/06/build-production-ready-apps-in-claude-code-with-salesforce-skills/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
	<post-id xmlns="com-wordpress:feed-additions:1">206498</post-id><media:thumbnail url="https://d259t2jj6zp7qm.cloudfront.net/images/20260608135949/SingleHeadshot-3-e1780952406426.png?w=1000" />
<media:content url="https://d259t2jj6zp7qm.cloudfront.net/images/20260608135949/SingleHeadshot-3-e1780952406426.png?w=1000" medium="image" />
	</item>
	</channel>
</rss>
