<?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, 11 Jun 2026 19:11:19 +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>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 fetchpriority="high" 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 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 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>
		<item>
		<title>The Salesforce Developer’s Guide to the Summer ’26 Release</title>
		<link>https://developer.salesforce.com/blogs/2026/06/the-salesforce-developers-guide-to-the-summer-26-release</link>
		<comments>https://developer.salesforce.com/blogs/2026/06/the-salesforce-developers-guide-to-the-summer-26-release#respond</comments>
		<pubDate>Mon, 08 Jun 2026 15:00:21 +0000</pubDate>
		<dc:creator><![CDATA[Mohith Shrivastava]]></dc:creator>
				<category><![CDATA[Agentforce]]></category>
		<category><![CDATA[Apex]]></category>
		<category><![CDATA[APIs and Integrations]]></category>
		<category><![CDATA[Data 360]]></category>
		<category><![CDATA[Developer Tooling]]></category>
		<category><![CDATA[Headless 360]]></category>
		<category><![CDATA[Lightning Web Components]]></category>
		<category><![CDATA[Salesforce Releases]]></category>
		<category><![CDATA[Agent Script]]></category>
		<category><![CDATA[Agentforce Builder]]></category>
		<category><![CDATA[Agentforce Mobile SDK]]></category>
		<category><![CDATA[and Accessibility]]></category>
		<category><![CDATA[Code Extension]]></category>
		<category><![CDATA[Data Cloud]]></category>
		<category><![CDATA[developer tooling]]></category>
		<category><![CDATA[GraphQL Mutations]]></category>
		<category><![CDATA[Model context protocol]]></category>
		<category><![CDATA[Salesforce CLI]]></category>
		<category><![CDATA[security]]></category>
		<category><![CDATA[SOAP login retirement]]></category>
		<category><![CDATA[State Managers]]></category>
		<category><![CDATA[trust]]></category>
		<category><![CDATA[User Mode by Default]]></category>
		<category><![CDATA[Web Console Beta]]></category>

		<guid isPermaLink="false">https://developer.salesforce.com/blogs/?p=206465</guid>
		<description><![CDATA[<p>Summer &rsquo;26 developer highlights: Hosted MCP Servers, LWC State Managers, Apex user-mode defaults, Agentforce Mobile SDK, and CLI updates with code examples.</p>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/06/the-salesforce-developers-guide-to-the-summer-26-release">The Salesforce Developer’s Guide to the Summer ’26 Release</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">The </span><a href="https://admin.salesforce.com/blog/2026/admin-summer-26-release-countdown"><span style="font-weight: 400">Summer ’26 release</span></a><span style="font-weight: 400"> is rolling out to your sandbox environments through May and June, and is scheduled to go live in production mid-June. Specifically, the key dates for this release are: May 8, 2026 (sandbox preview), and May 15, June 5, June 12, and June 13, 2026 (production rollouts), depending on your instance. Check the </span><a href="https://status.salesforce.com/products/all/maintenances?_ga=2.91603481.2022017460.1780421510-826685783.1780373061"><span style="font-weight: 400">maintenance calendar</span></a><span style="font-weight: 400"> for your specific org. </span></p>
<p><span style="font-weight: 400">In this post, we’ll take a look at the highlights for developers across Salesforce </span><span style="font-weight: 400">Headless 360, </span><span style="font-weight: 400">Lightning Web Components (LWC), Apex, Agentforce, Data 360, and Agentforce 360 Platform developer tools.</span></p>
<h2><span style="font-weight: 400">Salesforce Headless 360</span></h2>
<p><a href="https://developer.salesforce.com/blogs/2026/05/headless-360-what-it-means-for-developers"><span style="font-weight: 400">Headless 360</span></a><span style="font-weight: 400"> makes every major Salesforce capability available as an API, Model Context Protocol (MCP) tool, or CLI command — accessible to any authenticated caller, whether that&#8217;s an app, a human, or an autonomous AI agent. It&#8217;s the biggest theme of the Summer &#8217;26 release. Headless 360 brings several innovations: hosted MCP servers, new MCP tools, coding skills, and CLI and API enhancements. </span><span style="font-weight: 400">Let’s dive into the primary developer features related to Headless 360 available in this release.</span></p>
<h3><span style="font-weight: 400">Salesforce Hosted MCP Servers</span></h3>
<p><span style="font-weight: 400">Salesforce Hosted MCP Servers let you connect any MCP-compatible AI client, such as Claude, ChatGPT, Cursor, or custom agents, to your Salesforce org and data through the open MCP standard. Every connection uses standard </span><a href="https://developer.salesforce.com/docs/platform/hosted-mcp-servers/guide/setup-overview.html"><span style="font-weight: 400">OAuth authentication</span></a><span style="font-weight: 400">, so your agents interact with Salesforce data and automation in a secure, governed way. Because Salesforce hosts them, there&#8217;s no additional infrastructure to manage. </span></p>
<p><span style="font-weight: 400">You get two flavors: pre-built </span><b>standard servers</b><span style="font-weight: 400">, and </span><b>custom servers</b><span style="font-weight: 400"> that you define yourself.</span></p>
<h4><span style="font-weight: 400">Standard MCP Servers</span></h4>
<p><a href="https://developer.salesforce.com/docs/platform/hosted-mcp-servers/guide/servers-reference.html#sobject-servers"><span style="font-weight: 400">Salesforce Hosted Standard MCP Servers</span></a><span style="font-weight: 400"> are now generally available. Salesforce provides several pre-built standard hosted MCP servers, including:</span></p>
<ul>
<li style="font-weight: 400"><a href="https://developer.salesforce.com/docs/platform/hosted-mcp-servers/guide/servers-reference.html#sobject-servers"><span style="font-weight: 400">SObject Servers</span></a><span style="font-weight: 400">: SObject CRUD, SOQL queries, search</span></li>
<li style="font-weight: 400"><a href="https://developer.salesforce.com/docs/platform/hosted-mcp-servers/references/reference/data-cloud-sql.html"><span style="font-weight: 400">Data 360</span></a><span style="font-weight: 400">: Data 360 queries and graph traversal</span></li>
<li style="font-weight: 400"><a href="https://developer.salesforce.com/docs/platform/hosted-mcp-servers/references/reference/tableau-next.html"><span>Tableau</span></a><span style="font-weight: 400">: Analytics and visualization</span></li>
</ul>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206473" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260605093402/Salesforce-Hosted-Standard-MCP-Servers-e1780677258898.png?w=1000" class="postimages" width="1000" height="487" alt="Salesforce Hosted Standard MCP Servers" />
			  </span>
			</p>
<h4><span style="font-weight: 400">Custom MCP Servers</span></h4>
<p><span style="font-weight: 400">When standard MCP servers aren&#8217;t enough, you can build </span><a href="https://developer.salesforce.com/docs/platform/hosted-mcp-servers/guide/custom-servers.html"><span style="font-weight: 400">custom MCP servers</span></a><span style="font-weight: 400"> with granular control over which tools and prompts you expose. Custom MCP servers respect the full sharing and security model you have configured for your Salesforce org. Custom MCP tools can be built from:</span></p>
<ul>
<li><b>Apex Actions:</b> Expose <code>@</code><code>InvocableMethod</code> (see <a href="https://developer.salesforce.com/docs/platform/hosted-mcp-servers/guide/invocable-actions.html"><u>docs</u></a>) annotated methods as MCP tools</li>
<li><b>Lightning Flows:</b> Expose autolaunched flows as MCP tools</li>
<li><b>Apex REST:</b> Expose custom Apex REST endpoints as MCP tools</li>
<li><b>AuraEnabled:</b> Expose <code>@AuraEnabled</code> annotated Apex methods as MCP tools</li>
<li><b>Named Query API:</b> Expose <a href="https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_named_query_intro.htm"><u>parameterized SOQL</u></a> queries as MCP tools</li>
<li><b>Prompt Builder:</b> Expose <a href="https://developer.salesforce.com/docs/platform/hosted-mcp-servers/guide/prompt-builder.html"><u>prompts from Prompt Builder</u></a> as MCP prompts</li>
<li><b>Agentforce:</b> Expose <a href="https://developer.salesforce.com/docs/platform/hosted-mcp-servers/guide/agentforce.html"><u>Agentforce agents</u></a> as MCP tools</li>
<li><b>API Catalog:</b> Map <a href="https://developer.salesforce.com/docs/platform/hosted-mcp-servers/guide/api-catalog.html"><u>Salesforce API Catalog</u></a> (curated registry of REST API endpoints) to MCP tools</li>
</ul>
<p><span style="font-weight: 400">For a complete walkthrough, including OAuth configuration details and connecting Claude Desktop and Claude Code, read </span><a href="https://developer.salesforce.com/blogs/2026/05/connect-claude-with-salesforce-hosted-mcp-servers"><span style="font-weight: 400">Connect Claude with Salesforce Hosted MCP Servers</span></a><span style="font-weight: 400"> and </span><a href="https://developer.salesforce.com/blogs/2026/05/expose-custom-apex-as-a-hosted-mcp-tool-for-agents"><span style="font-weight: 400">Expose Custom Apex as a Hosted MCP Tool for Agents</span></a><span style="font-weight: 400">.</span></p>
<h3><span style="font-weight: 400">Developer and designer MCP servers and tools</span></h3>
<p><span style="font-weight: 400">Developers and designers get a productivity boost from MCP-powered tools that bring AI assistance directly into the IDE and coding agents.</span></p>
<p><a href="https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_mcp.htm"><b>Salesforce DX MCP server (Beta)</b></a><span style="font-weight: 400">: </span><b> </b><span style="font-weight: 400">T</span><span style="font-weight: 400">wo important tools land here. </span><a href="https://developer.salesforce.com/docs/platform/lwc/guide/mcp-slds.html"><span style="font-weight: 400">SLDS Guideline tools</span></a><span style="font-weight: 400"> speeds up UI work with instant Salesforce Lightning Design System (SLDS) styling-hook and component-blueprint guidance. </span><a href="https://developer.salesforce.com/blogs/2026/04/performance-first-apex-development-with-apexguru-in-salesforce-dx-mcp-server"><span style="font-weight: 400">ApexGuru  </span></a><span style="font-weight: 400">brings Apex code review into your coding agent from your org&#8217;s runtime metrics.  It flags and fixes anti-patterns inline, such as SOQL or DML inside loops and redundant SOQL, and its Test Case Insights surface inefficient tests that drag down coverage.</span></p>
<p><a href="https://developer.salesforce.com/docs/atlas.en-us.api_meta.meta/api_meta/meta_salesforce_api_mcp_intro.htm"><b>Metadata API Context MCP Server (Beta)</b></a><span style="font-weight: 400">: This server now ships five granular tools instead of one. These tools provide contextual information about Salesforce metadata types to help generate accurate metadata files, with faster responses and more efficient token usage.</span></p>
<p><a href="https://developer.salesforce.com/blogs/2026/05/introducing-the-data-360-mcp-server-developer-preview"><b>Data 360 MCP Server (Developer Preview)</b></a><span style="font-weight: 400">: </span><span style="font-weight: 400"> </span><span style="font-weight: 400">This open-source server connects your coding agent to Data 360. Instead of exposing roughly 200 REST operations one by one, it fronts them with three facade tools — </span><b>search</b><span style="font-weight: 400"> (find a capability by intent), </span><b>payload_examples</b><span style="font-weight: 400"> (fetch a working request body), and </span><b>execute</b><span style="font-weight: 400"> (run it) — which keep the coding agent from blowing its context window. </span></p>
<p><a href="https://developer.salesforce.com/blogs/2026/01/accelerate-flexcard-development-with-omnistudio-mcp"><b>Omnistudio MCP Server (Beta)</b></a><span style="font-weight: 400">: </span><span style="font-weight: 400"> This server </span><span style="font-weight: 400">bridges agentic and low-code development. Use your coding agent to turn requirements — plain text, screenshots, or UX mockups — into functional </span><a href="https://help.salesforce.com/s/articleView?id=xcloud.os_omnistudio_flexcards_24388.htm&amp;type=5"><span style="font-weight: 400">Flexcard templates</span></a><span style="font-weight: 400">.</span></p>
<p><a href="https://salesforcecommercecloud.github.io/b2c-developer-tooling/mcp/"><b>B2C DX MCP Server</b></a><span style="font-weight: 400">: Modify your Storefront Next components quickly with the Figma-to-Component tool set, converting design files directly into components.</span></p>
<p><a href="https://developer.salesforce.com/blogs/2026/06/the-mcp-server-for-marketing-cloud-engagement-is-now-ga"><b>Marketing Cloud Engagement MCP Server</b></a><span style="font-weight: 400">: </span><span style="font-weight: 400">Securely connect external AI agents to Marketing Cloud Engagement and expose core developer capabilities like data extensions and journeys as natural-language tools.</span></p>
<h3><span style="font-weight: 400">Agent Skills for coding agents</span></h3>
<p><a href="https://agentskills.io/home"><span style="font-weight: 400">Agent Skills</span></a><span style="font-weight: 400"> are a lightweight, open format for extending AI agent capabilities with specialized knowledge and workflows. In this release, we are open-sourcing a </span><a href="https://github.com/forcedotcom/sf-skills"><span style="font-weight: 400">library of Salesforce development skills</span></a><span style="font-weight: 400">. The skills are optimized to work with the Salesforce coding agent Agentforce Vibes, and are also compatible with any other coding agent, such as Claude Code or Codex.</span></p>
<p><span>Skills come pre-packaged with Agentforce Vibes. For any other coding agent, install them using the npx command:</span><span> </span><code>npx skills add forcedotcom/sf-skills.</code></p>
<h3><span style="font-weight: 400">Salesforce CLI updates</span></h3>
<p><a href="https://developer.salesforce.com/docs/atlas.en-us.sfdx_cli_reference.meta/sfdx_cli_reference/cli_reference_top.htm"><span style="font-weight: 400">Salesforce CLI&#8217;s</span></a><span style="font-weight: 400"> 220+ commands are a core part of Salesforce Headless 360. The CLI keeps shipping every week, and recent releases shipped several updates worth knowing about. We&#8217;ll look at the highlights for developers, organized by what they help you build. The emphasis is on </span><a href="https://developer.salesforce.com/docs/ai/agentforce/guide/agent-dx.html"><span style="font-weight: 400">Agentforce DX tooling</span></a><span style="font-weight: 400"> and a major credential security overhaul.</span></p>
<h4><span style="font-weight: 400">Build agents from a working starting point:</span></h4>
<ul>
<li><b>Agent project scaffolding:</b> Spin up a runnable Agentforce sample instead of starting from scratch. The <code>agent</code> template generates a Local Info Agent demonstrating Apex, Prompt Template, and Flow subagents.</li>
</ul>
<pre language="sh">sf template generate project --name my-agent --template agent
</pre>
<ul>
<li><b>One-command agent user:</b><span style="font-weight: 400"> Automate the setup of service agent users, eliminating the need for manual provisioning.</span></li>
</ul>
<pre language="sh">sf org create agent-user --first-name Service --last-name Agent --target-org my-org
</pre>
<h4><span style="font-weight: 400">Test, preview, and debug agents:</span></h4>
<ul>
<li><b>Agent preview is GA:</b> You can now script interactive test sessions end to end with <code>agent preview start</code>, <code>send</code>, <code>sessions</code>, and <code>end</code>.</li>
<li><b>Trace files for diagnosis:</b> Inspect and manage the traces recorded during a preview session to see exactly how your agent routed and acted.</li>
</ul>
<pre language="sh">sf agent trace read --session-id  --format detail --dimension actions
</pre>
<ul>
<li><b>Richer evaluations (Beta):</b><span style="font-weight: 400"> Run YAML- or JSON-defined evaluation tests for higher-quality, repeatable agent testing.</span></li>
</ul>
<pre language="sh">sf agent test run-eval --spec specs/my-agent-testSpec.yaml --target-org my-org
</pre>
<h4><span style="font-weight: 400">Keep credentials out of your logs:</span></h4>
<ul>
<li><b>Secrets redacted by default:</b> Access tokens, SFDX auth URLs, and passwords are now stripped from commands like <code>org display</code> and <code>org list --json</code>, preventing accidental leaks in continuous integration (CI) and logs.</li>
<li><b>Deliberate secret retrieval:</b> When you actually need a credential, ask for it explicitly.</li>
</ul>
<pre language="sh">sf org auth show-access-token --target-org my-org
</pre>
<p><span style="font-weight: 400">As always, the deep-dive details for every command and flag live in the </span><a href="https://github.com/forcedotcom/cli/blob/main/releasenotes/README.md"><span style="font-weight: 400">Salesforce CLI release notes</span></a><span style="font-weight: 400">. Read them to go further.</span></p>
<h3><span style="font-weight: 400">Salesforce Platform API updates</span></h3>
<p><span style="font-weight: 400">The platform&#8217;s APIs are a big part of Headless 360. Summer &#8217;26 ships </span><b>API version 67.0</b><span style="font-weight: 400">, and a few changes are worth knowing about as you build. </span></p>
<h4><span style="font-weight: 400">Plan now: SOAP </span><span style="font-weight: 400">login()</span><span style="font-weight: 400"> is being retired</span></h4>
<p><span>This is the most impactful change in this release for integration owners. SOAP </span><code>login()</code><span> in API versions 31.0–64.0 </span><a href="https://help.salesforce.com/s/articleView?id=005132110&amp;type=1"><u>retires in Summer &#8217;27</u></a><span>. Any integration that authenticates with a username and password over SOAP will stop working. Move those integrations to OAuth — using external client apps with JWT tokens — well ahead of the cutoff. A new </span><a href="https://help.salesforce.com/s/articleView?id=release-notes.rn_api_soap_login.htm&amp;release=262&amp;type=5"><u>Any API Auth</u></a><span> user permission lets you control who can authenticate via SOAP </span><code>login()</code><span>, and it&#8217;s enforced by default in newly created orgs.</span></p>
<h4><span style="font-weight: 400">Chain dependent records in one GraphQL request</span></h4>
<p><a href="https://developer.salesforce.com/docs/platform/graphql/guide/mutations-intro.html"><u>GraphQL mutations</u></a> can now reference any field returned by an earlier operation in the same request, not just its record ID. Use <code>@{ref.Record.FieldName.value}</code> for a field value, <code>@{ref.Record.Id}</code> (or shorthand <code>@{ref}</code>) for the ID. This lets you create linked records in a single round trip. Below is an example body for chaining dependent request in one GraphQL</p>
<pre language="js">mutation CreateChain {
  uiapi(input: { allOrNone: false }) {
    AccountCreate(input: { Account: { Name: "Headless 360 Test Co" } }) {
      Record { Id  Name { value } }
    }
    ContactCreate(input: { Contact: {
      LastName: "@{AccountCreate.Record.Name.value}"
      AccountId: "@{AccountCreate}"   # == AccountCreate.Record.Id
    }}) { Record { Id  LastName { value } } }
    OpportunityCreate(input: { Opportunity: {
      Name: "@{ContactCreate.Record.LastName.value}"
      AccountId: "@{AccountCreate.Record.Id}"
      StageName: "Value Proposition"
      CloseDate: "2026-12-31"
    }}) { Record { Id } }
  }
}
</pre>
<p><span style="font-weight: 400">You can use a Beta Salesforce CLI command to execute any GraphQL as shown below.</span></p>
<pre language="sh">sf api request graphql --body  --target-org my-org
</pre>
<h4><span style="font-weight: 400">Use JWT access tokens with SOAP API</span></h4>
<p><a href="https://developer.salesforce.com/docs/atlas.en-us.api.meta/api/sforce_api_quickstart_intro.htm"><u>SOAP API</u></a> now accepts <a href="https://help.salesforce.com/s/articleView?id=release-notes.rn_api_soap_jwt.htm&amp;release=262&amp;type=5"><u>JWT-based</u></a> access tokens from Salesforce OAuth flows in the <code>sessionId</code> header element, reaching parity with REST authentication and making token sharing with external services safer.</p>
<h4><span style="font-weight: 400">Connect REST API rate limits are relaxed</span></h4>
<p><span style="font-weight: 400">Orgs have been migrated off the restrictive per-user, per-application, per-hour </span><a href="https://help.salesforce.com/s/articleView?id=release-notes.rn_connect_api.htm&amp;release=262&amp;type=5"><span style="font-weight: 400">Connect REST API limit </span></a><span style="font-weight: 400">and onto the more generous per-org, per-24-hour Salesforce Platform API limit — the same pool that every other API call shares. Only requests that require Chatter keep the old hourly throttle. The identical change applies to Connect in Apex.</span></p>
<h4><span style="font-weight: 400">User Interface API CSRF token</span></h4>
<p><span>A new </span><code>GET /ui-api/session/csrf </code><span>resource (see </span><a href="https://developer.salesforce.com/docs/atlas.en-us.uiapi.meta/uiapi/ui_api_resources_session_csrf.htm"><u>docs</u></a><span>) returns a token that you can use to protect User Interface API requests from third-party forgeries.</span></p>
<h2><span style="font-weight: 400">LWC updates</span></h2>
<p><b>Summer &#8217;26 is a maturity release for LWC.</b><span style="font-weight: 400"> The big themes: </span><b>cleaner architecture</b><span style="font-weight: 400"> (state finally lives outside your components), a </span><b>quicker edit-and-preview cycle</b><span style="font-weight: 400"> (real-time previews in the browser and your IDE), </span><b>better defaults</b><span style="font-weight: 400"> (more virtualization, tighter security), and a </span><b>new bridge to Agentforce</b><span style="font-weight: 400">. </span></p>
<p><span style="font-weight: 400">Here are the five features most likely to change how you build — each with the problem it solves.</span></p>
<ul>
<li><b>State Managers (GA)</b>: These pull data and its logic out of components into a reusable, testable layer.</li>
<li><b>API 67.0 niceties</b>: This includes zero-JS accordions via grouped <span>&lt;details&gt;</span>, plus faster hot reload.</li>
<li><b>Secure downloads</b>: LWS now blocks <span>data:</span> URIs, so generate files the right way.</li>
<li><b>Dynamic lists (in Developer Preview)</b>: This renders thousands of rows smoothly with built-in virtualization.</li>
<li><b><code>lightning/accApi</code></b>:<span> This new module lets your components open and drive the Agentforce panel.</span></li>
</ul>
<h3><span style="font-weight: 400">State Managers for LWC</span></h3>
<p><a href="https://developer.salesforce.com/docs/platform/lwc/guide/state-management.html"><u>State Managers</u></a> is the most consequential LWC feature going GA during this release. State managers move data and the logic that mutates it <i>out</i> of components into a dedicated layer. Build one as a plain JS module with the new<span> </span><code>defineState</code><span> </span><span>primitive from</span><span> </span><code>@lwc/state</code>, which gives you three building blocks:</p>
<ul>
<li><code><span>atom(value)</span></code><span>: Reactive state, read through </span><code><span>.value</span></code><code></code></li>
<li><code><span>computed([deps], fn)</span></code><span>: A derived value that recomputes when a dependency changes</span></li>
<li><code>setAtom(atom, value)</code><span>: The </span><i><span>only</span></i><span> way to update an atom</span></li>
</ul>
<p><code>defineState</code><span> returns a </span><b><span>factory</span></b><span> — each call yields a fresh, independent instance. The essential shape: one atom as the source of truth, a derived computed, and an action that mutates via </span><code>setAtom</code>.</p>
<p><span style="font-weight: 400">Below is example code that demonstrates a state manager in action, handling a cart:</span></p>
<p><b><code>cartStateManager.js</code></b></p>
<pre language="js">import { defineState } from '@lwc/state';
export default defineState(({ atom, computed, setAtom }) =&gt; {
    const items = atom([]);                       // source of truth
    const count = computed([items], (l) =&gt; l.length); // derived
    const addItem = (item) =&gt;                       // action
        setAtom(items, [...items.value, item]);
    return { items, count, addItem };
});
</pre>
<p>A component imports the manager module, calls the factory, then reads reactive state through<span> </span><code>.value</code><span> — </span><span>in JS or the template, which re-renders automatically on change. No data logic, no manual subscription:</span></p>
<p><b><code>cartSummary.js</code></b></p>
<pre language="js">import { LightningElement } from 'lwc';
import createCartStateManager from 'c/cartStateManager';
export default class CartSummary extends LightningElement {
    cart = createCartStateManager();
}
</pre>
<p><b><code>cartSummary.html</code></b></p>
<pre language="html">&lt;template&gt;&lt;h2&gt;Your Cart ({cart.value.count})&lt;/h2&gt;&lt;p&gt;
&lt;/p&gt;&lt;/template&gt;
</pre>
<p><span style="font-weight: 400">For complete, runnable examples, see the open-source</span> <a href="https://github.com/trailheadapps/lwc-recipes/tree/main/force-app/main/default/lwc/opportunitiesStateManager"><span style="font-weight: 400">lwc-recipes repo</span></a><span style="font-weight: 400">. </span><span style="font-weight: 400">Because each instance is isolated, managers are trivially unit-testable</span><span style="font-weight: 400">.</span></p>
<p><span><span>Salesforce also ships </span></span><a href="https://developer.salesforce.com/docs/platform/lwc/guide/reference-state-managers.html"><u>built-in Lightning state managers</u></a><span><span> that wrap Lightning Data Service (LDS) access to the most common UI API data and metadata — for records, object info, page layouts, and related lists (for example, </span></span><code>lightning/stateManagerRecord</code><span><span> and </span></span><code>lightning/stateManagerObjectInfo</code><span><span>). They follow the same pattern as the ones you write and participate fully in LDS caching, normalization, and subscriptions, so reach for them before rolling your own.</span></span></p>
<h4><span>LWC API version 67.0: Group</span><b><span> </span></b><code>&lt;details&gt;</code><b><span> </span></b><span>and faster HMR</span></h4>
<p><span>To enable the features of this release, update your bundle </span><code>.js-meta.xml</code><span> by setting </span><code>&lt;apiVersion&gt;67.0&lt;/apiVersion&gt;</code><span>.</span></p>
<p><span>Two wins by using the 67.0 API version: hot module reloading (HMR) is faster and more memory-efficient, and you can now group native </span><code>&lt;details&gt;</code><span> elements with the </span><span>name</span><span> attribute for a zero-JavaScript exclusive accordion (it threw a compiler warning before 67.0). Same </span><code>name</code><span> = only one open at a time.</span></p>
<p><b><code>faqAccordion.html</code></b></p>
<pre language="html">&lt;details key={faq.id} name=&quot;summer26-faq&quot;&gt;
    &lt;summary&gt;{faq.question}&lt;/summary&gt;
    &lt;p&gt;{faq.answer}&lt;/p&gt;&lt;/details&gt;</pre>
<h3><span>Secure file downloads: LWS blocks </span><code>data:</code><span> URIs</span></h3>
<p><span>Lightning Web Security (LWS) adds a </span><a href="https://help.salesforce.com/s/articleView?id=release-notes.rn_lc_lws_distortion_changes.htm&amp;release=262&amp;type=5"><u>batch of API distortions</u></a><span> in this release. The one most likely to break code: </span><code>HTMLAnchorElement.prototype.href</code><span> now </span><b><span>blocks the </span><code>data:</code><span> URI scheme</span></b><span>. If you trigger client-side downloads by setting an anchor&#8217;s </span><code>href</code><span> to a </span><code>data:</code><span> URL, that stops working.</span></p>
<p><span>The fix is the supported pattern anyway — build a </span><a href="https://developer.mozilla.org/en-US/docs/Web/API/Blob"><u>blob</u></a><span> in JavaScript and use a </span><code>blob:</code><span> object URL (origin-bound, and revoked after use).</span></p>
<p><b><code>secureDownload.js</code></b></p>
<pre language="js">// LWS-safe: blob: URL, not data:
const blob = new Blob([this.content], { type: 'text/plain' });
const url = URL.createObjectURL(blob);
anchor.href = url;
anchor.download = this.fileName;
anchor.click();
URL.revokeObjectURL(url); // release memory
</pre>
<p>Other new distortions include<code> </code><code>Element.getAttribute</code><code>, </code><code>innerHTML/outerHTML</code> getters, <code>MutationObserver.observe</code>, the <code>IndexedDB</code> factory, <code>Promise.then/catch/finally</code>, and more — with matching ESLint rules. Run the updated <a href="https://developer.salesforce.com/docs/platform/lightning-components-security/guide/lws-tools-lint.html"><u>ESLint package</u></a> and review the <a href="https://developer.salesforce.com/tools/lws-distortion-viewer"><u>LWS Distortion Viewer </u></a>before you upgrade the components to this release.</p>
<h3><span style="font-weight: 400">Load large lists dynamically (Developer Preview)</span></h3>
<p><span>Rendering thousands of rows used to choke the browser, the new </span><code>lightning-dynamic-list-container</code><span> (see </span><a href="https://developer.salesforce.com/docs/platform/lightning-component-reference/guide/lightning-dynamic-list-container.html?type=Example"><u>docs</u></a><span>) and </span><code>lightning-dynamic-list-item</code><span> (see </span><a href="https://developer.salesforce.com/docs/platform/lightning-component-reference/guide/lightning-dynamic-list-item.html"><u>docs</u></a><span>) base components use </span><b><span>virtualization</span></b><span>. They render only the rows in the viewport and stream the rest in as you scroll, from 50 items to 5,000.</span></p>
<p><b><code>dynamicContactList.html</code></b></p>
<pre language="html">&lt;!-- Container needs a bounded height for scroll + virtualization --&gt;
&lt;div style=&quot;height: 400px&quot;&gt;
  &lt;lightning-dynamic-list-container
      onrenderlistitems={handleRenderListItems}
      onloadmore={handleLoadMore}&gt;
    &lt;template for:each={visibleContacts} for:item=&quot;contact&quot;&gt;
      &lt;lightning-dynamic-list-item
          key={contact.id} item-id={contact.id}&gt;
        &lt;div&gt;{contact.name}&lt;/div&gt;
      &lt;/lightning-dynamic-list-item&gt;
    &lt;/template&gt;
  &lt;/lightning-dynamic-list-container&gt;
&lt;/div&gt;
</pre>
<p><span>The container fires </span><code>renderlistitems</code><span> as you scroll (which slice to render) and </span><code>loadmore</code><span> near the end:</span></p>
<p><b><code>dynamicContactList.js</code></b></p>
<pre language="js">handleRenderListItems(event) {
    const { startIndex, endIndex } = event.detail;
    this.visibleContacts = this.allContacts.slice(startIndex, endIndex);
}
</pre>
<p><span style="font-weight: 400">You also get </span><b>focus preservation</b><span style="font-weight: 400"> and </span><b>built-in accessibility</b><span style="font-weight: 400"> (screen readers, Home/End/Arrow nav, Browse-Mode hint). </span></p>
<p>Here are some recommendations for working with dynamic lists: keep container and item adjacent, give every item a unique <code>item-id</code><span>, and don&#8217;t set </span><code>overflow: scroll</code><span> </span><span>on your own container — the component handles scrolling</span><span>.</span></p>
<h3>Talk to Agentforce from LWC with new LWC module <code>lightning/accApi</code></h3>
<p><span>The standout new module is </span><code>lightning/accApi</code><span> (see </span><a href="https://developer.salesforce.com/docs/platform/accsdk/guide/acc-api.html"><u>docs</u></a><span>) — the </span><b><span>Agentforce Conversation Client API</span></b><span>. This </span><i><span>headless</span></i><span> module lets your LWC components drive the native Agentforce side panel in Lightning Experience: open it, close it, point it at a specific Employee Agent, and send natural-language utterances. Think of a &#8220;Summarize this record&#8221; button, or a context-aware launcher in a console sidebar.</span></p>
<p>The entire API is three async methods:</p>
<table>
<tbody>
<tr>
<td><b>Method</b></td>
<td><b>Purpose</b></td>
</tr>
<tr>
<td><code>open(botId?)</code></td>
<td><span style="font-weight: 400">Open the side panel, optionally to a specific agent</span></td>
</tr>
<tr>
<td><span style="font-weight: 400"><code>close()</code></span></td>
<td><span style="font-weight: 400">Close the side panel</span></td>
</tr>
<tr>
<td><code>execute(utterance, botId)</code></td>
<td><span style="font-weight: 400">Run a natural-language utterance on an agent</span></td>
</tr>
</tbody>
</table>
<p>All three return a <code>Promise</code> and are <b>queued</b>, running in sequence. Note <code>execute</code> does <i>not</i> return the reply — the conversation renders in the panel, not your component. Import the methods and call them.</p>
<p><b><code>agentforceLauncher.js</code></b></p>
<pre language="js">import { open, close, execute } from 'lightning/accApi';

@api botId; // design-time property, set on the page

async handleOpen() {
    await open(this.botId);
}
async handleQuickAction(event) {
    const { utterance } = event.currentTarget.dataset;
    await execute(utterance, this.botId); // wrap in try/catch + toast
}
</pre>
<div style="height: 400px">
<p>Expose <code>botId</code> as a design-time property, so admins can wire up the agent without touching code (a <code>&lt;property name="botId" type="String"&gt;</code> entry in the bundle&#8217;s <code>.js-meta.xml targetConfig</code>). <code>botId</code> can be obtained from the URL in Agentforce Builder.</p>
<h2><span style="font-weight: 400">Apex updates</span></h2>
<p><b>API version 67.0</b> <span style="font-weight: 400">reinforces Apex security with safer defaults, and adds some long-requested ergonomics along the way</span><span style="font-weight: 400">. </span></p>
<h3><span style="font-weight: 400">Database operations run in user mode by default</span></h3>
<p>SOQL, SOSL, DML, and <code>Database</code> (see <a href="https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex_methods_system_database.htm"><u>docs</u></a>) methods now default to <a href="https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_classes_enforce_usermode.htm"><u>user mode</u></a> instead of system mode, so ever operation enforces the running user&#8217;s object permissions, field-level security (FLS), and sharing rules. The platform no longer assumes that the surface in front of it has already filtered the data; elevated access is now something you opt into explicitly.</p>
<h3><code>with sharing</code><b> is the new default, and </b><code>WITH SECURITY_ENFORCED</code><b> is retired</b></h3>
<p>Two changes reinforce the same idea. A class compiled at 67.0 with <b>no</b> sharing keyword now defaults to <code>with sharing</code> (previously <code>without sharing</code>), so bypassing sharing is now a deliberate <code>without sharing</code> declaration. And the old <code>WITH SECURITY_ENFORCED</code> clause no longer compiles.</p>
<p><span style="font-weight: 400">Here&#8217;s the exact error from a 67.0 org:</span></p>
<pre language="apex">// Deploying this class at API 67.0 fails with:
//   "WITH SECURITY_ENFORCED is no longer supported, use WITH USER_MODE instead."
[SELECT Id FROM Account WITH SECURITY_ENFORCED LIMIT 1];
</pre>
<p><code>WITH USER_MODE</code> isn&#8217;t just a rename. It handles polymorphic fields (<code>Owner</code>, <code>Task.whatId</code>), checks the <code>WHERE</code> clause and not just the <code>SELECT</code> list, and reports <i>every</i> FLS violation instead of only the first, which you can read off the <code>QueryException</code>.</p>
<pre language="apex">try {
    List a = [SELECT Id, Name, AnnualRevenue FROM Account WITH USER_MODE LIMIT 1];
} catch (QueryException e) {
    // returns Map fieldNames&gt;
    Map&lt;String, Set&gt; blocked = e.getInaccessibleFields();
}
</pre>
<h3>Multiline strings and <code>String.template()</code></h3>
<p>Triple single-quotes (<code>'''</code>) give you real <a href="https://help.salesforce.com/s/articleView?id=release-notes.rn_apex_multiline_string.htm&amp;release=262&amp;type=5"><u>multiline string</u></a> literals — no more <code>+ '\n' +</code> chains for JSON payloads, email bodies, or SOQL. And <code>String.template()</code> (see <a href="https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex_methods_system_string.htm#apex_System_String_template"><u>docs</u></a>) does named interpolation with <code>${variableName}</code> placeholders, replacing the index-juggling of <code>String.format()</code>.</p>
<p>Below is example Apex code showing multiline string and templating in action.</p>
<pre language="apex">String payload = '''
{
    "Account": "${accountName}",
    "Last Updated": "${date}"
}'''.template(new Map&lt;String, Object&gt;{
    'accountName' =&gt; 'My Account',
    'date' =&gt; Datetime.newInstance(2018, 11, 15, 8, 0, 0)
});
</pre>
<p>Two things to keep in mind when running this:</p>
<ul>
<li>The newline right after the opening <code>'''</code> is trimmed, so the literal above starts with <code>{</code>, not a blank line.</li>
<li><code>String.template()</code> renders a <code>Datetime</code> in <b>GMT</b> using <code>yyyy-MM-dd HH:mm:ss</code>, not the user&#8217;s local time in the way that <code>String.valueOf()</code> does. Format it yourself if you need a specific zone.</li>
</ul>
<h3><span style="font-weight: 400">Integration tests for Agentforce and Data 360 (Developer Preview)</span></h3>
<p>Integration tests for Agentforce and Data 360 are currently in Developer Preview and are supported in scratch orgs only. Standard unit tests mock every callout and roll back data, which prevents asserting on real Agentforce or Data 360 interactions. The new <code>@IntegrationTest</code> annotation overcomes these limitations by allowing live callouts and enabling data commits mid-transaction using <code>IntegrationTest.commitTestOnly()</code>, with cleanup handled in a <code>@TearDown</code> method. To enable this, add &#8216;<b>ApexIntegrationTests</b>&#8216; to the features array in your scratch org definition file. These tests run asynchronously, one at a time, via the Tooling API&#8217;s runTestsAsynchronous resource.</p>
<pre language="apex">@IntegrationTest
public with sharing class MyServiceIntegrationTest {
    @IntegrationTest
    public static void testServiceInteraction() {
        Account a = new Account(Name = 'Integration Test Account');
        insert as user a;
        IntegrationTest.commitTestOnly();           // data survives mid-test
        Account r = [SELECT Name FROM Account WHERE Id = :a.Id WITH USER_MODE];
        Assert.areEqual('Integration Test Account', r.Name);
    }
    @TearDown
    public static void tearDown() {
        delete as user [SELECT Id FROM Account WHERE Name = 'Integration Test Account' WITH USER_MODE];
    }
}
</pre>
<h3><span style="font-weight: 400">Elastic limits, trigger system mode, and other Apex changes</span></h3>
<ul>
<li><b>Elastic limits for async jobs (Beta):</b> Enqueue <code>Queueable</code> and @<code>future</code> jobs up to <a href="https://help.salesforce.com/s/articleView?id=release-notes.rn_apex_elastic_async_limit.htm&amp;release=262&amp;type=5"><i><u>twice</u></i><u> your licensed daily limit</u></a> instead of hitting a hard wall; overflow is throttled, not rejected. Track it with the new <code>DailyAsyncApexElasticExecutions</code> and <code>DailyAsyncApexProcessed</code> entries in <code>System.OrgLimits.getMap()</code>.</li>
<li><b>Apex triggers always run in system mode:</b> <a href="https://help.salesforce.com/s/articleView?id=release-notes.rn_apex_triggers_system_mode.htm&amp;release=262&amp;type=5"><u>Triggers now uniformly bypass</u></a> sharing/FLS and can&#8217;t declare sharing or access modes. Push security-sensitive logic into a handler class where you control the access mode.</li>
<li><b>Block anonymous Apex from managed packages:</b> <a href="https://help.salesforce.com/s/articleView?id=release-notes.rn_apex_block_exec_anon_ru.htm&amp;release=262&amp;type=5"><u>Managed package</u></a> session IDs can no longer authenticate anonymous Apex. Enforced Summer &#8217;27 — package authors should move to a shared <code>global</code> interface plus <code>Type.forName()</code>.</li>
<li><b>No-arg constructors required for invocable-action parameter classes:</b> Any custom Apex type used as an invocable action input must expose a<a href="https://help.salesforce.com/s/articleView?id=release-notes.rn_apex_constructor_visibility_invocable_custom_classes_v66.htm&amp;release=262&amp;type=5"><u> visible no-argument constructor</u></a> (public, or global for packaged classes). This applies to Apex at API version 67.0 and beyond.</li>
</ul>
<h2><span style="font-weight: 400">Agentforce monthly updates</span></h2>
<p><span style="font-weight: 400">Agentforce enables you to customize pre-built agents, or create and deploy enterprise-ready agents, that work across Salesforce apps, Slack, and third-party platforms. We&#8217;re adding some important developer features in the upcoming monthly releases.</span></p>
<h3><span style="font-weight: 400">Agentforce Builder and Agent Script are Generally Available (GA)</span></h3>
<p><a href="https://developer.salesforce.com/docs/ai/agentforce/guide/agent-script.html"><span style="font-weight: 400">Agent Script</span></a><span style="font-weight: 400"> — a scripting language for AI agents that gives builders precise control by blending deterministic rules with agentic reasoning — and the new </span><a href="https://help.salesforce.com/s/articleView?id=ai.agent_builder_tour.htm&amp;type=5"><span style="font-weight: 400">Agentforce Builder</span></a><span style="font-weight: 400"> are now generally available.</span></p>
<ul>
<li style="font-weight: 400"><b>The new builder is the default:</b><span style="font-weight: 400"> Starting the week of </span><b>July 13, 2026</b><span style="font-weight: 400">, the New Agent button no longer opens the legacy builder in Setup. New agents are created only in the new Agentforce Builder.</span></li>
<li style="font-weight: 400"><b>One-click upgrade:</b> <a href="https://help.salesforce.com/s/articleView?id=ai.agent_setup_create_upgrade.htm&amp;type=5"><span style="font-weight: 400">Upgrading a legacy agent</span></a><span style="font-weight: 400"> converts all subagents, actions, system messages, data, and connections to Agent Script, then optionally optimizes it for reliability.</span></li>
<li style="font-weight: 400"><b style="color: #4a4a4a">Models are configurable in script:</b><span style="font-weight: 400"> Pin the </span><a href="https://developer.salesforce.com/docs/ai/agentforce/guide/ascript-model.html"><span>model for an agent directly in Agent Script</span></a><span style="font-weight: 400"> rather than relying only on the org-wide model option.</span></li>
</ul>
<h3><span style="font-weight: 400">Agent Script is now open source</span></h3>
<p><span style="font-weight: 400">Salesforce open-sourced the </span><a href="https://github.com/salesforce/agentscript"><span style="font-weight: 400">Agent Script toolchain</span></a><span style="font-weight: 400"> under an Apache 2.0 license: parser, linter, compiler, Language Server Protocol (LSP), and editor integrations.</span></p>
<p><span style="font-weight: 400">This lets developers build custom tools. We&#8217;re excited to see the </span><a href="https://www.linkedin.com/posts/jasonlantz_the-pydantic-ai-harness-for-running-compiled-share-7464850529576357889-FJNi"><span style="font-weight: 400">community</span></a><span style="font-weight: 400"> (shout out to Jason Lantz) building new tools with the open-source Agent Script.</span></p>
<h3><span style="font-weight: 400">Skills for Agentforce development</span></h3>
<p><span style="font-weight: 400">The </span><a href="https://github.com/forcedotcom/sf-skills/tree/main/skills"><span style="font-weight: 400">sf-skills</span></a><span style="font-weight: 400"> GitHub repo covered above under “Agent Skills for Coding Agents” also includes skills that teach AI coding assistants to </span><a href="https://github.com/forcedotcom/sf-skills/tree/main/skills/developing-agentforce"><span style="font-weight: 400">build</span></a><span style="font-weight: 400">, </span><a href="https://github.com/forcedotcom/sf-skills/tree/main/skills/testing-agentforce"><span style="font-weight: 400">test</span></a><span style="font-weight: 400"> and </span><a href="https://github.com/forcedotcom/sf-skills/tree/main/skills/observing-agentforce"><span style="font-weight: 400">observe</span></a><span style="font-weight: 400"> Agentforce.</span></p>
<h3><span style="font-weight: 400">Agentforce Data Library Connect API (Beta)</span></h3>
<p><b>Agentforce Data Libraries (ADL)</b><span style="font-weight: 400"> ground an agent in your trusted content. They index Knowledge articles or uploaded files into a vector search index and expose a retriever for retrieval-augmented generation (RAG). Creating one used to be a manual step in Setup; the new </span><b>ADL Connect API (Beta)</b><span style="font-weight: 400"> makes the whole lifecycle scriptable and ready for continuous integration/continuous delivery (CI/CD). It&#8217;s the data half of Headless 360 — grounding itself becomes an API.</span></p>
<p>All endpoints sit under the base resource: <code>/services/data/v67.0/einstein/data-libraries</code></p>
<p><span style="font-weight: 400">There are five steps to provisioning a file-based library and grounding an agent on it:</span></p>
<p><strong>Step 1: Create the library</strong></p>
<p>A single <code>POST</code> — note <code>sourceType</code> — is <i>nested</i> under <code>groundingSource</code> (<code>SFDRIVE</code> for files, or <code>KNOWLEDGE</code> / <code>RETRIEVER</code>). The response returns the <code>libraryId</code> that every later step needs.</p>
<pre language="sh">sf api request rest "/services/data/v67.0/einstein/data-libraries" \
  --method POST --target-org my-org \
  --body '{
    "masterLabel": "Product Docs Library",
    "developerName": "Product_Docs_Library",
    "groundingSource": { "sourceType": "SFDRIVE" }
  }'
# → { "libraryId": "1JD...", "status": "IN_PROGRESS" }
</pre>
<p><strong>Step 2: Wait for upload readiness</strong></p>
<p>Poll <code>GET …/{libraryId}/upload-readiness</code> until it reports ready. Data 360 is provisioning the objects that hold your file metadata behind the scenes.</p>
<p><strong>Step 3: Upload the file</strong></p>
<p>Request a pre-signed S3 URL from <code>POST …/{libraryId}/file-upload-urls</code>, then <code>PUT</code> the file straight to that URL (forward the returned headers verbatim, or S3 rejects it with a 403).</p>
<p><strong>Step 4: Index it</strong></p>
<p>Trigger <code>POST …/{libraryId}/indexing</code> to chunk, embed, and build the retriever. Then poll <code>GET …/{libraryId}</code> and treat the library as ready when <code>retrieverId</code> goes non-null — <b>not</b> when the top-level <code>status</code> flips, which lags the retriever by 10–30 minutes.</p>
<p><strong>Step 5: Ground the agent</strong></p>
<p><b></b>Wire the finished library into a <code>.agent</code> file&#8217;s <code>knowledge:</code> block, then invoke <code>AnswerQuestionsWithKnowledge</code> from a subagent. The <code>rag_feature_config_id</code> is <code>"ARFPC_"</code> + the <code>libraryId</code> — <i>not</i> the raw ID.</p>
<pre language="yaml">knowledge:
    rag_feature_config_id: "ARFPC_1JDcf0000024ZZ7GAM"
    citations_enabled: True
</pre>
<h3><span style="font-weight: 400">Agentforce Mobile SDK</span></h3>
<p><span style="font-weight: 400">The </span><a href="https://github.com/salesforce/AgentforceMobileSDK-iOS"><span style="font-weight: 400">Agentforce Mobile SDK</span></a><span style="font-weight: 400"> (Software Development Kit) embeds your agents in native </span><b>iOS</b><span style="font-weight: 400">, </span><b>Android</b><span style="font-weight: 400">, and </span><b>React Native</b><span style="font-weight: 400"> apps, as a pre-built chat UI or </span><i><span style="font-weight: 400">headless</span></i><span style="font-weight: 400">, where you own the UI. Three things landed for Summer &#8217;26:</span></p>
<h4><span style="font-weight: 400">React Native: One codebase, both platforms</span></h4>
<p>One TypeScript codebase ships the agent to both iOS and Android. You work through a single object, <code>AgentforceService</code>, and the whole integration is three calls: <b>configure</b> → (optional) <b>add context</b> → <b>launch</b>. First decide which kind of agent you&#8217;re embedding, for example:</p>
<ul>
<li><b>Service Agent</b>: Customer-facing and <i>anonymous</i> (no login). Best for support in a public app.</li>
<li><b>Employee Agent</b>: Internal and <i><span>authenticated</span></i><span>. The SDK gets OAuth tokens from the Salesforce Mobile SDK.</span></li>
</ul>
<p><span>To integrate the Agentforce Mobile SDK into your React native mobile applications, follow these three steps. These steps are essential to establish a secure, authorized connection between your application and your chosen agent.</span></p>
<p><strong>Step 1: Configure the agent</strong></p>
<p><span style="font-weight: 400">First, tell the SDK which agent to connect to. The fields differ slightly by type, so here&#8217;s each one:</span></p>
<pre language="js">import { AgentforceService } from 'react-native-agentforce';
// Option A — Service Agent (anonymous, customer-facing)
await AgentforceService.configure({
    type: 'service',
    serviceApiURL: 'https://service.salesforce.com',
    organizationId: '00DWs00000Ip47F',
    esDeveloperName: 'Order_Support_Agent',        // the agent's API name
    serviceUISettings: { enableLightningType: true }  // render Custom Lightning Types as cards
});

// Option B — Employee Agent (internal, authenticated)
await AgentforceService.configure({
    type: 'employee',
    instanceUrl: 'https://your-domain.my.salesforce.com',
    organizationId: '00DWs00000Ip47F',
    userId: '005xx0000001234',
    agentId: '0XxWs000001DTDJK'                       // Bot Id from publishing the agent
});
</pre>
<p><strong>Step 2: Add session context (optional)</strong></p>
<p><span style="font-weight: 400">Pass typed variables that the agent can use to personalize its replies, for example, the identity of the user.</span></p>
<pre language="js">await AgentforceService.setAdditionalContext({
    variables: [{ name: 'userId', type: 'Text', value: '005xx0000001234' }]
});
</pre>
<p><strong>Step 3: Launch</strong></p>
<p><span style="font-weight: 400">Open the SDK&#8217;s pre-built native chat screen.</span></p>
<pre language="js">await AgentforceService.launchConversation();
</pre>
<h4><span style="font-weight: 400">Embed the Agentforce as a native iOS chat screen</span></h4>
<p><span style="font-weight: 400">The SDK gives you a ready-made chat screen to drop into your iOS app. You write a small bit of code that supplies the logged-in user&#8217;s access token, then point it at your published agent&#8217;s </span><b>Bot Id</b><span style="font-weight: 400"> (the 18-character ID you get when you publish). The SDK returns a complete native chat view that you present like any other screen. The screenshot below is our Order Support Agent answering live inside that app.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206474" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260605093436/Native-iOS-mobile-app-built-with-the-Agentforce-Mobile-SDK-rendering-a-custom-LWC.png?w=460" class="postimages" width="460" height="1000" alt="Native iOS mobile app built with the Agentforce Mobile SDK, rendering a custom LWC" />
			  </span>
			</p>
<h4><span style="font-weight: 400">Make agent replies rich, on every surface, with Custom Lightning Types</span></h4>
<p><span style="font-weight: 400">Notice the reply in the above screenshot is a clean </span><i><span style="font-weight: 400">card</span></i><span style="font-weight: 400"> — an order number, a green </span><i><span style="font-weight: 400">Shipped</span></i><span style="font-weight: 400"> badge, dates, and a total — not a raw list of field names and values. That&#8217;s </span><a href="https://developer.salesforce.com/blogs/2026/05/use-custom-lightning-types-in-agent-script-for-rich-agent-ui"><span style="font-weight: 400">Custom Lightning Types</span></a><span style="font-weight: 400">. When an agent action returns structured data, a custom Lightning type lets you attach a purpose-built UI to that data, so the agent shows a designed component instead of plain text. </span></p>
<p><span style="font-weight: 400">Note that </span><a href="https://developer.salesforce.com/blogs/2026/05/use-custom-lightning-types-in-agent-script-for-rich-agent-ui"><span style="font-weight: 400">Custom Lightning Types</span></a><span style="font-weight: 400"> is a cross-surface feature, not mobile specifically. You define it once against the action&#8217;s output, and it renders idiomatically on every surface where the agent runs — a Lightning web component on desktop and web, and the matching native UI in a mobile app.</span></p>
<h3><span style="font-weight: 400">Multi-Agent Orchestration (Beta)</span></h3>
<p><span style="font-weight: 400">Real workflows rarely fit a standalone agent. With </span><a href="https://help.salesforce.com/s/articleView?id=ai.agent_multi_orch.htm&amp;type=5"><b>Multi-Agent Orchestration</b></a><span style="font-weight: 400">, an orchestrator agent connects to other specialized agents in your org and presents one unified point of contact, so users handle cross-domain tasks without juggling separate sessions.</span></p>
<p>In Agentforce Builder, open a draft agent as your orchestrator, then from the Explorer panel click <b>+ → Connect Agent as Subagent (Beta)</b> and give each connected subagent a description that governs its behavior. With Agent Router, you add each subagent under Actions Available for Reasoning and reference it with <code>@</code>.</p>
<h3>Observability: Custom Scorers (Beta)</h3>
<p><a href="https://help.salesforce.com/s/articleView?id=release-notes.rn_einstein_analytics_new_experience.htm&amp;release=262&amp;type=5"><b>Refined Agent Analytics</b></a> unifies Service Agent and Employee Agent analytics into one view with 40+ metrics. On top of that, <a href="https://help.salesforce.com/s/articleView?id=ai.generative_ai_optimize_scorers.htm&amp;type=5"><b>Custom Scorers (Beta)</b></a> lets you grade sessions against your own key performance indicators (KPIs) — Sentiment, Tone of Voice, Product Interest, Escalation Trigger, Politeness — alongside Salesforce&#8217;s standard quality metrics.</p>
<p>For developers, the workflow matters most: build scorers with <a href="https://help.salesforce.com/s/articleView?id=ai.agent_studio_testing_center_setup_tests.htm&amp;type=5"><b><u>Next Gen Testing</u></b></a> in Agentforce Studio or deploy them via the <b>Metadata API (using </b><b><code>aiAgentScorerDefinitions</code></b><b>),</b> so they live in source control, then activate them from the <b>Scorer Hub</b> to run on live sessions. Custom Scorers require the Agentforce Scorer Beta permission set.</p>
<h2><span style="font-weight: 400">Data 360 monthly updates</span></h2>
<p><span style="font-weight: 400">Like Agentforce, Data 360 features are released as often as monthly, so check the </span><a href="https://help.salesforce.com/s/articleView?id=release-notes.rn_c360_truth.htm&amp;release=262&amp;type=5"><span style="font-weight: 400">monthly section</span></a><span style="font-weight: 400"> of the release notes often. Here are the developer-relevant highlights currently slated for the Summer ’26 timeframe. </span></p>
<h3><span style="font-weight: 400">Query Data 360 more precisely</span></h3>
<p>Use the new <b>SET OPTIONS</b> clause (see <a href="https://developer.salesforce.com/docs/atlas.en-us.soql_sosl.meta/soql_sosl/sforce_api_calls_soql_select_set_options.htm"><u>docs</u></a>) in SOQL queries to specify Data 360 dataspaces and control how <code>NULL</code> and empty string values are handled. When querying Data 360 data lake objects, add the clause at the end of your SOQL query to get more precise results.</p>
<pre language="sql">SELECT Id, EmailOptIn__c
FROM ContactDLO__dlm
WHERE EmailOptIn__c = ''
SET OPTIONS (dataspace = 'default', honorEmptyStrings = true)
</pre>
<p>The clause goes at the very end. Dataspace is required for DLO queries — omit it and the query returns zero records. <code>honorEmptyStrings = true</code> makes Data 360 treat <code>NULL</code> and <code>''</code> as distinct values; the default, false, collapses them the way Salesforce Platform objects do.</p>
<h3><span style="font-weight: 400">Extend Data 360 with custom code using Code Extension</span></h3>
<p><a href="https://developer.salesforce.com/docs/data/data-cloud-code-ext/guide/use-custom-code.html"><b>Code Extension</b></a><span style="font-weight: 400"> is a Data 360 feature that allows you to deploy custom Python scripts and functions that run on isolated containers on the platform. Currently, code extensions support deploying functions for complex batch data transformations, such as string manipulation, custom computations, or data cleansing, and deploying scripts that implement custom chunking logic on search index creation. In the future, Code Extension will support other Data 360 capabilities and programming languages.</span></p>
<p><span style="font-weight: 400">You write and debug Python scripts locally using the project scaffold provided by the </span><a href="https://pypi.org/project/salesforce-data-customcode/"><span style="font-weight: 400">Data Custom Code Python SDK</span></a><span style="font-weight: 400"> and the </span><a href="https://developer.salesforce.com/docs/data/data-cloud-code-ext/guide/set-up-sdk.html"><span style="font-weight: 400">Salesforce CLI Code Extension plugin</span></a><span style="font-weight: 400">, validating them with the Salesforce CLI against a sandbox. Then, you deploy them to the sandbox and run them. There you can monitor them through the new code extensions log DLO. While developers author the code, users with the Data Cloud Architect permission set run and monitor it. We strongly recommend using the new </span><a href="https://github.com/forcedotcom/sf-skills/tree/main/skills/developing-datacloud-code-extension"><span style="font-weight: 400">code extension skill in afv-library</span></a><span style="font-weight: 400"> to automate building, debugging, and deploying, and the new Data 360 MCP Server to run and monitor them.</span></p>
<p><a href="https://www.youtube.com/watch?v=96PC1KSnmfk"><span style="font-weight: 400">Watch a demo</span></a><span style="font-weight: 400"> of how to work with code extensions.</span></p>
<h3><span style="font-weight: 400">Deploy code extension components using data kits</span></h3>
<p><span style="font-weight: 400">Use a DevOps data kit to move </span><a href="https://help.salesforce.com/s/articleView?id=release-notes.rn_cdp_2026_summer_code_extension_data_kit.htm&amp;release=262&amp;type=5"><span style="font-weight: 400">code extensions</span></a><span style="font-weight: 400"> or the data transforms built from them, from sandbox to production. When you add such a data transform to your data kit, its associated code extension is automatically included. This enables headless DevOps workflows; your CI/CD pipeline can promote Data 360 logic the same way it promotes Apex and LWC metadata.</span></p>
<h2><span style="font-weight: 400">Agentforce 360 Platform development tool updates</span></h2>
<p><span style="font-weight: 400">The Summer ’26 pro-code toolchain picked up a few notable upgrades:</span></p>
<ul>
<li style="font-weight: 400"><a href="https://marketplace.visualstudio.com/items?itemName=salesforce.salesforcedx-agentforce-vibes-2"><b>Agenforce Vibes 2.0 (Developer Preview):</b></a> <span style="font-weight: 400">The first public pre-release of Agentforce Vibes 2.0 is here. This agentic development environment does far more than generate code. It reasons through complex tasks, builds structured implementation plans, and asks clarifying questions before acting. You stay in control through approvals, permissions, and native VS Code diff reviews. This release gives you a redesigned multi-tab chat experience and Plan Mode for breaking down complex work. You also get deeper Model Context Protocol (MCP) integration, built-in Skills and Rules, live Lightning Web Component previews, and the latest Claude and GPT models in one unified picker.</span></li>
<li style="font-weight: 400"><a href="https://developer.salesforce.com/docs/platform/webconsole/guide/get-started"><b>Web Console (Beta)</b><span style="font-weight: 400">:</span></a> <span style="font-weight: 400">This is a full IDE that runs inside your org, right in the browser. Write, debug, and deploy Apex, LWC, and other metadata without leaving Salesforce. You can edit and save classes, run anonymous Apex, and set trace flags and debug log levels in one place. It differs from the </span><a href="https://developer.salesforce.com/docs/platform/code-builder/overview"><span style="font-weight: 400">Agentforce Vibes (AFV) IDE</span></a><span style="font-weight: 400"> in three ways: it&#8217;s available on every org, it loads faster, and it runs entirely in the browser. The trade-off is that it supports only Salesforce-provided extensions, not custom ones. Reach for it for quick, in-org edits, and use the AFV IDE when you need a richer, extensible environment. Enable it in Setup under Development → Web Console (Beta).</span></li>
<li style="font-weight: 400"><a href="https://developer.salesforce.com/docs/platform/lwc/guide/get-started-test-components.html"><b>Live Preview VS Code Extension</b></a><span style="font-weight: 400">: </span><span style="font-weight: 400">This is the renamed Local Dev. See a single Lightning web component update in real time as you edit it, either in the browser or inside VS Code and the Agentforce Web IDE, using the Live Preview extension.</span></li>
<li><a href="https://marketplace.visualstudio.com/items?itemName=salesforce.salesforcedx-metadata-visualizer-vscode"><b>Metadata Visualizer</b></a><span style="font-weight: 400"> vs Code Extension: </span><span style="font-weight: 400">This extension turns raw metadata files into interactive diagrams, so you can see structure and relationships at a glance instead of reading XML. It updates in real time as you edit, and plugs into Agentforce Vibes to visualize AI-generated metadata. This extension is actively developed and currently supports visualizers for objects, permission sets and flexipages (Beta). Additional metadata visualizers are scheduled for delivery.</span></li>
</ul>
<h2><span style="font-weight: 400">Conclusion</span></h2>
<p><span style="font-weight: 400">Summer ’26 is the release that makes Salesforce truly headless. Every major capability — data, automation, grounding, and agents — is now reachable from a CLI, an API, your IDE, or an autonomous AI agent, with security enforced by default. For you, that means less glue code, safer defaults, and quicker feedback as you code — whether you build with Apex, LWC, or Agent Script.</span></p>
<p><span style="font-weight: 400">The best way to get ready is to spin up a sandbox, scratch org or a developer edition org and try these features before they reach production. Have questions, or want to share what you&#8217;re building? 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">, or connect with us on the </span><a href="https://developer.salesforce.com/"><span style="font-weight: 400">Salesforce Developers</span></a><span style="font-weight: 400"> channels.</span></p>
<h2><span style="font-weight: 400">More Summer ’26 learning resources</span></h2>
<ul>
<li style="font-weight: 400"><span style="font-weight: 400">Read the </span><a href="https://help.salesforce.com/s/articleView?id=release-notes.salesforce_release_notes.htm&amp;release=262&amp;type=5"><span style="font-weight: 400">official release notes</span></a></li>
<li style="font-weight: 400"><span style="font-weight: 400">Join 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 group</span></a><span style="font-weight: 400"> to connect with the global developer community</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Join the </span><a href="https://trailhead.salesforce.com/trailblazer-community/groups/0F9300000001okuCAA?tab=discussion&amp;sort=LAST_MODIFIED_DATE_DESC"><span style="font-weight: 400">Release Readiness Trailblazers Community group</span></a><span style="font-weight: 400"> to get early access to release information and discuss changes with other developers</span></li>
</ul>
<h2><span style="font-weight: 400">About the author</span></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>
</div>
<p>&nbsp;</p>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/06/the-salesforce-developers-guide-to-the-summer-26-release">The Salesforce Developer’s Guide to the Summer ’26 Release</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/the-salesforce-developers-guide-to-the-summer-26-release/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
	<post-id xmlns="com-wordpress:feed-additions:1">206465</post-id><media:thumbnail url="https://d259t2jj6zp7qm.cloudfront.net/images/20260605092828/Dev-BlogImage-1000x563-1.png?w=1000" />
<media:content url="https://d259t2jj6zp7qm.cloudfront.net/images/20260605092828/Dev-BlogImage-1000x563-1.png?w=1000" medium="image" />
	</item>
		<item>
		<title>Security Anti-Patterns in Lightning Web Components</title>
		<link>https://developer.salesforce.com/blogs/2026/06/security-anti-patterns-in-lightning-web-components</link>
		<comments>https://developer.salesforce.com/blogs/2026/06/security-anti-patterns-in-lightning-web-components#respond</comments>
		<pubDate>Fri, 05 Jun 2026 16:00:37 +0000</pubDate>
		<dc:creator><![CDATA[Tim Dionne]]></dc:creator>
				<category><![CDATA[Architecture]]></category>
		<category><![CDATA[Lightning Web Components]]></category>
		<category><![CDATA[Trust, Security, and Accessibility]]></category>
		<category><![CDATA[Content Security Policy]]></category>
		<category><![CDATA[document.cookie]]></category>
		<category><![CDATA[lightning web security]]></category>
		<category><![CDATA[localStorage]]></category>
		<category><![CDATA[LWS Distortion Viewer]]></category>
		<category><![CDATA[Proxy objects]]></category>
		<category><![CDATA[Shadow DOM]]></category>

		<guid isPermaLink="false">https://developer.salesforce.com/blogs/?p=206449</guid>
		<description><![CDATA[<p>Learn how the platform&rsquo;s three independent security layers operate, how Lightning Web Security (LWS) distorts common APIs, and how to avoid the cross-namespace mistakes that lead to silent data failures.</p>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/06/security-anti-patterns-in-lightning-web-components">Security Anti-Patterns in Lightning Web Components</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 write a Lightning web component. It works in your scratch org. You deploy it. And then it silently returns wrong data in production — no errors, no warnings, just the wrong result. If this sounds familiar, you&#8217;ve probably run into </span><a href="https://developer.salesforce.com/docs/platform/lightning-components-security/guide/lws-intro.html"><span style="font-weight: 400">Lightning Web Security</span></a><span style="font-weight: 400"> (LWS). But the real problem isn&#8217;t LWS itself; it&#8217;s misunderstanding what LWS actually does.</span></p>
<p><span style="font-weight: 400">This is the first post in a series on common anti-patterns in Lightning Web Components (LWC). In this post, you&#8217;ll learn how the three security layers in the Lightning platform work, which APIs are namespaced versus blocked, and how to avoid the most common mistakes that lead to silent failures in production. The patterns covered here apply to any custom LWC running on the Salesforce Platform.</span></p>
<p><b>Note: LWS distortions can change between Salesforce releases as the platform evolves. The </b><a href="https://developer.salesforce.com/tools/lws-distortion-viewer"><b>LWS Distortion Viewer</b></a><b> is the live source of truth for the exact behavior of every distorted API. If a pattern in this post no longer matches what you observe in your org, check the Distortion Viewer first before filing a bug.</b></p>
<h2><span style="font-weight: 400">Three security layers instead of one</span></h2>
<p><span style="font-weight: 400">Your LWC code runs inside three independent security layers. Knowing which layer does what saves you hours of debugging.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206452" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260604221827/Diagram-that-illustrates-the-three-independent-security-layers-in-which-your-Lightning-web-component-runs-e1780636721444.png?w=1000" class="postimages" width="1000" height="622" alt="Diagram that illustrates the three independent security layers in which your Lightning web component runs." />
			  </span>
			</p>
<p><span style="font-weight: 400">Lightning Web Security (LWS) is the namespace isolation layer. It places your component&#8217;s JavaScript in a sandboxed environment and applies </span><i><span style="font-weight: 400">distortions</span></i><span style="font-weight: 400"> to browser APIs. Distortions don&#8217;t simply block APIs — they namespace storage, sanitize HTML on shared DOM elements, sandbox code evaluation, and block only a small number of APIs that could escape the sandbox. You can see every distortion and its exact behavior in the </span><a href="https://developer.salesforce.com/tools/lws-distortion-viewer"><span style="font-weight: 400">LWS Distortion Viewer</span></a><span style="font-weight: 400">.</span></p>
<p><span style="font-weight: 400">The LWC framework enforces </span><a href="https://developer.salesforce.com/docs/platform/lwc/guide/create-dom.html"><span style="font-weight: 400">shadow DOM</span></a><span style="font-weight: 400"> scoping, prevents access to legacy Aura globals like </span><span style="font-weight: 400">$A</span><span style="font-weight: 400">, and manages the component lifecycle.</span></p>
<p><a href="https://developer.salesforce.com/docs/platform/lightning-components-security/guide/content-security-policy-intro.html"><span style="font-weight: 400">Content Security Polic</span></a><span style="font-weight: 400">y (CSP) and platform-level restrictions block inline scripts, external CDN loading, and certain URL schemes. These operate independently of LWS.</span></p>
<p><span style="font-weight: 400">When something doesn&#8217;t work, your first question should be: which layer is responsible? Getting this wrong leads to workarounds that solve the wrong problem.</span></p>
<h2><span style="font-weight: 400">Five key anti-patterns</span></h2>
<p><span style="font-weight: 400">The patterns below fall into five categories. The first three describe how LWS distorts APIs — namespacing, sanitizing/sandboxing, and outright blocking. The fourth covers what happens at the boundary between namespaces. The fifth covers patterns that look like LWS issues, but actually come from the LWC framework or CSP. Identifying the right category is usually the fastest path to the right fix.</span></p>
<h3><span style="font-weight: 400">1. Namespaced APIs</span></h3>
<p><span style="font-weight: 400">LWS does not block these APIs — it applies a namespace isolation layer. The APIs function as expected within your own component set, but they remain blind to data established by the platform or by components residing in other namespaces.</span></p>
<h4><span>Assuming </span><code>localStorage</code><span> and </span><code>sessionStorage</code><span> are global</span></h4>
<p><span>LWS does not block </span><code>localStorage</code><span> (see </span><a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage"><u>docs</u></a><span>) or </span><code>sessionStorage</code><span> (see </span><a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage"><u>docs</u></a><span>). It namespaces them. Every key is transparently prefixed with </span><code>LSKey[namespace]</code><span>, so each namespace gets its own isolated storage. When you call </span><code>localStorage.setItem('myKey', 'value')</code><span> in namespace </span><code>c</code><span>, Salesforce actually stores </span><code>LSKey[c]myKey</code><span>. Your component sees only its own keys.</span></p>
<p><span>This means </span><code>localStorage</code><span> works — but only within your namespace. If you expect to read keys set by the platform or by a component in a different namespace, you&#8217;ll get back </span><code>null</code><span> with no error.</span></p>
<pre language="javascript">// Returns null — the key was set outside this namespace's sandbox
connectedCallback() {
    const token = window.localStorage.getItem('sessionToken');
}
</pre>
<p><span>Use </span><code>localStorage</code><span> for same-namespace persistence like caching user preferences. For cross-namespace data or system-level state, use custom settings, Apex calls, or the </span><a href="https://developer.salesforce.com/docs/platform/lwc/guide/use-message-channel.html"><u>Lightning Message Service</u></a><span>.</span></p>
<h4><span>Assuming </span><code>document.cookie</code><span> is global</span></h4>
<p><span>Like storage, </span><code>document.cookie</code><span> (see </span><a href="https://developer.mozilla.org/en-US/docs/Web/API/Document/cookie"><u>docs</u></a><span>) is namespaced by LWS, not blocked. The getter returns only cookies belonging to your sandbox, and the setter adds a sandbox prefix to new cookie keys. Platform cookies and cookies from other namespaces are invisible.</span></p>
<p><span>Don&#8217;t rely on </span><code>document.cookie</code><span> to read platform or cross-namespace cookies. Use server-side Apex to access session or authentication state instead.</span></p>
<h3><span style="font-weight: 400">2. Sanitized and sandboxed APIs</span></h3>
<p><span>LWS does not block APIs like </span><code>innerHTML</code><span> (see </span><a href="https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML"><u>docs</u></a><span>), </span><code>eval()</code><span> (see </span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval"><u>docs</u></a><span>), or </span><code>Function()</code><span> (see </span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/Function"><u>docs</u></a><span>). Instead, it applies specific distortions based on the execution context. The key to avoiding silent failures is understanding exactly how the platform modifies these behaviors rather than attempting to bypass them entirely.</span></p>
<h4><strong>DOM mutation on shared elements</strong></h4>
<p><span>LWS runs in the main </span><code>window</code><span>, where </span><code>&lt;html&gt;</code><span>, </span><code>&lt;head&gt;</code><span>, and </span><code>&lt;body&gt;</code><span> are shared across all components. LWS protects these shared elements by sanitizing HTML strings and restricting which child elements you can add. For elements inside your component&#8217;s own shadow DOM, these APIs work normally.</span></p>
<p><span>Here&#8217;s the key distinction: </span><code>innerHTML</code><span>, </span><code>outerHTML</code><span>, </span><code>insertAdjacentHTML</code><span>, and related APIs are not blocked. They&#8217;re sanitized when targeting shared elements, and unrestricted on elements your component owns.</span></p>
<pre language="javascript">// innerHTML works on component-owned elements — but this is still an XSS risk
renderedCallback() {
    const container = this.template.querySelector('.container');
    container.innerHTML = `<span>${this.userProvidedValue}</span>`;
}
</pre>
<p><span>LWS will sanitize this if </span><code>container</code><span> is a shared element, but the real issue is the </span><a href="https://owasp.org/www-community/attacks/xss/"><u>Cross Site Scripting</u></a><span> (XSS) risk. Relying on LWS sanitization as a security mechanism is fragile — it protects shared DOM elements, not your component&#8217;s shadow DOM.</span></p>
<p><span>Use LWC&#8217;s declarative template directives (</span><code>lwc:if</code><span>, </span><code>for:each</code><span>, template expressions) for dynamic content. For trusted rich text from a CMS, use </span><code>lightning-formatted-rich-text</code><span> (see </span><a href="https://developer.salesforce.com/docs/platform/lightning-component-reference/guide/lightning-formatted-rich-text.html?type=Example"><u>docs</u></a><span>).</span></p>
<h4><strong>Code evaluation is sandboxed, not blocked</strong></h4>
<p><span>This one surprises many developers: </span><code>eval()</code><span> (see </span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval"><u>docs</u></a><span>) and the </span><code>Function()</code><span> (see </span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function"><u>docs</u></a><span>) constructor are not blocked by LWS. They run inside the sandbox — LWS ensures the evaluated code executes in the same sandboxed context as your component. Passing a string to </span><code>setTimeout()</code><span> (see </span><a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/setTimeout"><u>docs</u></a><span>) or </span><code>setInterval()</code><span> (see </span><a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/setInterval"><u>docs</u></a><span>) works the same way.</span></p>
<p><code>eval('1 + 1')</code><span> returns </span><code>2</code><span> in a Salesforce org. But </span><code>eval()</code><span> is still a bad practice. It makes code harder to analyze, blocks compiler optimizations, and creates injection vectors if the evaluated string includes user input.</span></p>
<pre language="javascript">// eval works in the sandbox — but don't do this
processRule(ruleString) {
    return eval(ruleString);
}</pre>
<pre><span style="font-weight: 400">Replace dynamic code evaluation with explicit logic, a lookup table, or a strategy pattern. If you need to evaluate user-defined expressions, implement a safe parser scoped to the expression language you support.</span></pre>
<h3><span style="font-weight: 400">3. APIs that are actually blocked</span></h3>
<p>&nbsp;</p>
<h4><strong>APIs that throw at runtime</strong></h4>
<p><span style="font-weight: 400">A small number of APIs are genuinely blocked by LWS because they provide direct paths to escape the sandbox. Calling any of these throws a runtime exception.</span></p>
<table>
<tbody>
<tr>
<td><b>API</b></td>
<td><b>Why it&#8217;s blocked</b></td>
</tr>
<tr>
<td><code>document.write()</code><span> / </span><code>writeln()</code></td>
<td><span style="font-weight: 400">Can write arbitrary JavaScript that bypasses the sandbox</span></td>
</tr>
<tr>
<td><code>Worker()</code><span> / </span><code>SharedWorker()</code></td>
<td><span style="font-weight: 400">Script execution outside the sandbox</span></td>
</tr>
<tr>
<td><span style="font-weight: 400"><code>ServiceWorkerContainer</code> </span><span style="font-weight: 400">(all methods)</span></td>
<td><span style="font-weight: 400">Can intercept responses to run unsandboxed code</span></td>
</tr>
<tr>
<td><code>window.find()</code></td>
<td><span style="font-weight: 400">Cross-namespace content access</span></td>
</tr>
<tr>
<td><code>XSLTProcessor.transformToDocument()</code><span> / </span><code>transformToFragment()</code></td>
<td><span style="font-weight: 400">XSLT can generate HTML that bypasses distortions</span></td>
</tr>
<tr>
<td><code>Document.parseHTMLUnsafe()</code><span> / </span><code>Element.setHTMLUnsafe()</code></td>
<td><span style="font-weight: 400">Unsanitized HTML injection</span></td>
</tr>
</tbody>
</table>
<p><span>Use LWC&#8217;s declarative template system for HTML rendering and </span><code>lightning/platformResourceLoader</code><span> (see </span><a href="https://developer.salesforce.com/docs/platform/lightning-component-reference/guide/lightning-platform-resource-loader.html?type=Develop"><u>docs</u></a><span>) for script loading. There is no supported workaround for Workers inside the LWS sandbox. If your use case requires them, consider an iframe-based isolation strategy.</span></p>
<h4><strong>Network requests are not broadly restricted</strong></h4>
<p><code>fetch()</code><span> (see </span><a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/fetch"><u>docs</u></a><span>), </span><code>XMLHttpRequest</code><span> (see </span><a href="https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest"><u>docs</u></a><span>), </span><code>navigator.sendBeacon()</code><span> (see </span><a href="https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon"><u>docs</u></a><span>), and </span><code>fetchLater()</code><span> (see </span><a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/fetchLater"><u>docs</u></a><span>) work normally under LWS. The only distortion blocks requests to URLs containing </span><code>/aura</code><span> or </span><code>/webruntime</code><span> — these are internal framework endpoints that your components should not access directly. All other network requests work normally, subject to standard </span><a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CORS"><u>Cross-Origin Resource Sharing</u></a><span> (CORS) rules and your org&#8217;s CSP configuration.</span></p>
<pre language="javascript">// Blocked — hitting internal framework endpoints
fetch('/aura?aura.ApexAction.execute=1', { method: 'POST', body: payload });
</pre>
<p><span>Use Apex </span><code>@AuraEnabled</code><span> methods via the wire service or imperative calls instead.</span></p>
<h3><span style="font-weight: 400">4. Cross-namespace boundaries</span></h3>
<p><span style="font-weight: 400">LWS employs a proxy membrane to enforce strict isolation across namespace boundaries. Consequently, objects traversing this boundary exhibit different behaviors compared to those residing within a single namespace. For further details on the performance impacts of this membrane proxying, consult the </span><a href="https://developer.salesforce.com/docs/platform/lightning-components-security/guide/lws-performance.html"><span style="font-weight: 400">LWS Performance documentation</span></a><span style="font-weight: 400">.</span></p>
<h4><strong>Cross-namespace object isolation</strong></h4>
<p><span>When your component receives an object from a different namespace — through an </span><code>@api</code><span> property, event </span><code>detail</code><span>, or a shared service — and you mutate that object in place, the change is not propagated back outside the sandbox. No error is thrown. The originating component just keeps seeing the original value. This is one of the hardest LWS traits to diagnose because the only symptom is stale data.</span></p>
<p><span style="font-weight: 400">For more on the performance implications of cross-namespace membrane proxying, see the </span><a href="https://developer.salesforce.com/docs/platform/lightning-components-security/guide/lws-performance.html"><span style="font-weight: 400">LWS Performance documentation</span></a><span style="font-weight: 400">.</span></p>
<pre language="javascript">// childComponent.js — namespace 'c'
@api record;

handleEdit() {
    // Mutation is silently absorbed by the LWS proxy — parent never sees it
    this.record.Name = 'Updated Name';
}
</pre>
<p><span>Clone the received object before modifying it, then communicate changes back with a </span><code>CustomEvent</code><span>.</span></p>
<pre language="javascript">handleEdit() {
    const updated = { ...this.record, Name: 'Updated Name' };
    this.dispatchEvent(new CustomEvent('recordchange', {
        detail: { ...updated }
    }));
}
</pre>
<h4><strong>Passing objects across namespace boundaries</strong></h4>
<p><span>Objects passed between components in different namespaces are wrapped in LWS </span><code>Proxy</code><span> objects (see </span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy"><u>docs</u></a><span>). Proxy unwrapping can fail silently, giving you </span><code>null</code><span> or incorrect values on the receiving end. Browser extensions that listen to custom events also receive </span><code>null</code><span> for </span><code>detail</code><span> when it contains a proxied object.</span></p>
<pre language="javascript">// Namespace 'foo' — dispatching
this.dispatchEvent(new CustomEvent('selected', {
    detail: { record: this.selectedRecord }  // may arrive as null in another namespace
}));
</pre>
<p><span style="font-weight: 400">Ensuring your object can be </span><a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/structuredClone"><span style="font-weight: 400">structured cloned</span></a><span style="font-weight: 400"> is the key. Or, serialize the payload to JSON before dispatching and parse it on receipt. A plain string crosses the namespace boundary without proxy wrapping.</span></p>
<pre language="javascript">// Namespace 'foo'
this.dispatchEvent(new CustomEvent('selected', {
    detail: JSON.stringify(this.selectedRecord)
}));

// Namespace 'bar'
handleSelected(event) {
    const record = JSON.parse(event.detail);
}
</pre>
<h3><span style="font-weight: 400">5. Misattributed restrictions</span></h3>
<p><span style="font-weight: 400">These patterns fail at runtime, but the cause is not LWS. Blaming LWS leads to the wrong fix.</span></p>
<h4><strong>Referencing legacy Salesforce globals</strong></h4>
<p><span>The </span><b><span>LWC framework</span></b><span> (not LWS) blocks access to </span><code>$A</code><span>, </span><code>Aura</code><span>, </span><code>Sfdc</code><span>, and </span><code>sforce</code><span> in Lightning web components. These belong to the Aura framework or are deprecated platform globals. Any reference to them in LWC code fails at runtime.</span></p>
<p><span>This is the most common mistake when migrating Aura components to LWC.</span></p>
<pre language="javascript">// Pattern carried over from Aura — fails at runtime
connectedCallback() {
    const userId = $A.get('$SObjectType.CurrentUser.Id');
}
</pre>
<p><span style="font-weight: 400">Use the LWC platform equivalents instead.</span></p>
<pre language="javascript">import userId from '@salesforce/user/Id';
import { getRecord } from 'lightning/uiRecordApi';
import NAME_FIELD from '@salesforce/schema/User.Name';
</pre>
<p><span style="font-weight: 400">For a complete mapping, see the </span><a href="https://developer.salesforce.com/docs/component-library/documentation/en/lwc/lwc.migrate_intro"><span style="font-weight: 400">Migrate Aura Components to LWC</span></a><span style="font-weight: 400"> documentation.</span></p>
<h4><strong>Loading third-party scripts from external CDNs</strong></h4>
<p><span>Loading a JavaScript library with a </span><code>&lt;script&gt;</code><span> tag pointing to an external CDN URL violates Salesforce&#8217;s CSP. The platform blocks it before LWS even comes into play. Beyond the CSP violation, external CDN loading introduces a versioning risk: the CDN may update the library at any time, silently introducing sandbox incompatibilities.</span></p>
<pre language="javascript">connectedCallback() {
    const script = document.createElement('script');
    script.src = 'https://cdn.example.com/somelib/v3.min.js'; // CSP violation
    document.head.appendChild(script);
}
</pre>
<p><span>Download the library, upload it as a Static Resource, and load it with </span><code>lightning/platformResourceLoader</code><span>.</span></p>
<pre language="javascript">import { loadScript } from 'lightning/platformResourceLoader';
import SOMELIB from '@salesforce/resourceUrl/somelib';

async renderedCallback() {
    if (this._libLoaded) return;
    this._libLoaded = true;
    await loadScript(this, SOMELIB);
    this.initializeLib();
}
</pre>
<p><span>Test the static-resource version in a sandbox before promoting to production. If the library requires APIs that LWS blocks (like Workers or </span><code>document.write</code><span>), it won&#8217;t work inside Salesforce regardless of how you load it.</span></p>
<h2><span style="font-weight: 400">Conclusion</span></h2>
<p><span style="font-weight: 400">These anti-patterns share a root cause: misunderstanding which security layer does what. LWS doesn&#8217;t block most things — it namespaces, sanitizes, and sandboxes. The APIs it actually blocks are a small, specific set. Meanwhile, the LWC framework and CSP enforce their own independent restrictions.</span></p>
<p><span style="font-weight: 400">The mental model to remember: LWS is a namespace isolation layer, not a blanket firewall. When an API seems &#8220;blocked,&#8221; it&#8217;s usually namespaced — you&#8217;re looking at an empty namespace, not a wall. When in doubt, check the </span><a href="https://developer.salesforce.com/tools/lws-distortion-viewer"><span style="font-weight: 400">LWS Distortion Viewer</span></a><span style="font-weight: 400"> for the exact behavior.</span></p>
<p><span style="font-weight: 400">In the next post in this series, we&#8217;ll cover data and communication: the Wire Service, </span><span style="font-weight: 400"><code>@api</code></span><span style="font-weight: 400"> properties, and event handling. These are the three surfaces where most component-to-component bugs originate, and where a few common misunderstandings cause the most trouble.</span></p>
<h2><span style="font-weight: 400">Resources</span></h2>
<ul>
<li style="font-weight: 400"><a href="https://developer.salesforce.com/tools/lws-distortion-viewer"><span style="font-weight: 400">LWS Distortion Viewer</span></a><span style="font-weight: 400"> — the definitive reference for every LWS distortion and its behavior</span></li>
<li style="font-weight: 400"><a href="https://developer.salesforce.com/docs/platform/lightning-components-security/guide/lws-architecture.html"><span style="font-weight: 400">Lightning Web Security architecture</span></a></li>
<li style="font-weight: 400"><a href="https://developer.salesforce.com/docs/platform/lightning-components-security/guide/lws-performance.html"><span style="font-weight: 400">LWS Performance</span></a><span style="font-weight: 400"> — performance implications of cross-namespace membrane proxying</span></li>
<li style="font-weight: 400"><a href="https://developer.salesforce.com/docs/platform/lwc/guide/security-lwsec-intro"><span style="font-weight: 400">Lightning Web Security Developer Guide</span></a></li>
<li style="font-weight: 400"><a href="https://developer.salesforce.com/docs/component-library/documentation/en/lwc/lwc.migrate_intro"><span style="font-weight: 400">Migrate Aura Components to LWC</span></a></li>
</ul>
<h2><span style="font-weight: 400">About the author</span></h2>
<p><b>Tim Dionne</b><span style="font-weight: 400"> is a PMTS on the Customer Centric Engineering team. He’s worked on many UI features of Salesforce over the years, starting with VisualForce, Aura Components, and Lightning Web Components with an emphasis on Lightning Web Security and Lightning Data Service.</span></p>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/06/security-anti-patterns-in-lightning-web-components">Security Anti-Patterns in Lightning Web 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/06/security-anti-patterns-in-lightning-web-components/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
	<post-id xmlns="com-wordpress:feed-additions:1">206449</post-id><media:thumbnail url="https://d259t2jj6zp7qm.cloudfront.net/images/20260608003817/6_5-Security-Anti-Patterns-in-Lightning-Web-Components-e1780904306103.png?w=1000" />
<media:content url="https://d259t2jj6zp7qm.cloudfront.net/images/20260608003817/6_5-Security-Anti-Patterns-in-Lightning-Web-Components-e1780904306103.png?w=1000" medium="image" />
	</item>
		<item>
		<title>Simplify Your SOQL Queries Using SOQL FORMULA() in WHERE</title>
		<link>https://developer.salesforce.com/blogs/2026/06/simplify-your-soql-queries-using-soql-formula-in-where</link>
		<comments>https://developer.salesforce.com/blogs/2026/06/simplify-your-soql-queries-using-soql-formula-in-where#respond</comments>
		<pubDate>Thu, 04 Jun 2026 16:35:02 +0000</pubDate>
		<dc:creator><![CDATA[Dikshita Patel]]></dc:creator>
				<category><![CDATA[Apex]]></category>
		<category><![CDATA[APIs and Integrations]]></category>
		<category><![CDATA[Architecture]]></category>
		<category><![CDATA[Salesforce Releases]]></category>
		<category><![CDATA[Data Modeling]]></category>
		<category><![CDATA[Database Performance]]></category>
		<category><![CDATA[ISV App Development]]></category>
		<category><![CDATA[Query Optimization]]></category>
		<category><![CDATA[SOQL]]></category>
		<category><![CDATA[Summer 26]]></category>
		<category><![CDATA[WHERE Clause]]></category>

		<guid isPermaLink="false">https://developer.salesforce.com/blogs/?p=206439</guid>
		<description><![CDATA[<p>Turn formula style expressions into first class WHERE filters, so other integrations and Apex can query computed business rules without extra formula fields or in-memory filtering.</p>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/06/simplify-your-soql-queries-using-soql-formula-in-where">Simplify Your SOQL Queries Using SOQL FORMULA() in WHERE</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">With the Summer ’26 release, Salesforce is piloting <code>FORMULA()</code> in <code>SOQL WHERE</code></span><span style="font-weight: 400"> clauses, enabling you to filter rows using compiled formula expressions instead of adding one-off formula fields or post-processing in Apex. This helps developers reduce schema clutter, simplify queries, and write less “query everything, filter later” code, particularly for ISVs working across customer schemas. </span></p>
<p><span style="font-weight: 400">In this post, you’ll learn the basic syntax and mental model, typical use cases (including e‑commerce examples), and pilot gates and current constraints, so you can try it safely in a sandbox.</span></p>
<p><span style="font-weight: 400">The pilot is open to any Salesforce customer — contact your Salesforce Account Executive or Customer Success Manager to request access.</span></p>
<h2><span style="font-weight: 400">Why use FORMULA() in SOQL WHERE?</span></h2>
<p><span style="font-weight: 400">When a filter depends on something that you calculate from fields, such as margin, revenue per head, days idle, or days late to ship, the usual choices are: add a formula field, maintain a duplicate rule in Apex, or query too many rows and throw away what you do not need. </span></p>
<p><span style="font-weight: 400"><code>FORMULA('…')</code> in <code>WHERE</code> is for the case where the rule belongs with the query: you describe the computed condition once, the platform evaluates it as part of filtering, and callers (Apex, APIs, downstream jobs) get a smaller, already qualified result set. This is especially valuable when you can not or do not want to change the data model for every subscriber org, a common scenario for ISVs or developers who want to keep operational segmentation logic visible in SOQL instead of buried in loops.</span></p>
<p><span style="font-weight: 400">For example, say you&#8217;re working with an</span> <span style="font-weight: 400">Order__c</span><span style="font-weight: 400"> object and need to find every order where profit (revenue minus cost) exceeds $150. Without </span><span style="font-weight: 400">FORMULA()</span><span style="font-weight: 400">, the rule lives in Apex after a broader query; with </span><span style="font-weight: 400">FORMULA()</span><span style="font-weight: 400">, the rule moves into the </span><span style="font-weight: 400">WHERE</span><span style="font-weight: 400"> clause itself.</span></p>
<p>For example, say you&#8217;re working with an<span> </span><code>Order__c</code> object and need to find every order where profit (revenue minus cost) exceeds $150. Without <code>FORMULA()</code>, the rule lives in Apex after a broader query; with <code>FORMULA()</code>, the rule moves into the <code>WHERE</code> clause itself.</p>
<pre language="apex">// Without FORMULA(): business rule lives in Apex after a wider query

 List rows = [SELECT Id, Name, Revenue__c, Cost__c FROM Order__c];
  List highMargin = new List();
  for (Order__c o : rows) {
      if (o.Revenue__c != null &amp;&amp; o.Cost__c != null
          &amp;&amp; (o.Revenue__c - o.Cost__c) &gt; 150) {
          highMargin.add(o);
      }
  }
</pre>
<pre language="soql">--With FORMULA(): same rule expressed in the WHERE clause 

SELECT Id, Name FROM Order__c WHERE FORMULA('Revenue__c - Cost__c') &gt; 150
</pre>
<p>The formula expression supports addition and subtraction (+ and -). Expressions can evaluate to <code>DOUBLE, INTEGER, DATETIME, DATE &amp; CURRENCY</code>.</p>
<p>In practice:</p>
<ul>
<li><code>INTEGER</code> behaves like <code>DOUBLE</code></li>
<li><code>DATE</code> behaves like <code>DATETIME</code></li>
</ul>
<p>So the rest of this post focuses on the three illustrative types: <code>DOUBLE</code>, <code>DATETIME</code>, and <code>CURRENCY</code>.</p>
<p><span style="font-weight: 400">This pattern fits Apex and SOQL developers, ISV teams shipping logic across subscriber orgs where schema changes are hard, and anyone trying to move off the &#8220;query a large candidate set, filter in code&#8221; approach.</span></p>
<p>Beyond fitting that audience, <code>FORMULA()</code> in <code>WHERE</code> matters today because it:</p>
<ul>
<li style="font-weight: 400"><span style="font-weight: 400">Eliminates unnecessary formula fields</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Reduces Apex complexity</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Improves performance by filtering at the query level</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Is especially valuable for ISVs who can’t modify customer schemas</span></li>
</ul>
<h2><span style="font-weight: 400">Syntax: FORMULA() in WHERE</span></h2>
<p>Today, <code>FORMULA()</code> in SOQL is available as a pilot capability: it is only supported in the <code>WHERE</code> clause (not <code>HAVING</code>).</p>
<p>The general shape is:</p>
<p><code>WHERE FORMULA('…') &lt;operator&gt; &lt;literal&gt;</code></p>
<p><span style="font-weight: 400">The string is a formula expression evaluated for each row as part of filtering.</span></p>
<h2><span style="font-weight: 400">Example: E-commerce order management </span></h2>
<p><span style="font-weight: 400">Imagine that you&#8217;re building an order analytics dashboard for a growing e-commerce platform.</span></p>
<p>Your custom <code>Order__c</code> object tracks:</p>
<ul>
<li><code>Revenue__c</code> (Currency): Total order value</li>
<li><code>Cost__c</code> (Currency): Fulfillment cost</li>
<li><code>OrderDate__c</code> (Date): When customer placed order</li>
<li><code>ShipDate__c</code> (Date): When order shipped</li>
<li><code>Status__c</code> (Text): Order status</li>
</ul>
<p>Previously, filtering orders by calculated metrics meant either:</p>
<ol>
<li>Creating formula fields like <code>Profit__c</code>, <code>ShippingDelay__c</code>, <code>ProfitMargin__c</code></li>
<li>Querying all orders and filtering in Apex loops</li>
</ol>
<p>Let&#8217;s see how <code>FORMULA() </code>eliminates both.</p>
<p>The screenshot below shows a <code>Sample Order__c</code> records table that includes seven orders (ORD-001 through ORD-007) with Revenue, Cost, OrderDate, ShipDate, and Status columns used throughout the <code>FORMULA()</code> examples.</p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206443" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260604091700/Screenshot-of-Sample-Order__c-records-table-showing-seven-orders-e1780589845175.png?w=1000" class="postimages" width="1000" height="140" alt="Screenshot of Sample Order__c records table showing seven orders." />
			  </span>
			</p>
<h3><span style="font-weight: 400">Three key use cases</span></h3>
<p><span style="font-weight: 400">For our e-commerce example, </span><span style="font-weight: 400"><code>FORMULA() </code></span><span style="font-weight: 400">is particularly helpful in the following three use cases:</span></p>
<ol>
<li style="font-weight: 400"><span style="font-weight: 400">Find high-profit orders using currency arithmetic.</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Detect late shipments using datetime arithmetic.</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Filter by premium order criteria using combined conditions.</span></li>
<li style="font-weight: 400"></li>
</ol>
<h4><span style="font-weight: 400">1. Find high-profit orders (<code>CURRENCY</code></span><span style="font-weight: 400"> arithmetic)</span></h4>
<p><span style="font-weight: 400">Our business requirement for this first use case is to identify orders with profit &gt; $250 for priority fulfillment review.</span></p>
<p><b>The old way</b></p>
<pre language="apex"> // Query everything, filter in memory
  List allOrders = [
      SELECT Id, Name, Revenue__c, Cost__c
      FROM Order__c
      WHERE Status__c = 'Shipped'
  ];

  List highProfitOrders = new List();
  for (Order__c order : allOrders) {
      if (order.Revenue__c != null &amp;&amp;
          order.Cost__c != null &amp;&amp;
          (order.Revenue__c - order.Cost__c) &gt; 250) {
          highProfitOrders.add(order);
      }
  }

// Result: 3 orders (ORD-001, ORD-003, ORD-005)
</pre>
<p><b>The new way</b></p>
<pre language="sql">SELECT Id, Name, Revenue__c, Cost__c
FROM Order__c
WHERE Status__c = 'Shipped'
AND FORMULA('Revenue__c - Cost__c') &gt; 250
</pre>
<p>The following screenshot shows a SOQL query using the FORMULA() function to filter orders by profit: <code>SELECT </code><code>Id</code>, <code>Name</code>, <code>Revenue__c</code>, <code>Cost__c FROM Order__c WHERE Status__c</code> equals “Shipped” AND <code>FORMULA('Revenue__c - Cost__c')</code> is greater than 250.Query results show three records: ORD-001 with 500 revenue and 200 cost, ORD-003 with 800 revenue and 300 cost, and ORD-005 with 1200 revenue and 400 cost.</p>
<p><strong>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206444" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260604091804/Screenshot-of-a-SOQL-query-using-the-FORMULA-function-to-filter-orders-by-profit-e1780589901667.png?w=1000" class="postimages" width="1000" height="107" alt="Screenshot of a SOQL query using the FORMULA() function to filter orders by profit." />
			  </span>
			 </strong></p>
<h4><span style="font-weight: 400">2. Detect late shipments (<code>DATETIME</code></span><span style="font-weight: 400"> arithmetic)</span></h4>
<p><span style="font-weight: 400">Our business requirement here is to find orders that shipped more than three days after order date for SLA analysis. </span></p>
<pre language="sql">SELECT Id, Name, OrderDate__c, ShipDate__c
FROM Order__c
WHERE FORMULA('ShipDate__c - OrderDate__c') &gt; 3
</pre>
<p>The screenshot below shows a SOQL query using the <code>FORMULA()</code> function to calculate shipping delays: <code>SELECT Id</code>, <code>Name</code>, <code>OrderDate__c</code>, <code>ShipDate__c</code> <code>FROM Order__c WHERE FORMULA('ShipDate__c minus OrderDate__c')</code> greater than three. Query results display two records: ORD-002 ordered April 5 and shipped April 12, and ORD-005 ordered April 15 and shipped April 25, both exceeding the three-day shipping threshold.</p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206445" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260604091902/Screenshot-of-a-SOQL-query-using-the-FORMULA-function-to-calculate-shipping-delays-e1780589954238.png?w=1000" class="postimages" width="1000" height="93" alt="Screenshot of a SOQL query using the FORMULA() function to calculate shipping delays." />
			  </span>
			</p>
<h4>3. <span style="font-weight: 400">Premium order criteria (combined conditions)</span></h4>
<p><b>Our </b><span style="font-weight: 400">business requirement for the third use case is to</span> <span style="font-weight: 400">find &#8220;premium&#8221; orders: high revenue (&gt; $600) AND fast shipping (≤ 2 days).                   </span><b>                                                                                        </b></p>
<pre language="sql">SELECT Id, Name, Revenue__c, OrderDate__c, ShipDate__c
FROM Order__c
WHERE Revenue__c &gt; 600
AND FORMULA('ShipDate__c - OrderDate__c') &lt;= 2
</pre>
<p>The screenshot below shows a SOQL query combining standard field filter with the <code>FORMULA() </code>function: <code>SELECT Id</code>, <code>Name</code>, <code>Revenue__c</code>, <code>OrderDate__c</code>, <code>ShipDate__c FROM Order__c WHERE Revenue__c</code> is greater than 600 AND <code>FORMULA('ShipDate__c minus OrderDate__c')</code> is less than or equal to two. Query results show two records: ORD-003 with 800 revenue, ordered April 10 and shipped April 11, and ORD-007 with 650 revenue, ordered April 20 and shipped April 21, with both records meeting the high-revenue and fast-shipping criteria.</p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206446" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260604091932/Screenshot-of-a-SOQL-query-combining-standard-field-filter-with-FORMULA-function-e1780589984880.png?w=1000" class="postimages" width="1000" height="87" alt="Screenshot of a SOQL query combining standard field filter with FORMULA function." />
			  </span>
			</p>
<h2><span style="font-weight: 400">Conclusion</span></h2>
<p>By applying the ideas in this post, you can express computed filters directly in SOQL. <code>FORMULA()</code> in <code>WHERE</code> turns the question from “what rows might be relevant?” into “what rows already match my computed rule?” with less schema churn. This tends to shrink Apex branching, reduce one-off formula fields for query-only rules, and make the business condition readable next to the <code>SELECT</code>, which helps both integrations and teams reviewing SOQL in code review and operations.</p>
<p><span style="font-weight: 400">Beyond individual queries, the same pattern reinforces a broader habit: keep data access and the rule that defines “the right rows” together, so batch jobs, services, and packaged logic stay easier to maintain, especially when subscriber org schemas are not yours to reshape at will. As always, treat what you validate in a non-production org as the contract for syntax, supported types, and supported operators.</span></p>
<p><span style="font-weight: 400">We would love your feedback on the capabilities of this pilot, and your comments will influence the GA release. Please post your questions and/or feedback on the Pilot to 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">. Just let us know!</span></p>
<h2><span style="font-weight: 400">Resources</span></h2>
<ul>
<li style="font-weight: 400"><span style="font-weight: 400">Pilot enrollment: Contact your Salesforce Account Executive or Customer Success Manager to request access to the SOQL <code>FORMULA() </code></span><span style="font-weight: 400">pilot</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Trailhead: </span><a href="https://trailhead.salesforce.com/content/learn/modules/soql-for-admins"><span style="font-weight: 400">SOQL for Admins</span></a><span style="font-weight: 400"> &#8211; Create SOQL Queries to get data from your Salesforce org</span></li>
</ul>
<h2><b>About the author</b></h2>
<p><b>Dikshita Patel</b><span style="font-weight: 400"> is a Software Engineer on Salesforce’s Enterprise API team, where she builds Platform APIs allowing developers, partners, and customers to query and access Salesforce data without using the Salesforce user interface. You can follow and connect with her on </span><a href="https://www.linkedin.com/in/dikshita-patel/"><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/simplify-your-soql-queries-using-soql-formula-in-where">Simplify Your SOQL Queries Using SOQL FORMULA() in WHERE</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/simplify-your-soql-queries-using-soql-formula-in-where/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
	<post-id xmlns="com-wordpress:feed-additions:1">206439</post-id><media:thumbnail url="https://d259t2jj6zp7qm.cloudfront.net/images/20260604082729/Generic-B-1-e1780586873385.png?w=1000" />
<media:content url="https://d259t2jj6zp7qm.cloudfront.net/images/20260604082729/Generic-B-1-e1780586873385.png?w=1000" medium="image" />
	</item>
		<item>
		<title>The MCP Server for Marketing Cloud Engagement is Now GA</title>
		<link>https://developer.salesforce.com/blogs/2026/06/the-mcp-server-for-marketing-cloud-engagement-is-now-ga</link>
		<comments>https://developer.salesforce.com/blogs/2026/06/the-mcp-server-for-marketing-cloud-engagement-is-now-ga#respond</comments>
		<pubDate>Tue, 02 Jun 2026 15:00:08 +0000</pubDate>
		<dc:creator><![CDATA[Swetha Pinninti]]></dc:creator>
				<category><![CDATA[Announcements]]></category>
		<category><![CDATA[APIs and Integrations]]></category>
		<category><![CDATA[Automation]]></category>
		<category><![CDATA[Developer Tooling]]></category>
		<category><![CDATA[New Developments]]></category>
		<category><![CDATA[AI agent]]></category>
		<category><![CDATA[automations]]></category>
		<category><![CDATA[data extensions]]></category>
		<category><![CDATA[Installed Package]]></category>
		<category><![CDATA[journeys]]></category>
		<category><![CDATA[Marketing Cloud Engagement]]></category>
		<category><![CDATA[Model context protocol]]></category>

		<guid isPermaLink="false">https://developer.salesforce.com/blogs/?p=206424</guid>
		<description><![CDATA[<p>Learn how to securely connect external AI agents to Marketing Cloud Engagement and expose core capabilities like data extensions and journeys as natural language tools.</p>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/06/the-mcp-server-for-marketing-cloud-engagement-is-now-ga">The MCP Server for Marketing Cloud Engagement is Now GA</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">Model Context Protocol (MCP) is an open standard that lets any LLM-powered AI agent — Claude, ChatGPT, Cursor, Gemini, and more — connect to and control an external platform using a standardized protocol. Think of it like a universal adapter for AI. Now, Salesforce is releasing a generally available MCP interface for Marketing Cloud Engagement APIs.</span></p>
<p><span style="font-weight: 400">The </span><a href="https://developer.salesforce.com/docs/marketing/mce-mcp/guide/mce-mcp.html"><span style="font-weight: 400">MCE MCP Server</span></a><span style="font-weight: 400"> is Salesforce&#8217;s first-party, enterprise-grade, hosted MCP server for Marketing Cloud Engagement. It’s a first step towards a truly headless experience, exposing MCE&#8217;s core marketing capabilities as tools that any MCP-compatible AI agent can call — in plain language, without writing code or navigating the UI. </span></p>
<p><span style="font-weight: 400">The server allows almost any external agent that supports MCP to manage data extensions, journeys, automations, and more. Note that Marketing Cloud API limits and guidelines apply (see </span><a href="https://help.salesforce.com/s/articleView?id=mktg.mc_overview_limits_api.htm&amp;type=5"><span style="font-weight: 400">documentation</span></a><span style="font-weight: 400"> for more information).</span></p>
<p><span style="font-weight: 400">In this post, we’ll walk you through the MCE MCP Server setup, tools, and typical use cases.</span></p>
<h2><b>How to set up the MCE MCP Server</b></h2>
<p><span style="font-weight: 400">An agent’s permissions are a combination of the scopes in a dedicated, installed package and your own user permissions. If your installed package has access to read data extensions, but you (as a user) do not, then the MCP server does not have access. Likewise, you can limit what it can do by not granting permissions to the installed package, even if the user still has them. </span></p>
<p><span style="font-weight: 400">From the install package, you’ll be able to get an MCP URL that you and potentially other teammates can use. This is specific to your organization and the install package. It’s not sensitive, but it’s worth keeping in a safe place for convenience. Registering it with Claude Code, for example, is simple.</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<pre language="shell">claude mcp add --transport http <a name=""></a> https://
</pre>
<p><span style="font-weight: 400">Other agents will be very similar. You should only need to do this once, and then authenticate from inside the agent. The details on how this works will vary depending on how the agent implements OAuth login flows, but you will be prompted to log into your Marketing Cloud account if you are not already logged in. This gives the agent a token that it can use to act on your behalf, but only for a limited time. </span></p>
<p><span style="font-weight: 400">For more information on MCE MCP Server setup, please see the </span><a href="https://developer.salesforce.com/docs/marketing/mce-mcp/guide/mce-mcp-setup.html"><span style="font-weight: 400">documentation</span></a><span style="font-weight: 400">.</span></p>
<h2><b>MCE MCP Server capabilities and tool reference</b></h2>
<p>The MCE MCP Server is designed to simply wrap existing functionality provided by the Marketing Cloud Engagement APIs. For example, to get lists of data extensions, we offer a tool called <code>sfmc_get_data_extensions</code>. This is nearly 1:1 with a request to <code>data/v1/customobjects</code> on the original REST route, but made more easily discoverable for an agent. It can read the embedded documentation and discover that it needs to provide a <code>$search</code> variable with the search string.</p>
<p><span style="font-weight: 400">As an example, this is how this tool is shown to the agent:</span></p>
<pre language="json">{
  "name": "sfmc_get_data_extensions",
  "description": "Retrieve a list of data extensions. GET /data/v1/customobjects.\n\nIMPORTANT: The $search query parameter is REQUIRED and must be a valid search term (no wildcards like * or %).\nYou must provide query_json with the structure: {\"$search\": \"term\"}\n\nExamples:\n- Search for 'customer': {\"$search\": \"customer\"}\n- Search for 'email': {\"$search\": \"email\"}\n\nSee resource 'sfmc://docs/data/v1/customobjects' for full API documentation.",
  "inputSchema": {
    "type": "object",
    "properties": {
      "query_json": {
        "type": "string",
        "description": "JSON object string containing $search parameter. REQUIRED format: {\"$search\": \"search_term\"}. The search term cannot be empty or contain wildcards (* or %)."
      }
    },
    "required": [
      "query_json"
    ]
  },
  "annotations": {
    "title": "sfmc get data extensions",
    "readOnlyHint": true,
    "destructiveHint": false,
    "idempotentHint": true,
    "openWorldHint": true
  }
}
</pre>
<p><span style="font-weight: 400">Just as our documentation promised, the resulting tool call is simple. </span></p>
<pre language="json">{ "$search": "test" }
</pre>
<p><span style="font-weight: 400">The results are also pretty straightforward, even for a human to read.</span></p>
<pre language="json">Status: 200 OK
URL: https://mcfdpyw7c97kdckv65lk2j1vspd0.rest.marketingcloudapis.com/data/v1/customobjects?%24search=test

{
  "count" : 8,
  "page" : 1,
  "pageSize" : 25,
  "links" : { },
  "items" : [ {
    "id" : "bdc04e14-0209-f111-a5e8-5cba2c701e38",
    "name" : "test_7",
    "key" : "test_7",
    "description" : "Subscribers who purchased in 2018",
    "isActive" : true,
    "isSendable" : false,
    "isTestable" : false,
    "categoryId" : 1998298,
    "ownerId" : 11280369,
    "isObjectDeletable" : true,
    "isFieldAdditionAllowed" : true,
    "isFieldModificationAllowed" : true,
    "createdDate" : "2026-02-13T11:33:22.133",
    "createdById" : 11280369,
    "createdByName" : "GI Admin",
    "modifiedDate" : "2026-02-13T11:33:22.133",
    "modifiedById" : 11280369,
    "modifiedByName" : "GI Admin",
    "ownerName" : "GI Admin",
    "partnerApiObjectTypeId" : 310,
    "partnerApiObjectTypeName" : "DataExtension",
    "rowCount" : 0,
    "dataRetentionProperties" : {
      "isDeleteAtEndOfRetentionPeriod" : false,
      "isRowBasedRetention" : false,
      "isResetRetentionPeriodOnImport" : false
    },
    "fieldCount" : 15
  }, ...
</pre>
<p><span style="font-weight: 400">It’s worth noting that the result is NOT JSON as far as the agent is concerned. It’s simply text, which in this case represents the result of making an API call to a REST endpoint. It is up to the agent to interpret this information and use it, and that’s what provides the maximum flexibility.</span></p>
<p>While the original intent of this API was to integrate with custom software, an AI agent can instead make it available interactively and with minimal friction. You don’t need to know about <code>$search</code> (did you forget the dollar sign?) or its syntax, the agent can deal with that. If the tool fails, it has access to the error message and relevant documentation to try again with different arguments without requiring a human to copy and paste an error code into a search engine. In this case, the tool offered <code>sfmc://docs/data/v1/customobjects</code> as a resource to selectively load and find out more about custom object definitions if necessary.</p>
<p><span style="font-weight: 400">Another advantage is that if you are making a complex request, the AI agent (like Claude Client) can sequence tools that need to be executed in order to achieve the given functionality. For example, If you just tell the AI client to “Update the loyalty status of subscriberId 5544 to gold in XYZ DE,” the agent first gets the XYZ DE, fetches the row in DE, and updates the row without user intervention.</span></p>
<p><span style="font-weight: 400">See the </span><a href="https://developer.salesforce.com/docs/marketing/mce-mcp/references/mce-mcp-tools/mce-mcp-tools.html"><span style="font-weight: 400">MCE MCP tool reference</span></a><span style="font-weight: 400"> for more information.</span></p>
<h2><b>What you can build with MCP + Marketing Cloud</b></h2>
<p><span style="font-weight: 400">We tested a range of use cases, from creating a new campaign journey using a single prompt to sending transactional messages in real time. AI agents are not necessarily the most creative things around, but our test agent was able to put most of the pieces we needed together, and it prompted for more context if it needed clarity on intent. Typically, you really want to hand off mundane tasks that you’d rather not deal with to an agent, such as the following.</span></p>
<ul>
<li style="font-weight: 400"><b>Create a Data Extension from a plain English description</b><span style="font-weight: 400">: Ask your AI agent, &#8220;Create a Data Extension for Holiday Shoppers with Email, First Name, Last Name, and Purchase Date.&#8221; The agent maps field descriptions to Marketing Cloud datatypes, sets SubscriberKey as the primary key, and returns the CustomerKey and a direct link to the new Data Extension — no Contact Builder navigation required.</span></li>
<li style="font-weight: 400"><b>Launch a basic automated campaign without touching Journey Builder:</b><span style="font-weight: 400"> Ask, &#8220;Create a Welcome Email journey that sends immediately when someone joins the Welcome List DE.&#8221; The agent verifies the entry source DE, builds the Entry → Email → Exit structure, resolves the email asset, and creates the journey in Draft. It then asks if you want to activate it.</span></li>
<li style="font-weight: 400"><b>Build a nurture series conversationally:</b><span style="font-weight: 400"> Ask, &#8220;Create a 3-email Welcome Series: send Email 1 immediately, wait 3 days, send Email 2, wait 7 days, send Email 3.&#8221; The agent parses the wait durations, names each activity descriptively, resolves all email assets, and returns the Journey Builder URL ready to review and activate.</span></li>
<li style="font-weight: 400"><b>Add Einstein optimization to any journey step</b><span style="font-weight: 400">: Ask, &#8220;Add Einstein Send Time Optimization before the promotional email in my Black Friday Journey.&#8221; The agent verifies that Einstein STO is provisioned for your Business Unit, and inserts the activity immediately before the specified email.</span></li>
<li style="font-weight: 400"><b>Propagate a new data field across every dependent DE and query in one command</b><span style="font-weight: 400">: Ask, &#8220;Add Propensity_Score (Number) from the Customer_Signals DE to all downstream Data Extensions and queries.&#8221; The agent traces transitive dependencies across all Query Activities and target DEs, shows you a full impact report (X DEs, Y query steps across Z automations), and — on your confirmation — adds the field to every DE, and updates every SELECT clause in dependency order. It then returns a summary of what changed and any errors encountered. This supports a dry-run mode to preview all changes before touching anything.</span></li>
</ul>
<p><span style="font-weight: 400">Having said that, we encourage you to try all your use cases with this MCP server and let us know if we are missing any crucial or nice-to-have tools on </span><a href="https://ideas.salesforce.com/s/"><span style="font-weight: 400">IdeaExchange</span></a><span style="font-weight: 400">.</span></p>
<h2><b>Permissions, destructive operations, and safety guardrails</b></h2>
<p><span style="font-weight: 400">We’ve done our best to make this service as flexible and useful as possible, for as many people as possible. We decided early on to even support “destructive” operations like deleting data, even though that’s almost always a bad idea to hand off to an agent. Operations like this are annotated as destructive in a way that the agent can read, but the agent is still capable of choosing to execute them if it has the permissions to do so. It’s crucial to think through the “worst case” when assigning permissions during setup, because an agent can make mistakes. Customers are also sensitive to reputational harm if an unintentional send is triggered, so bear that in mind as well! Please make sure that you review your Installed Package scopes before using them. More details in this </span><a href="https://developer.salesforce.com/docs/marketing/mce-mcp/guide/mce-mcp-setup.html"><span style="font-weight: 400">documentation</span></a><span style="font-weight: 400">.</span></p>
<h2><b>Future growth</b></h2>
<p><span style="font-weight: 400">The MCE MCP Server is designed to be extended in the future, so be sure to check back often. We plan to extend coverage to other important Marketing Cloud APIs and improve what we’ve already built. Agents and the LLMs that power them are also continuously releasing and improving, so workflows that weren’t possible before might get more practical over time. </span></p>
<p><span style="font-weight: 400">We plan on continually improving the server and extending it to other Marketing Cloud APIs over time. Please keep an eye on our release notes, and provide feedback through your account manager or provide on </span><a href="https://ideas.salesforce.com/s/"><span style="font-weight: 400">IdeaExchange</span></a><span style="font-weight: 400">.</span></p>
<h2><b>Resources</b></h2>
<ul>
<li style="font-weight: 400"><span style="font-weight: 400">Documentation: </span><a href="https://developer.salesforce.com/docs/marketing/mce-mcp/guide/mce-mcp.html"><span style="font-weight: 400">MCE MCP Server Setup Guide</span></a></li>
<li style="font-weight: 400"><span style="font-weight: 400">Documentation</span><span style="font-weight: 400">: </span><a href="https://developer.salesforce.com/docs/marketing/mce-mcp/references"><span style="font-weight: 400">MCE MCP Server Tool Reference</span></a></li>
</ul>
<h2><b>About the author</b></h2>
<p><b>​​Swetha Pinninti</b><span style="font-weight: 400"> is a Director of Engineering at Salesforce on the Marketing Cloud Einstein team. </span></p>
<p><span style="font-weight: 400"></span><b>Patrick Frampton</b><span style="font-weight: 400"> is a Lead Member of Technical Staff at Salesforce on the Marketing Cloud Einstein team. </span></p>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/06/the-mcp-server-for-marketing-cloud-engagement-is-now-ga">The MCP Server for Marketing Cloud Engagement is Now GA</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/the-mcp-server-for-marketing-cloud-engagement-is-now-ga/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
	<post-id xmlns="com-wordpress:feed-additions:1">206424</post-id><media:thumbnail url="https://d259t2jj6zp7qm.cloudfront.net/images/20260601114100/Generic-A-2-e1780339272284.png?w=1000" />
<media:content url="https://d259t2jj6zp7qm.cloudfront.net/images/20260601114100/Generic-A-2-e1780339272284.png?w=1000" medium="image" />
	</item>
		<item>
		<title>Build a Salesforce Agent Skill with Claude Code</title>
		<link>https://developer.salesforce.com/blogs/2026/05/build-a-salesforce-agent-skill-with-claude-code</link>
		<comments>https://developer.salesforce.com/blogs/2026/05/build-a-salesforce-agent-skill-with-claude-code#respond</comments>
		<pubDate>Fri, 29 May 2026 15:26:35 +0000</pubDate>
		<dc:creator><![CDATA[Dave Norris]]></dc:creator>
				<category><![CDATA[Agentforce]]></category>
		<category><![CDATA[Agentforce Vibes]]></category>
		<category><![CDATA[App Development]]></category>
		<category><![CDATA[Developer Tooling]]></category>
		<category><![CDATA[ai]]></category>
		<category><![CDATA[Apex]]></category>
		<category><![CDATA[architecture]]></category>
		<category><![CDATA[Claude Code]]></category>
		<category><![CDATA[developer tooling]]></category>
		<category><![CDATA[LLMs]]></category>

		<guid isPermaLink="false">https://developer.salesforce.com/blogs/?p=206402</guid>
		<description><![CDATA[<p>Learn how to build authoring skills for consistently producing high quality code across the Salesforce ecosystem using structured workflows, code templates, and automated validators.</p>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/05/build-a-salesforce-agent-skill-with-claude-code">Build a Salesforce Agent Skill with Claude Code</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">Getting AI to write code is easy. Getting it to write code that passes your security review, respects governor limits, follows your preferred frameworks, and scores well on static analysis, consistently, is often problematic. A good way to solve this is to use an </span><a href="https://agentskills.io/home"><span style="font-weight: 400">agent skill</span></a><span style="font-weight: 400">: a structured prompt package that encodes your team&#8217;s definition of &#8220;production-ready&#8221; into something that an agent follows and a validator enforces. Because LLMs are probabilistic, outliers are inevitable and the architecture assumes this and catches them.</span></p>
<p><span style="font-weight: 400">This post breaks down the structure of a Salesforce agent skill using Apex as an example. For clarity, we’ll use Claude Code to highlight specific examples, but the principles apply to any agent. We’ll cover what a skill is, the project structure, and how to get started creating your first skill.</span></p>
<h2><span style="font-weight: 400">What is an agent skill?</span></h2>
<p><span style="font-weight: 400">An agent skill is a structured prompt package that transforms Claude from a general-purpose assistant into a specialist. Rather than relying on the model&#8217;s training data alone, a skill provides explicit workflow contracts, reference material, code templates, and automated validators that load into context when the skill activates.</span></p>
<p><span style="font-weight: 400">The large language models (LLMs) that power Claude already know the programming languages across the Salesforce ecosystem: Apex, LWC, SOQL, etc. But knowing a language and consistently producing high-quality code that adheres to best practices are two different things. A skill bridges that gap.</span></p>
<p><span style="font-weight: 400">Each skill lives in a folder with a predictable structure:</span></p>
<pre>skills/authoring-apex/
├── SKILL.md              # The execution contract
├── references/           # Decision trees, patterns, guardrails
├── assets/               # Example code
└── hooks/scripts/        # Python validators
</pre>
<p><span>At minimum, you need a </span><code>SKILL.md</code>,<span> which is the entry point that Claude reads when the skill activates. Everything else is scaffolding that you add as the skill grows. Start with one </span><a href="http://skill.md"><u><code>SKILL.md</code></u></a><span> and one reference doc, and add hooks when you want mechanical enforcement rather than relying on instructions alone.</span></p>
<p><span>Skills activate based on frontmatter. Frontmatter is the YAML metadata block between the </span><code>‘---’ </code><span>delimiters at the top of a markdown file. It&#8217;s not rendered as content, but it’s there to be read as machine-readable configuration. In a skill, the frontmatter tells an agent when to activate: the name field identifies the skill, and the description field contains the trigger rules that the agent uses to decide whether this skill is relevant to the current task. </span></p>
<p><span>In the example below, the description uses a </span><code>TRIGGER when</code><span> / </span><code>DO NOT TRIGGER when</code><span> structure that gives the agent both positive and negative match criteria. This eliminates the ambiguous middle ground (like &#8220;is this SOQL query part of Apex authoring or the SOQL skill?&#8221;) that causes misfires when you only tell the model what to activate on.</span></p>
<pre language="yaml">---
name: authoring-apex
description: &gt;
  TRIGGER when: user writes, edits, or reviews Salesforce Apex code —
  .cls or .trigger files including service classes, selector classes,
  trigger handlers, test classes, batch jobs, queueable classes,
  invocable methods, or Apex REST endpoints.
  DO NOT TRIGGER when: user works on LWC JavaScript, Flow XML,
  standalone SOQL queries, or running existing tests.
---
</pre>
<p>This is a simplified version. In practice, the description is more comprehensive: listing every class type (<code>schedulable</code>, <code>AuraEnabled</code>, <code>HttpCalloutMock</code>) and naming which skill handles each excluded case (e.g. &#8220;use authoring-lwc&#8221; for LWC JavaScript). The more specific the boundary, the fewer misfires.</p>
<p>When a matching task is detected, Claude loads the <code>SKILL.md</code> and follows the workflow. You can also trigger a skill manually with <code>/skill-name</code> in the prompt. Either way, the full skill context — workflow, references, templates — is injected automatically. You never paste the <code>SKILL.md</code> content into the conversation yourself.</p>
<p><span>The skill format itself (a</span><span> </span><code>SKILL.md</code><span> with frontmatter, references, and assets) is an </span><a href="https://agentskills.io/home"><u>open specification</u></a><span> maintained by Anthropic under Apache 2.0 and open to community contributions. The</span><span> </span><code>SKILL.md</code><span>, references, and templates that you write are portable. The hooks mechanism (hooks.yaml with PostToolUse lifecycle events) is Claude Code-specific — if you use another agent runtime, you&#8217;d wire validation differently, but the skill content travels with you. This makes skills headless. Use them with Agentforce Vibes, Claude Code, Cursor, VS Code, Gemini CLI, OpenAI Codex, Windsurf, Roo Code, Goose, or any of the 30+ agents that support the open specification.</span></p>
<h2><span style="font-weight: 400">The structure of an agent skill</span></h2>
<h3><span style="font-weight: 400">SKILL.md: The execution contract</span></h3>
<p><span style="font-weight: 400">This is the key, non-reorderable workflow with hard exit criteria for each phase that will be followed when the skill is used. Here is a simplified example showing the structure. Your real </span><a href="http://skill.md"><span style="font-weight: 400">SKILL.md</span></a><span style="font-weight: 400"> would have more detail in each phase, but the shape is the same: ordered phases with exit criteria.</span></p>
<pre language="markdown">Follow this workflow in order. Do not skip, merge, or reorder steps.
If blocked, stop and ask for missing context. If not applicable, mark `N/A`
with a one-line justification in the report.

## Required Inputs

Gather or infer before authoring:

- Class type (service, selector, batch, queueable, invocable, trigger handler)
- Target object(s) and business goal
- Sharing default (`with sharing` unless justified)
- Trigger framework already in use (or explicit choice)

The phases:

### Phase 1 — Author
1. **Discover project conventions** — scan for existing patterns, trigger
     framework, naming style
2. **Choose the smallest correct pattern**:

  | Need               | Pattern                              |
  | ------------------ | ------------------------------------ |
  | Business logic     | Service class                        |
  | Data access        | Selector class                       |
  | Trigger logic      | Trigger handler (framework required) |
  | Flow integration   | @InvocableMethod                     |
  | Bulk processing    | Batch Apex                           |
  | Async work         | Queueable                            |

3. **Read the matching template** from `assets/` before authoring
4. **Author with guardrails** — apply every rule in the Rules section below
5. **Generate test class** — delegate to testing skill

### Phase 2 — Validate

6. **Run code analyzer** — remediate all blocking violations; re-run until clean
7. **Execute tests** — capture pass/fail and coverage percentage

### Phase 3 — Report

8. **Report** — files, design decisions, analyzer output, test results, deploy note

</pre>
<p><span style="font-weight: 400">If a phase doesn&#8217;t apply, Claude must document it as N/A with justification. This maps directly to Anthropic&#8217;s best practice of providing &#8220;instructions as sequential steps using numbered lists when order or completeness matters.&#8221;</span></p>
<h3><span style="font-weight: 400">references/: The knowledge library</span></h3>
<p><span style="font-weight: 400">These are your reference documents that target a specific failure mode that general training handles inconsistently. Below are some </span><a href="https://platform.claude.com/docs/en/docs/build-with-claude/prompt-engineering/claude-prompting-best-practices"><span style="font-weight: 400">best practices</span></a><span style="font-weight: 400"> from Anthropic to follow since the temptation will be to include too much information.</span></p>
<table>
<tbody>
<tr>
<td><b>Best Practice</b></td>
<td><b>Reasoning</b></td>
<td><b>Example for Apex</b></td>
</tr>
<tr>
<td><span style="font-weight: 400">File size</span></td>
<td><span style="font-weight: 400">Claude prioritizes unevenly as documents grow. Short, focused files get more consistent attention.</span></td>
<td><span style="font-weight: 400">Split by concern: core patterns (Factory, Strategy, Selector, Service) in one file, and advanced patterns (Unit of Work, Domain Model, Facade) in another.</span></td>
</tr>
<tr>
<td><span style="font-weight: 400">Structured hierarchy</span></td>
<td><span style="font-weight: 400">Table of contents and headers let Claude locate relevant sections without reading everything. </span></td>
<td><span style="font-weight: 400">SKILL.md says &#8220;read best-practices.md before authoring&#8221; and Claude navigates to the heading it needs, not the whole file.</span></td>
</tr>
<tr>
<td><span style="font-weight: 400">One rule per section</span></td>
<td><span style="font-weight: 400">One point per section eliminates ambiguity about which rule applies.</span></td>
<td><span style="font-weight: 400">&#8220;SOQL in Loops&#8221; covers SOQL in loops, not DML, sharing, or null safety.</span></td>
</tr>
<tr>
<td><span style="font-weight: 400">Short sections, many of them</span></td>
<td><span style="font-weight: 400">Self-contained sections (~20-40 lines) let Claude apply one rule without loading the full document.</span></td>
<td><span style="font-weight: 400">One anti-pattern entry = one heading, one &#8220;Why this fails&#8221; paragraph, one BAD block, one &#8220;Fix&#8221; line, and one GOOD block.</span></td>
</tr>
<tr>
<td><span style="font-weight: 400">Concrete over abstract</span></td>
<td><span style="font-weight: 400">BAD/GOOD pairs are implicit few-shot examples. Anthropic says examples are &#8220;one of the most reliable ways to steer output.&#8221; Abstract rules (&#8220;avoid governor limits&#8221;) get interpreted loosely; concrete pairs anchor the behavior.</span></td>
<td><span style="font-weight: 400">// BAD: 1 SOQL per iteration. Hits 100-query limit at record 101.</span></p>
<p><span style="font-weight: 400">&lt;your demonstrable example&gt;</span></p>
<p><span style="font-weight: 400">// GOOD: 1 SOQL total.</span></p>
<p><span style="font-weight: 400">&lt;your demonstrable example&gt;</span></td>
</tr>
<tr>
<td><span style="font-weight: 400">Consequences over commands</span></td>
<td><span style="font-weight: 400">&#8220;Don&#8217;t do X&#8221; is weaker than &#8220;X fails because Y.&#8221; Anthropic notes &#8220;explaining why helps Claude generalize&#8221; to novel cases. If Claude knows the reason, it applies the rule even to patterns that look different from the example.</span></td>
<td><span style="font-weight: 400">&#8220;Salesforce allows only 150 DML statements per transaction. A trigger on 200 records with DML inside the loop hits 150 at record 151 and the entire batch rolls back.&#8221;</span></td>
</tr>
<tr>
<td><span style="font-weight: 400">Positive framing</span></td>
<td><span style="font-weight: 400">&#8220;Don&#8217;t do X&#8221; forces Claude to infer what to do. Stating the desired behavior directly gives Claude a single target to hit.</span></td>
<td><span style="font-weight: 400">Declare sharing explicitly on every class, with sharing for user-facing logic, and inherited sharing for utility classes called from both contexts.</span></td>
</tr>
<tr>
<td><span style="font-weight: 400">Diverse examples</span></td>
<td><span style="font-weight: 400">Cover edge cases so Claude doesn&#8217;t over-fit to one shape. Anthropic recommends 3-5 varied examples to prevent narrow pattern matching.</span></td>
<td><code>selector.cls</code><span> could show four methods: </span><code>getByIds</code><span> (bulk), </span><code>getByName</code><span> (LIKE with sanitization), </span><code>getWithContacts</code><span> (parent-child subquery), </span><code>getContactsWithAccount</code><span> (child-to-parent). Same pattern, four shapes. Claude then learns &#8220;selector&#8221; and not &#8220;single query method.”</span></td>
</tr>
</tbody>
</table>
<p>For an <code>authoring-apex</code> skill let’s explore the reference files you might consider including.</p>
<table>
<tbody>
<tr>
<td><b>Reference</b></td>
<td><b>What the doc contains</b></td>
<td><b>What goes wrong without it</b></td>
</tr>
<tr>
<td><code>best-practices.md</code></td>
<td><span style="font-weight: 400">Your coding conventions (4-space indent, 120-char lines), naming rules, ApexDoc requirements, API version management, and general guidance</span></td>
<td><span style="font-weight: 400">Drift from project conventions &#8211; Claude generates valid code that doesn’t look like </span><i><span style="font-weight: 400">your</span></i><span style="font-weight: 400"> code.</span></td>
</tr>
<tr>
<td><code>design-patterns.md</code></td>
<td><span style="font-weight: 400">Decision trees for patterns (Factory, Strategy, Selector, Service, Batch, Queueable, Unit of Work, Domain Model, etc.). Each pattern includes a &#8220;when to use&#8221; table so Claude doesn&#8217;t default to the most common pattern</span></td>
<td><span style="font-weight: 400">Without this, Claude tends to produce Service classes for everything, even when a Selector or Strategy would be more appropriate.</span></td>
</tr>
<tr>
<td><code>anti-patterns.md</code></td>
<td><span style="font-weight: 400">Common Apex mistakes, each with a BAD/GOOD code pair and an explanation of *</span><i><span style="font-weight: 400">why</span></i><span style="font-weight: 400">* it fails at scale</span></td>
<td><span style="font-weight: 400">Covers common mistakes Claude makes more often than human developers (e.g., generating d</span><span style="font-weight: 400">atabase.query()</span><span style="font-weight: 400"> without </span><span style="font-weight: 400">AccessLevel</span><span style="font-weight: 400"> hints, or using legacy </span><span style="font-weight: 400">System.assertEquals</span><span style="font-weight: 400"> instead of </span><span style="font-weight: 400">Assert.areEqual</span><span style="font-weight: 400">).</span></td>
</tr>
<tr>
<td><code>security-guide.md</code></td>
<td><span style="font-weight: 400">CRUD/FLS enforcement using </span><span style="font-weight: 400">USER_MODE</span><span style="font-weight: 400"> (API 56+) and </span><span style="font-weight: 400">Security.stripInaccessible()</span><span style="font-weight: 400"> for backward compatibility. SOQL injection prevention. XSS protection patterns</span></td>
<td><span style="font-weight: 400">Without this reference, Claude sometimes generates code that works for someone assigned an admin profile but throws </span><span style="font-weight: 400">INSUFFICIENT_ACCESS</span><span style="font-weight: 400"> for standard users</span></td>
</tr>
<tr>
<td><code>transaction-security-policy.md</code></td>
<td><span style="font-weight: 400">Specialized references. This example covers </span><span style="font-weight: 400">TxnSecurity.EventCondition</span><span style="font-weight: 400"> implementations (Enhanced Transaction Security). These classes are </span><span style="font-weight: 400">global</span><span style="font-weight: 400">, run in system context, and evaluate monitoring events</span></td>
<td><span style="font-weight: 400">These are special cases. They break every normal rule about sharing and visibility. Without this reference, Claude applies </span><span style="font-weight: 400">with sharing</span><span style="font-weight: 400"> to these types of classes and breaks them.</span></td>
</tr>
</tbody>
</table>
<p><span style="font-weight: 400">This leverages what Anthropic calls providing &#8220;context and motivation behind instructions.&#8221; The references don&#8217;t just say </span><i><span style="font-weight: 400">what</span></i><span style="font-weight: 400"> to do, they explain </span><i><span style="font-weight: 400">why</span></i><span style="font-weight: 400">, enabling Claude to generalize correctly to novel situations rather than pattern-matching blindly.</span></p>
<p><span style="font-weight: 400">As an example the <a href="http://anti-patterns.md"><u><code>anti-patterns.md</code></u></a><span> </span></span><span style="font-weight: 400"> could look like this. This structure is designed for practices that are likely to go wrong repeatedly. Every section follows the same BAD/GOOD formula because Claude needs to recognise and avoid independent mistakes. Learn </span><a href="https://architect.salesforce.com/docs/architect/well-architected-tools/guide/anti-patterns.html"><span style="font-weight: 400">more about these anti-patterns</span></a><span style="font-weight: 400"> in the documentation.</span></p>
<pre language="markdown"># Apex Anti-Patterns

## Table of Contents

- [SOQL in Loops](#soql-in-loops)
- 

---

## SOQL in Loops
**Anti-pattern:** Executing SOQL queries inside a `for` or `while` loop.

**Why this fails:** Salesforce enforces a hard limit of 100 SOQL queries
per synchronous transaction. Triggers fire in batches of up to 200 records,
so a single SOQL inside a loop exhausts the limit after just 100 records.

```apex
// BAD: 1 SOQL per iteration — hits 100-query limit at record 101
for (Account acc : accounts) {
    List contacts = [SELECT Id FROM Contact WHERE AccountId = :acc.Id];
}

Fix: Query once in bulk, then iterate over results.

// GOOD: 1 SOQL total
Map&lt;Id, List&gt; contactsByAccount = new Map&lt;Id, List&gt;();
for (Contact c : [SELECT Id, AccountId FROM Contact WHERE AccountId IN :accountIds]) {
    ...
}
// add other entries to align with the anti-patterns in salesforce well-architected
</pre>
<p><span style="font-weight: 400">For speciality examples, like transaction security policies, the markdown could follow this structure and cover one specific Apex feature that breaks all the normal rules. It needs to teach Claude when the normal rules don&#8217;t apply, what to do instead, and why. There&#8217;s no BAD/GOOD pair because the &#8220;bad&#8221; code (e.g., global without sharing) is actually the correct code in this context. Note that the example in this case refers to a worked example in the assets folder.</span></p>
<pre language="markdown"># Transaction Security Policy (Apex condition)

## Table of Contents

- [Overview](#overview)
- [Class shape](#class-shape)
- [Sharing](#sharing)
- [Guardrails](#guardrails)
- [Example](#example)

---

## Overview

Enhanced Transaction Security policies call Apex when Condition Builder
is not enough. Implement `TxnSecurity.EventCondition` with a single
`evaluate(SObject event)` method.

## Class shape

- Declare the class **`global`** (required for Setup selection)
- Implement `TxnSecurity.EventCondition`
- Signature: `global Boolean evaluate(SObject event)`

## Sharing

These classes are **not** user-facing controllers. Use `without sharing`.
The normal `with sharing` default does NOT apply here -- applying it
will break the policy silently.

## Guardrails

- One bulk SOQL query max (no SOQL in loops -- governor limits still apply)
- Return `true` to block the transaction, `false` to allow

## Example

See assets/transaction-security-policy.cls

</pre>
<h3><span style="font-weight: 400">assets/: Few-shot by example</span></h3>
<p><span style="font-weight: 400">Anthropic highlights that one of the </span><span style="font-weight: 400">most reliable ways to steer Claude&#8217;s output format, tone, and structure is with implicit few-shot examples. Few-shot just means that you provide a few good examples as a template. Each template embeds the non-negotiable requirements: ApexDoc comments, bulk-safe logic, explicit sharing declarations, CRUD/FLS enforcement, and dependency injection for testability. When Claude adapts a template, it inherits these properties by construction rather than needing to recall them from training.</span></p>
<p><span style="font-weight: 400">For Apex specifically, the world is your oyster here, but let’s explore some good use cases.</span></p>
<table>
<tbody>
<tr>
<td><b>Asset</b></td>
<td><b>What the doc contains</b></td>
<td><b>What goes wrong without it</b></td>
</tr>
<tr>
<td><code>service.cls</code></td>
<td><span style="font-weight: 400">Business logic orchestrator. Delegates queries, collects DML, handles errors.</span></td>
<td><span style="font-weight: 400">Claude puts query logic, DML, and orchestration in one huge method instead of looking into a separation of concerns.</span></td>
</tr>
<tr>
<td><code>selector.cls</code></td>
<td><span style="font-weight: 400">Centralized SOQL access. One selector per sObject.</span></td>
<td><span style="font-weight: 400">Claude scatters ad-hoc queries throughout service and trigger code. Makes SOQL difficult to audit for security/performance in code review.</span></td>
</tr>
<tr>
<td><code>batch.cls</code></td>
<td>Large-volume async processing (10,000+ records). <code>Database.Batchable</code> and <code>Database.Stateful</code>.</td>
<td><span style="font-weight: 400">Claude writes batch jobs that swallow errors silently, or using non-stateful batches with no way to report failures.</span></td>
</tr>
<tr>
<td><code>queueable.cls</code></td>
<td><span style="font-weight: 400">Async processing with object passing and job chaining.</span></td>
<td><span style="font-weight: 400">Claude chains without depth limits (infinite recursion), or drops errors because partial DML isn&#8217;t used.</span></td>
</tr>
<tr>
<td><code>transaction-security-policy.cls</code></td>
<td>Enhanced Transaction Security <code>EventCondition</code> implementation.</td>
<td>Claude applies normal rules (with sharing, <code>USER_MODE</code>) to a class that <i>must</i> break them. Transaction Security Policy (TSP) classes are global without sharing by platform requirement.</td>
</tr>
</tbody>
</table>
<p><span style="font-weight: 400">Each template isn&#8217;t just &#8220;how to write X.&#8221; It&#8217;s a pre-loaded, few-shot example with the non-negotiable requirements baked into the structure.</span></p>
<h3><span style="font-weight: 400">hooks.yaml and validator scripts: The safety net</span></h3>
<p>A hook is a shell command that Claude Code runs automatically at a specific lifecycle moment. You configure them in <code>hooks.yaml</code>. The script is whatever that command executes, typically a Python validator that inspects the tool&#8217;s input/output and returns structured feedback.</p>
<p><span style="font-weight: 400">Hooks close the feedback loop without human intervention. Claude writes something, the hook evaluates it immediately, and Claude sees the result in the same conversation turn. The correction happens in context while Claude still has the full problem loaded.</span></p>
<p><span style="font-weight: 400">The hooks file wires the validator to the right moment. For example, a preflight check runs when the user submits a prompt.</span></p>
<pre language="json">{
  "hooks": {
    "UserPromptSubmit": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "python3 ${CLAUDE_SKILL_DIR}/hooks/scripts/preflight-apex-check.py",
            "timeout": 10000
          }
        ]
      }
    ]
  }
}
</pre>
<p>Or a <code>PostToolUse</code> hook fires after every file write or edit.</p>
<pre language="yaml">---
name: authoring-apex
description: ...
hooks:
  PostToolUse:
    - matcher: "Write|Edit"
      hooks:
        - type: command
          command: "python3 ${CLAUDE_SKILL_DIR}/hooks/scripts/preflight-apex-check.py"
          timeout: 90000
---
</pre>
<p>Claude Code delivers the hook context as JSON on stdin. The payload shape varies by lifecycle event: <code>PostToolUse</code> includes the tool name, input parameters, and output; <code>UserPromptSubmit</code> includes the user&#8217;s prompt text.</p>
<p>Consider a preflight check for your development team. The <code>SKILL.md</code> workflow says &#8220;run Code Analyzer&#8221; in the validate phase. But what if Code Analyzer isn&#8217;t installed or is the wrong version? Without a hook, Claude attempts the command mid-workflow, gets an error, and the developer has to diagnose a missing plugin. With a hook, the check runs the moment Apex work begins.</p>
<pre language="python">import json, subprocess, sys

MINIMUM_VERSION = "5.5.0"

def parse_version(v):
    """Parse version string into comparable tuple."""
    return tuple(int(x) for x in v.split(".")[:3])

def main():
    hook_input = json.load(sys.stdin)
    prompt = hook_input.get("prompt", "").lower()

    # Only check when Apex work is likely
    apex_keywords = ["apex", ".cls", "class", "trigger", "service", "selector", "batch"]
    if not any(kw in prompt for kw in apex_keywords):
        sys.exit(0)

    try:
        result = subprocess.run(
            ["sf", "plugins", "--json"],
            capture_output=True, text=True, timeout=15
        )
        if result.returncode == 0:
            plugins = json.loads(result.stdout)
            for plugin in plugins:
                if plugin.get("name") == "@salesforce/plugin-code-analyzer":
                    version = plugin.get("version", "0.0.0")
                    if parse_version(version) &gt;= parse_version(MINIMUM_VERSION):
                        sys.exit(0)
                    else:
                        print("=== Preflight Check ===")
                        print(f"  [WARNING] Code Analyzer {version} is below minimum {MINIMUM_VERSION}.")
                        print(f"  Install: sf plugins install @salesforce/plugin-code-analyzer@latest")
                        sys.exit(0)
    except (FileNotFoundError, subprocess.TimeoutExpired, json.JSONDecodeError):
        pass

    print("=== Preflight Check ===")
    print("  [WARNING] Salesforce Code Analyzer is not installed.")
    print("  The authoring-apex skill requires it for validation.")
    print("  Install: sf plugins install @salesforce/plugin-code-analyzer")
    sys.exit(0)

if __name__ == "__main__":
    main()

</pre>
<p><span style="font-weight: 400">The developer is told up front that their toolchain is incomplete rather than discovering it mid-workflow when the validate phase fails. </span></p>
<p><span style="font-weight: 400">That&#8217;s what separates an instruction from a hook. The</span><span style="font-weight: 400"> <code>SKILL.md</code></span><span style="font-weight: 400"> says &#8220;run Code Analyzer in the validate phase.&#8221; The hook proves that it can run before the workflow even starts. Instructions are aspirational; hooks are mechanical. You want both capabilities.</span></p>
<h2><span style="font-weight: 400">Why agent skills need human and mechanical validation</span></h2>
<p><span style="font-weight: 400">Skills are not a silver bullet. They dramatically narrow the probability distribution of Claude&#8217;s outputs, but they operate on a fundamentally non-deterministic system. Two things work against you:</span></p>
<p><b>You can&#8217;t fully specify behavior</b><span style="font-weight: 400">. No matter how precise your</span> <span style="font-weight: 400"><code>SKILL.md</code></span><span style="font-weight: 400">, real-world requirements contain novel combinations that aren&#8217;t covered by templates or references. Claude must still make judgement calls and newer models interpret prompts more literally, so slight underspecification produces inconsistent results. If your</span> <span style="font-weight: 400"><code>SKILL.md</code></span><span style="font-weight: 400"> says &#8220;add error handling&#8221; without specifying the pattern, you&#8217;ll get different approaches each time.</span></p>
<p><b>Outputs are probabilistic</b><span style="font-weight: 400">. Even with structured prompts, role assignment, examples, and validation hooks, you&#8217;re optimizing a hit rate. You move from 60% correct to 95% correct — a massive improvement —  but the remaining 5% is why validators exist. This is why the architecture includes both preventive measures (structured prompts, references, templates) and detective measures (automated validators, scoring rubrics, blocking on errors). The skill assumes Claude will sometimes get it wrong and builds correction into the workflow rather than pretending perfection is achievable.</span></p>
<h2><span style="font-weight: 400">Getting started</span></h2>
<p><span style="font-weight: 400">Anthropic&#8217;s golden rule for prompts applies directly: &#8220;Show your prompt to a colleague with minimal context and ask them to follow it. If they&#8217;d be confused, Claude would be too.&#8221; A well-built agent skill is unambiguous to both a human reader and the model.</span></p>
<p><span style="font-weight: 400">You don&#8217;t need to build a skill from scratch. Anthropic provides a </span><span style="font-weight: 400"><code>skill-creator</code></span> <a href="https://github.com/anthropics/skills/blob/main/skills/skill-creator/SKILL.md"><span style="font-weight: 400">skill</span></a><span style="font-weight: 400"> that walks you through the full process: capturing intent, writing the <code>SKILL.md</code></span><span style="font-weight: 400">, creating test cases, running evals, and iterating until the output meets your bar. Install it and tell Claude what you want the skill to do; it handles the scaffolding, interviews you on edge cases, and generates a working draft you can refine.</span></p>
<p><span style="font-weight: 400">If you&#8217;d prefer to work from an existing Salesforce-specific example, the </span><a href="https://github.com/forcedotcom/afv-library"><span style="font-weight: 400">Agentforce Vibes skill library</span></a><span style="font-weight: 400"> includes production-ready skills for Apex, LWC, Flow, and more. Install them, use them, and look at how they&#8217;re structured; they follow similar patterns described in this post. The Agentforce Vibes skill library puts rules and rationale inline in the </span><span style="font-weight: 400"><code>SKILL.md</code></span><span style="font-weight: 400"> and uses complete Apex source files as templates rather than Markdown reference docs. This keeps the skill self-contained in fewer files, though it trades modularity: updating one rule means editing the main workflow file. </span></p>
<p><span style="font-weight: 400">The skill grows with your needs. Start with the </span><span style="font-weight: 400"><code>skill-creator</code></span><span style="font-weight: 400"> or an existing skill, customize what doesn&#8217;t fit your project, and build new skills only when you have a gap not covered by existing ones.</span></p>
<h2><span style="font-weight: 400">Resources</span></h2>
<ul>
<li style="font-weight: 400"><span style="font-weight: 400">GitHub: </span><a href="https://github.com/forcedotcom/afv-library"><span style="font-weight: 400">Salesforce’s collection of agent skills</span></a></li>
<li style="font-weight: 400"><span style="font-weight: 400">Trailhead: </span><a href="https://trailhead.salesforce.com/content/learn/modules/prompt-fundamentals"><span style="font-weight: 400">Prompt Fundamentals</span></a></li>
<li style="font-weight: 400"><span style="font-weight: 400">Trailhead: </span><a href="https://trailhead.salesforce.com/content/learn/modules/prompt-engineering-techniques"><span style="font-weight: 400">Prompt Engineering Techniques</span></a></li>
<li style="font-weight: 400"><span style="font-weight: 400">GitHub: </span><a href="https://github.com/anthropics/skills/blob/main/skills/skill-creator/SKILL.md"><span style="font-weight: 400">Anthropics Skill Creator</span></a></li>
<li style="font-weight: 400"><span style="font-weight: 400">Documentation: </span><a href="https://docs.anthropic.com/en/docs/claude-code/skills"><span style="font-weight: 400">Claude Code</span></a><span style="font-weight: 400"> </span></li>
</ul>
<h2><span style="font-weight: 400">About the author</span></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>&nbsp;</p>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/05/build-a-salesforce-agent-skill-with-claude-code">Build a Salesforce Agent Skill with Claude Code</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/05/build-a-salesforce-agent-skill-with-claude-code/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
	<post-id xmlns="com-wordpress:feed-additions:1">206402</post-id><media:thumbnail url="https://d259t2jj6zp7qm.cloudfront.net/images/20260528154202/SingleHeadshot-2-e1780008137933.png?w=1000" />
<media:content url="https://d259t2jj6zp7qm.cloudfront.net/images/20260528154202/SingleHeadshot-2-e1780008137933.png?w=1000" medium="image" />
	</item>
		<item>
		<title>Capture Rich Data within Agent Conversations by Using Discovery Framework</title>
		<link>https://developer.salesforce.com/blogs/2026/05/capture-rich-data-within-agent-conversations-by-using-discovery-framework</link>
		<comments>https://developer.salesforce.com/blogs/2026/05/capture-rich-data-within-agent-conversations-by-using-discovery-framework#respond</comments>
		<pubDate>Wed, 27 May 2026 15:00:43 +0000</pubDate>
		<dc:creator><![CDATA[adityabhansali]]></dc:creator>
				<category><![CDATA[Agentforce]]></category>
		<category><![CDATA[Apex]]></category>
		<category><![CDATA[Architecture]]></category>
		<category><![CDATA[Lightning Web Components]]></category>
		<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[Custom LWC]]></category>
		<category><![CDATA[Discovery Framework]]></category>
		<category><![CDATA[Financial Services Cloud]]></category>
		<category><![CDATA[Omniscript]]></category>
		<category><![CDATA[Salesforce Developer]]></category>

		<guid isPermaLink="false">https://developer.salesforce.com/blogs/?p=206387</guid>
		<description><![CDATA[<p>Learn how to integrate Discovery Framework, an Omniscript-based digital form engine in Agentforce for Financial Services, directly into an Agentforce agent.</p>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/05/capture-rich-data-within-agent-conversations-by-using-discovery-framework">Capture Rich Data within Agent Conversations by Using Discovery Framework</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">Complex enterprise workflows, such as dispute intake, require capturing large amounts of structured information accurately. However, relying solely on text-based conversational interfaces creates a suboptimal experience: data entry is error-prone, UI patterns like radio buttons or checkboxes can&#8217;t be rendered in a chat stream, and the risk of misinterpretation increases with every unstructured exchange. Therefore, a structured approach to data capture is essential.</span></p>
<p><span style="font-weight: 400">In this post, we’ll show you how to integrate </span><a href="https://help.salesforce.com/s/articleView?id=ind.fsc_discovery_framework_overview.htm&amp;type=5"><span style="font-weight: 400">Discovery Framework</span></a><span style="font-weight: 400">, an Omniscript-based digital form engine 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 for Financial Services</span></a><span style="font-weight: 400">, directly into an Agentforce agent. </span></p>
<p><span style="font-weight: 400">By the end, you&#8217;ll have a working solution that renders structured forms inside the Agent window, replacing error-prone conversational capture with a reliable, UI-driven experience. </span><span style="font-weight: 400">This solution is for Salesforce Developers and architects building Agentforce experiences for the financial services industry. It requires familiarity with Apex, Lightning Web Components (LWC), and Omniscript basics.</span></p>
<h2><span style="font-weight: 400">The problem: Conversational capture falls short</span></h2>
<p><span style="font-weight: 400">In financial services, a typical transaction dispute intake process has five stages:</span></p>
<ol>
<li style="font-weight: 400"><span style="font-weight: 400">Identifying the impacted customer and account</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Identifying the impacted transaction with enrichment for ease of identification</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Capturing the reason for dispute and disputed amount</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Creating a case for dispute intake</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Capturing a dispute questionnaire</span></li>
</ol>
<p><span style="font-weight: 400">For high-volume contact center agents, Stage 5 is the hardest. Questionnaires are dynamic, changing based on payment network rules and transaction type. </span></p>
<p><span style="font-weight: 400">A pure conversational experience creates real problems:</span></p>
<ul>
<li style="font-weight: 400"><span style="font-weight: 400">Users can&#8217;t navigate backwards to update responses</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Special UI patterns (radio buttons, checkboxes) don&#8217;t translate to text</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Any AI paraphrase of a question risks capturing the wrong answer</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Questions must appear in strict order, with required/optional labels intact</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Longer questionnaires need save-and-resume support</span></li>
</ul>
<p><span style="font-weight: 400">The screen recording below shows an agent in conversational mode, asking dispute questionnaire questions one at a time.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206404" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260526134558/Screen-recording-of-an-agent-in-conversational-mode-asking-dispute-questionnaire-questions-one-at-a-time_F.gif?w=800" class="postimages" width="800" height="450" alt="Screen recording of an agent in conversational mode, asking dispute questionnaire questions one at a time" />
			  </span>
			</p>
<h2><span style="font-weight: 400">The solution: Discovery Framework in agents</span></h2>
<p><span style="font-weight: 400">Agentforce Financial Services provides Discovery Framework, a feature that creates digital forms to collect and validate data while avoiding time-consuming, error-prone manual methods.</span></p>
<p><span style="font-weight: 400">This framework was originally built for non-agentic, UI-driven scenarios. When you bring it into agents, you give users navigable, structured forms that address all five problems discussed above.</span></p>
<p><b>Key benefits:</b></p>
<ul>
<li style="font-weight: 400"><span style="font-weight: 400">Navigation between questions with ability to update responses</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Proper rendering of radio buttons, checkboxes, and other form controls</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Exact question display without AI interpretation</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Correct ordering and required/optional field marking</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">State persistence for incomplete forms</span></li>
</ul>
<p><span style="font-weight: 400">The framework renders a structured form directly in the agent output, with no conversational questions and no AI paraphrase risk. The screen recording below shows an agent in a Discovery Framework experience with structured form fields, radio buttons, and navigation controls embedded in the Agent interface.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206405" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260526134649/Screen-recording-of-an-agent-in-a-Discovery-Framework-experience-with-structured-form-fields-radio-buttons-and-navigation-controls-embedded-in-the-Agent-interface._F.gif?w=800" class="postimages" width="800" height="450" alt="Screen recording of an agent in a Discovery Framework experience with structured form fields, radio buttons, and navigation controls embedded in the Agent interface." />
			  </span>
			</p>
<p><b>Prerequisites:</b><span style="font-weight: 400"> This experience is available only in UI-based channels — Lightning Experience, Messaging for Experience Sites, and third-party sites. It is not supported in Voice or Short Message Service (SMS) channels, where complex UI elements can&#8217;t be displayed.</span></p>
<h2><span style="font-weight: 400">Technical architecture overview</span></h2>
<p><span style="font-weight: 400">The integration uses Agentforce&#8217;s support for custom Lightning types and custom LWC components. Here&#8217;s the component architecture:</span></p>
<ol>
<li style="font-weight: 400"><b>Discovery Framework Omniscript:</b><span style="font-weight: 400"> The digital form definition with assessment questions</span></li>
<li style="font-weight: 400"><b>Custom LWC component:</b><span style="font-weight: 400"> Wraps the Omniscript and handles data binding</span></li>
<li style="font-weight: 400"><b>Apex class:</b><span style="font-weight: 400"> Executes business logic when the Agent action runs</span></li>
<li style="font-weight: 400"><b>Custom Lightning type:</b><span style="font-weight: 400"> Associates the Apex output type with the LWC renderer</span></li>
<li style="font-weight: 400"><b>Agent action:</b><span style="font-weight: 400"> Uses the Apex class as its reference action and the custom Lightning type as its output renderer</span></li>
</ol>
<p><span style="font-weight: 400">When an Agent action executes via a subagent, the custom Lightning type renders in the output, displaying the Discovery Framework Omniscript directly in the Agent window. </span></p>
<p><span style="font-weight: 400">The architecture diagram below illustrates the runtime flow: a subagent triggers an agent action, which then executes Apex class business logic and renders the custom Lightning type LWC component, wrapping the Discovery Framework Omniscript in the Agent interface.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206390" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260526090803/Architecture-diagram-illustrating-the-Agentforce-and-Discovery-Framework-runtime-flow-e1779811763197.png?w=1000" class="postimages" width="1000" height="546" alt="Architecture diagram illustrating the Agentforce and Discovery Framework runtime flow" />
			  </span>
			</p>
<p><span style="font-weight: 400">To learn more about the underlying capability, see</span><a href="https://developer.salesforce.com/docs/ai/agentforce/guide/lightning-types.html"> <span style="font-weight: 400">Enhance the Agent UI with Custom LWCs and Lightning Types</span></a><span style="font-weight: 400">.</span></p>
<h2><span style="font-weight: 400">Steps to implement Discovery Framework in Agentforce</span></h2>
<p><span style="font-weight: 400">The implementation involves five steps. You start by creating a Discovery Framework Omniscript, then build the Apex classes that define the agent action&#8217;s business logic. Next, you create a Lightning web component to wrap and render the Omniscript inside the Agent window. You then configure a custom Lightning type to associate the Apex output with the LWC, and finally, you set up the agent action, which uses the Apex class as the reference action and the Lightning type as the output render. Let’s take a detailed look.</span></p>
<h3><span style="font-weight: 400">Step 1: Create a Discovery Framework Omniscript</span></h3>
<p><span style="font-weight: 400">First, we’ll need to create a series of assessment questions, then add them as steps in an </span><a href="https://help.salesforce.com/s/articleView?id=xcloud.os_omniscripts_8355.htm&amp;type=5"><span style="font-weight: 400">Omniscript</span></a><span style="font-weight: 400"> (a drag-and-drop digital form builder in Discovery Framework). See the </span><a href="https://help.salesforce.com/s/articleView?language=en_US&amp;id=ind.fsc_discovery_framework_overview.htm&amp;type=5"><span style="font-weight: 400">Discovery Framework documentation</span></a><span style="font-weight: 400"> for a step-by-step walkthrough.</span></p>
<p><span style="font-weight: 400">For this example, we’ll create two sample dispute questions of type </span><b>Radio Button</b><span style="font-weight: 400"> with Yes/No responses.</span></p>
<p><b>Omniscript properties:</b></p>
<ul>
<li style="font-weight: 400"><b>Type:</b><span style="font-weight: 400"> FinancialServices</span></li>
<li style="font-weight: 400"><b>SubType:</b><span style="font-weight: 400"> DisputeQuestionnaire</span></li>
<li style="font-weight: 400"><b>Language:</b><span style="font-weight: 400"> English</span></li>
</ul>
<p><b>Important:</b><span style="font-weight: 400"> Configure the Omniscript for a smaller viewport, so it renders correctly inside the agent window. Open the Omniscript properties panel and set the </span><b>Viewport</b><span style="font-weight: 400"> width to a maximum of 600px. Enable </span><b>Responsive Mode</b><span style="font-weight: 400"> if available. This ensures that the form fits within the agent output area without scrolling issues.</span></p>
<p><b>Navigation configuration:</b></p>
<ul>
<li style="font-weight: 400"><span style="font-weight: 400">Enable </span><b>Allow Save for Later</b><span style="font-weight: 400"> to let users save incomplete forms</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Enable </span><b>Show Progress Indicator</b><span style="font-weight: 400"> to display form completion status</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Configure step-by-step navigation with </span><b>Previous</b><span style="font-weight: 400"> and </span><b>Next</b><span style="font-weight: 400"> buttons</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Select </span><b>Hide step chart</b><span style="font-weight: 400"> in Step Chart Options</span></li>
</ul>
<p><span style="font-weight: 400">For detailed instructions on creating Discovery Framework Omniscripts, see the </span><a href="https://help.salesforce.com/s/articleView?language=en_US&amp;id=ind.fsc_discovery_framework_overview.htm&amp;type=5"><span style="font-weight: 400">Discovery Framework documentation</span></a><span style="font-weight: 400">.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206395" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260526090820/Screenshot-of-the-Omniscript-properties-panel-showing-viewport-configuration-and-navigation-settings.png?w=1999" class="postimages" width="1999" height="620" alt="Screenshot of the Omniscript properties panel showing viewport configuration and navigation settings" />
			  </span>
			</p>
<p><span style="font-weight: 400">To learn more, watch the video: </span><a href="https://salesforce.vidyard.com/watch/twss6ysqr7WQ8yyEi7WfF2"><span style="font-weight: 400">Discovery Framework for Financial Services Cloud</span></a><span style="font-weight: 400"> </span></p>
<h3><span style="font-weight: 400">Step 2: Create Apex classes</span></h3>
<p><span style="font-weight: 400">Next, we’ll create an Apex class that captures the business logic for the agent action. In this example, the class creates a real Case record and returns the case number and ID. You’ll want to adapt the logic to your business needs.</span></p>
<p><span style="font-weight: 400">The following Apex classes define the input, output, and business logic for the agent action. Each class plays a specific role in handling the incoming dispute request, structuring the output data, and executing the case creation logic.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206396" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260526090822/Screenshot-showing-needed-Apex-classes.png?w=716" class="postimages" width="716" height="404" alt="Screenshot showing needed Apex classes" />
			  </span>
			</p>
<p><span style="font-weight: 400">Here is sample code for the Apex classes noted above.</span></p>
<p><b>DisputeQuestionnaireInput.cls</b><br />
This class serves as the input of the agent action and contains a <code>disputeRequest</code> String field.<br />
<a href="https://github.com/AdityaBhansali1/salesforce-integrate-discovery-framework-omniscript-in-agentforce/blob/master/force-app/main/default/classes/DisputeQuestionnaireInput.cls"><u>See sample code.</u></a></p>
<p><b>DisputeQuestionnaireOutput.cls</b><br />
This class serves as the output of the agent action and contains a <code>DisputeQuestionnaireCaseOutput</code> object.<br />
<a href="https://github.com/AdityaBhansali1/salesforce-integrate-discovery-framework-omniscript-in-agentforce/blob/master/force-app/main/default/classes/DisputeQuestionnaireOutput.cls"><u>See sample code</u></a>.</p>
<p><b>DisputeQuestionnaireCaseOutput.cls</b><br />
This class contains the <code>caseNumber</code> and <code>caseId</code> fields as Strings.<br />
<a href="https://github.com/AdityaBhansali1/salesforce-integrate-discovery-framework-omniscript-in-agentforce/blob/master/force-app/main/default/classes/DisputeQuestionnaireCaseOutput.cls"><u>See sample code</u></a>.</p>
<p><b>DisputeQuestionnaireAction.cls</b><br />
This class has the business logic that is executed by the agent to create a case and add it to a <code>DisputeQuestionnaireCaseOutput</code> object which is embedded inside a <code>DisputeQuestionnaireOutput</code> object.<br />
<a href="https://github.com/AdityaBhansali1/salesforce-integrate-discovery-framework-omniscript-in-agentforce/blob/master/force-app/main/default/classes/DisputeQuestionnaireAction.cls"><u>See sample code</u></a>.</p>
<h3><span style="font-weight: 400">Step 3: Create a Lightning web component</span></h3>
<p><span style="font-weight: 400">Next, we’ll create a Lightning Web Component that wraps the Discovery Framework Omniscript from Step 1. </span></p>
<p><span style="font-weight: 400">The <code>displayDisputeQuestionnaire</code></span><span style="font-weight: 400"> Lightning web component wraps the Discovery Framework Omniscript and renders it inside the Agent window. This component includes an HTML, a JavaScript and an XML file. The HTML defines the component structure, the JavaScript handles data binding and event subscription, and the metadata XML registers the component as an Agentforce output target.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206393" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260526090817/Screenshot-of-the-displayDisputeQuestionnaire-Lightning-web-component-configuration.png?w=640" class="postimages" width="640" height="232" alt="Screenshot of the displayDisputeQuestionnaire Lightning web component configuration" />
			  </span>
			</p>
<p><span style="font-weight: 400">Here is a sample code for the <code>displayDisputeQuestionnaire</code></span><span style="font-weight: 400"> Lightning web component.</span></p>
<p><b>displayDisputeQuestionnaire.html<br />
</b>This HTML component wraps the Discovery Framework Omniscript created in Step 1, using <code>&lt;omnistudio-omnistudio-standard-runtime-wrapper&gt;</code><span>.</span></p>
<pre language="html">&lt;template&gt;
   &lt;div class=&quot;slds-card&quot;&gt;
           &lt;div class=&quot;slds-card&quot;&gt;
               &lt;div class=&quot;slds-modal__content&quot;&gt;
                   &lt;omnistudio-omnistudio-standard-runtime-wrapper
                           type=&quot;FinancialServices&quot;
                           subtype=&quot;DisputeQuestionnaire&quot;
                           language=&quot;English&quot;
                           prefill={prefill}&gt;
                  &lt;/omnistudio-omnistudio-standard-runtime-wrapper&gt;
               &lt;/div&gt;
           &lt;/div&gt;
   &lt;/div&gt;
&lt;/template&gt;
</pre>
<p><b>Note:</b><span style="font-weight: 400"> We’ll use the same Type, SubType, and Language values in the HTML as configured in Step 1, as shown in above code snippet.</span></p>
<p><a href="https://github.com/AdityaBhansali1/salesforce-integrate-discovery-framework-omniscript-in-agentforce/blob/master/force-app/main/default/lwc/displayDisputeQuestionnaire/displayDisputeQuestionnaire.html"><span style="font-weight: 400">See sample code.</span></a></p>
<p><b>displayDisputeQuestionnaire.js<br />
</b><span style="font-weight: 400">This is the JavaScript for the LWC, supporting the above HTML.</span></p>
<pre language="javascript">import { LightningElement, api, track } from 'lwc';
import { NavigationMixin } from 'lightning/navigation';
import pubsub from 'omnistudio/pubsub';

export default class DisplayDisputeQuestionnaire extends NavigationMixin(LightningElement) {
   _value;
   @api
   get value() {
       return this._value;
   }
   set value(value) {
       this._value = value;
   }
   @track prefill; assessmentId; caseNumber; caseId;
   connectedCallback() {
       this.caseNumber = this.value?.caseOutput?.caseNumber || null;
       this.caseId = this.value?.caseOutput?.caseId || null;
       pubsub.register('omniscript_action', {
           data: this.handleOmniAction.bind(this),
       });
   }
   handleOmniAction(data) {
       this.assessmentId = data?.assessmentId || null;
   }
}
</pre>
<p><a href="https://github.com/AdityaBhansali1/salesforce-integrate-discovery-framework-omniscript-in-agentforce/blob/master/force-app/main/default/lwc/displayDisputeQuestionnaire/displayDisputeQuestionnaire.js"><span style="font-weight: 400">See sample code.</span></a></p>
<p><b>displayDisputeQuestionnaire.js-meta.xml<br />
</b>This is the<code> js-meta.xml</code> for the LWC, which captures the target as <code>AgentforceOutput</code>.<br />
<a href="https://github.com/AdityaBhansali1/salesforce-integrate-discovery-framework-omniscript-in-agentforce/blob/master/force-app/main/default/lwc/displayDisputeQuestionnaire/displayDisputeQuestionnaire.js-meta.xml"><u>See sample code.</u></a><span style="font-weight: 400">    </span></p>
<p><b>Key implementation details:</b></p>
<ul>
<li>The component subscribes to <code>omniscript_action</code> events to capture form completion</li>
<li>The <code>prefill</code> property allows pre-populating form fields with contextual data</li>
<li>The <code>value</code> property receives output from the Apex class</li>
<li>Navigation handles redirecting users to the created Case record after completion</li>
<li>The <code>lightning__AgentforceOutput</code> target in the metadata XML is required for the component to render in Agentforce</li>
</ul>
<h3><b>Step 4: Create a custom Lightning type</b></h3>
<p><span style="font-weight: 400">Next, we’ll create a custom Lightning type to associate the Apex output types from Step 2 with the LWC from Step 3.</span></p>
<p><span style="font-weight: 400">The <code>displayQuestionnaireResponse</code></span><span style="font-weight: 400"> Lightning type defines the custom Lightning type. This Lightning type includes the JSON files, which tells Agentforce how to map your Apex output to the LWC renderer and validates the data structure at runtime.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206394" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260526090818/Screenshot-of-the-displayQuestionnaireResponse-Lightning-type-configuration.png?w=530" class="postimages" width="530" height="230" alt="Screenshot of the displayQuestionnaireResponse Lightning type configuration" />
			  </span>
			</p>
<p><span style="font-weight: 400">Here is a sample code for the <code>displayQuestionnaireResponse</code></span><span style="font-weight: 400"> Lightning type</span><span style="font-weight: 400">.</span></p>
<p><b>renderer.json<br />
</b><span style="font-weight: 400">The<code>renderer.json</code></span><span style="font-weight: 400"> file maps the root output ($) to your LWC component.<br />
</span><a href="https://github.com/AdityaBhansali1/salesforce-integrate-discovery-framework-omniscript-in-agentforce/blob/master/force-app/main/default/lightningTypes/disputeQuestionnaireResponse/lightningDesktopGenAi/renderer.json"><span style="font-weight: 400">See sample code.</span></a></p>
<p><b>schema.json<br />
</b><span style="font-weight: 400">The </span><span style="font-weight: 400"><code>schema.json</code></span><span style="font-weight: 400"> file defines the data type structure matching your Apex output class.<br />
</span><a href="https://github.com/AdityaBhansali1/salesforce-integrate-discovery-framework-omniscript-in-agentforce/blob/master/force-app/main/default/lightningTypes/disputeQuestionnaireResponse/schema.json"><span style="font-weight: 400">See sample code.</span></a></p>
<p><span style="font-weight: 400">For detailed setup instructions, see</span><a href="https://developer.salesforce.com/docs/ai/agentforce/guide/lightning-types.html"> <span style="font-weight: 400">Enhance the Agent UI with Custom LWCs and Lightning Types</span></a><span style="font-weight: 400">.</span></p>
<h3><span style="font-weight: 400">Step 5: Create an agent action</span></h3>
<p><span style="font-weight: 400">Finally, we’ll create an agent action that uses the Apex classes from Step 2 for execution and the custom Lightning type from Step 4 for output rendering.</span></p>
<p><b>Agent action configuration steps:</b></p>
<ol>
<li style="font-weight: 400"><span style="font-weight: 400">Navigate to </span><b>Setup</b><span style="font-weight: 400"> &gt; </span><b>Agentforce</b><span style="font-weight: 400"> &gt; </span><b>Agent Actions</b></li>
<li style="font-weight: 400"><span style="font-weight: 400">Click </span><b>New Agent Action</b></li>
<li style="font-weight: 400"><span style="font-weight: 400">Set </span><b>Reference Action Type</b><span style="font-weight: 400"> to Apex</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Select </span><b>DisputeQuestionnaireAction</b><span style="font-weight: 400"> as the Apex class</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Set </span><b>Output Rendering</b><span style="font-weight: 400"> to the </span><b>DisputeQuestionnaireResponse</b><span style="font-weight: 400"> custom Lightning type</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Configure input parameters to match the Agent Topic context</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Add instructions for when the agent should invoke this action</span></li>
</ol>
<p><b>Example subagent instruction:</b></p>
<p><span style="font-weight: 400">When a customer wants to dispute a transaction, execute the Process Dispute Request action to capture detailed dispute information through a structured questionnaire. When this agent action fires as part of a subagent, the Discovery Framework Omniscript renders directly in the Agent window.</span></p>
<p><span style="font-weight: 400">The screenshot below shows a sample agent action configuration in Agent Builder, with the <code>disputeQuestionnaireResponse</code></span><span style="font-weight: 400"> Lightning type created in Step 4 set as the output renderer.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206397" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260526090824/Screenshot-showing-the-sample-agent-action-configuration-in-Agent-Builder-e1779811868668.png?w=1000" class="postimages" width="1000" height="618" alt="Screenshot showing the sample agent action configuration in Agent Builder" />
			  </span>
			</p>
<h3><span style="font-weight: 400">Rendering the Discovery Framework Omniscript in an Agent Script action</span></h3>
<p><span style="font-weight: 400">If your Agent is built using Agent Script, you can also render the Discovery Framework Omniscript in an action which is part of the Agent Script. </span></p>
<p><span style="font-weight: 400">For the dispute example, the code snippet below would help to render the output of the action using the Discovery Framework Omniscript.</span></p>
<pre language="text">outputs:
    caseOutput: object
        label: "Case Output"
        description: "The dispute case details including case number and case Id"
        complex_data_type_name: "c__DisputeQuestionnaireResponse"
        developer_name: "caseOutput"
        is_displayable: True
        filter_from_agent: False
</pre>
<h2><span style="font-weight: 400">Testing the integration</span></h2>
<p><span style="font-weight: 400">The following are steps to test the complete integration.</span></p>
<ol>
<li style="font-weight: 400"><span style="font-weight: 400">Create a subagent that invokes your new agent action</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Start a conversation with the agent</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Trigger the dispute intake flow</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Verify that the Discovery Framework form renders in the Agent interface</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Complete the questionnaire and verify that data is captured correctly</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Confirm that the Case record is created with the captured information</span></li>
</ol>
<p><b>Troubleshooting tips:</b></p>
<ul>
<li>If the form doesn&#8217;t render, verify that the Omniscript Type, SubType, and Language match exactly in both the Omniscript definition and LWC component</li>
<li>If data doesn&#8217;t pass correctly, check that <code>@AuraEnabled</code> annotations are present on all Apex output class properties</li>
<li>If navigation fails, ensure that the LWC component&#8217;s <code>js-meta.xml</code> includes the <code>lightning__AgentforceOutput</code> target</li>
</ul>
<h2><span style="font-weight: 400">Extending this pattern</span></h2>
<p><span style="font-weight: 400">This mechanism isn&#8217;t limited to Discovery Framework Omniscripts; you can apply it to any Omniscript in your org, as long as the user experience fits within a compact viewport.</span></p>
<p><b>Additional use cases:</b></p>
<ul>
<li style="font-weight: 400"><b>Loan application intake</b><span style="font-weight: 400">: Multi-step loan applications with document upload</span></li>
<li style="font-weight: 400"><b>Insurance claims processing</b><span style="font-weight: 400">: Complex claim forms with conditional logic</span></li>
<li style="font-weight: 400"><b>Customer onboarding</b><span style="font-weight: 400">: Know Your Customer (KYC) questionnaires with validation rules</span></li>
<li style="font-weight: 400"><b>Financial planning</b><span style="font-weight: 400">: Goal-setting forms with calculation logic</span></li>
</ul>
<p><span style="font-weight: 400">The key requirement: design Omniscripts for responsive display within the agent interface viewport constraints.</span></p>
<h2><span style="font-weight: 400">Conclusion</span></h2>
<p><span style="font-weight: 400">With these five steps, you can replace fragile conversational question-and-answer flows with a reliable, structured form experience — directly inside Agentforce. This approach combines the conversational intelligence of Agentforce with the rich data collection capabilities of Discovery Framework, delivering the best of both worlds.</span></p>
<p><span style="font-weight: 400">By following this approach, you maintain accurate data capture without AI hallucination, while giving users intuitive, navigable forms that feel natural within the Agent conversation flow. Try it out and share what you build. Post questions on 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 Developer Community</span></a><span style="font-weight: 400"> or Stack Overflow.</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://help.salesforce.com/s/articleView?id=ind.fsc_admin_transaction_dispute_management.htm&amp;type=5"><span style="font-weight: 400">Transaction Dispute Management Intake in Financial Services Cloud</span></a></li>
<li style="font-weight: 400"><span style="font-weight: 400">Documentation: </span><a href="https://help.salesforce.com/s/articleView?id=ind.fsc_discovery_framework_overview.htm&amp;type=5"><span style="font-weight: 400">Discovery Framework</span></a></li>
<li style="font-weight: 400"><span style="font-weight: 400">Video: </span><a href="https://salesforce.vidyard.com/watch/twss6ysqr7WQ8yyEi7WfF2"><span style="font-weight: 400">Discovery Framework for Financial Services Cloud</span></a></li>
<li style="font-weight: 400"><span style="font-weight: 400">Developer Guide: </span><a href="https://developer.salesforce.com/docs/ai/agentforce/guide/lightning-types.html"><span style="font-weight: 400">Enhance the Agent UI with Custom LWCs and Lightning Types</span></a><span style="font-weight: 400"> </span></li>
</ul>
<h2><span style="font-weight: 400">About the author</span></h2>
<p><b>Aditya Bhansali</b><span style="font-weight: 400"> is a Lead Member of Technical Staff in the Agentforce Financial Services organization, where he leads Salesforce engineering teams in building enterprise products on the Salesforce Platform. He shares updates on</span><a href="https://www.linkedin.com/in/abhansali/"><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/05/capture-rich-data-within-agent-conversations-by-using-discovery-framework">Capture Rich Data within Agent Conversations by Using Discovery Framework</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/05/capture-rich-data-within-agent-conversations-by-using-discovery-framework/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
	<post-id xmlns="com-wordpress:feed-additions:1">206387</post-id><media:thumbnail url="https://d259t2jj6zp7qm.cloudfront.net/images/20260526081321/Generic-D-2-e1779808430413.png?w=1000" />
<media:content url="https://d259t2jj6zp7qm.cloudfront.net/images/20260526081321/Generic-D-2-e1779808430413.png?w=1000" medium="image" />
	</item>
		<item>
		<title>Connect Claude with Salesforce Hosted MCP Servers</title>
		<link>https://developer.salesforce.com/blogs/2026/05/connect-claude-with-salesforce-hosted-mcp-servers</link>
		<comments>https://developer.salesforce.com/blogs/2026/05/connect-claude-with-salesforce-hosted-mcp-servers#respond</comments>
		<pubDate>Tue, 26 May 2026 15:00:12 +0000</pubDate>
		<dc:creator><![CDATA[Philippe Ozil]]></dc:creator>
				<category><![CDATA[Announcements]]></category>
		<category><![CDATA[APIs and Integrations]]></category>
		<category><![CDATA[Developer Tooling]]></category>
		<category><![CDATA[New Developments]]></category>
		<category><![CDATA[AI Agents]]></category>
		<category><![CDATA[Claude Code]]></category>
		<category><![CDATA[External Client App]]></category>
		<category><![CDATA[Headless 360]]></category>
		<category><![CDATA[Model context protocol]]></category>
		<category><![CDATA[OAuth 2.0]]></category>
		<category><![CDATA[Salesforce Hosted MCP Servers]]></category>
		<category><![CDATA[SOQL Queries]]></category>

		<guid isPermaLink="false">https://developer.salesforce.com/blogs/?p=206373</guid>
		<description><![CDATA[<p>Stop logging into Lightning Experience for daily operations. Learn how to securely configure external tools like Claude Code to interact directly with the Salesforce Platform using standard and custom Hosted MCP Servers.</p>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/05/connect-claude-with-salesforce-hosted-mcp-servers">Connect Claude with 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><span style="font-weight: 400">What if you could act on Salesforce records and run business logic without having to log into Lightning Experience? For example, in the context of a hotel management application, you could use an agent with tools that load upcoming reservations, analyze popular events around the hotel’s location and then create personalized marketing campaigns around those events without having to leave the chat interface of the agent. Now, you can do so with </span><a href="https://www.salesforce.com/news/stories/salesforce-headless-360-announcement/"><span style="font-weight: 400">Headless 360</span></a><span style="font-weight: 400"> using agentic tools like Claude Desktop or Claude Code to interact with the Salesforce Platform through the </span><a href="https://modelcontextprotocol.io/docs/getting-started/intro"><span style="font-weight: 400">Model Context Protocol</span></a><span style="font-weight: 400"> (MCP).</span></p>
<p><span style="font-weight: 400">In this post, we&#8217;ll explore the benefits of working with MCP servers, then we’ll focus specifically on how to configure Claude for MCP, and finally, we’ll share some sample use cases.</span></p>
<h2><span style="font-weight: 400">What are Salesforce Hosted MCP Servers?</span></h2>
<p><span style="font-weight: 400">The Model Context Protocol (MCP) is an open standard that lets agentic tools like </span><a href="https://www.postman.com/salesforce-developers/salesforce-developers/collection/689673309a230a0874d2fb86"><span style="font-weight: 400">Postman</span></a><span style="font-weight: 400"> or AI agents like </span><a href="https://claude.com/product/overview"><span style="font-weight: 400">Claude</span></a><span style="font-weight: 400"> interact with external tools and data sources from various vendors. In the context of Salesforce, it means that you can run SOQL queries, modify records, and execute actions directly from a third-party agent. In other words, you don’t have to log into Salesforce and you’re no longer limited to using Agentforce to interact with your data from agents.</span></p>
<p><span style="font-weight: 400">In order to perform operations on Salesforce with agents, you use MCP servers that expose tools and prompts. You can configure and host your own Salesforce MCP Server or use the </span><a href="https://developer.salesforce.com/docs/platform/hosted-mcp-servers/references/reference/servers-reference.html"><span style="font-weight: 400">Salesforce Hosted MCP Servers</span></a><span style="font-weight: 400"> that are </span><a href="https://developer.salesforce.com/blogs/2026/04/salesforce-hosted-mcp-servers-are-now-generally-available"><span style="font-weight: 400">now generally available</span></a><span style="font-weight: 400">. We&#8217;ll only focus on the latter in this post since this approach saves you from the burden of hosting, securing, and maintaining the server.</span></p>
<h3><span style="font-weight: 400">Standard Salesforce Hosted MCP Servers</span></h3>
<p><span style="font-weight: 400">In addition to the </span><a href="https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_mcp.htm"><span style="font-weight: 400">Salesforce DX MCP Server</span></a><span style="font-weight: 400"> used for development purposes, Salesforce offers a number of </span><a href="https://developer.salesforce.com/docs/platform/hosted-mcp-servers/references/reference/servers-reference.html"><span style="font-weight: 400">standard hosted MCP servers</span></a><span style="font-weight: 400"> out of the box for daily operations. These allow agents to access our Headless 360 portfolio ranging from the Salesforce Platform, Data 360, Tableau, and MuleSoft. You can also connect directly to the </span><a href="https://docs.slack.dev/ai/slack-mcp-server/connect-to-claude"><span style="font-weight: 400">Slack hosted MCP server</span></a><span style="font-weight: 400">.</span></p>
<h3><span style="font-weight: 400">Custom Salesforce Hosted MCP Servers</span></h3>
<p><span style="font-weight: 400">If the standard hosted MCP servers do not fit your needs, you can also create custom hosted MCP servers. This option gives you granular control over the tools and prompts that the server exposes, and lets you add custom tools and prompts.</span></p>
<p><span style="font-weight: 400">You can build </span><b>custom MCP tools</b><span style="font-weight: 400"> from the following types:</span></p>
<ul>
<li style="font-weight: 400"><b>Apex Action</b><span style="font-weight: 400">: Expose invocable </span><a href="https://developer.salesforce.com/docs/platform/hosted-mcp-servers/references/reference/invocable-actions.html"><span style="font-weight: 400">Apex Actions</span></a><span style="font-weight: 400"> (</span><span style="font-weight: 400"><code>@InvocableMethod</code> </span><span style="font-weight: 400">annotated Apex methods) as MCP tools</span></li>
<li style="font-weight: 400"><b>Lightning Flow</b><span style="font-weight: 400">s: Expose </span><a href="https://developer.salesforce.com/docs/platform/hosted-mcp-servers/references/reference/flows.html"><span style="font-weight: 400">autolaunched flows</span></a><span style="font-weight: 400"> as MCP tools</span></li>
<li style="font-weight: 400"><b>Apex REST</b><span style="font-weight: 400">: Expose </span><a href="https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_rest.htm"><span style="font-weight: 400">custom Apex REST endpoints</span></a><span style="font-weight: 400"> as MCP tools.</span></li>
<li style="font-weight: 400"><b>AuraEnabled</b><span style="font-weight: 400">: Expose </span><span style="font-weight: 400"><code>@AuraEnabled</code></span><span style="font-weight: 400"> (see </span><a href="https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_classes_annotation_AuraEnabled.htm"><span style="font-weight: 400">docs</span></a><span style="font-weight: 400">) annotated Apex methods as MCP tools</span></li>
<li style="font-weight: 400"><b>Named Query API</b><span style="font-weight: 400">: Expose </span><a href="https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_named_query_intro.htm"><span style="font-weight: 400">Named Query APIs</span></a><span style="font-weight: 400"> as MCP tools to provide access to specific parametrized SOQL queries</span></li>
</ul>
<p><span style="font-weight: 400">You can also create </span><b>custom MCP prompts</b><span style="font-weight: 400"> from </span><a href="https://developer.salesforce.com/docs/platform/hosted-mcp-servers/references/reference/prompt-builder.html"><span style="font-weight: 400">Flex prompt templates</span></a><span style="font-weight: 400"> built with Prompt Builder. Using MCP prompts in an agent lets you call those parametrized prompt templates as commands. For example, if you expose a “Generate Personalized Schedule” prompt template, you can run the following command in Claude (you don’t have to type the full tool name, autocomplete provides the prompt description).</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206377" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260522154910/Screenshot-of-Claude-Code-autocompleting-a-custom-Salesforce-MCP-prompt-command.png?w=853" class="postimages" width="853" height="94" alt="Screenshot of Claude Code autocompleting a custom Salesforce MCP prompt command." />
			  </span>
			</p>
<p><span style="font-weight: 400">Now that we’ve seen the benefits of working with the Salesforce Hosted MCP Servers, let’s look at how to configure Claude to leverage them.</span></p>
<h2><span style="font-weight: 400">Configure Claude to work with Salesforce Hosted MCP Servers</span></h2>
<p><span style="font-weight: 400">Configuring Claude to connect with the hosted MCP servers is a three-step process:</span></p>
<ol>
<li style="font-weight: 400"><span style="font-weight: 400">Activate Salesforce Hosted MCP Servers</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Create an external client app</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Connect Claude to the Salesforce Hosted MCP Servers</span></li>
</ol>
<p><b>Note:</b><span style="font-weight: 400"> Claude is available in different surfaces. In this post, we’ll cover Claude Code (the CLI experience) and Claude Desktop (the desktop app).</span></p>
<p><span style="font-weight: 400">As part of the setup, you&#8217;ll need to set up 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) so that Claude can authenticate via OAuth 2.0 with Salesforce to use the hosted MCP servers. An ECA tells Salesforce which external applications are allowed to connect to your org and what they&#8217;re allowed to do. Standard Salesforce user access permissions apply in addition to the app-level access checks.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206375" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260522154904/Schema-illustrating-how-the-Claude-MCP-client-authenticates-with-the-external-client-app-and-calls-Salesforce-Hosted-MCP-Servers-e1779490298827.png?w=1000" class="postimages" width="1000" height="485" alt="Schema illustrating how the Claude MCP client authenticates with the external client app and calls Salesforce Hosted MCP Servers" />
			  </span>
			</p>
<h3><span style="font-weight: 400">Step 1: Activate Salesforce Hosted MCP Servers</span></h3>
<p><span style="font-weight: 400">The standard Salesforce Hosted MCP Servers are inactive by default. Follow these steps to activate them:</span></p>
<ol>
<li style="font-weight: 400"><span style="font-weight: 400">In </span><b>Setup</b><span style="font-weight: 400">, search for </span><b>MCP Servers</b><span style="font-weight: 400">.</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Click the </span><b>Salesforce Servers</b><span style="font-weight: 400"> tab.</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Repeat the following steps for each server that you want to activate:</span>
<ol>
<li style="font-weight: 400"><span style="font-weight: 400">Click on the server name.</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Click </span><b>Activate</b><span style="font-weight: 400">.</span></li>
<li style="font-weight: 400">Copy the server&#8217;s <b style="color: #4a4a4a">API Name</b><span style="font-weight: 400"> without the <code>platform</code></span><span style="font-weight: 400">.</span><span style="font-weight: 400"> suffix and the </span><b style="color: #4a4a4a">Server URL</b><span style="font-weight: 400">. We&#8217;ll need those in the next step.</span></li>
</ol>
</li>
</ol>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206381" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260522154919/Screenshot-showing-a-list-of-Salesforce-Hosted-MCP-Servers-with-some-active-e1779490206908.png?w=1000" class="postimages" width="1000" height="496" alt="Screenshot showing a list of Salesforce Hosted MCP Servers with some active" />
			  </span>
			</p>
<h3><span style="font-weight: 400">Step 2: Create an external client app</span></h3>
<p><span style="font-weight: 400">Follow these steps to create an ECA:</span></p>
<ol>
<li style="font-weight: 400"><span style="font-weight: 400">In </span><b>Setup</b><span style="font-weight: 400">, search for </span><b>External Client App Manager</b><span style="font-weight: 400"> and click </span><b>New External Client App</b><span style="font-weight: 400">.</span></li>
<li style="font-weight: 400">Under <b style="color: #4a4a4a">Basic Information</b><span style="font-weight: 400"><span style="font-weight: 400">, fill in the required fields:<br />
</span></span></p>
<table>
<thead>
<tr>
<th><b>Field name</b></th>
<th><b>Field Value</b></th>
</tr>
</thead>
<tbody>
<tr>
<td><b>App Name</b></td>
<td><span style="font-weight: 400"><code>Claude MCP Client</code></span></td>
</tr>
<tr>
<td><b>API Name</b></td>
<td><span style="font-weight: 400"><code>Claude_MCP_Client</code></span><span style="font-weight: 400"> (auto-filled)</span></td>
</tr>
<tr>
<td><b>Contact Email</b></td>
<td><span style="font-weight: 400">Your email address</span></td>
</tr>
</tbody>
</table>
</li>
<li style="font-weight: 400"><span style="font-weight: 400">Expand the </span><b>API (Enable OAuth Settings)</b><span style="font-weight: 400"> section, check </span><b>Enable OAuth</b><span style="font-weight: 400"> and configure the following app settings:</span>
<ol>
<li style="font-weight: 400"><b>Callback URL</b><span style="font-weight: 400"> with either:</span>
<ul>
<li><code>https://claude.ai/api/mcp/auth_callback</code> for Claude Desktop</li>
<li><code>http://localhost:38000/callback</code> for Claude Code. Use this if you only have an Anthropic API key provisioned for you by your enterprise. Otherwise, we recommend setting up connectors in your <a href="http://Claude.ai">Claude.ai</a> account, then <a href="https://code.claude.com/docs/en/mcp#use-mcp-servers-from-claude-ai"><u>using them in Claude Code</u></a>.</li>
</ul>
</li>
<li style="font-weight: 400"><b>Selected OAuth scopes:</b>
<ul>
<li>Perform requests at any time (<code>refresh_token, offline_access</code>)</li>
<li>Access Salesforce Hosted MCP Servers (<code>mcp_api</code>)</li>
</ul>
</li>
</ol>
</li>
<li>Under <strong>Security</strong>, apply the following:
<ol>
<li>U<span style="font-weight: 400">ncheck these boxes:</span>
<ul>
<li>Require <span style="font-weight: 400">secret for Web Server Flow</span></li>
<li><span style="font-weight: 400">Require secret for Refresh Token Flow</span></li>
</ul>
</li>
<li>Check these boxes:
<ul>
<li>Require <span style="font-weight: 400">Proof Key for Code Exchange (PKCE) extension for Supported Authorization Flows</span></li>
<li>Issue <span style="font-weight: 400"><span style="font-weight: 400"><span style="font-weight: 400"><span style="font-weight: 400">JSON Web Token (JWT)-based access tokens for named users</span></span></span></span>&nbsp;
<p><span style="font-weight: 400">At this point, your ECA configuration should look like this:<br />
</span>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206380" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260522154917/Screenshot-of-the-ECA-configuration-e1779490271629.png?w=857" class="postimages" width="857" height="1000" alt="Screenshot of the ECA configuration" />
			  </span>
			</li>
</ul>
</li>
</ol>
</li>
<li>Click<span style="font-weight: 400"> </span><b>Create</b><span style="font-weight: 400"> to save the app.</span></li>
</ol>
<p><span style="font-weight: 400">Now that the app is created, you need to retrieve the connection information:</span></p>
<ol>
<li style="font-weight: 400"><span style="font-weight: 400">Navigate to the </span><b>Settings</b><span style="font-weight: 400"> tab and expand </span><b>OAuth Settings</b><span style="font-weight: 400">.</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Click </span><b>Consumer Key and Secret</b><span style="font-weight: 400">.</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Complete the authentication dialog with the verification code.</span></li>
<li style="font-weight: 400">Copy the <b style="color: #4a4a4a">Consumer Key</b><span style="font-weight: 400"> and </span><b style="color: #4a4a4a">Consumer Secret</b><span style="font-weight: 400">.</span></li>
</ol>
<h3><span style="font-weight: 400">Step 3: Connect Claude to the Salesforce Hosted MCP Servers</span></h3>
<p><span style="font-weight: 400">The last step of the integration process is to configure the Salesforce MCP Server connections in Claude.</span></p>
<p><span style="font-weight: 400">The instructions differ depending on whether you use Claude Code or Desktop (in particular the ECA callback URL). We&#8217;ll dive into how to configure Claude Code here, and you can find </span><a href="https://developer.salesforce.com/docs/platform/hosted-mcp-servers/guide/claude.html"><span style="font-weight: 400">instructions for Claude Desktop</span></a><span style="font-weight: 400"> in the docs.</span></p>
<h4><span style="font-weight: 400">Instructions for Claude Code</span></h4>
<ol>
<li style="font-weight: 400"><span style="font-weight: 400"><span style="font-weight: 400"><span style="font-weight: 400">Run the following command in a terminal where:<br />
&#8211; <code>MY_MCP_SERVER_NAME</code> is replaced by the name of the server that you&#8217;re connecting to. You&#8217;re free to enter any value, but we recommend that you stick to the following convention: <code>salesforce-</code> followed by the API name of the MCP server. For example: <code>salesforce-sobject-all</code> for the <code>sobject-all</code> server.<br />
&#8211;<code>MY_MCP_SERVER_URL</code> is replaced by the MCP server URL that you copied earlier. Pay attention to the fact that server URLs differ depending on whether you are connecting to a production or sandbox/scratch org.<br />
&#8211;<code>MY_ECA_CONSUMER_KEY</code> is replaced by the ECA consumer key you&#8217;ve obtained in the previous step.</span></span></span></p>
<pre language="shell">claude mcp add --transport http MY_MCP_SERVER_NAME MY_MCP_SERVER_URL --callback-port 38000 --client-id "MY_ECA_CONSUMER_KEY" --client-secret
</pre>
<p><span style="font-weight: 400"><span style="font-weight: 400">For example, to install the <code>sobject-all</code> MCP server from a scratch org, run:</span></span></p>
<pre language="shell">claude mcp add --transport http salesforce-sobject-all https://api.salesforce.com/platform/mcp/v1/sandbox/platform/sobject-all --callback-port 38000 --client-id "XXX" --client-secret
</pre>
</li>
<li style="font-weight: 400"><span style="font-weight: 400">When prompted to do so, paste the ECA consumer secret you&#8217;ve obtained previously.</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Launch Claude Code with the <code>claude</code></span><span style="font-weight: 400"> terminal command.</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Run </span><span style="font-weight: 400"><code>/mcp</code></span><span style="font-weight: 400">to list the MCP servers.</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Select your newly added MCP server from the list (it is annotated with a “needs authentication” message).</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Select </span><b>Authenticate</b><span style="font-weight: 400">.</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">When prompted, allow access to the MCP server.</span></li>
</ol>
<ul></ul>
<ol></ol>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206379" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260522154915/Screenshot-of-the-dialog-asking-to-grant-access-to-the-MCP-server.png?w=438" class="postimages" width="438" height="733" alt="Screenshot of the dialog asking to grant access to the MCP server" />
			  </span>
			</p>
<p><span style="font-weight: 400">Once you authorize the connection, Claude Code lists your MCP server as “connected.”</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206378" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260522154913/Screenshot-of-Claude-Code-showing-the-newly-added-MCP-server-in-the-connected-state-e1779490245662.png?w=1000" class="postimages" width="1000" height="317" alt="Screenshot of Claude Code showing the newly added MCP server in the “connected” state" />
			  </span>
			</p>
<h2><span style="font-weight: 400">Some practical examples of Claude working with Salesforce Hosted MCP Servers</span></h2>
<p><span style="font-weight: 400">Let’s assume that you work at </span><a href="https://github.com/trailheadapps/coral-cloud"><span style="font-weight: 400">Coral Cloud Resorts</span></a><span style="font-weight: 400">, a fictitious resort that offers tailored experience to their guests using Headless 360. You can use Claude and our MCP servers for a number of tasks.</span></p>
<h3><span style="font-weight: 400">Analyzing daily bookings</span></h3>
<p><span style="font-weight: 400">Let’s imagine that as the resort manager, you want to review customer bookings on a weekly basis.</span></p>
<p><span style="font-weight: 400">In traditional Salesforce operations, you would build a report and then generate a dashboard out of it. To access it, you would need tens of clicks in Lightning Experience.</span><span style="font-weight: 400"><br />
</span><span style="font-weight: 400"><br />
</span><span style="font-weight: 400">With Claude, all you have to do is use natural language, such as “Show me a report of daily bookings. Use the start date to group bookings,” to get the trend of your bookings with some additional insights that would require that you set up Tableau reports. You can also drill down by interacting further with Claude with follow up questions.</span></p>
<pre>❯ Show me a report of daily bookings. Use the start date to group bookings.                                                                                      

&#x23fa; I'll query the bookings grouped by start date now.
  
  Called salesforce-sobject-all (ctrl+o to expand)

&#x23fa; Let me find the correct booking object name in this org.

  Called salesforce-sobject-all 2 times (ctrl+o to expand)

&#x23fa; The date field is Date__c. Let me query the bookings grouped by that field.

  Called salesforce-sobject-all (ctrl+o to expand)

&#x23fa; Here's the daily bookings report:

  ┌────────────┬──────────┬──────────────┬───────────────┐
  │    Date    │ Bookings │ Total Guests │ Total Revenue │
  ├────────────┼──────────┼──────────────┼───────────────┤
  │ 2026-05-10 │ 347      │ 1,096        │ $144,918      │
  ├────────────┼──────────┼──────────────┼───────────────┤
  │ 2026-05-11 │ 316      │ 1,026        │ $132,874      │
  ├────────────┼──────────┼──────────────┼───────────────┤
  │ 2026-05-12 │ 325      │ 996          │ $131,169      │
  ├────────────┼──────────┼──────────────┼───────────────┤
  │ 2026-05-13 │ 347      │ 1,050        │ $135,670      │
  ├────────────┼──────────┼──────────────┼───────────────┤
  │ 2026-05-14 │ 285      │ 887          │ $106,410      │
  ├────────────┼──────────┼──────────────┼───────────────┤
  │ Total      │ 1,620    │ 5,055        │ $651,041      │
  └────────────┴──────────┴──────────────┴───────────────┘

  A 5-day window with fairly consistent volume (~315–347/day), except May 14 which is notably lower at 285 bookings and $106K revenue — about 18% below the average of the other days.
</pre>
<p><span style="font-weight: 400">Reading data is nice, but agents can take things further with MCP and take action.</span></p>
<h3><span style="font-weight: 400">Mass cancelling bookings using a custom server and a flow</span></h3>
<p><span style="font-weight: 400">Running a resort is no simple feat, and there can be unforeseen events (such as jellyfish invasions) that lead to booking cancellations. Should such unlikely scenarios occur, the resort staff is left scrambling to mass cancel bookings with hundreds of clicks, which is a repetitive and error-prone process.</span></p>
<p><span style="font-weight: 400">Instead, you can leverage Headless 360 and expose our existing Cancel Booking flow as a custom hosted MCP server.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206376" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260522154907/Screenshot-of-a-custom-hosted-MCP-server-exposing-a-Cancel-Booking-flow-e1779490220752.png?w=1000" class="postimages" width="1000" height="421" alt="Screenshot of a custom hosted MCP server exposing a Cancel Booking flow" />
			  </span>
			</p>
<p><b>Note:</b><span style="font-weight: 400"> in this basic example, you use a flow to cancel a single booking at a time. However, in production, we recommend creating a dedicated flow to process cancellations in bulk to reduce API calls and scale.</span></p>
<p><span style="font-weight: 400">Once the custom MCP server is added to Claude, you can run the cancellations with a prompt that combines multiple calls to our various MCP servers. Note how the prompt is structured to keep the human in the loop: you validate the data before running the cancellation batch.</span></p>
<pre>❯ Identify all bookings for tomorrow morning for the "Surf's Up Lesson" and "Tropical Snorkel Adventure" experience.            
  Let me verify the list then cancel them with the following cancellation reason: "Dear guest, we're sorry to cancel your aquatic experience due to a jellyfish invasion. You'll be refunded shortly."                                                  

&#x23fa; Let me query tomorrow's morning bookings for those two experiences.

  Called salesforce-sobject-all (ctrl+o to expand)

&#x23fa; Here are the 12 bookings found for tomorrow morning (May 13) — please verify before I proceed with cancellations:

  Surf's Up Lesson (9:00 AM – 10:00 AM) — 5 bookings

  ┌─────┬────────────┬───────────────────┬──────────────────────────────┬────────┐
  │  #  │  Booking   │       Guest       │            Email             │ Guests │
  ├─────┼────────────┼───────────────────┼──────────────────────────────┼────────┤
  │ 1   │ B-00001602 │ Ferne Ackwood     │ ferneackwood@example.com     │ 1      │
  ├─────┼────────────┼───────────────────┼──────────────────────────────┼────────┤
  │ 2   │ B-00001603 │ Deeyn Dumsday     │ deeyndumsday@example.com     │ 2      │
  ├─────┼────────────┼───────────────────┼──────────────────────────────┼────────┤
  │ 3   │ B-00001604 │ Alessandro Krates │ alessandrokrates@example.com │ 2      │
  ├─────┼────────────┼───────────────────┼──────────────────────────────┼────────┤
  │ 4   │ B-00001605 │ Quinlan Soppit    │ quinlansoppit@example.com    │ 2      │
  ├─────┼────────────┼───────────────────┼──────────────────────────────┼────────┤
  │ 5   │ B-00001606 │ Osborn Libbe      │ osbornlibbe@example.com      │ 3      │
  └─────┴────────────┴───────────────────┴──────────────────────────────┴────────┘

  Tropical Snorkel Adventure (9:00 AM – 1:00 PM) — 7 bookings

  ┌─────┬────────────┬────────────────────┬───────────────────────────────┬────────┐
  │  #  │  Booking   │       Guest        │             Email             │ Guests │
  ├─────┼────────────┼────────────────────┼───────────────────────────────┼────────┤
  │ 6   │ B-00000972 │ Gay Ragge          │ gayragge@example.com          │ 1      │
  ├─────┼────────────┼────────────────────┼───────────────────────────────┼────────┤
  │ 7   │ B-00000973 │ Baxie Donaher      │ baxiedonaher@example.com      │ 5      │
  ├─────┼────────────┼────────────────────┼───────────────────────────────┼────────┤
  │ 8   │ B-00000974 │ Munmro Semken      │ munmrosemken@example.com      │ 3      │
  ├─────┼────────────┼────────────────────┼───────────────────────────────┼────────┤
  │ 9   │ B-00000975 │ Ira Kemble         │ irakemble@example.com         │ 1      │
  ├─────┼────────────┼────────────────────┼───────────────────────────────┼────────┤
  │ 10  │ B-00000976 │ Suzie Patience     │ suziepatience@example.com     │ 3      │
  ├─────┼────────────┼────────────────────┼───────────────────────────────┼────────┤
  │ 11  │ B-00000977 │ Stephine Suttill   │ stephinesuttill@example.com   │ 1      │
  ├─────┼────────────┼────────────────────┼───────────────────────────────┼────────┤
  │ 12  │ B-00000978 │ Christyna Gianelli │ christynagianelli@example.com │ 1      │
  └─────┴────────────┴────────────────────┴───────────────────────────────┴────────┘

  Total: 12 bookings, 25 guests affected.

  All are currently Confirmed. Confirm and I'll cancel all 12 with the jellyfish invasion message.

✻ Cogitated for 18s

❯ confirmed                                                                                                                      

&#x23fa; Let me load the Cancel Booking flow tool and cancel all 12 bookings in parallel.

&#x23fa; Cancelling all 12 bookings in parallel now.

  Called salesforce-coral-cloud-flows 12 times (ctrl+o to expand)

&#x23fa; All 12 bookings cancelled successfully. Here's the summary:

  Surf's Up Lesson — 5 cancelled

  ┌────────────┬───────────────────┐
  │  Booking   │       Guest       │
  ├────────────┼───────────────────┤
  │ B-00001602 │ Ferne Ackwood     │
  ├────────────┼───────────────────┤
  │ B-00001603 │ Deeyn Dumsday     │
  ├────────────┼───────────────────┤
  │ B-00001604 │ Alessandro Krates │
  ├────────────┼───────────────────┤
  │ B-00001605 │ Quinlan Soppit    │
  ├────────────┼───────────────────┤
  │ B-00001606 │ Osborn Libbe      │
  └────────────┴───────────────────┘

  Tropical Snorkel Adventure — 7 cancelled

  ┌────────────┬────────────────────┐
  │  Booking   │       Guest        │
  ├────────────┼────────────────────┤
  │ B-00000972 │ Gay Ragge          │
  ├────────────┼────────────────────┤
  │ B-00000973 │ Baxie Donaher      │
  ├────────────┼────────────────────┤
  │ B-00000974 │ Munmro Semken      │
  ├────────────┼────────────────────┤
  │ B-00000975 │ Ira Kemble         │
  ├────────────┼────────────────────┤
  │ B-00000976 │ Suzie Patience     │
  ├────────────┼────────────────────┤
  │ B-00000977 │ Stephine Suttill   │
  ├────────────┼────────────────────┤
  │ B-00000978 │ Christyna Gianelli │
  └────────────┴────────────────────┘

  All 25 affected guests have been notified and will be refunded.
</pre>
<p><span style="font-weight: 400">These are two basic examples of what you can do with Claude and Salesforce Hosted MCP Servers, ranging from standard actions to custom scenarios.</span></p>
<h2><span style="font-weight: 400">Conclusion</span></h2>
<p><span style="font-weight: 400">You’ve now had a glimpse at how Salesforce Hosted MCP Servers let you use agents like Claude to interact with Headless 360 without requiring users to log into Lightning Experience. You’ve learned how to configure Claude Code and Claude Desktop to connect with Salesforce Hosted MCP Servers, and you saw some practical examples of what can be done with those servers.</span></p>
<p><span style="font-weight: 400">Now, it’s your turn to put Headless 360 to the test by building custom tools and configuring MCP servers to power your business.</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/references/reference/servers-reference.html"><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://code.claude.com/docs/en/mcp#installing-mcp-servers"><span style="font-weight: 400">Claude Code</span></a></li>
<li style="font-weight: 400"><span style="font-weight: 400">Postman collection: </span><a href="https://www.postman.com/salesforce-developers/salesforce-developers/collection/689673309a230a0874d2fb86"><span style="font-weight: 400">Salesforce Hosted MCP Servers</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>
<ol></ol>
<ol></ol>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/05/connect-claude-with-salesforce-hosted-mcp-servers">Connect Claude with 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/05/connect-claude-with-salesforce-hosted-mcp-servers/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
	<post-id xmlns="com-wordpress:feed-additions:1">206373</post-id><media:thumbnail url="https://d259t2jj6zp7qm.cloudfront.net/images/20260522152913/SingleHeadshot-1-e1779488980107.png?w=1000" />
<media:content url="https://d259t2jj6zp7qm.cloudfront.net/images/20260522152913/SingleHeadshot-1-e1779488980107.png?w=1000" medium="image" />
	</item>
		<item>
		<title>What Salesforce Headless 360 Means For Developers</title>
		<link>https://developer.salesforce.com/blogs/2026/05/headless-360-what-it-means-for-developers</link>
		<comments>https://developer.salesforce.com/blogs/2026/05/headless-360-what-it-means-for-developers#respond</comments>
		<pubDate>Thu, 21 May 2026 15:02:33 +0000</pubDate>
		<dc:creator><![CDATA[René Winkelmeyer]]></dc:creator>
				<category><![CDATA[Announcements]]></category>
		<category><![CDATA[APIs and Integrations]]></category>
		<category><![CDATA[App Development]]></category>
		<category><![CDATA[Architecture]]></category>
		<category><![CDATA[New Developments]]></category>
		<category><![CDATA[apis]]></category>
		<category><![CDATA[Coding agents]]></category>
		<category><![CDATA[Developers]]></category>
		<category><![CDATA[Governance]]></category>
		<category><![CDATA[Headless 360]]></category>
		<category><![CDATA[MCP tools]]></category>
		<category><![CDATA[Salesforce Platform]]></category>
		<category><![CDATA[System design]]></category>

		<guid isPermaLink="false">https://developer.salesforce.com/blogs/?p=206326</guid>
		<description><![CDATA[<p>Headless 360 exposes every Salesforce capability as an API, MCP tool, or CLI command. Learn why your platform expertise matters more in an agentic world.</p>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/05/headless-360-what-it-means-for-developers">What Salesforce Headless 360 Means For Developers</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">Salesforce Headless 360 makes every major Salesforce capability available as an API, Model Context Protocol (MCP) tool, or CLI command. MCP is a standardized protocol that lets AI models discover and call external tools, APIs, and data sources at runtime, so any compatible client can connect to any compatible server without custom integration code. This post covers what that means when you build on the platform, and why the fundamentals you&#8217;ve been learning matter more than ever.</span></p>
<p><span style="font-weight: 400">As the Salesforce Developer Advocacy team lead, I</span> <a href="https://developer.salesforce.com/blogs/2025/03/future-salesforce-developer-agentic-ai"><span style="font-weight: 400">recently posted on the future of the Salesforce Developer role</span></a><span style="font-weight: 400">. In that post, I argued that AI is reshaping our work around system design, quality engineering, and cross-system thinking. Headless 360 is the platform shift that turns those arguments into daily practice.</span></p>
<h2><span style="font-weight: 400">The platform provides context and capability</span></h2>
<p><span style="font-weight: 400">The LLM provides intelligence. The platform provides everything else: identity, access, capabilities, governance, and the context that makes intelligence useful. When an agent calls an MCP tool, it&#8217;s invoking accumulated state. Without it, the agent has nothing to reason over, no permissions to inherit, no business process to follow.</span></p>
<p><span style="font-weight: 400">This was always true. Every customer record, every workflow, every approval chain, every permission boundary your org has accumulated &#8211; that&#8217;s not overhead. That&#8217;s the business itself, encoded in a system that enforces it.</span></p>
<p><span style="font-weight: 400">That combination of intelligence from the model and everything else from the platform is what separates a prototype from a production system. It&#8217;s also what makes Headless 360 different from “just expose some APIs”.</span></p>
<h2><span style="font-weight: 400">Two kinds of MCP tools</span></h2>
<p><span style="font-weight: 400">When we talk about MCP tools in the Salesforce ecosystem, we mean two things:</span></p>
<ul>
<li style="font-weight: 400"><b>MCP tools for coding agents.</b><span style="font-weight: 400"> These enable developers to connect Claude Code, Cursor, or any MCP-compatible coding agent to their org and read metadata, generate Apex, run tests, or deploy through the pipeline. The consumer is a </span><i><span style="font-weight: 400">developer building software</span></i><span style="font-weight: 400">.</span></li>
<li style="font-weight: 400"><b style="color: #4a4a4a">MCP tools for business agents.</b><span style="font-weight: 400"> These enable </span><span style="font-weight: 400">users to access Salesforce capabilities from any MCP-compatible client: including Slack, ChatGPT, and Claude, among others. These MCP tools also allow agents to access Salesforce capabilities autonomously</span><span style="font-weight: 400">. The consumer is an </span><i style="color: #4a4a4a"><span>agent serving an end user</span></i><span style="font-weight: 400">.</span></li>
</ul>
<p><span style="font-weight: 400">Both kinds of MCP tools share the same open surface and the same trust layer. The difference is who&#8217;s calling and why. Headless 360 covers both, and both have implications for the expertise you&#8217;ve built.</span></p>
<p><span style="font-weight: 400">Headless 360 changes how the platform can be consumed. Coding agents can now reach into your org through MCP tools to build and deploy. Business agents can call the same platform to serve customers in Slack, ChatGPT, Claude, or any MCP-compatible client. The Salesforce browser-based UI is one surface among many.</span><i><span style="font-weight: 400">.</span></i></p>
<h2><span style="font-weight: 400">How software is built, delivered, and used is changing all at once</span></h2>
<p><span style="font-weight: 400">Agentic AI is changing three things simultaneously.</span></p>
<p><b>How software is written.</b><span style="font-weight: 400"> Agents are becoming the development team. The first draft of an Apex class, an LWC, a test suite, or an Agentforce agent arrives in seconds. Your job shifts from writing code to specifying intent, evaluating output, and deciding what fits your existing system design.</span></p>
<p><b>How quickly it is delivered.</b><span style="font-weight: 400"> Implementations compress from months to days. When an agent can execute against a development org, with human review gates before anything reaches production, the bottleneck moves from execution to clarity of intent.</span></p>
<p><b>How it is used.</b><span style="font-weight: 400"> Humans will use Salesforce from anywhere, not necessarily the Salesforce UI. A sales rep gets a customer summary in Slack. A support agent resolves a case in Teams. And increasingly, the consumer isn&#8217;t a human at all; it&#8217;s a business agent reasoning over your data. The destination isn&#8217;t a Lightning Experience tab. It&#8217;s wherever the work is already happening.</span></p>
<p><span style="font-weight: 400">These three changes are happening simultaneously, and they put pressure on the platform itself. </span><b>Headless 360 is the platform shift designed to address all three at once.</b></p>
<table>
<thead>
<tr>
<th><b>Dimension</b></th>
<th><b>Traditional Salesforce</b></th>
<th><b>Headless 360</b></th>
</tr>
</thead>
<tbody>
<tr>
<td><b>Who writes code</b></td>
<td><span style="font-weight: 400">Human developer, start to finish</span></td>
<td><span style="font-weight: 400">Human + coding agent via MCP tools; developer specifies intent and reviews output</span></td>
</tr>
<tr>
<td><b>Implementation timeline</b></td>
<td><span style="font-weight: 400">Weeks to months across multiple roles</span></td>
<td><span style="font-weight: 400">Days; agent executes against development org with human review gates</span></td>
</tr>
<tr>
<td><b>Primary consumer</b></td>
<td><span style="font-weight: 400">Human navigating Lightning UI</span></td>
<td><span style="font-weight: 400">Any authenticated caller &#8211; human, agent, app, or CLI</span></td>
</tr>
</tbody>
</table>
<p><span style="font-weight: 400">In addition to these three core dimensions, Headless 360 also changes how governance is enforced and how you think about channel strategy. When the UI is no longer the primary surface, you can&#8217;t rely on a mix of UI constraints and schema rules for governance; instead, full schema-level enforcement (via validation rules, your sharing model, and Apex) is required. And rather than building per-channel UIs (for Lightning, mobile, and community, for example), with Headless 360 you define a capability once and let it render wherever it&#8217;s called, including Slack, ChatGPT, mobile, or web. </span></p>
<h2><span style="font-weight: 400">What Headless 360 actually is</span></h2>
<p><span style="font-weight: 400">Headless 360 makes every major Salesforce capability openly composable by agents, not just by code paths a human developer wrote in advance. The full surface, including 60+ MCP tools, 30+ coding skills, 4,000+ existing APIs, and 220+ CLI commands, is accessible to any authenticated caller that your trust layer authorizes.</span></p>
<p><span style="font-weight: 400">This isn&#8217;t new territory for Salesforce developers. You&#8217;ve been building headless for years. This includes Experience Cloud sites with React frontends, Node.js apps over REST APIs, and mobile apps via the Mobile SDK. What&#8217;s new is that AI models can now dynamically discover, use, and compose capabilities at runtime, without custom integration code written in advance for each one. </span></p>
<p><span style="font-weight: 400">When we say “openly composable by agents” the </span><i><span style="font-weight: 400">openly</span></i><span style="font-weight: 400"> matters. Openness is the default posture. Governance is what makes it safe.</span></p>
<h2><span style="font-weight: 400">Headless 360 in practice</span></h2>
<p><span style="font-weight: 400">The capabilities Headless 360 unlocks aren&#8217;t abstract. Here&#8217;s what they look like in practice. </span></p>
<h3><span style="font-weight: 400">Build with any coding agent</span></h3>
<p><span style="font-weight: 400">The </span><a href="https://www.salesforce.com/news/stories/salesforce-headless-360-announcement/"><span style="font-weight: 400">60+ MCP tools and 30+ preconfigured coding skills</span></a><span style="font-weight: 400"> give any compatible coding agent live access to your org. </span><a href="https://www.salesforce.com/agentforce/developers/vibe-coding/"><span style="font-weight: 400">Agentforce Vibes 2.0</span></a><span style="font-weight: 400"> brings that natively inside Salesforce, but the MCP surface is the same regardless of which agent you choose.</span></p>
<p><span style="font-weight: 400">Here is a typical session: your coding agent connects to a scratch org via MCP, reads the object model, scaffolds an Apex service class with governor-limit-aware test coverage, and pushes it through your DevOps pipeline. Same deployment gates, same quality checks, same review process as code written by hand. The pipeline doesn&#8217;t care who initiated it. It cares whether the gates pass.</span></p>
<p><span style="font-weight: 400">The platform doesn&#8217;t privilege one agent over another. Your org&#8217;s capabilities are the constant. The tooling around it is your choice. For more on the MCP tools and how they work, see our post </span><a href="https://developer.salesforce.com/blogs/2025/06/introducing-mcp-support-across-salesforce"><span style="font-weight: 400">Introducing MCP Support Across Salesforce</span></a><span style="font-weight: 400">.</span></p>
<h3><span style="font-weight: 400">Render in any UI framework</span></h3>
<p><span style="font-weight: 400">Headless decouples capability from rendering. In practice, this means the platform no longer constrains your UI framework. A clear example of this is </span><a href="https://developer.salesforce.com/blogs/2026/04/build-with-react-run-on-salesforce-introducing-salesforce-multi-framework"><span style="font-weight: 400">multi-framework support</span></a><span style="font-weight: 400">.</span></p>
<p><span style="font-weight: 400">With multi-framework support, React developers can now build on Salesforce using the same patterns they already know, with no requirement to use LWC. A front-end engineer who&#8217;s never touched Salesforce can build a governed, data-connected application using familiar tools, because the platform adapts to their stack.</span></p>
<p><span style="font-weight: 400">That hiring constraint where you needed someone who knew LWC or Aura? It&#8217;s no longer a hard constraint. The platform meets developers where they already are.</span></p>
<h3><span style="font-weight: 400">Deploy on any surface</span></h3>
<p><span style="font-weight: 400">The </span><a href="https://www.salesforce.com/platform/orchestration-platform/"><span style="font-weight: 400">Headless Experience Layer</span></a><span style="font-weight: 400"> is a new runtime that decouples capability definitions from their rendering surface. You define a UI fragment and the Headless Experience Layer (HXL) handles rendering it as a Slack block, a mobile card, a ChatGPT response, or a voice interaction. Business logic, data, and permissions stay separated from the screen, so you define intent once and render natively everywhere, including Slack, mobile, ChatGPT, Claude, Teams, or any MCP-compatible client.</span></p>
<p><span style="font-weight: 400">This is where the two MCP lanes start to converge. The same openness that lets a coding agent build against your org will also let a business agent serve your customers from it. Today, the build-time surface is mature. The runtime surface already supports straightforward use cases, such as a support agent pulling a case summary inside a Slack thread. The broader vision is that the same capability can be delivered across surfaces such as voice interactions or partner mobile apps, and that ultimately capabilities can be made available on any surface.</span></p>
<h3><span style="font-weight: 400">Protect with built-in governance</span></h3>
<p><a href="https://www.salesforce.com/blog/headless-trust-model-agentic-architecture/"><span style="font-weight: 400">Headless doesn&#8217;t mean ungoverned</span></a><span style="font-weight: 400">. Governance is what makes it safe to build with any coding agent, render in any UI framework, and deploy on any surface. </span></p>
<p><span style="font-weight: 400">When a coding agent pushes an Apex class, it goes through the same validation, tests, and deployment gates as code written by a human. When an agent queries customer data through MCP, it hits the same sharing rules and field-level security as a user in Lightning.</span></p>
<p><span style="font-weight: 400">Four things are enforced on every API call, every MCP tool invocation, and every CLI command:</span></p>
<ul>
<li style="font-weight: 400"><b>Identity.</b><span style="font-weight: 400"> The agent acts as a specific user, not an anonymous caller. Permissions are scoped to that identity.</span></li>
<li style="font-weight: 400"><b>Access.</b><span style="font-weight: 400"> Sharing rules, field-level security, and permission sets enforce what the caller can see and do.</span></li>
<li style="font-weight: 400"><b>Invocation scope.</b><span style="font-weight: 400"> The agent can invoke only those tools and actions that have been explicitly exposed.</span></li>
<li style="font-weight: 400"><b>Governance.</b><span style="font-weight: 400"> Validation rules, triggers, approval chains, and governor limits all fire regardless of entry point.</span></li>
</ul>
<p><span style="font-weight: 400">The platform is open about </span><i><span style="font-weight: 400">what</span></i><span style="font-weight: 400"> can be accessed. It is not open about </span><i><span style="font-weight: 400">who</span></i><span style="font-weight: 400"> can access it, or </span><i><span style="font-weight: 400">how</span></i><span style="font-weight: 400">. That&#8217;s what makes it safe for </span><i><span style="font-weight: 400">openness</span></i><span style="font-weight: 400"> to be the default posture.</span></p>
<h2><span style="font-weight: 400">Why most agentic prototypes don&#8217;t reach production</span></h2>
<p><span style="font-weight: 400">If you&#8217;ve tried to ship an agent, you&#8217;ve likely encountered this problem. The demo works. The stakeholder asks a question that breaks it. For example:</span><i></i></p>
<ul>
<li style="font-weight: 400"><i><span style="font-weight: 400">&#8220;Will it respect the sharing rule on this object?&#8221;</span></i></li>
<li style="font-weight: 400"><i><span style="font-weight: 400">&#8220;What happens if the user doesn&#8217;t have permission to update that field?&#8221;</span></i></li>
<li style="font-weight: 400"><i><span style="font-weight: 400">&#8220;How does it know which approval policy applies?&#8221;</span></i></li>
</ul>
<p><span style="font-weight: 400">The LLM alone is not enough. The hard part is everything around it: business context, identity-aware permissions, policy enforcement, and the operational guarantees that make a system safe to run autonomously.</span></p>
<p><b>That gap is exactly what the Salesforce platform fills.</b><span style="font-weight: 400"> The context an agent needs didn&#8217;t show up because someone wrote a clever prompt. It accumulated over years, in your data model, your validation rules, your sharing architecture, your trigger framework. Headless 360 makes that platform accessible to agents through a governed surface. </span><b>You don&#8217;t rebuild context, permissions, or policy for your agents. They inherit it.</b></p>
<p><span style="font-weight: 400">Testing is part of this too. Testing agents is different from testing deterministic code. You&#8217;re evaluating whether an agent&#8217;s reasoning led to an acceptable outcome across a distribution of inputs. Session tracing, custom scoring evals, and the Agentforce Testing Center are how you build confidence that an agent does the right thing in production. The discipline is familiar if you&#8217;ve built test suites for complex Apex, but the techniques are new.</span></p>
<h2><span style="font-weight: 400">What comes next</span></h2>
<p><span style="font-weight: 400">Headless 360 ships with MCP tools for coding and business agents, coding skills, multi-framework support, and the Experience Layer. That&#8217;s its surface. What makes it work is everything underneath: the state, the governance, the accumulated business logic of a platform you already know.</span></p>
<p><span style="font-weight: 400">On the </span><a href="https://developer.salesforce.com/blogs"><span style="font-weight: 400">Salesforce Developer Blog</span></a><span style="font-weight: 400"> and </span><a href="https://www.youtube.com/@salesforcedevs"><span style="font-weight: 400">YouTube</span></a><span style="font-weight: 400">, we&#8217;re going deep on every building block, including skills for coding agents, multi-framework walkthroughs, trust and governance deep dives, testing and observability patterns, and more. Follow along and tell us where to go deeper.</span></p>
<p><span style="font-weight: 400">The platform is open. Build on it.</span></p>
<h2><span style="font-weight: 400">Related Reading</span></h2>
<ul>
<li style="font-weight: 400"><a href="https://developer.salesforce.com/blogs/2026/03/future-salesforce-developer-agentic-ai"><span style="font-weight: 400">The Future of the Salesforce Developer in the Agentic AI Era</span></a></li>
<li style="font-weight: 400"><a href="https://www.salesforce.com/news/stories/salesforce-headless-360-announcement/"><span style="font-weight: 400">Introducing Salesforce Headless 360</span></a></li>
<li style="font-weight: 400"><a href="https://www.salesforce.com/agentforce/developers/vibe-coding/"><span style="font-weight: 400">Agentforce Vibes 2.0</span></a></li>
<li style="font-weight: 400"><a href="https://www.salesforce.com/platform/orchestration-platform/"><span style="font-weight: 400">Headless Experience Layer</span></a></li>
<li style="font-weight: 400"><a href="https://www.salesforce.com/platform/multi-framework/"><span style="font-weight: 400">Salesforce Multi-Framework</span></a></li>
<li style="font-weight: 400"><a href="https://labs.agentforce.com/"><span style="font-weight: 400">Agentforce Labs</span></a></li>
<li style="font-weight: 400"><a href="https://github.com/forcedotcom/afv-library/tree/main/skills/developing-agentforce"><span style="font-weight: 400">Open Source Agentforce Skills</span></a></li>
</ul>
<h2><span style="font-weight: 400">About the Author</span></h2>
<p><b>Rene Winkelmeyer</b><span style="font-weight: 400"> leads the Developer Advocacy team at Salesforce. His team focuses on Agentforce, Data 360, and the Salesforce Platform. In his spare time, you might find him </span><a href="https://github.com/muenzpraeger"><span style="font-weight: 400">still coding on GitHub</span></a><span style="font-weight: 400">. Follow Rene on </span><a href="https://www.linkedin.com/in/muenzpraeger"><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/05/headless-360-what-it-means-for-developers">What Salesforce Headless 360 Means For Developers</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/05/headless-360-what-it-means-for-developers/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
	<post-id xmlns="com-wordpress:feed-additions:1">206326</post-id><media:thumbnail url="https://d259t2jj6zp7qm.cloudfront.net/images/20260521080801/1000x563-B-2.png?w=1000" />
<media:content url="https://d259t2jj6zp7qm.cloudfront.net/images/20260521080801/1000x563-B-2.png?w=1000" medium="image" />
	</item>
	</channel>
</rss>
