@salesforce/analytics-embedding-sdk
v2.1.0
Published
Use the Analytics Embedding SDK to embed Tableau Next analytical components in any web page. This SDK supports typescript, javascript and HTML formats. This version of the SDK is compatible with Salesforce API v65.0 and above.
Maintainers
Keywords
Readme
Analytics Embedding SDK
Use the Analytics Embedding SDK to embed Tableau Next analytical components in any web page. This SDK supports typescript, javascript and HTML formats. This version of the SDK is compatible with Salesforce API v65.0 and above.
Install
npm install @salesforce/analytics-embedding-sdk --saveUsage
Note: The orgUrl parameter must be the Lightning URL (e.g., https://yourorg.lightning.force.com), not the my.salesforce.com domain URL.
TypeScript
import {AnalyticsDashboard, initializeAnalyticsSdk, type AnalyticsSdkConfig} from '@salesforce/analytics-embedding-sdk';
const config: AnalyticsSdkConfig = {
authCredential: "<%- auth-credential %>",
orgUrl: "<%- org_url %>" // Must be Lightning URL
};
await initializeAnalyticsSdk(config);
// parentIdOrElement is the target container (ID or element) and idOrApiName is the identifier or API name of the component to embed.
const dashboard: AnalyticsDashboard = new AnalyticsDashboard({parentIdOrElement: 'embed-here', idOrApiName: 'My_Sales_Dashboard'});
dashboard.render();
JavaScript
import {initializeAnalyticsSdk, AnalyticsDashboard} from '@salesforce/analytics-embedding-sdk';
const config = {
authCredential: "<%- auth-credential %>",
orgUrl: "<%- org_url %>" // Must be Lightning URL
};
await initializeAnalyticsSdk(config);
// parentIdOrElement is the target container (ID or element) and idOrApiName is the identifier or API name of the component to embed.
const dashboard = new AnalyticsDashboard({parentIdOrElement: 'embed-here', idOrApiName: 'My_Sales_Dashboard'});
dashboard.render();
HTML
<!DOCTYPE html>
<html>
<head>
<script type="module">
import {initializeAnalyticsSdk} from '@salesforce/analytics-embedding-sdk';
const config = {
authCredential: "<%- auth-credential %>",
orgUrl: "<%- org_url %>" // Must be Lightning URL
};
await initializeAnalyticsSdk({
authCredential: "<%- auth-credential %>",
orgUrl: "<%- org_url %>", // Must be Lightning URL
});
</script>
</head>
<body>
<analytics-dashboard id-or-api-name="My_Sales_Dashboard" height="500px">
</analytics-dashboard>
</body>
</html>
AnalyticsDashboard: custom views and dashboard actions
Custom view ID (customViewId)
You can pass an optional custom view ID so the embedded dashboard opens with the
same saved filter state (custom view) as in Tableau Next. This aligns with share
links that include a customViewId query parameter, and helps preserve dashboard
interactivity when embedding in other surfaces (for example Slack).
TypeScript / JavaScript — set customViewId on the dashboard props (or assign
dashboard.customViewId after construction):
const dashboard = new AnalyticsDashboard({
parentIdOrElement: 'embed-here',
idOrApiName: 'My_Sales_Dashboard',
customViewId: 'f5f0e1234aabcde67890'
});
await dashboard.render();HTML — use the custom-view-id attribute on <analytics-dashboard>:
<analytics-dashboard
id-or-api-name="My_Sales_Dashboard"
custom-view-id="f5f0e1234aabcde67890"
height="500px"
></analytics-dashboard>If your app parses a dashboard URL, read the customViewId query parameter and pass it
through as shown above.
Dashboard button actions
Configure actions on the dashboard in Tableau Next using Salesforce Help: add actions to a dashboard.
Dashboard actions (buttons configured on the dashboard) are supported in third-party embedding. That includes actions such as Salesforce Flows, page navigation, and URL navigation, consistent with the embedded dashboard experience in Tableau Next.
Filter Simplification
The SDK provides two ways to apply filters: a simplified API that automatically constructs filter objects from individual parameters, and the original API that accepts pre-built filter objects.
Simplified applyFilters API (Recommended)
The simplified API eliminates manual filter object construction by accepting Field and DataSource objects directly. It automatically constructs fieldName from Field objects (retrieved via getFields()) and accepts DataSource objects (from getDataSources()).
TypeScript / JavaScript:
import {AnalyticsDashboard, FilterOperator, SimplifiedFilter} from '@salesforce/analytics-embedding-sdk';
const dashboard = new AnalyticsDashboard({parentIdOrElement: 'embed', idOrApiName: 'My_Dashboard'});
await dashboard.render();
// Get fields and data sources from the component
const fieldsMap = await dashboard.getFields();
const fields = fieldsMap.get('SalesData');
const accountField = fields.find((f) => f.apiName === 'Name' && f.objectName === 'Account');
const stageField = fields.find((f) => f.apiName === 'Stage' && f.objectName === 'Opportunity');
const dataSources = await dashboard.getDataSources();
const salesDataSource = dataSources.find((ds) => ds.apiName === 'SalesData');
// Apply filters using SimplifiedFilter array format
const filters: SimplifiedFilter[] = [
{
field: accountField, // Field object from getFields()
operator: FilterOperator.Equals, // FilterOperator enum
values: ['Acme Corp'], // Values as array
dataSource: salesDataSource // DataSource object from getDataSources() (for dashboards)
},
{
field: stageField,
operator: FilterOperator.In, // FilterOperator enum
values: ['Prospecting', 'Negotiation'],
dataSource: salesDataSource // Can reuse the same DataSource object
}
];
await dashboard.applyFilters(filters);For metrics and visualizations, omit the dataSource property:
import {AnalyticsMetric, FilterOperator, SimplifiedFilter} from '@salesforce/analytics-embedding-sdk';
const metric = new AnalyticsMetric({parentIdOrElement: 'embed', idOrApiName: 'My_Metric'});
await metric.render();
const fieldsMap = await metric.getFields();
const fields = Array.from(fieldsMap.values())[0]; // Single data source
const regionField = fields.find((f) => f.apiName === 'Region');
// No dataSource needed for metrics/visualizations
const filters: SimplifiedFilter[] = [
{
field: regionField,
operator: FilterOperator.Equals,
values: ['West']
}
];
await metric.applyFilters(filters);Original applyFilters API (Still Supported)
Use the SDK to build valid filters. The SDK provides metadata you can use to assemble a UnifiedFilterJson object. See the applyFilters method and the filters property in the component classes. Filter validation informs you what you can filter on, not the final JSON object. Follow these rules to combine datasources, fields, operators, and values into a filter that passes validation.
What Each Method Provides
Data Sources
Supported for dashboards only.
Methods: getDataSources(), getInteractionDetails().dataSources
Returns which semantic model a row belongs to. On dashboards with multiple data sources, set dataSource on each filter to the API name that matches the map key for that field. With a single data source, dataSource is optional in the type; setting it anyway avoids ambiguity.
Fields
Methods: getFields(), getInteractionDetails().fields — use filterFields from getInteractionDetails() when you only need columns that support filtering.
Each field includes dataType, fieldType, apiName, and optional objectName.
Filter Operators
Enum: FilterOperator — choose a value that is valid for the field’s data type, for example, range operators for numbers, relative-date operators for dates. The SDK returns dataType on each field, not a list of allowed operators. Mapping data type → operator, and any extra rules, is up to your app unless your product supplies a separate list.
Operators that don’t take operands, such as IsNull, should omit values or use an empty array as required. Others, for example relative date operators, may require specific values. Follow the operator’s contract.
Field Values
Method: getFilterFieldValues(fieldApiName, fieldObjectName?, searchTerm?)
Use for discrete lists or typeahead. Pass fieldObjectName when getFields() / getInteractionDetails() include objectName on that field; otherwise omit it.
How to Combine Method Values into One Valid Filter
- Wait until the component is ready — Call filter helpers after
render()resolves and the asset has loaded. For example afterEventName.COMPONENT_LOADEDorEventName.RECEIVED_DATASOURCESon dashboards when you rely on datasource lists. - Pick scope — Dashboard: choose a
dataSourceAPI name fromgetDataSources()orgetInteractionDetails().dataSources. It must match the map key for the field you use. Metric or visualization: there is a single implicit context; don’t setdataSourceon the filter object. - Pick a field — Use the field list for that data source’s key in the map for a dashboard. Iterate
getFields()/getInteractionDetails()maps for metrics and visualizations, which often have one key. BuildfieldNameasobjectName + "." + apiNamewhenobjectNameis present; otherwise use the field’sapiNamealone, consistent with how filters are shown in the embedding API examples. - Pick an operator — Must match the field’s data type and your product’s allowed set. Metric note:
EqualsandDoesNotEqualsare translated toInandNotInin current behavior. - Pick values — Use
getFilterFieldValueswhen the user needs actual members. All entries invaluesmust share the same type (all strings, all numbers, and so on). Omitvalueswhen the operator doesn’t need them. - Apply — Pass an array to
applyFilters. Dashboards and metrics replace the filter set. Visualizations add to existing filters. When applying filters repeatedly, plangetFilters()/clearFilters()accordingly.
Minimal dashboard example:
import {AnalyticsDashboard, FilterOperator} from '@salesforce/analytics-embedding-sdk';
const dashboard = new AnalyticsDashboard({parentIdOrElement: 'embed', idOrApiName: 'My_Dashboard'});
await dashboard.render();
const {dataSources, filterFields} = await dashboard.getInteractionDetails();
// Assumes at least one data source and a filterable Account.Name field on that source.
const ds = dataSources![0].apiName;
const field = filterFields.get(ds)!.find((f) => f.apiName === 'Name' && f.objectName === 'Account')!;
const filters = [
{
dataSource: ds,
fieldName: `${field.objectName}.${field.apiName}`,
operator: FilterOperator.Equals,
values: ['Acme Corp']
}
];
await dashboard.applyFilters(filters);For metrics and visualizations, the same fieldName / operator / values rules apply; omit dataSource.
Invalid or mismatched combinations, such as wrong data source for the field, bad fieldName shape, operator incompatible with type, or mixed-type values, can cause an ERROR event and the filter isn’t applied. Validate inputs before calling applyFilters.
AnalyticsAgent
The AnalyticsAgent component embeds your Analytics and Visualization agent powered by Agentforce. It helps users understand data through natural language insights, visualizations, and proactive alerts.
The agent supports two operating modes:
- Single-context mode — Provide
contextConfigto bind the agent to a specific dashboard, metric, or semantic model. - Multi-component mode — Omit
contextConfigto automatically track all embeddedAnalyticsDashboardandAnalyticsMetriccomponents on the page.
TypeScript
import {
AnalyticsAgent,
AgentContextType,
initializeAnalyticsSdk,
analyticsEventTarget,
EventName,
type AgentProps,
type AnalyticsSdkConfig
} from '@salesforce/analytics-embedding-sdk';
const config: AnalyticsSdkConfig = {
authCredential: '<%- auth-credential %>',
orgUrl: '<%- org_url %>' // Must be Lightning URL
};
await initializeAnalyticsSdk(config);
// Single-context mode: bind the agent to a specific dashboard
const agentProps: AgentProps = {
parentIdOrElement: 'agent-container',
idOrApiName: '<%- agent-id %>',
contextConfig: {
contextType: AgentContextType.DASHBOARD,
contextTypeIdOrApiName: 'My_Sales_Dashboard'
},
showHeader: true,
showHeaderActions: true,
agentName: 'Sales Insights',
welcomeText: 'Ask me anything about your sales data.'
};
const agent: AnalyticsAgent = new AnalyticsAgent(agentProps);
agent.render();JavaScript
import {initializeAnalyticsSdk, AnalyticsAgent, AgentContextType} from '@salesforce/analytics-embedding-sdk';
const config = {
authCredential: '<%- auth-credential %>',
orgUrl: '<%- org_url %>' // Must be Lightning URL
};
await initializeAnalyticsSdk(config);
// Multi-component mode: omit contextConfig to track all embedded components automatically
const agent = new AnalyticsAgent({
parentIdOrElement: 'agent-container',
idOrApiName: '<%- agent-id %>',
showHeader: true,
showHeaderActions: true,
agentName: 'Sales Insights',
welcomeText: 'Ask me anything about your sales data.'
});
agent.render();HTML
<!doctype html>
<html>
<head>
<script type="module">
import {initializeAnalyticsSdk} from '@salesforce/analytics-embedding-sdk';
await initializeAnalyticsSdk({
authCredential: '<%- auth-credential %>',
orgUrl: '<%- org_url %>' // Must be Lightning URL
});
</script>
</head>
<body>
<analytics-agent
id-or-api-name="<%- agent-id %>"
context-type="dashboard"
context-type-id-or-api-name="My_Sales_Dashboard"
show-header="true"
show-header-actions="true"
agent-name="Sales Insights"
welcome-text="Ask me anything about your sales data."
height="600px"
></analytics-agent>
</body>
</html>AgentContextType
The AgentContextType enum specifies the type of analytics asset to bind the agent to:
| Value | Description |
| ---------------------------- | ---------------------- |
| AgentContextType.DASHBOARD | Dashboard context |
| AgentContextType.METRIC | Metric context |
| AgentContextType.SDM | Semantic Model context |
Styling with AgentStyleTokens
Use the styleTokens property to theme the agent UI:
const agent = new AnalyticsAgent({
parentIdOrElement: 'agent-container',
idOrApiName: '<%- agent-id %>',
styleTokens: {
containerBackground: '#faf5ff',
headerBackground: '#ede9fe',
headerBlockTextColor: '#5b21b6',
messageBlockInboundBackgroundColor: '#ede9fe',
messageBlockOutboundBackgroundColor: '#7c3aed',
messageBlockOutboundTextColor: '#ffffff',
messageInputFooterSendButton: '#ec4899'
}
});
agent.render();Welcome image and agent avatar
Override the welcome-screen image and the avatar shown next to inbound (agent) messages with the optional welcomeImage and agentAvatar props. Each takes an image src and an optional alt string for screen readers.
const agent = new AnalyticsAgent({
parentIdOrElement: 'agent-container',
idOrApiName: '<%- agent-id %>',
welcomeImage: {src: 'https://cdn.example.com/img/welcome.png', alt: 'Welcome to Sales Insights'},
agentAvatar: {src: 'https://cdn.example.com/img/agent-avatar.png', alt: 'Sales Insights agent'}
});
agent.render();When embedding via the <analytics-agent> HTML element, pass each override as a JSON-encoded welcome-image / agent-avatar attribute:
<analytics-agent
id-or-api-name="<%- agent-id %>"
welcome-image='{"src":"https://cdn.example.com/img/welcome.png","alt":"Welcome to Sales Insights"}'
agent-avatar='{"src":"https://cdn.example.com/img/agent-avatar.png","alt":"Sales Insights agent"}'
></analytics-agent>| Property | Type | Description |
| -------------- | -------------- | -------------------------------------------- |
| welcomeImage | WelcomeImage | Image shown on the agent welcome screen. |
| agentAvatar | AgentAvatar | Avatar shown next to inbound agent messages. |
Each src must be an https:// URL (with no embedded credentials) or a root-relative path under /img/; any other value is ignored and the default image is used.
The image host must also be trusted by your org, or the browser blocks the image and the default is shown. To trust the host:
- In Setup, go to Security > Trusted URLs (historically labeled CSP Trusted Sites).
- Click New Trusted URL.
- In URL, enter the image host (for example,
https://cdn.example.com). - Under CSP Directives, enable img-src.
- Click Save.
Restarting the Agent Session
Call startNewAgentSession() to programmatically restart the conversation:
await agent.startNewAgentSession();Multi-org Support
The SDK supports embedding components from multiple Salesforce orgs in a single application. Use orgConfigs instead of a single orgUrl and authCredential:
import {
initializeAnalyticsSdk,
AnalyticsDashboard,
AnalyticsVisualization,
AnalyticsAgent,
AgentContextType
} from '@salesforce/analytics-embedding-sdk';
// Initialize with multiple orgs
const initPayload = {
orgConfigs: [
{
orgUrl: 'https://org1.lightning.force.com', // Lightning URL required
authCredential: 'https://org1-frontdoor.salesforce.com/...'
},
{
orgUrl: 'https://org2.lightning.force.com', // Lightning URL required
authCredential: 'https://org2-frontdoor.salesforce.com/...'
}
]
};
const response = await initializeAnalyticsSdk(initPayload);
// Embed components from different orgs - always specify orgUrl
const dashboard = new AnalyticsDashboard({
parentIdOrElement: 'container1',
idOrApiName: 'Dashboard1',
orgUrl: 'https://org1.lightning.force.com' // Required for multi-org
});
dashboard.render();
const visualization = new AnalyticsVisualization({
parentIdOrElement: 'container2',
idOrApiName: 'Viz2',
orgUrl: 'https://org2.lightning.force.com' // Different org
});
visualization.render();
const agent = new AnalyticsAgent({
parentIdOrElement: 'container3',
idOrApiName: 'Agent1',
contextConfig: {
contextType: AgentContextType.DASHBOARD,
contextTypeIdOrApiName: 'Dashboard1'
},
orgUrl: 'https://org1.lightning.force.com' // Required for multi-org
});
agent.render();Note:
- The
orgUrlis required when creating components in a multi-org scenario, to ensure your component connects to the correct org.
Adding Orgs Dynamically
You can add new orgs or retry failed orgs after initial SDK initialization using retryOrAddOrgs:
import {retryOrAddOrgs} from '@salesforce/analytics-embedding-sdk';
const newOrgs = [
{
orgUrl: 'https://org3.lightning.force.com', // Lightning URL required
authCredential: 'https://org3-frontdoor.salesforce.com/...'
},
{
orgUrl: 'https://org4.lightning.force.com', // Lightning URL required
authCredential: 'https://org4-frontdoor.salesforce.com/...'
}
];
const response = await retryOrAddOrgs(newOrgs);
console.log(response.status); // Check if orgs were added successfullyThe retryOrAddOrgs function returns the same BootstrapResponse format as initializeAnalyticsSdk.
Response
The initializeAnalyticsSdk function returns a BootstrapResponse object:
const response = await initializeAnalyticsSdk(config);
// Response structure:
{
"message": "Sdk Initialize Complete",
"status": "Success",
"orgStates": {
"https://org1.lightning.force.com": {
"state": "INITIALIZATION_SUCCESS",
"reason": ""
},
"https://org2.lightning.force.com": {
"state": "INITIALIZATION_SUCCESS",
"reason": ""
}
}
}
console.log(response.status); // Check initialization status
console.log(response.message); // Get detailed message
// For multi-org scenarios, check individual org states:
if (response.orgStates) {
response.orgStates.forEach((state, orgUrl) => {
console.log(`Org ${orgUrl}: ${state.state}`);
});
}Sample response:
{
"message": "Sdk Initialize Complete",
"status": "Success",
"orgStates": {
"https://org1.lightning.force.com": {
"state": "INITIALIZATION_SUCCESS",
"reason": ""
},
"https://org2.lightning.force.com": {
"state": "INITIALIZATION_SUCCESS",
"reason": ""
}
}
}Status values:
Status.SUCCESS- All orgs initialized successfullyStatus.PARTIAL_SUCCESS- Some orgs initialized successfully (multi-org only)Status.FAILURE- Initialization failed
MFA and password reset (auth redirect)
For a single org or multiple orgs, Salesforce may require an extra step before the session is valid—such as multi-factor authentication (MFA) or a forced password reset. In that case initializeAnalyticsSdk can return a non-success overall status while the affected org appears in response.orgStates with:
state:AUTH_REDIRECT(seeOrgStates.AUTH_REDIRECTwhen importing the enum)redirectUrl: org-provided path (often relative)redirectOrigin: origin to combine withredirectUrl
What to do
- For each org entry in
AUTH_REDIRECT, send the user to the challenge UI, typically by opening${redirectOrigin}${redirectUrl}in a new window or tab (popups may be blocked; fall back to a full tab). - After the user finishes MFA or password reset and the browser session is established for that org, resume the SDK by calling
retryOrAddOrgsfor that org.
Credentials when retrying
- Normal retry: pass a fresh frontdoor URL (or equivalent session credential) as
authCredentialtogether with the same LightningorgUrl. - After MFA / password reset: once the session exists in the browser, you can retry with the Lightning org URL as the credential:
{ orgUrl, authCredential: orgUrl }, without generating a new frontdoor URL first. If that retry does not succeed, obtain a new frontdoor URL and retry with it.
The retryOrAddOrgs response uses the same BootstrapResponse shape as initializeAnalyticsSdk, so you can inspect orgStates again for any remaining AUTH_REDIRECT or errors.
Session management
The SDK exposes per-org APIs for checking and extending an initialized Salesforce session. Calls for different orgs are independent, while concurrent calls for the same org are coalesced by Lightning Out.
import {getSessionExpiry, reAuthenticateSession, refreshSession} from '@salesforce/analytics-embedding-sdk';
const orgUrl = 'https://org1.lightning.force.com';
async function assignFreshFrontdoorUrl() {
const freshFrontdoorUrl = await fetchFreshFrontdoorUrl(orgUrl);
await reAuthenticateSession(orgUrl, freshFrontdoorUrl);
}
async function ensureSession() {
let ttl;
try {
ttl = await getSessionExpiry(orgUrl); // Remaining lifetime in seconds
} catch {
await assignFreshFrontdoorUrl();
return;
}
if (ttl === 0) {
await assignFreshFrontdoorUrl();
} else if (ttl < 600) {
try {
await refreshSession(orgUrl);
} catch {
await assignFreshFrontdoorUrl();
}
}
}
await ensureSession();getSessionExpiry(orgUrl)resolves with the remaining session lifetime in seconds.refreshSession(orgUrl)extends an active session without recreating embedded components.reAuthenticateSession(orgUrl, authCredential)assigns a fresh frontdoor URL to the existing Lightning Out application when the session has expired or cannot be refreshed. Its Promise resolves after Lightning Out reloads the authentication iframe and rejects if Lightning Out reports an error or requires an additional authentication redirect.retryOrAddOrgs()remains the API for adding an org or retrying an org that did not initialize. Do not use it to renew an already initialized org session.- The SDK does not run a background timer or silently extend sessions. The host application decides when to check and refresh based on its own activity and security policy.
- Concurrent
getSessionExpiry()calls share Lightning Out's in-flight expiry Promise, and concurrentrefreshSession()calls share its in-flight refresh Promise. Frontdoor reauthentication exposes Lightning Out's completion event through its returned Promise. - Session refresh and reauthentication cannot overlap for the same org. A conflicting call rejects with
SESSION_REFRESH_IN_PROGRESSorSESSION_REAUTHENTICATION_IN_PROGRESS; callers can retry after the active Promise settles. - Lightning Out application errors reject the active session-operation Promise and are also dispatched through the global
analyticsEventTargetasSESSION_ERRORevents. Other terminal outcomes such as timeout, logout, redirect, and credential reuse reject the Promise without dispatching a second global error event. - Lightning Out applies a 30-second timeout to expiry checks and a 60-second timeout to active-session refreshes. Frontdoor reauthentication also has a 60-second SDK timeout and rejects with
SESSION_REAUTHENTICATION_TIMEOUTif it expires orSESSION_REAUTHENTICATION_ABORTEDif the org logs out before completion. - Every reauthentication attempt requires a newly generated frontdoor URL. After a failed attempt, do not retry the previously assigned credential; obtain a fresh URL before calling
reAuthenticateSession()again. - If frontdoor reauthentication requires an additional authentication challenge, the Promise rejects immediately with
SESSION_REAUTHENTICATION_REDIRECT_REQUIRED. The error includes Lightning Out'sredirectUrlandredirectOriginwhen provided.
Logout
The SDK provides a logout function to log out from Salesforce orgs. The function returns a LogoutResponse with the same structure as BootstrapResponse.
import {logout} from '@salesforce/analytics-embedding-sdk';
// Logout from all orgs
const response = await logout();
// Logout from specific orgs
const response = await logout(['https://org1.lightning.force.com', 'https://org2.lightning.force.com']);
console.log(response.status); // 'Success', 'Partial Success', or 'Failure'
console.log(response.message); // Detailed logout messageSample response:
{
"message": "Log out for all orgs complete",
"status": "Success",
"orgStates": {
"https://org1.lightning.force.com": {
"state": "LOGGED_OUT",
"reason": ""
},
"https://org2.lightning.force.com": {
"state": "LOGGED_OUT",
"reason": ""
}
}
}Need Help
- Visit https://developer.salesforce.com/docs/analytics/sdk/guide/sdk-overview.html
- AI-assisted onboarding skill (for AI coding assistants): https://unpkg.com/@salesforce/analytics-embedding-sdk/SKILL.md
Supported Desktop and Laptop Browsers
The SDK supports all browsers supported in Salesforce Lightning Experience.
